This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 4729255 org.apache.juneau.http improvements.
4729255 is described below
commit 47292551a79094f11f7949b47ae4eee53fb64950
Author: JamesBognar <[email protected]>
AuthorDate: Tue Jul 14 11:49:03 2020 -0400
org.apache.juneau.http improvements.
---
.../java/org/apache/juneau/http/MediaRange.java | 144 +++++++++++
.../java/org/apache/juneau/http/MediaRanges.java | 242 ++++++++++++++++++
.../java/org/apache/juneau/http/MediaType.java | 65 +++--
.../org/apache/juneau/http/MediaTypeRange.java | 272 ---------------------
.../java/org/apache/juneau/http/header/Accept.java | 115 ++-------
.../org/apache/juneau/http/header/StringRange.java | 2 +-
.../org/apache/juneau/serializer/Serializer.java | 6 +-
.../apache/juneau/serializer/SerializerGroup.java | 10 +-
.../org/apache/juneau/http/MediaType_Test.java | 47 ++++
.../juneau/http/header/AcceptExtensionsTest.java | 76 +++---
.../org/apache/juneau/http/header/AcceptTest.java | 5 +-
.../apache/juneau/http/header/MediaRangeTest.java | 36 +--
.../org/apache/juneau/rest/jaxrs/BaseProvider.java | 6 +-
.../java/org/apache/juneau/rest/RestRequest.java | 16 +-
.../java/org/apache/juneau/rest/RestResponse.java | 9 +-
15 files changed, 575 insertions(+), 476 deletions(-)
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRange.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRange.java
new file mode 100644
index 0000000..662aa09
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRange.java
@@ -0,0 +1,144 @@
+//
***************************************************************************************************************************
+// * 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.juneau.http;
+
+import static org.apache.juneau.internal.ObjectUtils.*;
+
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
+
+
+/**
+ * Describes a single type used in content negotiation between an HTTP client
and server, as described in
+ * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
+ *
+ * <ul class='seealso'>
+ * <li class='extlink'>{@doc RFC2616}
+ * </ul>
+ */
+@BeanIgnore
+public class MediaRange extends MediaType {
+
+ private final NameValuePair[] extensions;
+ private final Float qValue;
+ private final String value;
+
+ /**
+ * Constructor.
+ *
+ * @param value The raw media range string.
+ */
+ public MediaRange(String value) {
+ this(parse(value));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param e The parsed media range element.
+ */
+ public MediaRange(HeaderElement e) {
+ super(e);
+
+ Float qValue = 1f;
+
+ // The media type consists of everything up to the q parameter.
+ // The q parameter and stuff after is part of the range.
+ List<NameValuePair> extensions = AList.of();
+ boolean foundQ = false;
+ for (NameValuePair p : e.getParameters()) {
+ if (p.getName().equals("q")) {
+ qValue = Float.parseFloat(p.getValue());
+ foundQ = true;
+ } else if (foundQ) {
+
extensions.add(BasicNameValuePair.of(p.getName(), p.getValue()));
+ }
+ }
+
+ this.qValue = qValue;
+ this.extensions = extensions.toArray(new
NameValuePair[extensions.size()]);
+
+ StringBuffer sb = new StringBuffer().append(super.toString());
+
+ // '1' is equivalent to specifying no qValue. If there's no
extensions, then we won't include a qValue.
+ if (qValue.floatValue() == 1.0) {
+ if (this.extensions.length > 0) {
+ sb.append(";q=").append(qValue);
+ for (NameValuePair p : extensions)
+
sb.append(';').append(p.getName()).append('=').append(p.getValue());
+ }
+ } else {
+ sb.append(";q=").append(qValue);
+ for (NameValuePair p : extensions)
+
sb.append(';').append(p.getName()).append('=').append(p.getValue());
+ }
+ value = sb.toString();
+ }
+
+ /**
+ * Returns the <js>'q'</js> (quality) value for this type, as described
in Section 3.9 of RFC2616.
+ *
+ * <p>
+ * The quality value is a float between <c>0.0</c> (unacceptable) and
<c>1.0</c> (most acceptable).
+ *
+ * <p>
+ * If 'q' value doesn't make sense for the context (e.g. this range was
extracted from a <js>"content-*"</js>
+ * header, as opposed to <js>"accept-*"</js> header, its value will
always be <js>"1"</js>.
+ *
+ * @return The 'q' value for this type, never <jk>null</jk>.
+ */
+ public Float getQValue() {
+ return qValue;
+ }
+
+ /**
+ * Returns the optional set of custom extensions defined for this type.
+ *
+ * <p>
+ * Values are lowercase and never <jk>null</jk>.
+ *
+ * @return The optional list of extensions, never <jk>null</jk>.
+ */
+ public List<NameValuePair> getExtensions() {
+ return Collections.unmodifiableList(Arrays.asList(extensions));
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified object is also a
<c>MediaType</c>, and has the same qValue, type,
+ * parameters, and extensions.
+ *
+ * @return <jk>true</jk> if object is equivalent.
+ */
+ @Override /* Object */
+ public boolean equals(Object o) {
+ return (o instanceof MediaRange) && eq(this, (MediaRange)o,
(x,y)->eq(x.value, y.value));
+ }
+
+ /**
+ * Returns a hash based on this instance's <c>media-type</c>.
+ *
+ * @return A hash based on this instance's <c>media-type</c>.
+ */
+ @Override /* Object */
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return value;
+ }
+}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRanges.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRanges.java
new file mode 100644
index 0000000..e94f06e
--- /dev/null
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaRanges.java
@@ -0,0 +1,242 @@
+//
***************************************************************************************************************************
+// * 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.juneau.http;
+
+import static org.apache.juneau.http.Constants.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.http.message.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.internal.*;
+
+/**
+ * A parsed <c>Accept</c> or similar header value.
+ *
+ * <p>
+ * The returned media ranges are sorted such that the most acceptable media is
available at ordinal position
+ * <js>'0'</js>, and the least acceptable at position n-1.
+ *
+ * <p>
+ * The syntax expected to be found in the referenced <c>value</c> complies
with the syntax described in
+ * RFC2616, Section 14.1, as described below:
+ * <p class='bcode w800'>
+ * Accept = "Accept" ":"
+ * #( media-range [ accept-params ] )
+ *
+ * media-range = ( "*\/*"
+ * | ( type "/" "*" )
+ * | ( type "/" subtype )
+ * ) *( ";" parameter )
+ * accept-params = ";" "q" "=" qvalue *( accept-extension )
+ * accept-extension = ";" token [ "=" ( token | quoted-string ) ]
+ * </p>
+ */
+@BeanIgnore
+public class MediaRanges {
+
+ private static final MediaRanges DEFAULT = new MediaRanges("*/*");
+ private static final Cache<String,MediaRanges> CACHE = new
Cache<>(NOCACHE, CACHE_MAX_SIZE);
+
+ private final MediaRange[] ranges;
+ private final String value;
+
+ /**
+ * Returns a parsed <c>Accept</c> header value.
+ *
+ * @param value The raw <c>Accept</c> header value.
+ * @return A parsed <c>Accept</c> header value.
+ */
+ public static MediaRanges of(String value) {
+ if (value == null || value.length() == 0)
+ return DEFAULT;
+
+ MediaRanges mr = CACHE.get(value);
+ if (mr == null)
+ mr = CACHE.put(value, new MediaRanges(value));
+ return mr;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param value The <c>Accept</c> header value.
+ */
+ public MediaRanges(String value) {
+ this(parse(value));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param e The parsed <c>Accept</c> header value.
+ */
+ public MediaRanges(HeaderElement[] e) {
+
+ List<MediaRange> l = AList.of();
+ for (HeaderElement e2 : e)
+ l.add(new MediaRange(e2));
+
+ l.sort(RANGE_COMPARATOR);
+ ranges = l.toArray(new MediaRange[l.size()]);
+
+ this.value = ranges.length == 1 ? ranges[0].toString() :
StringUtils.join(l, ',');
+ }
+
+ /**
+ * Compares two MediaRanges for equality.
+ *
+ * <p>
+ * The values are first compared according to <c>qValue</c> values.
+ * Should those values be equal, the <c>type</c> is then
lexicographically compared (case-insensitive) in
+ * ascending order, with the <js>"*"</js> type demoted last in that
order.
+ * <c>MediaRanges</c> with the same type but different sub-types are
compared - a more specific subtype is
+ * promoted over the 'wildcard' subtype.
+ * <c>MediaRanges</c> with the same types but with extensions are
promoted over those same types with no
+ * extensions.
+ */
+ private static final Comparator<MediaRange> RANGE_COMPARATOR = new
Comparator<MediaRange>() {
+ @Override
+ public int compare(MediaRange o1, MediaRange o2) {
+ // Compare q-values.
+ int qCompare = Float.compare(o2.getQValue(),
o1.getQValue());
+ if (qCompare != 0)
+ return qCompare;
+
+ // Compare media-types.
+ // Note that '*' comes alphabetically before letters,
so just do a reverse-alphabetical comparison.
+ int i = o2.toString().compareTo(o1.toString());
+ return i;
+ }
+ };
+
+ /**
+ * Given a list of media types, returns the best match for this
<c>Accept</c> header.
+ *
+ * <p>
+ * Note that fuzzy matching is allowed on the media types where the
<c>Accept</c> header may
+ * contain additional subtype parts.
+ * <br>For example, given identical q-values and an <c>Accept</c> value
of <js>"text/json+activity"</js>,
+ * the media type <js>"text/json"</js> will match if
<js>"text/json+activity"</js> or <js>"text/activity+json"</js>
+ * isn't found.
+ * <br>The purpose for this is to allow serializers to match when
artifacts such as <c>id</c> properties are
+ * present in the header.
+ *
+ * <p>
+ * See {@doc https://www.w3.org/TR/activitypub/#retrieving-objects
ActivityPub / Retrieving Objects}
+ *
+ * @param mediaTypes The media types to match against.
+ * @return The index into the array of the best match, or <c>-1</c> if
no suitable matches could be found.
+ */
+ public int findMatch(List<? extends MediaType> mediaTypes) {
+ int matchQuant = 0, matchIndex = -1;
+ float q = 0f;
+
+ // Media ranges are ordered by 'q'.
+ // So we only need to search until we've found a match.
+ for (MediaRange mr : ranges) {
+ float q2 = mr.getQValue();
+
+ if (q2 < q || q2 == 0)
+ break;
+
+ for (int i = 0; i < mediaTypes.size(); i++) {
+ MediaType mt = mediaTypes.get(i);
+ int matchQuant2 = mr.match(mt, false);
+
+ if (matchQuant2 > matchQuant) {
+ matchIndex = i;
+ matchQuant = matchQuant2;
+ q = q2;
+ }
+ }
+ }
+
+ return matchIndex;
+ }
+
+ /**
+ * Same as {@link #findMatch(List)} but matching against media type
ranges.
+ *
+ * <p>
+ * Note that the q-types on both the <c>mediaTypeRanges</c> parameter
and this header
+ * are taken into account when trying to find the best match.
+ * <br>When both this header and the matching range have q-values, the
q-value for the match is the result of multiplying them.
+ * <br>(e.g. Accept=<js>"text/html;q=0.9"</js> and
mediaTypeRange=<js>"text/html;q=0.9"</js> ==>, q-value=<c>0.81</c>).
+ *
+ * @param mediaRanges The media type ranges to match against.
+ * @return The index into the array of the best match, or <c>-1</c> if
no suitable matches could be found.
+ */
+ public int findMatch(MediaRanges mediaRanges) {
+ return findMatch(mediaRanges.getRanges());
+ }
+
+ /**
+ * Returns the {@link MediaRange} at the specified index.
+ *
+ * @param index The index position of the media range.
+ * @return The {@link MediaRange} at the specified index or
<jk>null</jk> if the index is out of range.
+ */
+ public MediaRange getRange(int index) {
+ if (index < 0 || index >= ranges.length)
+ return null;
+ return ranges[index];
+ }
+
+ /**
+ * Convenience method for searching through all of the subtypes of all
the media ranges in this header for the
+ * presence of a subtype fragment.
+ *
+ * <p>
+ * For example, given the header <js>"text/json+activity"</js>, calling
+ * <code>hasSubtypePart(<js>"activity"</js>)</code> returns
<jk>true</jk>.
+ *
+ * @param part The media type subtype fragment.
+ * @return <jk>true</jk> if subtype fragment exists.
+ */
+ public boolean hasSubtypePart(String part) {
+
+ for (MediaRange mr : ranges)
+ if (mr.getQValue() > 0 &&
mr.getSubTypes().indexOf(part) >= 0)
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Parses the specified header element part.
+ *
+ * @param value The header element part.
+ * @return Thew parsed header element part. Never <jk>null</jk>.
+ */
+ protected static HeaderElement[] parse(String value) {
+ return
BasicHeaderValueParser.parseElements(emptyIfNull(trim(value)), null);
+ }
+
+ /**
+ * Returns the media ranges that make up this object.
+ *
+ * @return The media ranges that make up this object.
+ */
+ public List<MediaRange> getRanges() {
+ return Collections.unmodifiableList(Arrays.asList(ranges));
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return value;
+ }
+}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaType.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaType.java
index b861a02..aafeef6 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaType.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaType.java
@@ -14,12 +14,14 @@ package org.apache.juneau.http;
import static org.apache.juneau.http.Constants.*;
import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ObjectUtils.*;
import java.util.*;
import org.apache.http.*;
import org.apache.http.message.*;
import org.apache.juneau.annotation.*;
+import org.apache.juneau.collections.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
@@ -122,20 +124,24 @@ public class MediaType implements Comparable<MediaType> {
* @param mt The media type string.
*/
public MediaType(String mt) {
- mt = trim(mt);
- value = mt;
-
- HeaderElement[] elements =
BasicHeaderValueParser.parseElements(mt, null);
- HeaderElement element = (elements.length > 0 ? elements[0] :
DEFAULT_ELEMENT);
-
- mediaType = element.getName();
- parameters = element.getParameters();
+ this(parse(mt));
+ }
- // Convert the parameters to BasicNameValuePair.
- for (int j = 0; j < parameters.length; j++) {
- NameValuePair p = parameters[j];
- parameters[j] = BasicNameValuePair.of(p.getName(),
p.getValue());
+ /**
+ * Constructor.
+ *
+ * @param e The parsed media type string.
+ */
+ public MediaType(HeaderElement e) {
+ mediaType = e.getName();
+
+ List<NameValuePair> parameters = AList.of();
+ for (NameValuePair p : e.getParameters()) {
+ if (p.getName().equals("q"))
+ break;
+ parameters.add(BasicNameValuePair.of(p.getName(),
p.getValue()));
}
+ this.parameters= parameters.toArray(new
NameValuePair[parameters.size()]);
String x = mediaType.replace(' ', '+');
int i = x.indexOf('/');
@@ -146,6 +152,23 @@ public class MediaType implements Comparable<MediaType> {
subTypesSorted = Arrays.copyOf(subTypes, subTypes.length);
Arrays.sort(this.subTypesSorted);
hasSubtypeMeta = ArrayUtils.contains("*", this.subTypes);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(mediaType);
+ for (NameValuePair p : parameters)
+
sb.append(';').append(p.getName()).append('=').append(p.getValue());
+ this.value = sb.toString();
+ }
+
+ /**
+ * Parses the specified header element part.
+ *
+ * @param value The header element part.
+ * @return Thew parsed header element part. Never <jk>null</jk>.
+ */
+ protected static HeaderElement parse(String value) {
+ HeaderElement[] elements =
BasicHeaderValueParser.parseElements(emptyIfNull(trim(value)), null);
+ return (elements.length > 0 ? elements[0] : DEFAULT_ELEMENT);
}
/**
@@ -300,29 +323,27 @@ public class MediaType implements Comparable<MediaType> {
*
* @return The map of additional parameters, or an empty map if there
are no parameters.
*/
- public NameValuePair[] getParameters() {
- return parameters;
+ public List<NameValuePair> getParameters() {
+ return Collections.unmodifiableList(Arrays.asList(parameters));
}
@Override /* Object */
- public final String toString() {
+ public String toString() {
return value;
}
@Override /* Object */
- public final int hashCode() {
- return mediaType.hashCode();
+ public int hashCode() {
+ return value.hashCode();
}
@Override /* Object */
- public final boolean equals(Object o) {
- if (o instanceof MediaType)
- return ObjectUtils.eq(mediaType,
((MediaType)o).mediaType);
- return false;
+ public boolean equals(Object o) {
+ return (o instanceof MediaType) && eq(this, (MediaType)o,
(x,y)->eq(x.value, y.value));
}
@Override
public final int compareTo(MediaType o) {
- return mediaType.compareTo(o.mediaType);
+ return toString().compareTo(o.toString());
}
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaTypeRange.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaTypeRange.java
deleted file mode 100644
index 508392d..0000000
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/MediaTypeRange.java
+++ /dev/null
@@ -1,272 +0,0 @@
-//
***************************************************************************************************************************
-// * 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.juneau.http;
-
-import static org.apache.juneau.http.Constants.*;
-import static org.apache.juneau.internal.ObjectUtils.*;
-
-import java.util.*;
-import java.util.Map.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.internal.*;
-
-/**
- * Describes a single type used in content negotiation between an HTTP client
and server, as described in
- * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification).
- *
- * <ul class='seealso'>
- * <li class='extlink'>{@doc RFC2616}
- * </ul>
- */
-@BeanIgnore
-public class MediaTypeRange implements Comparable<MediaTypeRange> {
-
- private static final MediaTypeRange[] DEFAULT = new
MediaTypeRange[]{new MediaTypeRange("*/*")};
- private static final Cache<String,MediaTypeRange[]> CACHE = new
Cache<>(NOCACHE, CACHE_MAX_SIZE);
-
- private final MediaType mediaType;
- private final Float qValue;
- private final Map<String,Set<String>> extensions;
-
- /**
- * Parses an <c>Accept</c> header value into an array of media ranges.
- *
- * <p>
- * The returned media ranges are sorted such that the most acceptable
media is available at ordinal position
- * <js>'0'</js>, and the least acceptable at position n-1.
- *
- * <p>
- * The syntax expected to be found in the referenced <c>value</c>
complies with the syntax described in
- * RFC2616, Section 14.1, as described below:
- * <p class='bcode w800'>
- * Accept = "Accept" ":"
- * #( media-range [ accept-params ] )
- *
- * media-range = ( "*\/*"
- * | ( type "/" "*" )
- * | ( type "/" subtype )
- * ) *( ";" parameter )
- * accept-params = ";" "q" "=" qvalue *( accept-extension )
- * accept-extension = ";" token [ "=" ( token | quoted-string ) ]
- * </p>
- *
- * @param value
- * The value to parse.
- * If <jk>null</jk> or empty, returns a single
<c>MediaTypeRange</c> is returned that represents all types.
- * @return
- * The media ranges described by the string.
- * The ranges are sorted such that the most acceptable media is
available at ordinal position <js>'0'</js>, and
- * the least acceptable at position n-1.
- */
- public static MediaTypeRange[] parse(String value) {
-
- if (value == null || value.length() == 0)
- return DEFAULT;
-
- MediaTypeRange[] mtr = CACHE.get(value);
- if (mtr != null)
- return mtr;
-
- if (value.indexOf(',') == -1) {
- mtr = new MediaTypeRange[]{new MediaTypeRange(value)};
- } else {
- Set<MediaTypeRange> ranges = new TreeSet<>();
- for (String r : StringUtils.split(value)) {
- r = r.trim();
- if (r.isEmpty())
- continue;
- ranges.add(new MediaTypeRange(r));
- }
- mtr = ranges.toArray(new MediaTypeRange[ranges.size()]);
- }
- return CACHE.put(value, mtr);
- }
-
- private MediaTypeRange(String token) {
- Builder b = new Builder(token);
- this.mediaType = b.mediaType;
- this.qValue = b.qValue;
- this.extensions = AMap.unmodifiable(b.extensions);
- }
-
- static final class Builder {
- MediaType mediaType;
- Float qValue = 1f;
- Map<String,Set<String>> extensions;
-
- Builder(String token) {
-
- token = token.trim();
-
- int i = token.indexOf(";q=");
-
- if (i == -1) {
- mediaType = MediaType.of(token);
- return;
- }
-
- mediaType = MediaType.of(token.substring(0, i));
-
- String[] tokens = token.substring(i+1).split(";");
-
- // Only the type of the range is specified
- if (tokens.length > 0) {
- boolean isInExtensions = false;
- for (int j = 0; j < tokens.length; j++) {
- String[] parm = tokens[j].split("=");
- if (parm.length == 2) {
- String k = parm[0], v = parm[1];
- if (isInExtensions) {
- if (extensions == null)
- extensions =
new TreeMap<>();
- if (!
extensions.containsKey(k))
-
extensions.put(k, new TreeSet<String>());
-
extensions.get(k).add(v);
- } else if (k.equals("q")) {
- qValue = new Float(v);
- isInExtensions = true;
- }
- }
- }
- }
- }
- }
-
- /**
- * Returns the media type enclosed by this media range.
- *
- * <h5 class='section'>Examples:</h5>
- * <ul>
- * <li><js>"text/html"</js>
- * <li><js>"text/*"</js>
- * <li><js>"*\/*"</js>
- * </ul>
- *
- * @return The media type of this media range, lowercased, never
<jk>null</jk>.
- */
- public MediaType getMediaType() {
- return mediaType;
- }
-
- /**
- * Returns the <js>'q'</js> (quality) value for this type, as described
in Section 3.9 of RFC2616.
- *
- * <p>
- * The quality value is a float between <c>0.0</c> (unacceptable) and
<c>1.0</c> (most acceptable).
- *
- * <p>
- * If 'q' value doesn't make sense for the context (e.g. this range was
extracted from a <js>"content-*"</js>
- * header, as opposed to <js>"accept-*"</js> header, its value will
always be <js>"1"</js>.
- *
- * @return The 'q' value for this type, never <jk>null</jk>.
- */
- public Float getQValue() {
- return qValue;
- }
-
- /**
- * Returns the optional set of custom extensions defined for this type.
- *
- * <p>
- * Values are lowercase and never <jk>null</jk>.
- *
- * @return The optional list of extensions, never <jk>null</jk>.
- */
- public Map<String,Set<String>> getExtensions() {
- return extensions;
- }
-
- /**
- * Provides a string representation of this media range, suitable for
use as an <c>Accept</c> header value.
- *
- * <p>
- * The literal text generated will be all lowercase.
- *
- * @return A media range suitable for use as an Accept header value,
never <c>null</c>.
- */
- @Override /* Object */
- public String toString() {
- StringBuffer sb = new StringBuffer().append(mediaType);
-
- // '1' is equivalent to specifying no qValue. If there's no
extensions, then we won't include a qValue.
- if (qValue.floatValue() == 1.0) {
- if (! extensions.isEmpty()) {
- sb.append(";q=").append(qValue);
- for (Entry<String,Set<String>> e :
extensions.entrySet()) {
- String k = e.getKey();
- for (String v : e.getValue())
-
sb.append(';').append(k).append('=').append(v);
- }
- }
- } else {
- sb.append(";q=").append(qValue);
- for (Entry<String,Set<String>> e :
extensions.entrySet()) {
- String k = e.getKey();
- for (String v : e.getValue())
-
sb.append(';').append(k).append('=').append(v);
- }
- }
- return sb.toString();
- }
-
- /**
- * Returns <jk>true</jk> if the specified object is also a
<c>MediaType</c>, and has the same qValue, type,
- * parameters, and extensions.
- *
- * @return <jk>true</jk> if object is equivalent.
- */
- @Override /* Object */
- public boolean equals(Object o) {
- return (o instanceof MediaTypeRange) && eq(this,
(MediaTypeRange)o, (x,y)->eq(x.qValue, y.qValue) && eq(x.mediaType,
y.mediaType) && eq(x.extensions, y.extensions));
- }
-
- /**
- * Returns a hash based on this instance's <c>media-type</c>.
- *
- * @return A hash based on this instance's <c>media-type</c>.
- */
- @Override /* Object */
- public int hashCode() {
- return mediaType.hashCode();
- }
-
- /**
- * Compares two MediaRanges for equality.
- *
- * <p>
- * The values are first compared according to <c>qValue</c> values.
- * Should those values be equal, the <c>type</c> is then
lexicographically compared (case-insensitive) in
- * ascending order, with the <js>"*"</js> type demoted last in that
order.
- * <c>MediaRanges</c> with the same type but different sub-types are
compared - a more specific subtype is
- * promoted over the 'wildcard' subtype.
- * <c>MediaRanges</c> with the same types but with extensions are
promoted over those same types with no
- * extensions.
- *
- * @param o The range to compare to. Never <jk>null</jk>.
- */
- @Override /* Comparable */
- public int compareTo(MediaTypeRange o) {
-
- // Compare q-values.
- int qCompare = Float.compare(o.qValue, qValue);
- if (qCompare != 0)
- return qCompare;
-
- // Compare media-types.
- // Note that '*' comes alphabetically before letters, so just
do a reverse-alphabetical comparison.
- int i = o.mediaType.toString().compareTo(mediaType.toString());
- return i;
- }
-}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/Accept.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/Accept.java
index 990497e..2bd9bfe 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/Accept.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/Accept.java
@@ -17,7 +17,6 @@ import static org.apache.juneau.http.Constants.*;
import java.util.*;
import java.util.function.*;
-import org.apache.juneau.collections.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.internal.*;
@@ -206,7 +205,7 @@ public class Accept extends BasicParameterizedArrayHeader {
return new Accept(value);
}
- private List<MediaTypeRange> parsed;
+ private MediaRanges ranges;
/**
* Constructor
@@ -223,7 +222,7 @@ public class Accept extends BasicParameterizedArrayHeader {
public Accept(Object value) {
super("Accept", value);
if (! isSupplier(value))
- parsed = getParsedValue();
+ ranges = getRanges();
}
/**
@@ -244,8 +243,18 @@ public class Accept extends BasicParameterizedArrayHeader {
*
* @return An unmodifiable list of media ranges.
*/
- public List<MediaTypeRange> asRanges() {
- return getParsedValue();
+ public MediaRanges asRanges() {
+ return getRanges();
+ }
+
+ /**
+ * Returns the {@link MediaRange} at the specified index.
+ *
+ * @param index The index position of the media range.
+ * @return The {@link MediaRange} at the specified index or
<jk>null</jk> if the index is out of range.
+ */
+ public MediaRange getRange(int index) {
+ return getRanges().getRange(index);
}
/**
@@ -266,100 +275,16 @@ public class Accept extends
BasicParameterizedArrayHeader {
* @param mediaTypes The media types to match against.
* @return The index into the array of the best match, or <c>-1</c> if
no suitable matches could be found.
*/
- public int findMatch(MediaType[] mediaTypes) {
- int matchQuant = 0, matchIndex = -1;
- float q = 0f;
-
- // Media ranges are ordered by 'q'.
- // So we only need to search until we've found a match.
- for (MediaTypeRange mr : getParsedValue()) {
- float q2 = mr.getQValue();
-
- if (q2 < q || q2 == 0)
- break;
-
- for (int i = 0; i < mediaTypes.length; i++) {
- MediaType mt = mediaTypes[i];
- int matchQuant2 = mr.getMediaType().match(mt,
false);
-
- if (matchQuant2 > matchQuant) {
- matchIndex = i;
- matchQuant = matchQuant2;
- q = q2;
- }
- }
- }
-
- return matchIndex;
- }
-
- /**
- * Same as {@link #findMatch(MediaType[])} but matching against media
type ranges.
- *
- * <p>
- * Note that the q-types on both the <c>mediaTypeRanges</c> parameter
and this header
- * are taken into account when trying to find the best match.
- * <br>When both this header and the matching range have q-values, the
q-value for the match is the result of multiplying them.
- * <br>(e.g. Accept=<js>"text/html;q=0.9"</js> and
mediaTypeRange=<js>"text/html;q=0.9"</js> ==>, q-value=<c>0.81</c>).
- *
- * @param mediaTypeRanges The media type ranges to match against.
- * @return The index into the array of the best match, or <c>-1</c> if
no suitable matches could be found.
- */
- public int findMatch(MediaTypeRange[] mediaTypeRanges) {
- float matchQuant = 0;
- int matchIndex = -1;
- float q = 0f;
-
- // Media ranges are ordered by 'q'.
- // So we only need to search until we've found a match.
- for (MediaTypeRange mr : getParsedValue()) {
- float q2 = mr.getQValue();
-
- if (q2 < q || q2 == 0)
- break;
-
- for (int i = 0; i < mediaTypeRanges.length; i++) {
- MediaTypeRange mt = mediaTypeRanges[i];
- float matchQuant2 =
mr.getMediaType().match(mt.getMediaType(), false) * mt.getQValue();
-
- if (matchQuant2 > matchQuant) {
- matchIndex = i;
- matchQuant = matchQuant2;
- q = q2;
- }
- }
- }
-
- return matchIndex;
- }
-
-
- /**
- * Convenience method for searching through all of the subtypes of all
the media ranges in this header for the
- * presence of a subtype fragment.
- *
- * <p>
- * For example, given the header <js>"text/json+activity"</js>, calling
- * <code>hasSubtypePart(<js>"activity"</js>)</code> returns
<jk>true</jk>.
- *
- * @param part The media type subtype fragment.
- * @return <jk>true</jk> if subtype fragment exists.
- */
- public boolean hasSubtypePart(String part) {
-
- for (MediaTypeRange mr : this.getParsedValue())
- if (mr.getQValue() > 0 &&
mr.getMediaType().getSubTypes().indexOf(part) >= 0)
- return true;
-
- return false;
+ public int findMatch(List<? extends MediaType> mediaTypes) {
+ return getRanges().findMatch(mediaTypes);
}
- private List<MediaTypeRange> getParsedValue() {
- if (parsed != null)
- return parsed;
+ private MediaRanges getRanges() {
+ if (ranges != null)
+ return ranges;
Object o = getRawValue();
if (o == null)
return null;
- return
AList.of(MediaTypeRange.parse(o.toString())).unmodifiable();
+ return MediaRanges.of(o.toString());
}
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/StringRange.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/StringRange.java
index 802f566..49bbcb7 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/StringRange.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/header/StringRange.java
@@ -27,7 +27,7 @@ import org.apache.juneau.internal.*;
* comparison and extension parameters.
*
* <p>
- * Similar in concept to {@link MediaTypeRange} except instead of media types
(e.g. <js>"text/json"</js>),
+ * Similar in concept to {@link MediaRanges} except instead of media types
(e.g. <js>"text/json"</js>),
* it's a simple type (e.g. <js>"iso-8601"</js>).
*
* <p>
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/Serializer.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/Serializer.java
index 539bed1..2dc894c 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/Serializer.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/Serializer.java
@@ -847,7 +847,7 @@ public abstract class Serializer extends
BeanTraverseContext {
private final UriRelativity uriRelativity;
private final Class<? extends SerializerListener> listener;
- private final MediaTypeRange[] accept;
+ private final MediaRanges accept;
private final MediaType[] accepts;
private final MediaType produces;
@@ -894,7 +894,7 @@ public abstract class Serializer extends
BeanTraverseContext {
listener = getClassProperty(SERIALIZER_listener,
SerializerListener.class, null);
this.produces = MediaType.of(produces);
- this.accept = accept == null ? MediaTypeRange.parse(produces) :
MediaTypeRange.parse(accept);
+ this.accept = accept == null ? MediaRanges.of(produces) :
MediaRanges.of(accept);
this.accepts = accept == null ? new MediaType[] {this.produces}
: MediaType.ofAll(StringUtils.split(accept, ','));
}
@@ -1015,7 +1015,7 @@ public abstract class Serializer extends
BeanTraverseContext {
*
* @return The list of media types. Never <jk>null</jk>.
*/
- public final MediaTypeRange[] getMediaTypeRanges() {
+ public final MediaRanges getMediaTypeRanges() {
return accept;
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
index 1aefe8d..f5f7b68 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerGroup.java
@@ -75,7 +75,7 @@ public final class SerializerGroup extends
BeanTraverseContext {
// Maps Accept headers to matching serializers.
private final ConcurrentHashMap<String,SerializerMatch> cache = new
ConcurrentHashMap<>();
- private final MediaTypeRange[] mediaTypeRanges;
+ private final List<MediaRange> mediaTypeRanges;
private final Serializer[] mediaTypeRangeSerializers;
private final List<MediaType> mediaTypesList;
@@ -118,11 +118,11 @@ public final class SerializerGroup extends
BeanTraverseContext {
super(ps);
this.serializers = AList.unmodifiable(serializers);
- AList<MediaTypeRange> lmtr = AList.of();
+ AList<MediaRange> lmtr = AList.of();
ASet<MediaType> lmt = ASet.of();
AList<Serializer> l = AList.of();
for (Serializer s : serializers) {
- for (MediaTypeRange m: s.getMediaTypeRanges()) {
+ for (MediaRange m: s.getMediaTypeRanges().getRanges()) {
lmtr.add(m);
l.add(s);
}
@@ -130,7 +130,7 @@ public final class SerializerGroup extends
BeanTraverseContext {
lmt.add(mt);
}
- this.mediaTypeRanges = lmtr.asArrayOf(MediaTypeRange.class);
+ this.mediaTypeRanges = lmtr.unmodifiable();
this.mediaTypesList = AList.of(lmt).unmodifiable();
this.mediaTypeRangeSerializers = l.asArrayOf(Serializer.class);
}
@@ -168,7 +168,7 @@ public final class SerializerGroup extends
BeanTraverseContext {
Accept a = Accept.of(acceptHeader);
int match = a.findMatch(mediaTypeRanges);
if (match >= 0) {
- sm = new
SerializerMatch(mediaTypeRanges[match].getMediaType(),
mediaTypeRangeSerializers[match]);
+ sm = new SerializerMatch(mediaTypeRanges.get(match),
mediaTypeRangeSerializers[match]);
cache.putIfAbsent(acceptHeader, sm);
}
diff --git
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/MediaType_Test.java
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/MediaType_Test.java
new file mode 100644
index 0000000..0f8c75c
--- /dev/null
+++
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/MediaType_Test.java
@@ -0,0 +1,47 @@
+//
***************************************************************************************************************************
+// * 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.juneau.http;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.junit.Assert.*;
+import static org.junit.runners.MethodSorters.*;
+
+import java.util.*;
+
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class MediaType_Test {
+
+ @Test
+ public void a01_basic() {
+ assertEquals(new MediaType("text/foo"), new
MediaType("text/foo"));
+ assertNotEquals(new MediaType("text/foo"), "text/foo");
+
+ Set<MediaType> x = new TreeSet<>();
+ x.add(MediaType.of("text/foo"));
+ x.add(MediaType.of("text/bar"));
+ assertObject(x).json().is("['text/bar','text/foo']");
+
+ MediaType x2 = new MediaType((String)null); // Interpreted as
"/*"
+ assertString(x2.getType()).isEmpty();
+ assertString(x2.getSubType()).is("*");
+ assertObject(x2.getSubTypes()).json().is("['*']");
+ assertTrue(x2.isMeta());
+
+ MediaType x3 = MediaType.of("text/foo+bar");
+ assertTrue(x3.hasSubType("bar"));
+ assertFalse(x3.hasSubType("baz"));
+ assertFalse(x3.hasSubType(null));
+ }
+}
diff --git
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptExtensionsTest.java
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptExtensionsTest.java
index 68f5af6..98e26c4 100644
---
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptExtensionsTest.java
+++
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptExtensionsTest.java
@@ -31,79 +31,70 @@ public class AcceptExtensionsTest {
@Test
public void testExtensions() throws Exception {
Accept accept;
- MediaTypeRange mr;
+ MediaRange mr;
accept = Accept.of("text/json");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json");
- assertString(mr.getMediaType()).is("text/json");
- assertObject(mr.getMediaType().getParameters()).json().is("[]");
+ assertObject(mr.getParameters()).json().is("[]");
assertString(mr.getQValue()).is("1.0");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of("foo,bar");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("foo");
- assertString(mr.getMediaType()).is("foo");
- assertObject(mr.getMediaType().getParameters()).json().is("[]");
+ assertObject(mr.getParameters()).json().is("[]");
assertString(mr.getQValue()).is("1.0");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of(" foo , bar ");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("foo");
- assertString(mr.getMediaType()).is("foo");
- assertObject(mr.getMediaType().getParameters()).json().is("[]");
+ assertObject(mr.getParameters()).json().is("[]");
assertString(mr.getQValue()).is("1.0");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of("text/json;a=1;q=0.9;b=2");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json;a=1;q=0.9;b=2");
- assertString(mr.getMediaType()).is("text/json;a=1");
-
assertObject(mr.getMediaType().getParameters()).json().is("['a=1']");
+ assertObject(mr.getParameters()).json().is("['a=1']");
assertString(mr.getQValue()).is("0.9");
- assertObject(mr.getExtensions()).json().is("{b:['2']}");
+ assertObject(mr.getExtensions()).json().is("['b=2']");
accept = Accept.of("text/json;a=1;a=2;q=0.9;b=3;b=4");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json;a=1;a=2;q=0.9;b=3;b=4");
- assertString(mr.getMediaType()).is("text/json;a=1;a=2");
-
assertObject(mr.getMediaType().getParameters()).json().is("['a=1','a=2']");
+ assertObject(mr.getParameters()).json().is("['a=1','a=2']");
assertString(mr.getQValue()).is("0.9");
- assertObject(mr.getExtensions()).json().is("{b:['3','4']}");
+ assertObject(mr.getExtensions()).json().is("['b=3','b=4']");
accept = Accept.of("text/json;a=1");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json;a=1");
- assertString(mr.getMediaType()).is("text/json;a=1");
-
assertObject(mr.getMediaType().getParameters()).json().is("['a=1']");
+ assertObject(mr.getParameters()).json().is("['a=1']");
assertString(mr.getQValue()).is("1.0");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of("text/json;a=1;");
- mr = accept.asRanges().get(0);
- assertString(mr).is("text/json;a=1;");
- assertString(mr.getMediaType()).is("text/json;a=1;");
-
assertObject(mr.getMediaType().getParameters()).json().is("['a=1']");
+ mr = accept.getRange(0);
+ assertString(mr).is("text/json;a=1");
+ assertObject(mr.getParameters()).json().is("['a=1']");
assertString(mr.getQValue()).is("1.0");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of("text/json;q=0.9");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json;q=0.9");
- assertString(mr.getMediaType()).is("text/json");
- assertObject(mr.getMediaType().getParameters()).json().is("[]");
+ assertObject(mr.getParameters()).json().is("[]");
assertString(mr.getQValue()).is("0.9");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
accept = Accept.of("text/json;q=0.9;");
- mr = accept.asRanges().get(0);
+ mr = accept.getRange(0);
assertString(mr).is("text/json;q=0.9");
- assertString(mr.getMediaType()).is("text/json");
- assertObject(mr.getMediaType().getParameters()).json().is("[]");
+ assertObject(mr.getParameters()).json().is("[]");
assertString(mr.getQValue()).is("0.9");
- assertObject(mr.getExtensions()).json().is("{}");
+ assertObject(mr.getExtensions()).json().is("[]");
}
//-----------------------------------------------------------------------------------------------------------------
@@ -112,9 +103,10 @@ public class AcceptExtensionsTest {
@Test
public void testHasSubtypePart() {
Accept accept = Accept.of("text/json+x,text/foo+y;q=0.0");
- assertTrue(accept.hasSubtypePart("json"));
- assertTrue(accept.hasSubtypePart("x"));
- assertFalse(accept.hasSubtypePart("foo"));
- assertFalse(accept.hasSubtypePart("y"));
+ MediaRanges mr = accept.asRanges();
+ assertTrue(mr.hasSubtypePart("json"));
+ assertTrue(mr.hasSubtypePart("x"));
+ assertFalse(mr.hasSubtypePart("foo"));
+ assertFalse(mr.hasSubtypePart("y"));
}
}
diff --git
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptTest.java
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptTest.java
index cad873b..fa20da2 100644
---
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptTest.java
+++
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/AcceptTest.java
@@ -17,6 +17,7 @@ import static org.junit.runners.MethodSorters.*;
import java.util.*;
+import org.apache.juneau.collections.*;
import org.apache.juneau.http.*;
import org.apache.juneau.json.*;
import org.junit.*;
@@ -165,7 +166,7 @@ public class AcceptTest {
public void test() throws Exception {
Accept accept = Accept.of(this.accept);
MediaType[] mt = JsonParser.DEFAULT.parse(mediaTypes,
MediaType[].class);
- int r = accept.findMatch(mt);
+ int r = accept.findMatch(AList.ofa(mt));
assertInteger(r).msg("{0} failed", label).is(expected);
}
@@ -174,7 +175,7 @@ public class AcceptTest {
Accept accept = Accept.of(this.accept);
MediaType[] mt = JsonParser.DEFAULT.parse(mediaTypes,
MediaType[].class);
Collections.reverse(Arrays.asList(mt));
- int r = accept.findMatch(mt);
+ int r = accept.findMatch(AList.ofa(mt));
int expected2 = expectedReverse == -1 ? -1 :
mt.length-expectedReverse-1;
assertInteger(r).msg("{0} failed", label).is(expected2);
}
diff --git
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/MediaRangeTest.java
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/MediaRangeTest.java
index cd8b946..654a8c7 100644
---
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/MediaRangeTest.java
+++
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/http/header/MediaRangeTest.java
@@ -32,23 +32,23 @@ public class MediaRangeTest {
@Parameterized.Parameters
public static Collection<Object[]> getParameters() {
return Arrays.asList(new Object[][] {
- { "0", "text/json", "['text/json']" },
- { "1", "text/json,text/*", "['text/json','text/*']" },
- { "2", "text/*,text/json", "['text/json','text/*']" },
- { "3", "text/*,text/*", "['text/*']" },
- { "4", "*/text,text/*", "['text/*','*/text']" },
- { "5", "text/*,*/text", "['text/*','*/text']" },
- { "6", "a;q=0.9,b;q=0.1", "['a;q=0.9','b;q=0.1']" },
- { "7", "b;q=0.9,a;q=0.1", "['b;q=0.9','a;q=0.1']" },
- { "8", "a,b;q=0.9,c;q=0.1,d;q=0",
"['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
- { "9", "d;q=0,c;q=0.1,b;q=0.9,a",
"['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
- { "10", "a;q=1,b;q=0.9,c;q=0.1,d;q=0",
"['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
- { "11", "d;q=0,c;q=0.1,b;q=0.9,a;q=1",
"['a','b;q=0.9','c;q=0.1','d;q=0.0']" },
- { "12", "a;q=0,b;q=0.1,c;q=0.9,d;q=1",
"['d','c;q=0.9','b;q=0.1','a;q=0.0']" },
- { "13", "*", "['*']" },
- { "14", "", "['*/*']" },
- { "15", null, "['*/*']" },
- { "16", "foo/bar/baz", "['foo/bar/baz']" },
+ { "0", "text/json", "'text/json'" },
+ { "1", "text/json,text/*", "'text/json,text/*'" },
+ { "2", "text/*,text/json", "'text/json,text/*'" },
+ { "3", "text/*,text/*", "'text/*,text/*'" },
+ { "4", "*/text,text/*", "'text/*,*/text'" },
+ { "5", "text/*,*/text", "'text/*,*/text'" },
+ { "6", "a;q=0.9,b;q=0.1", "'a;q=0.9,b;q=0.1'" },
+ { "7", "b;q=0.9,a;q=0.1", "'b;q=0.9,a;q=0.1'" },
+ { "8", "a,b;q=0.9,c;q=0.1,d;q=0",
"'a,b;q=0.9,c;q=0.1,d;q=0.0'" },
+ { "9", "d;q=0,c;q=0.1,b;q=0.9,a",
"'a,b;q=0.9,c;q=0.1,d;q=0.0'" },
+ { "10", "a;q=1,b;q=0.9,c;q=0.1,d;q=0",
"'a,b;q=0.9,c;q=0.1,d;q=0.0'" },
+ { "11", "d;q=0,c;q=0.1,b;q=0.9,a;q=1",
"'a,b;q=0.9,c;q=0.1,d;q=0.0'" },
+ { "12", "a;q=0,b;q=0.1,c;q=0.9,d;q=1",
"'d,c;q=0.9,b;q=0.1,a;q=0.0'" },
+ { "13", "*", "'*'" },
+ { "14", "", "'*/*'" },
+ { "15", null, "'*/*'" },
+ { "16", "foo/bar/baz", "'foo/bar/baz'" },
});
}
@@ -62,7 +62,7 @@ public class MediaRangeTest {
@Test
public void test() {
- MediaTypeRange[] r = MediaTypeRange.parse(mediaRange);
+ MediaRanges r = MediaRanges.of(mediaRange);
assertEquals(label + " failed", expected,
SimpleJsonSerializer.DEFAULT.toString(r));
}
}
diff --git
a/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/BaseProvider.java
b/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/BaseProvider.java
index 5cd1e34..229fbb4 100644
---
a/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/BaseProvider.java
+++
b/juneau-rest/juneau-rest-server-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/BaseProvider.java
@@ -176,9 +176,9 @@ public class BaseProvider implements
MessageBodyReader<Object>, MessageBodyWrite
if (headers.containsKey("Accept-Language") &&
headers.get("Accept-Language") != null) {
String h =
String.valueOf(headers.get("Accept-Language"));
if (h != null) {
- MediaTypeRange[] mr = MediaTypeRange.parse(h);
- if (mr.length > 0)
- return
toLocale(mr[0].getMediaType().getType());
+ MediaRanges mr = MediaRanges.of(h);
+ if (! mr.getRanges().isEmpty())
+ return
toLocale(mr.getRange(0).getType());
}
}
return null;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 7c43a4b..33b9039 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -454,9 +454,9 @@ public final class RestRequest extends
HttpServletRequestWrapper {
public Locale getLocale() {
String h = headers.getString("Accept-Language");
if (h != null) {
- MediaTypeRange[] mr = MediaTypeRange.parse(h);
- if (mr.length > 0)
- return toLocale(mr[0].getMediaType().getType());
+ MediaRanges mr = MediaRanges.of(h);
+ if (! mr.getRanges().isEmpty())
+ return toLocale(mr.getRange(0).getType());
}
return super.getLocale();
}
@@ -465,11 +465,11 @@ public final class RestRequest extends
HttpServletRequestWrapper {
public Enumeration<Locale> getLocales() {
String h = headers.getString("Accept-Language");
if (h != null) {
- MediaTypeRange[] mr = MediaTypeRange.parse(h);
- if (mr.length > 0) {
- List<Locale> l = new ArrayList<>(mr.length);
- for (MediaTypeRange r : mr)
-
l.add(toLocale(r.getMediaType().getType()));
+ MediaRanges mr = MediaRanges.of(h);
+ if (! mr.getRanges().isEmpty()) {
+ List<Locale> l = new
ArrayList<>(mr.getRanges().size());
+ for (MediaRange r : mr.getRanges())
+ l.add(toLocale(r.getType()));
return enumeration(l);
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index 7a5dbfe..a3b23db 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -115,13 +115,12 @@ public final class RestResponse extends
HttpServletResponseWrapper {
String charset = null;
if (h == null)
charset = rjm.defaultCharset;
- else for (MediaTypeRange r : MediaTypeRange.parse(h)) {
+ else for (MediaRange r : MediaRanges.of(h).getRanges()) {
if (r.getQValue() > 0) {
- MediaType mt = r.getMediaType();
- if (mt.getType().equals("*"))
+ if (r.getType().equals("*"))
charset = rjm.defaultCharset;
- else if (Charset.isSupported(mt.getType()))
- charset = mt.getType();
+ else if (Charset.isSupported(r.getType()))
+ charset = r.getType();
if (charset != null)
break;
}