[OLINGO-337] improve AcceptType class to allow '*'

This commit is contained in:
Stephan Klevenz 2014-07-03 12:54:07 +02:00
parent 434246abe9
commit f4640911af
6 changed files with 162 additions and 85 deletions

View File

@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,7 +42,6 @@ public class PingITCase {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty(HttpHeader.ACCEPT, "application/json");
connection.connect();
int code = connection.getResponseCode();
@ -59,7 +57,6 @@ public class PingITCase {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty(HttpHeader.ACCEPT, "application/json");
connection.connect();
int code = connection.getResponseCode();

View File

@ -46,11 +46,8 @@ import java.util.regex.Pattern;
*/
public class AcceptType {
private static final String MEDIA_TYPE_WILDCARD = "*";
private static final String PARAMETER_Q = "q";
private static final Pattern Q_PARAMETER_VALUE_PATTERN = Pattern.compile("1|0|1\\.0{1,3}|0\\.\\d{1,3}");
public static final AcceptType WILDCARD = create(MEDIA_TYPE_WILDCARD, MEDIA_TYPE_WILDCARD, createParameterMap(), 1F);
public static final AcceptType WILDCARD = create(TypeUtil.MEDIA_TYPE_WILDCARD, TypeUtil.MEDIA_TYPE_WILDCARD,
createParameterMap(), 1F);
private final String type;
private final String subtype;
@ -81,25 +78,51 @@ public class AcceptType {
}
List<String> typeSubtype = new ArrayList<String>();
parameters = createParameterMap();
ContentType.parse(type, typeSubtype, parameters);
parse(type, typeSubtype, parameters);
this.type = typeSubtype.get(0);
subtype = typeSubtype.get(1);
if (MEDIA_TYPE_WILDCARD.equals(this.type) && !MEDIA_TYPE_WILDCARD.equals(subtype)) {
if (TypeUtil.MEDIA_TYPE_WILDCARD.equals(this.type) && !TypeUtil.MEDIA_TYPE_WILDCARD.equals(subtype)) {
throw new IllegalArgumentException("Illegal combination of WILDCARD type with NONE WILDCARD subtype.");
}
final String q = parameters.get(PARAMETER_Q);
final String q = parameters.get(TypeUtil.PARAMETER_Q);
if (q == null) {
quality = 1F;
} else {
if (Q_PARAMETER_VALUE_PATTERN.matcher(q).matches()) {
try {
quality = Float.valueOf(q);
} else {
throw new IllegalArgumentException("Illegal quality parameter.");
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Illegal quality parameter.", e);
}
parameters.remove(PARAMETER_Q);
}
}
private static void
parse(final String format, final List<String> typeSubtype, final Map<String, String> parameters) {
final String[] typesAndParameters = format.split(TypeUtil.PARAMETER_SEPARATOR, 2);
final String types = typesAndParameters[0];
final String params = (typesAndParameters.length > 1 ? typesAndParameters[1] : null);
String[] tokens = types.split(TypeUtil.TYPE_SUBTYPE_SEPARATOR);
if (tokens.length == 1) {
typeSubtype.add(tokens[0]);
typeSubtype.add(TypeUtil.MEDIA_TYPE_WILDCARD);
} else if (tokens.length == 2) {
if (tokens[0] == null || tokens[0].isEmpty()) {
throw new IllegalArgumentException("No type found in format '" + format + "'.");
} else if (tokens[1] == null || tokens[1].isEmpty()) {
throw new IllegalArgumentException("No subtype found in format '" + format + "'.");
} else {
typeSubtype.add(tokens[0]);
typeSubtype.add(tokens[1]);
}
} else {
throw new IllegalArgumentException("Too many '" + TypeUtil.TYPE_SUBTYPE_SEPARATOR + "' in format '" + format
+ "'.");
}
TypeUtil.parseParameters(params, parameters);
}
/**
* Creates an accept type.
* @param type
@ -172,7 +195,7 @@ public class AcceptType {
result.append(';').append(key).append('=').append(parameters.get(key));
}
if (quality < 1F) {
result.append(';').append(PARAMETER_Q).append('=').append(quality);
result.append(';').append(TypeUtil.PARAMETER_Q).append('=').append(quality);
}
return result.toString();
}
@ -189,13 +212,13 @@ public class AcceptType {
* @return whether this accept type matches the given content type
*/
public boolean matches(final ContentType contentType) {
if (type.equals(MEDIA_TYPE_WILDCARD)) {
if (type.equals(TypeUtil.MEDIA_TYPE_WILDCARD)) {
return true;
}
if (!type.equalsIgnoreCase(contentType.getType())) {
return false;
}
if (subtype.equals(MEDIA_TYPE_WILDCARD)) {
if (subtype.equals(TypeUtil.MEDIA_TYPE_WILDCARD)) {
return true;
}
if (!subtype.equalsIgnoreCase(contentType.getSubtype())) {
@ -246,13 +269,13 @@ public class AcceptType {
if (compare != 0) {
return compare;
}
compare = (a1.getType().equals(MEDIA_TYPE_WILDCARD) ? 1 : 0)
- (a2.getType().equals(MEDIA_TYPE_WILDCARD) ? 1 : 0);
compare = (a1.getType().equals(TypeUtil.MEDIA_TYPE_WILDCARD) ? 1 : 0)
- (a2.getType().equals(TypeUtil.MEDIA_TYPE_WILDCARD) ? 1 : 0);
if (compare != 0) {
return compare;
}
compare = (a1.getSubtype().equals(MEDIA_TYPE_WILDCARD) ? 1 : 0)
- (a2.getSubtype().equals(MEDIA_TYPE_WILDCARD) ? 1 : 0);
compare = (a1.getSubtype().equals(TypeUtil.MEDIA_TYPE_WILDCARD) ? 1 : 0)
- (a2.getSubtype().equals(TypeUtil.MEDIA_TYPE_WILDCARD) ? 1 : 0);
if (compare != 0) {
return compare;
}

View File

@ -24,7 +24,6 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
@ -46,36 +45,28 @@ import java.util.TreeMap;
*/
public class ContentType {
private static final char WHITESPACE_CHAR = ' ';
private static final String PARAMETER_SEPARATOR = ";";
private static final String PARAMETER_KEY_VALUE_SEPARATOR = "=";
private static final String TYPE_SUBTYPE_SEPARATOR = "/";
public static final String PARAMETER_TYPE = "type";
public static final String PARAMETER_CHARSET = "charset";
public static final String CHARSET_UTF_8 = "UTF-8";
public static final ContentType APPLICATION_XML = create("application", "xml");
public static final ContentType APPLICATION_XML_CS_UTF_8 = create(APPLICATION_XML, PARAMETER_CHARSET,
CHARSET_UTF_8);
public static final ContentType APPLICATION_XML_CS_UTF_8 = create(APPLICATION_XML, TypeUtil.PARAMETER_CHARSET,
TypeUtil.CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_XML = create("application", "atom+xml");
public static final ContentType APPLICATION_ATOM_XML_CS_UTF_8 = create(APPLICATION_ATOM_XML,
PARAMETER_CHARSET, CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_XML_ENTRY = create(APPLICATION_ATOM_XML, PARAMETER_TYPE, "entry");
TypeUtil.PARAMETER_CHARSET, TypeUtil.CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_XML_ENTRY = create(APPLICATION_ATOM_XML,TypeUtil. PARAMETER_TYPE, "entry");
public static final ContentType APPLICATION_ATOM_XML_ENTRY_CS_UTF_8 = create(APPLICATION_ATOM_XML_ENTRY,
PARAMETER_CHARSET, CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_XML_FEED = create(APPLICATION_ATOM_XML, PARAMETER_TYPE, "feed");
TypeUtil. PARAMETER_CHARSET, TypeUtil.CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_XML_FEED = create(APPLICATION_ATOM_XML,TypeUtil. PARAMETER_TYPE, "feed");
public static final ContentType APPLICATION_ATOM_XML_FEED_CS_UTF_8 = create(APPLICATION_ATOM_XML_FEED,
PARAMETER_CHARSET, CHARSET_UTF_8);
TypeUtil. PARAMETER_CHARSET,TypeUtil.CHARSET_UTF_8);
public static final ContentType APPLICATION_ATOM_SVC = create("application", "atomsvc+xml");
public static final ContentType APPLICATION_ATOM_SVC_CS_UTF_8 = create(APPLICATION_ATOM_SVC,
PARAMETER_CHARSET, CHARSET_UTF_8);
TypeUtil. PARAMETER_CHARSET, TypeUtil.CHARSET_UTF_8);
public static final ContentType APPLICATION_JSON = create("application", "json");
public static final ContentType APPLICATION_JSON_CS_UTF_8 = create(APPLICATION_JSON,
PARAMETER_CHARSET, CHARSET_UTF_8);
TypeUtil. PARAMETER_CHARSET,TypeUtil. CHARSET_UTF_8);
public static final ContentType APPLICATION_OCTET_STREAM = create("application", "octet-stream");
public static final ContentType TEXT_PLAIN = create("text", "plain");
public static final ContentType TEXT_PLAIN_CS_UTF_8 = create(TEXT_PLAIN, PARAMETER_CHARSET, CHARSET_UTF_8);
public static final ContentType TEXT_PLAIN_CS_UTF_8 = create(TEXT_PLAIN, TypeUtil.PARAMETER_CHARSET,TypeUtil. CHARSET_UTF_8);
public static final ContentType MULTIPART_MIXED = create("multipart", "mixed");
public static final ContentType APPLICATION_XHTML_XML = create("application", "xhtml+xml");
@ -118,7 +109,7 @@ public class ContentType {
}
int len = type.length();
for (int i = 0; i < len; i++) {
if (type.charAt(i) == WHITESPACE_CHAR) {
if (type.charAt(i) == TypeUtil.WHITESPACE_CHAR) {
throw new IllegalArgumentException("Illegal whitespace found for type '" + type + "'.");
}
}
@ -196,14 +187,14 @@ public class ContentType {
}
}
protected static void
private static void
parse(final String format, final List<String> typeSubtype, final Map<String, String> parameters) {
final String[] typesAndParameters = format.split(PARAMETER_SEPARATOR, 2);
final String[] typesAndParameters = format.split(TypeUtil.PARAMETER_SEPARATOR, 2);
final String types = typesAndParameters[0];
final String params = (typesAndParameters.length > 1 ? typesAndParameters[1] : null);
if (types.contains(TYPE_SUBTYPE_SEPARATOR)) {
String[] tokens = types.split(TYPE_SUBTYPE_SEPARATOR);
if (types.contains(TypeUtil.TYPE_SUBTYPE_SEPARATOR)) {
String[] tokens = types.split(TypeUtil.TYPE_SUBTYPE_SEPARATOR);
if (tokens.length == 2) {
if (tokens[0] == null || tokens[0].isEmpty()) {
throw new IllegalArgumentException("No type found in format '" + format + "'.");
@ -214,45 +205,14 @@ public class ContentType {
typeSubtype.add(tokens[1]);
}
} else {
throw new IllegalArgumentException("Too many '" + TYPE_SUBTYPE_SEPARATOR + "' in format '" + format + "'.");
throw new IllegalArgumentException("Too many '" +TypeUtil.TYPE_SUBTYPE_SEPARATOR + "' in format '" + format + "'.");
}
} else {
throw new IllegalArgumentException("No separator '" + TYPE_SUBTYPE_SEPARATOR
throw new IllegalArgumentException("No separator '" +TypeUtil.TYPE_SUBTYPE_SEPARATOR
+ "' was found in format '" + format + "'.");
}
parseParameters(params, parameters);
}
/**
* Valid input are <code>;</code> separated <code>key=value</code> pairs
* without spaces between key and value.
* <p>
* See RFC 7231:
* The type, subtype, and parameter name tokens are case-insensitive.
* Parameter values might or might not be case-sensitive, depending on
* the semantics of the parameter name. The presence or absence of a
* parameter might be significant to the processing of a media-type,
* depending on its definition within the media type registry.
* </p>
*
* @param parameters
* @param parameterMap
*/
private static void parseParameters(final String parameters, final Map<String, String> parameterMap) {
if (parameters != null) {
String[] splittedParameters = parameters.split(PARAMETER_SEPARATOR);
for (String parameter : splittedParameters) {
String[] keyValue = parameter.split(PARAMETER_KEY_VALUE_SEPARATOR);
String key = keyValue[0].trim().toLowerCase(Locale.ENGLISH);
String value = keyValue.length > 1 ? keyValue[1] : null;
if (value != null && Character.isWhitespace(value.charAt(0))) {
throw new IllegalArgumentException(
"Value of parameter '" + key + "' starts with whitespace ('" + parameters + "').");
}
parameterMap.put(key, value);
}
}
TypeUtil.parseParameters(params, parameters);
}
public String getType() {
@ -406,7 +366,7 @@ public class ContentType {
public String toContentTypeString() {
StringBuilder sb = new StringBuilder();
sb.append(type).append(TYPE_SUBTYPE_SEPARATOR).append(subtype);
sb.append(type).append(TypeUtil.TYPE_SUBTYPE_SEPARATOR).append(subtype);
for (String key : parameters.keySet()) {
sb.append(";").append(key).append("=").append(parameters.get(key));

View File

@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.format;
import java.util.Locale;
import java.util.Map;
class TypeUtil {
static final String MEDIA_TYPE_WILDCARD = "*";
static final String PARAMETER_Q = "q";
static final char WHITESPACE_CHAR = ' ';
static final String PARAMETER_SEPARATOR = ";";
static final String PARAMETER_KEY_VALUE_SEPARATOR = "=";
static final String TYPE_SUBTYPE_SEPARATOR = "/";
static final String TYPE_SUBTYPE_WILDCARD = "*";
static final String PARAMETER_TYPE = "type";
static final String PARAMETER_CHARSET = "charset";
static final String CHARSET_UTF_8 = "UTF-8";
/**
* Valid input are <code>;</code> separated <code>key=value</code> pairs
* without spaces between key and value.
* <p>
* See RFC 7231:
* The type, subtype, and parameter name tokens are case-insensitive.
* Parameter values might or might not be case-sensitive, depending on
* the semantics of the parameter name. The presence or absence of a
* parameter might be significant to the processing of a media-type,
* depending on its definition within the media type registry.
* </p>
*
* @param parameters
* @param parameterMap
*/
static void parseParameters(final String parameters, final Map<String, String> parameterMap) {
if (parameters != null) {
String[] splittedParameters = parameters.split(TypeUtil.PARAMETER_SEPARATOR);
for (String parameter : splittedParameters) {
String[] keyValue = parameter.split(TypeUtil.PARAMETER_KEY_VALUE_SEPARATOR);
String key = keyValue[0].trim().toLowerCase(Locale.ENGLISH);
String value = keyValue.length > 1 ? keyValue[1] : null;
if (value != null && Character.isWhitespace(value.charAt(0))) {
throw new IllegalArgumentException(
"Value of parameter '" + key + "' starts with whitespace ('" + parameters + "').");
}
parameterMap.put(key, value);
}
}
}
}

View File

@ -23,7 +23,6 @@ import static org.junit.Assert.assertNotNull;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
public class AcceptTypeTest {
@ -45,16 +44,21 @@ public class AcceptTypeTest {
assertEquals(1, atl.size());
assertEquals("a/a", atl.get(0).toString());
}
@Test(expected = IllegalArgumentException.class)
public void testWrongQParameter() {
AcceptType.create(" a/a;q=z ");
}
@Test
@Ignore("buggy and not yet fixed")
public void testWildcard() {
List<AcceptType> atl = AcceptType.create("*; q=.2");
assertNotNull(atl);
assertEquals(1, atl.size());
assertEquals("", atl.get(0).getType());
assertEquals("", atl.get(0).getSubtype());
assertEquals("*", atl.get(0).getType());
assertEquals("*", atl.get(0).getSubtype());
assertEquals(".2", atl.get(0).getParameters().get("q"));
assertEquals(new Float(0.2), atl.get(0).getQuality());
}
}

View File

@ -0,0 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.commons.api.format;
public class ContentTypeTest {
}