[OLINGO-317] Minor ContentType refactoring

This commit is contained in:
Michael Bolz 2014-06-23 15:19:23 +02:00
parent ab6fd5e2b8
commit ce3598521d
5 changed files with 332 additions and 368 deletions

View File

@ -36,8 +36,6 @@ public interface Value {
Object get();
NullValue asNull();
PrimitiveValue asPrimitive();
EnumValue asEnum();

View File

@ -0,0 +1,259 @@
/*******************************************************************************
* 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.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
* Internally used {@link AcceptType} for OData library.
*
* See RFC 7231, chapter 5.3.2:
* <pre>
* Accept = #( media-range [ accept-params ] )
* media-range = ( "&#42;/&#42;"
* / ( type "/" "&#42;" )
* / ( type "/" subtype )
* ) *( OWS ";" OWS parameter )
* accept-params = weight *( accept-ext )
* accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
* weight = OWS ";" OWS "q=" qvalue
* qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
* </pre>
*
* Once created a {@link AcceptType} is <b>IMMUTABLE</b>.
*/
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, null, 1F);
private final String type;
private final String subtype;
private final Map<String, String> parameters;
private final Float quality;
private AcceptType(final String type, final String subtype, final Map<String, String> parameters,
final Float quality) {
this.type = type;
this.subtype = subtype;
this.parameters = createParameterMap();
this.parameters.putAll(parameters);
this.quality = quality;
}
private TreeMap<String, String> createParameterMap() {
return new TreeMap<String, String>(new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareToIgnoreCase(o2);
}
});
}
private AcceptType(final String type) {
if (type == null) {
throw new IllegalArgumentException("Type parameter MUST NOT be null.");
}
List<String> typeSubtype = new ArrayList<String>();
this.parameters = createParameterMap();
ContentType.parse(type, typeSubtype, parameters);
this.type = typeSubtype.get(0);
this.subtype = typeSubtype.get(1);
if (MEDIA_TYPE_WILDCARD.equals(this.type) && !MEDIA_TYPE_WILDCARD.equals(this.subtype)) {
throw new IllegalArgumentException("Illegal combination of WILDCARD type with NONE WILDCARD subtype.");
}
final String q = parameters.get(PARAMETER_Q);
if (q == null) {
quality = 1F;
} else {
if (Q_PARAMETER_VALUE_PATTERN.matcher(q).matches()) {
quality = Float.valueOf(q);
} else {
throw new IllegalArgumentException("Illegal quality parameter.");
}
parameters.remove(PARAMETER_Q);
}
}
/**
* Creates an accept type.
* @param type
* @param subtype
* @param parameters
* @param quality
* @return a new <code>AcceptType</code> object
*/
public static AcceptType create(final String type, final String subtype, final Map<String, String> parameters,
final Float quality) {
return new AcceptType(type, subtype, parameters, quality);
}
public static AcceptType create(final ContentType contentType) {
return create(contentType.getType(), contentType.getSubtype(), contentType.getParameters(), 1F);
}
/**
* Create an {@link AcceptType} based on given input string (<code>format</code>).
* @param format
* @return a new <code>AcceptType</code> object
* @throws IllegalArgumentException if input string is not parseable
*/
public static AcceptType create(final String format) {
if (format == null) {
throw new IllegalArgumentException("Parameter format MUST NOT be NULL.");
}
return new AcceptType(format);
}
/**
* Parses the given input string (<code>format</code>) and returns created {@link AcceptType} if input was valid or
* return <code>NULL</code> if input was not parseable.
* @param format
* @return a new <code>ContentType</code> object
*/
public static AcceptType parse(final String format) {
try {
return AcceptType.create(format);
} catch (IllegalArgumentException e) {
return null;
}
}
public String getType() {
return type;
}
public String getSubtype() {
return subtype;
}
public Map<String, String> getParameters() {
return parameters;
}
public Float getQuality() {
return quality;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(type).append('/').append(subtype);
for (final String key : parameters.keySet()) {
result.append(';').append(key).append('=').append(parameters.get(key));
}
if (quality < 1F) {
result.append(';').append(PARAMETER_Q).append('=').append(quality);
}
return result.toString();
}
/**
* <p>Determines whether this accept type matches a given content type.</p>
* <p>A match is defined as fulfilling all of the following conditions:
* <ul>
* <li>the type must be '*' or equal to the content-type's type,</li>
* <li>the subtype must be '*' or equal to the content-type's subtype,</li>
* <li>all parameters must have the same value as in the content-type's parameter map.</li>
* </ul></p>
* @param contentType
* @return whether this accept type matches the given content type
*/
public boolean matches(final ContentType contentType) {
if (type.equals(MEDIA_TYPE_WILDCARD)) {
return true;
}
if (!type.equalsIgnoreCase(contentType.getType())) {
return false;
}
if (subtype.equals(MEDIA_TYPE_WILDCARD)) {
return true;
}
if (!subtype.equalsIgnoreCase(contentType.getSubtype())) {
return false;
}
Map<String, String> compareParameters = contentType.getParameters();
for (final String key : parameters.keySet()) {
if (compareParameters.containsKey(key)) {
if (!parameters.get(key).equalsIgnoreCase(compareParameters.get(key))) {
return false;
}
} else {
return false;
}
}
return true;
}
/**
* Create a list of {@link AcceptType} based on given input strings (<code>contentTypes</code>).
*
* If one of the given strings can not be parsed an exception is thrown (hence no list is returned with the parseable
* strings).
* @param acceptTypeStrings a list of strings
* @return a list of new <code>AcceptType</code> objects
* @throws IllegalArgumentException if one of the given input string is not parseable this exceptions is thrown
*/
public static List<AcceptType> create(final List<String> acceptTypeStrings) {
List<AcceptType> acceptTypes = new ArrayList<AcceptType>(acceptTypeStrings.size());
for (String contentTypeString : acceptTypeStrings) {
acceptTypes.add(create(contentTypeString));
}
return acceptTypes;
}
/**
* Sort given list of Accept types
* according to their quality-parameter values and their specificity
* as defined in RFC 7231, chapters 3.1.1.1, 5.3.1, and 5.3.2.
* @param toSort list which is sorted and hence re-arranged
*/
public static void sort(List<AcceptType> toSort) {
Collections.sort(toSort,
new Comparator<AcceptType>() {
@Override
public int compare(final AcceptType a1, final AcceptType a2) {
int compare = a1.getQuality().compareTo(a2.getQuality());
if (compare != 0) {
return compare;
}
compare = (a1.getType().equals(MEDIA_TYPE_WILDCARD) ? 1 : 0)
- (a2.getType().equals(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);
if (compare != 0) {
return compare;
}
return a2.getParameters().size() - a1.getParameters().size();
}
});
}
}

View File

@ -19,7 +19,6 @@
package org.apache.olingo.commons.api.format;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -29,7 +28,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
* Internally used {@link ContentType} for OData library.
@ -44,55 +42,19 @@ import java.util.regex.Pattern;
* OWS = *( SP / HTAB ) ; optional whitespace
* </pre>
*
* Especially for <code>Accept</code> Header as defined in
* RFC 7231, chapter 5.3.2:
* <pre>
* Accept = #( media-range [ accept-params ] )
* media-range = ( "&#42;/&#42;"
* / ( type "/" "&#42;" )
* / ( type "/" subtype )
* ) *( OWS ";" OWS parameter )
* accept-params = weight *( accept-ext )
* accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
* weight = OWS ";" OWS "q=" qvalue
* qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
* </pre>
*
* Once created a {@link ContentType} is <b>IMMUTABLE</b>.
*/
public class ContentType {
private static final Comparator<String> Q_PARAMETER_COMPARATOR = new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
Float f1 = parseQParameterValue(o1);
Float f2 = parseQParameterValue(o2);
return f2.compareTo(f1);
}
};
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 = "/";
private static final String MEDIA_TYPE_WILDCARD = "*";
public static final String PARAMETER_Q = "q";
public static final String PARAMETER_TYPE = "type";
public static final String PARAMETER_CHARSET = "charset";
public static final String CHARSET_UTF_8 = "UTF-8";
private static final Pattern Q_PARAMETER_VALUE_PATTERN = Pattern.compile("1|0|1\\.0{1,3}|0\\.\\d{1,3}");
public static final ContentType WILDCARD = create(MEDIA_TYPE_WILDCARD, MEDIA_TYPE_WILDCARD);
public static final ContentType APPLICATION_XHTML_XML = create("application", "xhtml+xml");
public static final ContentType APPLICATION_SVG_XML = create("application", "svg+xml");
public static final ContentType APPLICATION_FORM_URLENCODED = create("application", "x-www-form-urlencoded");
public static final ContentType MULTIPART_FORM_DATA = create("multipart", "form-data");
public static final ContentType TEXT_XML = create("text", "xml");
public static final ContentType TEXT_HTML = create("text", "html");
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);
@ -103,43 +65,37 @@ public class ContentType {
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");
public static final ContentType APPLICATION_ATOM_XML_FEED_CS_UTF_8 = ContentType.create(APPLICATION_ATOM_XML_FEED,
public static final ContentType APPLICATION_ATOM_XML_FEED_CS_UTF_8 = create(APPLICATION_ATOM_XML_FEED,
PARAMETER_CHARSET, 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);
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);
PARAMETER_CHARSET, 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 = ContentType
.create(TEXT_PLAIN, PARAMETER_CHARSET, CHARSET_UTF_8);
public static final ContentType TEXT_PLAIN_CS_UTF_8 = create(TEXT_PLAIN, PARAMETER_CHARSET, CHARSET_UTF_8);
public static final ContentType MULTIPART_MIXED = create("multipart", "mixed");
public static final ContentType APPLICATION_XHTML_XML = create("application", "xhtml+xml");
public static final ContentType APPLICATION_SVG_XML = create("application", "svg+xml");
public static final ContentType APPLICATION_FORM_URLENCODED = create("application", "x-www-form-urlencoded");
public static final ContentType MULTIPART_FORM_DATA = create("multipart", "form-data");
public static final ContentType TEXT_XML = create("text", "xml");
public static final ContentType TEXT_HTML = create("text", "html");
private final String type;
private final String subtype;
private final Map<String, String> parameters;
private ContentType(final String type) {
if (type == null) {
throw new IllegalArgumentException("Type parameter MUST NOT be null.");
}
this.type = validateType(type);
subtype = null;
parameters = Collections.emptyMap();
}
/**
* Creates a content type from type, subtype, and parameters.
* @param type
* @param subtype
* @param parameters
*/
* @param type
* @param subtype
* @param parameters
*/
private ContentType(final String type, final String subtype, final Map<String, String> parameters) {
if ((type == null || MEDIA_TYPE_WILDCARD.equals(type)) && !MEDIA_TYPE_WILDCARD.equals(subtype)) {
throw new IllegalArgumentException("Illegal combination of WILDCARD type with NONE WILDCARD subtype.");
}
this.type = validateType(type);
this.subtype = validateType(subtype);
@ -153,13 +109,12 @@ public class ContentType {
}
});
this.parameters.putAll(parameters);
this.parameters.remove(PARAMETER_Q);
}
}
private String validateType(final String type) {
if (type == null || type.isEmpty()) {
return MEDIA_TYPE_WILDCARD;
if (type == null || type.isEmpty() || "*".equals(type)) {
throw new IllegalArgumentException("Illegal type '" + type + "'.");
}
int len = type.length();
for (int i = 0; i < len; i++) {
@ -201,14 +156,14 @@ public class ContentType {
* @return a new <code>ContentType</code> object
*/
public static ContentType create(final ContentType contentType,
final String parameterKey, final String parameterValue) {
ContentType ct = new ContentType(contentType.type, contentType.subtype, contentType.parameters);
ct.parameters.put(parameterKey, parameterValue);
return ct;
final String parameterKey, final String parameterValue) {
ContentType newContentType = new ContentType(contentType.type, contentType.subtype, contentType.parameters);
newContentType.parameters.put(parameterKey, parameterValue);
return newContentType;
}
/**
* Create a {@link ContentType} based on given input string (<code>format</code>).
* Creates a {@link ContentType} based on given input string (<code>format</code>).
* Supported format is <code>Media Type</code> format as defined in RFC 7231, chapter 3.1.1.1.
* @param format a string in format as defined in RFC 7231, chapter 3.1.1.1
* @return a new <code>ContentType</code> object
@ -218,51 +173,10 @@ public class ContentType {
if (format == null) {
throw new IllegalArgumentException("Parameter format MUST NOT be NULL.");
}
// split 'types' and 'parameters'
String[] typesAndParameters = format.split(PARAMETER_SEPARATOR, 2);
String types = typesAndParameters[0];
String parameters = (typesAndParameters.length > 1 ? typesAndParameters[1] : null);
//
Map<String, String> parametersMap = parseParameters(parameters);
//
if (types.contains(TYPE_SUBTYPE_SEPARATOR)) {
String[] tokens = types.split(TYPE_SUBTYPE_SEPARATOR);
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 {
return new ContentType(tokens[0], tokens[1], parametersMap);
}
} else {
throw new IllegalArgumentException("Too many '" + TYPE_SUBTYPE_SEPARATOR + "' in format '" + format + "'.");
}
} else if (MEDIA_TYPE_WILDCARD.equals(types)) {
return ContentType.WILDCARD;
} else {
throw new IllegalArgumentException("No separator '" + TYPE_SUBTYPE_SEPARATOR + "' was found in format '" + format
+ "'.");
}
}
/**
* Create a list of {@link ContentType} based on given input strings (<code>contentTypes</code>).
*
* Supported format is <code>Media Type</code> format as defined in RFC 7231, chapter 3.1.1.1.
* If one of the given strings can not be parsed an exception is thrown (hence no list is returned with the parseable
* strings).
* @param contentTypeStrings a list of strings in format as defined in <code>RFC 2616 section 3.7</code>
* @return a list of new <code>ContentType</code> object
* @throws IllegalArgumentException if one of the given input string is not parseable this exceptions is thrown
*/
public static List<ContentType> create(final List<String> contentTypeStrings) {
List<ContentType> contentTypes = new ArrayList<ContentType>(contentTypeStrings.size());
for (String contentTypeString : contentTypeStrings) {
contentTypes.add(create(contentTypeString));
}
return contentTypes;
List<String> typeSubtype = new ArrayList<String>();
Map<String, String> parameters = new HashMap<String, String>();
parse(format, typeSubtype, parameters);
return new ContentType(typeSubtype.get(0), typeSubtype.get(1), parameters);
}
/**
@ -282,23 +196,36 @@ public class ContentType {
}
}
/**
* Sort given list (which must contains content-type formatted string) for their {@value #PARAMETER_Q} value
* as defined in RFC 7231, chapter 3.1.1.1, and RFC 7231, chapter 5.3.1.
*
* <b>Attention:</b> For invalid values a {@value #PARAMETER_Q} value from <code>-1</code> is used for sorting.
*
* @param toSort list which is sorted and hence re-arranged
*/
public static void sortForQParameter(final List<String> toSort) {
Collections.sort(toSort, ContentType.Q_PARAMETER_COMPARATOR);
protected static void parse(final String format, List<String> typeSubtype, Map<String, String> parameters) {
final String[] typesAndParameters = format.split(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 (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 '" + TYPE_SUBTYPE_SEPARATOR + "' in format '" + format + "'.");
}
} else {
throw new IllegalArgumentException("No separator '" + 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.
* <b>Attention:</b> <code>q</code> parameter is validated but not added to result map
*
* <p>
* See RFC 7231:
* The type, subtype, and parameter name tokens are case-insensitive.
@ -309,10 +236,9 @@ public class ContentType {
* </p>
*
* @param parameters
* @return Map with keys mapped to values
* @param parameterMap
*/
private static Map<String, String> parseParameters(final String parameters) {
Map<String, String> parameterMap = new HashMap<String, String>();
private static void parseParameters(final String parameters, Map<String, String> parameterMap) {
if (parameters != null) {
String[] splittedParameters = parameters.split(PARAMETER_SEPARATOR);
for (String parameter : splittedParameters) {
@ -320,56 +246,12 @@ public class ContentType {
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
+ "').");
}
if (PARAMETER_Q.equals(key.toLowerCase(Locale.ENGLISH))) {
// q parameter is only validated but not added
if (!Q_PARAMETER_VALUE_PATTERN.matcher(value).matches()) {
throw new IllegalArgumentException("Value of 'q' parameter is not valid (q='" + value + "').");
}
} else {
parameterMap.put(key, value);
throw new IllegalArgumentException(
"Value of parameter '" + key + "' starts with whitespace ('" + parameters + "').");
}
parameterMap.put(key, value);
}
}
return parameterMap;
}
/**
* Parse value of {@value #PARAMETER_Q} <code>parameter</code> out of content type/parameters.
* If no {@value #PARAMETER_Q} <code>parameter</code> is in <code>content type/parameters</code> parameter found
* <code>1</code> is returned.
* If {@value #PARAMETER_Q} <code>parameter</code> is invalid <code>-1</code> is returned.
*
* @param contentType parameter which is parsed for {@value #PARAMETER_Q} <code>parameter</code> value
* @return value of {@value #PARAMETER_Q} <code>parameter</code> or <code>1</code> or <code>-1</code>
*/
private static Float parseQParameterValue(final String contentType) {
if (contentType != null) {
String[] splittedParameters = contentType.split(PARAMETER_SEPARATOR);
for (String parameter : splittedParameters) {
String[] keyValue = parameter.split(PARAMETER_KEY_VALUE_SEPARATOR);
String key = keyValue[0].trim().toLowerCase(Locale.ENGLISH);
if (PARAMETER_Q.equalsIgnoreCase(key)) {
String value = keyValue.length > 1 ? keyValue[1] : null;
if (Q_PARAMETER_VALUE_PATTERN.matcher(value).matches()) {
return Float.valueOf(value);
}
return Float.valueOf(-1);
}
}
}
return Float.valueOf(1);
}
/**
* Check if parameter with key value is an allowed parameter.
* @param key
* @return
*/
private static boolean isParameterAllowed(final String key) {
return key != null && !PARAMETER_Q.equals(key.toLowerCase(Locale.ENGLISH));
}
public String getType() {
@ -488,11 +370,7 @@ public class ContentType {
return false;
}
} else if (!subtype.equals(other.subtype)) {
if (other.subtype == null) {
return false;
} else if (!subtype.equals(MEDIA_TYPE_WILDCARD) && !other.subtype.equals(MEDIA_TYPE_WILDCARD)) {
return false;
}
return false;
}
// type checks
@ -501,14 +379,7 @@ public class ContentType {
return false;
}
} else if (!type.equals(other.type)) {
if (!type.equals(MEDIA_TYPE_WILDCARD) && !other.type.equals(MEDIA_TYPE_WILDCARD)) {
return false;
}
}
// if wildcards are set, content types are defined as 'equal'
if (countWildcards() > 0 || other.countWildcards() > 0) {
return true;
return false;
}
return null;
@ -523,19 +394,12 @@ public class ContentType {
* returned
*/
private static boolean areEqual(final String first, final String second) {
if (first == null) {
if (second != null) {
return false;
}
} else if (!first.equalsIgnoreCase(second)) {
return false;
}
return true;
return first == null && second == null || first.equalsIgnoreCase(second);
}
/**
* Get {@link ContentType} as string as defined in RFC 7231 (http://www.ietf.org/rfc/rfc7231.txt, chapter 3.1.1.1:
* Media Type)
* Get {@link ContentType} as string as defined in RFC 7231
* (http://www.ietf.org/rfc/rfc7231.txt, chapter 3.1.1.1: Media Type)
* @return string representation of <code>ContentType</code> object
*/
public String toContentTypeString() {
@ -544,10 +408,7 @@ public class ContentType {
sb.append(type).append(TYPE_SUBTYPE_SEPARATOR).append(subtype);
for (String key : parameters.keySet()) {
if (isParameterAllowed(key)) {
String value = parameters.get(key);
sb.append(";").append(key).append("=").append(value);
}
sb.append(";").append(key).append("=").append(parameters.get(key));
}
return sb.toString();
}
@ -556,150 +417,4 @@ public class ContentType {
public String toString() {
return toContentTypeString();
}
/**
* Find best match between this {@link ContentType} and the {@link ContentType} in the list.
* If a match (this {@link ContentType} is equal to a {@link ContentType} in list) is found either this or the
* {@link ContentType} from the list is returned based on which {@link ContentType} has less "**" characters set
* (checked with {@link #compareWildcardCounts(ContentType)}.
* If no match (none {@link ContentType} in list is equal to this {@link ContentType}) is found <code>NULL</code> is
* returned.
*
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
* @return best matched content type in list or <code>NULL</code> if none content type match to this content type
* instance
*/
public ContentType match(final List<ContentType> toMatchContentTypes) {
for (ContentType supportedContentType : toMatchContentTypes) {
if (equals(supportedContentType)) {
if (compareWildcardCounts(supportedContentType) < 0) {
return this;
} else {
return supportedContentType;
}
}
}
return null;
}
/**
* Find best match between this {@link ContentType} and the {@link ContentType} in the list ignoring all set
* parameters.
* If a match (this {@link ContentType} is equal to a {@link ContentType} in list) is found either this or the
* {@link ContentType} from the list is returned based on which {@link ContentType} has less "**" characters set
* (checked with {@link #compareWildcardCounts(ContentType)}.
* If no match (none {@link ContentType} in list is equal to this {@link ContentType}) is found <code>NULL</code> is
* returned.
*
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
* @return best matched content type in list or <code>NULL</code> if none content type match to this content type
* instance
*/
public ContentType matchCompatible(final List<ContentType> toMatchContentTypes) {
for (ContentType supportedContentType : toMatchContentTypes) {
if (isCompatible(supportedContentType)) {
if (compareWildcardCounts(supportedContentType) < 0) {
return this;
} else {
return supportedContentType;
}
}
}
return null;
}
/**
* Check if a valid compatible match for this {@link ContentType} exists in given list.
* Compatible in this case means that <b>all set parameters are ignored</b>.
* For more detail what a valid match is see {@link #matchCompatible(List)}.
*
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
* @return <code>true</code> if a compatible content type was found in given list
* or <code>false</code> if none compatible content type match was found
*/
public boolean hasCompatible(final List<ContentType> toMatchContentTypes) {
return matchCompatible(toMatchContentTypes) != null;
}
/**
* Check if a valid match for this {@link ContentType} exists in given list.
* For more detail what a valid match is see {@link #match(List)}.
*
* @param toMatchContentTypes list of {@link ContentType}s which are matches against this {@link ContentType}
* @return <code>true</code> if a matching content type was found in given list
* or <code>false</code> if none matching content type match was found
*/
public boolean hasMatch(final List<ContentType> toMatchContentTypes) {
return match(toMatchContentTypes) != null;
}
/**
* Compare wildcards counts/weights of both {@link ContentType}.
*
* The smaller {@link ContentType} has lesser weighted wildcards then the bigger {@link ContentType}.
* As result this method returns this object weighted wildcards minus the given parameter object weighted wildcards.
*
* A type wildcard is weighted with <code>2</code> and a subtype wildcard is weighted with <code>1</code>.
*
* @param otherContentType {@link ContentType} to be compared to
* @return this object weighted wildcards minus the given parameter object weighted wildcards.
*/
public int compareWildcardCounts(final ContentType otherContentType) {
return countWildcards() - otherContentType.countWildcards();
}
private int countWildcards() {
int count = 0;
if (MEDIA_TYPE_WILDCARD.equals(type)) {
count += 2;
}
if (MEDIA_TYPE_WILDCARD.equals(subtype)) {
count++;
}
return count;
}
/**
*
* @return <code>true</code> if <code>type</code> or <code>subtype</code> of this instance is a "*".
*/
public boolean hasWildcard() {
return (MEDIA_TYPE_WILDCARD.equals(type) || MEDIA_TYPE_WILDCARD.equals(subtype));
}
/**
*
* @return <code>true</code> if both <code>type</code> and <code>subtype</code> of this instance are a "*".
*/
public boolean isWildcard() {
return (MEDIA_TYPE_WILDCARD.equals(type) && MEDIA_TYPE_WILDCARD.equals(subtype));
}
public static List<ContentType> convert(final List<String> types) {
List<ContentType> results = new ArrayList<ContentType>();
for (String contentType : types) {
results.add(ContentType.create(contentType));
}
return results;
}
/**
* Check if a valid match for given content type formated string (<code>toMatch</code>) exists in given list.
* Therefore the given content type formated string (<code>toMatch</code>) is converted into a {@link ContentType}
* with a simple {@link #create(String)} call (during which an exception can occur).
*
* For more detail in general see {@link #hasMatch(List)} and for what a valid match is see {@link #match(List)}.
*
* @param toMatch content type formated string (<code>toMatch</code>) for which is checked if a match exists in given
* list
* @param matchExamples list of {@link ContentType}s which are matches against content type formated string
* (<code>toMatch</code>)
* @return <code>true</code> if a matching content type was found in given list
* or <code>false</code> if none matching content type match was found
*/
public static boolean match(final String toMatch, final ContentType... matchExamples) {
ContentType toMatchContentType = ContentType.create(toMatch);
return toMatchContentType.hasMatch(Arrays.asList(matchExamples));
}
}

View File

@ -57,25 +57,25 @@ public enum ODataFormat {
private static final String JSON_METADATA_PARAMETER_V4 = "odata.metadata";
private static final Map<ODataServiceVersion, Map<ODataFormat, ContentType>> FORMAT_PER_VERSION = new
HashMap<ODataServiceVersion, Map<ODataFormat, ContentType>>();
HashMap<ODataServiceVersion, Map<ODataFormat, ContentType>>();
static {
final Map<ODataFormat, ContentType> v3 = new HashMap<ODataFormat, ContentType>();
v3.put(ODataFormat.JSON_NO_METADATA, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "nometadata"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "nometadata"));
v3.put(ODataFormat.JSON, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "minimalmetadata"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "minimalmetadata"));
v3.put(ODataFormat.JSON_FULL_METADATA, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "fullmetadata"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V3, "fullmetadata"));
FORMAT_PER_VERSION.put(ODataServiceVersion.V30, v3);
final Map<ODataFormat, ContentType> v4 = new HashMap<ODataFormat, ContentType>();
v4.put(ODataFormat.JSON_NO_METADATA, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "none"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "none"));
v4.put(ODataFormat.JSON, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "minimal"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "minimal"));
v4.put(ODataFormat.JSON_FULL_METADATA, ContentType.create(
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "full"));
ContentType.APPLICATION_JSON, JSON_METADATA_PARAMETER_V4, "full"));
FORMAT_PER_VERSION.put(ODataServiceVersion.V40, v4);
}
@ -121,12 +121,9 @@ public enum ODataFormat {
if (contentType == null) {
return null;
}
if (contentType.hasWildcard()) {
throw new IllegalArgumentException("Content Type must be fully specified!");
}
if (contentType.isCompatible(ContentType.APPLICATION_ATOM_XML)
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_SVC)) {
|| contentType.isCompatible(ContentType.APPLICATION_ATOM_SVC)) {
return ATOM;
} else if (contentType.isCompatible(ContentType.APPLICATION_XML)) {
return XML;
@ -135,8 +132,8 @@ public enum ODataFormat {
if (jsonVariant != null) {
for (ODataFormat candidate : FORMAT_PER_VERSION.get(ODataServiceVersion.V30).keySet()) {
if (FORMAT_PER_VERSION.get(ODataServiceVersion.V30).get(candidate).getParameters()
.get(JSON_METADATA_PARAMETER_V3)
.equals(jsonVariant)) {
.get(JSON_METADATA_PARAMETER_V3)
.equals(jsonVariant)) {
return candidate;
}
}
@ -145,8 +142,8 @@ public enum ODataFormat {
if (jsonVariant != null) {
for (ODataFormat candidate : FORMAT_PER_VERSION.get(ODataServiceVersion.V40).keySet()) {
if (FORMAT_PER_VERSION.get(ODataServiceVersion.V40).get(candidate).getParameters()
.get(JSON_METADATA_PARAMETER_V4)
.equals(jsonVariant)) {
.get(JSON_METADATA_PARAMETER_V4)
.equals(jsonVariant)) {
return candidate;
}
}

View File

@ -68,11 +68,6 @@ public abstract class AbstractValue implements Value {
return false;
}
@Override
public NullValue asNull() {
return isNull() ? (NullValue) this : null;
}
@Override
public PrimitiveValue asPrimitive() {
return isPrimitive() ? (PrimitiveValue) this : null;