http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroup.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroup.java b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroup.java index ce948c6..194a39f 100644 --- a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroup.java +++ b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroup.java @@ -15,7 +15,7 @@ package org.apache.juneau.encoders; import java.util.*; import java.util.concurrent.*; -import org.apache.juneau.*; +import org.apache.juneau.http.*; /** * Represents the group of {@link Encoder encoders} keyed by codings. @@ -53,9 +53,12 @@ import org.apache.juneau.*; public final class EncoderGroup { // Maps Accept-Encoding headers to matching encoders. - private final Map<String,EncoderMatch> cache = new ConcurrentHashMap<String,EncoderMatch>(); + private final ConcurrentHashMap<String,EncoderMatch> cache = new ConcurrentHashMap<String,EncoderMatch>(); - final Encoder[] encoders; + private final String[] encodings; + private final List<String> encodingsList; + private final Encoder[] encodingsEncoders; + private final List<Encoder> encoders; /** * Constructor @@ -63,9 +66,21 @@ public final class EncoderGroup { * @param encoders The encoders to add to this group. */ public EncoderGroup(Encoder[] encoders) { - this.encoders = Arrays.copyOf(encoders, encoders.length); - } + this.encoders = Collections.unmodifiableList(new ArrayList<Encoder>(Arrays.asList(encoders))); + + List<String> lc = new ArrayList<String>(); + List<Encoder> l = new ArrayList<Encoder>(); + for (Encoder e : encoders) { + for (String c: e.getCodings()) { + lc.add(c); + l.add(e); + } + } + this.encodings = lc.toArray(new String[lc.size()]); + this.encodingsList = Collections.unmodifiableList(lc); + this.encodingsEncoders = l.toArray(new Encoder[l.size()]); + } /** * Returns the coding string for the matching encoder that can handle the specified <code>Accept-Encoding</code> @@ -79,62 +94,47 @@ public final class EncoderGroup { * @return The coding value (e.g. <js>"gzip"</js>). */ public EncoderMatch getEncoderMatch(String acceptEncoding) { - if (encoders.length == 0) - return null; - EncoderMatch em = cache.get(acceptEncoding); if (em != null) return em; - MediaRange[] ae = MediaRange.parse(acceptEncoding); - - if (ae.length == 0) - ae = MediaRange.parse("*/*"); - - Map<Float,EncoderMatch> m = null; + AcceptEncoding ae = AcceptEncoding.forString(acceptEncoding); + int match = ae.findMatch(encodings); - for (MediaRange a : ae) { - for (Encoder e : encoders) { - for (String c : e.getCodings()) { - MediaType mt = MediaType.forString(c); - float q = a.matches(mt); - if (q == 1) { - em = new EncoderMatch(mt, e); - cache.put(acceptEncoding, em); - return em; - } else if (q > 0) { - if (m == null) - m = new TreeMap<Float,EncoderMatch>(Collections.reverseOrder()); - m.put(q, new EncoderMatch(mt, e)); - } - } - } + if (match >= 0) { + em = new EncoderMatch(encodings[match], encodingsEncoders[match]); + cache.putIfAbsent(acceptEncoding, em); } - return (m == null ? null : m.values().iterator().next()); + + return cache.get(acceptEncoding); } /** * Returns the encoder registered with the specified coding (e.g. <js>"gzip"</js>). * - * @param coding The coding string. + * @param encoding The coding string. * @return The encoder, or <jk>null</jk> if encoder isn't registered with that coding. */ - public Encoder getEncoder(String coding) { - EncoderMatch em = getEncoderMatch(coding); + public Encoder getEncoder(String encoding) { + EncoderMatch em = getEncoderMatch(encoding); return (em == null ? null : em.getEncoder()); } /** * Returns the set of codings supported by all encoders in this group. * - * @return The set of codings supported by all encoders in this group. Never <jk>null</jk>. + * @return An unmodifiable list of codings supported by all encoders in this group. Never <jk>null</jk>. */ public List<String> getSupportedEncodings() { - List<String> l = new ArrayList<String>(); - for (Encoder e : encoders) - for (String enc : e.getCodings()) - if (! l.contains(enc)) - l.add(enc); - return l; + return encodingsList; + } + + /** + * Returns the encoders in this group. + * + * @return An unmodifiable list of encoders in this group. + */ + public List<Encoder> getEncoders() { + return encoders; } }
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroupBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroupBuilder.java b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroupBuilder.java index 7a965c6..2180fd1 100644 --- a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroupBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderGroupBuilder.java @@ -12,6 +12,7 @@ // *************************************************************************************************************************** package org.apache.juneau.encoders; +import static org.apache.juneau.internal.CollectionUtils.*; import java.util.*; /** @@ -33,7 +34,8 @@ public class EncoderGroupBuilder { * @param copyFrom The encoder group that we're copying settings and encoders from. */ public EncoderGroupBuilder(EncoderGroup copyFrom) { - this.encoders = new ArrayList<Encoder>(Arrays.asList(copyFrom.encoders)); + this.encoders = new ArrayList<Encoder>(); + addReverse(encoders, copyFrom.getEncoders()); } /** @@ -43,9 +45,9 @@ public class EncoderGroupBuilder { * @return This object (for method chaining). */ public EncoderGroupBuilder append(Class<?>...e) { - for (Class<?> ee : e) { + for (int i = e.length-1; i >= 0; i--) { try { - encoders.add((Encoder)((Class<?>)ee).newInstance()); + encoders.add((Encoder)((Class<?>)e[i]).newInstance()); } catch (Exception x) { throw new RuntimeException(x); } @@ -60,7 +62,7 @@ public class EncoderGroupBuilder { * @return This object (for method chaining). */ public EncoderGroupBuilder append(Encoder...e) { - encoders.addAll(Arrays.asList(e)); + addReverse(encoders, e); return this; } @@ -70,8 +72,8 @@ public class EncoderGroupBuilder { * @param e The encoders to append to this group. * @return This object (for method chaining). */ - public EncoderGroupBuilder append(Collection<Encoder> e) { - encoders.addAll(e); + public EncoderGroupBuilder append(List<Encoder> e) { + addReverse(encoders, e); return this; } @@ -82,7 +84,7 @@ public class EncoderGroupBuilder { * @return This object (for method chaining). */ public EncoderGroupBuilder append(EncoderGroup eg) { - append(eg.encoders); + append(eg.getEncoders()); return this; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderMatch.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderMatch.java b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderMatch.java index e05b3e7..f5ecb3d 100644 --- a/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderMatch.java +++ b/juneau-core/src/main/java/org/apache/juneau/encoders/EncoderMatch.java @@ -12,18 +12,16 @@ // *************************************************************************************************************************** package org.apache.juneau.encoders; -import org.apache.juneau.*; - /** * Represents a encoder and encoding that matches an HTTP <code>Accept-Encoding</code> header value. */ public final class EncoderMatch { - private final MediaType mediaType; + private final String encoding; private final Encoder encoder; - EncoderMatch(MediaType mediaType, Encoder encoder) { - this.mediaType = mediaType; + EncoderMatch(String encoding, Encoder encoder) { + this.encoding = encoding; this.encoder = encoder; } @@ -33,7 +31,7 @@ public final class EncoderMatch { * @return The encoding of the match. */ public String getEncoding() { - return mediaType.getType(); + return encoding; } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java index b7030e5..d97afe4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java @@ -18,6 +18,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; import org.apache.juneau.dto.*; +import org.apache.juneau.http.*; import org.apache.juneau.internal.*; import org.apache.juneau.serializer.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java index 440c80b..0e334d1 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java @@ -18,6 +18,7 @@ import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.internal.*; import org.apache.juneau.json.*; import org.apache.juneau.serializer.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java index 304bfab..2095968 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java @@ -23,6 +23,7 @@ import javax.xml.stream.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.json.*; import org.apache.juneau.parser.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserBuilder.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserBuilder.java index be8cc9c..6c9f27c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserBuilder.java @@ -18,6 +18,7 @@ import javax.xml.stream.*; import javax.xml.stream.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.xml.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java index 5ebef14..2bf5e25 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParserSession.java @@ -22,6 +22,7 @@ import java.util.*; import javax.xml.stream.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.internal.*; import org.apache.juneau.xml.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java index eea6616..bc17387 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSchemaDocSerializer.java @@ -20,6 +20,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java index 168ac5c..c3d1088 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java @@ -21,6 +21,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; import org.apache.juneau.transform.*; import org.apache.juneau.xml.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerBuilder.java index fe0a7c5..6190c14 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerBuilder.java @@ -17,6 +17,7 @@ import static org.apache.juneau.html.HtmlSerializerContext.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.xml.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java index 4c1ee2f..3faf271 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java @@ -20,6 +20,7 @@ import java.util.*; import java.util.regex.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.internal.*; import org.apache.juneau.json.*; import org.apache.juneau.xml.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/Accept.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/Accept.java b/juneau-core/src/main/java/org/apache/juneau/http/Accept.java new file mode 100644 index 0000000..ce7119f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/Accept.java @@ -0,0 +1,231 @@ +// *************************************************************************************************************************** +// * 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 java.util.*; +import java.util.concurrent.*; + +import org.apache.juneau.internal.*; + +/** + * Represents a parsed <code>Accept:</code> HTTP header. + * <p> + * The formal RFC2616 header field definition is as follows: + * <p class='bcode'> + * 14.1 Accept + * + * The Accept request-header field can be used to specify certain media + * types which are acceptable for the response. Accept headers can be + * used to indicate that the request is specifically limited to a small + * set of desired types, as in the case of a request for an in-line + * image. + * + * Accept = "Accept" ": + * #( media-range [ accept-params ] ) + * + * media-range = ( "* /*" + * | ( type "/" "*" ) + * | ( type "/" subtype ) + * ) *( ";" parameter ) + * accept-params = ";" "q" "=" qvalue *( accept-extension ) + * accept-extension = ";" token [ "=" ( token | quoted-string ) ] + * + * The asterisk "*" character is used to group media types into ranges, + * with "* /*" indicating all media types and "type/*" indicating all + * subtypes of that type. The media-range MAY include media type + * parameters that are applicable to that range. + * + * Each media-range MAY be followed by one or more accept-params, + * beginning with the "q" parameter for indicating a relative quality + * factor. The first "q" parameter (if any) separates the media-range + * parameter(s) from the accept-params. Quality factors allow the user + * or user agent to indicate the relative degree of preference for that + * media-range, using the qvalue scale from 0 to 1 (section 3.9). The + * default value is q=1. + * + * Note: Use of the "q" parameter name to separate media type + * parameters from Accept extension parameters is due to historical + * practice. Although this prevents any media type parameter named + * "q" from being used with a media range, such an event is believed + * to be unlikely given the lack of any "q" parameters in the IANA + * media type registry and the rare usage of any media type + * parameters in Accept. Future media types are discouraged from + * registering any parameter named "q". + * + * The example + * + * Accept: audio/*; q=0.2, audio/basic + * + * SHOULD be interpreted as "I prefer audio/basic, but send me any audio + * type if it is the best available after an 80% mark-down in quality." + * + * If no Accept header field is present, then it is assumed that the + * client accepts all media types. If an Accept header field is present, + * and if the server cannot send a response which is acceptable + * according to the combined Accept field value, then the server SHOULD + * send a 406 (not acceptable) response. + * + * A more elaborate example is + * + * Accept: text/plain; q=0.5, text/html, + * text/x-dvi; q=0.8, text/x-c + * + * Verbally, this would be interpreted as "text/html and text/x-c are + * the preferred media types, but if they do not exist, then send the + * text/x-dvi entity, and if that does not exist, send the text/plain + * entity." + * + * Media ranges can be overridden by more specific media ranges or + * specific media types. If more than one media range applies to a given + * type, the most specific reference has precedence. For example, + * + * Accept: text/ *, text/html, text/html;level=1, * /* + * + * have the following precedence: + * + * 1) text/html;level=1 + * 2) text/html + * 3) text/* + * 4) * /* + * + * The media type quality factor associated with a given type is + * determined by finding the media range with the highest precedence + * which matches that type. For example, + * + * Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, + * text/html;level=2;q=0.4, * /*;q=0.5 + * + * would cause the following values to be associated: + * + * text/html;level=1 = 1 + * text/html = 0.7 + * text/plain = 0.3 + * image/jpeg = 0.5 + * text/html;level=2 = 0.4 + * text/html;level=3 = 0.7 + * + * Note: A user agent might be provided with a default set of quality + * values for certain media ranges. However, unless the user agent is + * a closed system which cannot interact with other rendering agents, + * this default set ought to be configurable by the user. + * </p> + */ +public final class Accept { + + private static final boolean nocache = Boolean.getBoolean("juneau.nocache"); + private static final ConcurrentHashMap<String,Accept> cache = new ConcurrentHashMap<String,Accept>(); + + private final MediaTypeRange[] mediaRanges; + private final List<MediaTypeRange> mediaRangesList; + + /** + * Returns a parsed <code>Accept</code> header. + * + * @param s The <code>Accept</code> header string. + * @return The parsed <code>Accept</code> header. + */ + public static Accept forString(String s) { + if (s == null) + s = "null"; + Accept a = cache.get(s); + if (a == null) { + a = new Accept(s); + if (nocache) + return a; + cache.putIfAbsent(s, a); + } + return cache.get(s); + } + + private Accept(String raw) { + this.mediaRanges = MediaTypeRange.parse(raw); + this.mediaRangesList = Collections.unmodifiableList(Arrays.asList(mediaRanges)); + } + + /** + * Returns the list of the media ranges that make up this header. + * <p> + * The media ranges in the list are sorted by their q-value in descending order. + * + * @return An unmodifiable list of media ranges. + */ + public List<MediaTypeRange> getMediaRanges() { + return mediaRangesList; + } + + /** + * Given a list of media types, returns the best match for this <code>Accept</code> header. + * <p> + * Note that fuzzy matching is allowed on the media types where the <code>Accept</code> header may + * contain additional subtype parts. + * <br>For example, given identical q-values and an <code>Accept</code> 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 <code>id</code> properties are present + * in the header. + * <p> + * See <a class='doclink' href='https://www.w3.org/TR/activitypub/#retrieving-objects'>ActivityPub / Retrieving Objects</a> + * <p> + * + * @param mediaTypes The media types to match against. + * @return The index into the array of the best match, or <code>-1</code> 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 : mediaRanges) { + float q2 = mr.getQValue(); + + if (q2 < q || q2 == 0) + break; + + for (int i = 0; i < mediaTypes.length; i++) { + MediaType mt = mediaTypes[i]; + int matchQuant2 = mt.match(mr.getMediaType()); + 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 fragement exists. + */ + public boolean hasSubtypePart(String part) { + + for (MediaTypeRange mr : this.mediaRanges) + if (mr.getQValue() > 0 && mr.getMediaType().getSubTypes().indexOf(part) >= 0) + return true; + + return false; + } + + @Override /* Object */ + public String toString() { + return StringUtils.join(mediaRanges, ','); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/AcceptEncoding.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/AcceptEncoding.java b/juneau-core/src/main/java/org/apache/juneau/http/AcceptEncoding.java new file mode 100644 index 0000000..e8c6886 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/AcceptEncoding.java @@ -0,0 +1,147 @@ +// *************************************************************************************************************************** +// * 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 java.util.*; +import java.util.concurrent.*; + +import org.apache.juneau.internal.*; + +/** + * Represents a parsed <code>Accept-Encoding:</code> HTTP header. + * <p> + * The formal RFC2616 header field definition is as follows: + * <p class='bcode'> + * The Accept-Encoding request-header field is similar to Accept, but restricts + * the content-codings (section 3.5) that are acceptable in the response. + * + * Accept-Encoding = "Accept-Encoding" ":" + * 1#( codings [ ";" "q" "=" qvalue ] ) + * codings = ( content-coding | "*" ) + * + * Examples of its use are: + * + * Accept-Encoding: compress, gzip + * Accept-Encoding: + * Accept-Encoding: * + * Accept-Encoding: compress;q=0.5, gzip;q=1.0 + * Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0 + * + * A server tests whether a content-coding is acceptable, according to an + * Accept-Encoding field, using these rules: + * + * 1. If the content-coding is one of the content-codings listed in + * the Accept-Encoding field, then it is acceptable, unless it is + * accompanied by a qvalue of 0. (As defined in section 3.9, a + * qvalue of 0 means "not acceptable.") + * 2. The special "*" symbol in an Accept-Encoding field matches any + * available content-coding not explicitly listed in the header + * field. + * 3. If multiple content-codings are acceptable, then the acceptable + * content-coding with the highest non-zero qvalue is preferred. + * 4. The "identity" content-coding is always acceptable, unless + * specifically refused because the Accept-Encoding field includes + * "identity;q=0", or because the field includes "*;q=0" and does + * not explicitly include the "identity" content-coding. If the + * Accept-Encoding field-value is empty, then only the "identity" + * encoding is acceptable. + * + * If an Accept-Encoding field is present in a request, and if the server cannot + * send a response which is acceptable according to the Accept-Encoding header, + * then the server SHOULD send an error response with the 406 (Not Acceptable) status code. + * + * If no Accept-Encoding field is present in a request, the server MAY assume + * that the client will accept any content coding. In this case, if "identity" + * is one of the available content-codings, then the server SHOULD use the "identity" + * content-coding, unless it has additional information that a different content-coding + * is meaningful to the client. + * + * Note: If the request does not include an Accept-Encoding field, + * and if the "identity" content-coding is unavailable, then + * content-codings commonly understood by HTTP/1.0 clients (i.e., + * "gzip" and "compress") are preferred; some older clients + * improperly display messages sent with other content-codings. The + * server might also make this decision based on information about + * the particular user-agent or client. + * Note: Most HTTP/1.0 applications do not recognize or obey qvalues + * associated with content-codings. This means that qvalues will not + * work and are not permitted with x-gzip or x-compress. + * </p> + */ +public final class AcceptEncoding { + + private static final boolean nocache = Boolean.getBoolean("juneau.nocache"); + private static final ConcurrentHashMap<String,AcceptEncoding> cache = new ConcurrentHashMap<String,AcceptEncoding>(); + + private final TypeRange[] typeRanges; + private final List<TypeRange> typeRangesList; + + /** + * Returns a parsed <code>Accept-Encoding</code> header. + * + * @param s The <code>Accept-Encoding</code> header string. + * @return The parsed <code>Accept-Encoding</code> header. + */ + public static AcceptEncoding forString(String s) { + if (s == null) + s = "null"; + AcceptEncoding a = cache.get(s); + if (a == null) { + a = new AcceptEncoding(s); + if (nocache) + return a; + cache.putIfAbsent(s, a); + } + return cache.get(s); + } + + private AcceptEncoding(String raw) { + this.typeRanges = TypeRange.parse(raw); + this.typeRangesList = Collections.unmodifiableList(Arrays.asList(typeRanges)); + } + + /** + * Returns the list of the types ranges that make up this header. + * <p> + * The types ranges in the list are sorted by their q-value in descending order. + * + * @return An unmodifiable list of type ranges. + */ + public List<TypeRange> getTypeRanges() { + return typeRangesList; + } + + /** + * Given a list of content codings, returns the best match for this <code>Accept-Encoding</code> header. + * <p> + * + * @param contentCodings The codings to match against. + * @return The index into the array of the best match, or <code>-1</code> if no suitable matches could be found. + */ + public int findMatch(String[] contentCodings) { + + // Type ranges are ordered by 'q'. + // So we only need to search until we've found a match. + for (TypeRange mr : typeRanges) + for (int i = 0; i < contentCodings.length; i++) + if (mr.matches(contentCodings[i])) + return i; + + return -1; + } + + @Override /* Object */ + public String toString() { + return StringUtils.join(typeRanges, ','); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/ContentType.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/ContentType.java b/juneau-core/src/main/java/org/apache/juneau/http/ContentType.java new file mode 100644 index 0000000..7f5570f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/ContentType.java @@ -0,0 +1,90 @@ +// *************************************************************************************************************************** +// * 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 java.util.concurrent.*; + +/** + * Represents a parsed <code>Content-Type:</code> HTTP header. + * <p> + * The formal RFC2616 header field definition is as follows: + * <p class='bcode'> + * 14.17 Content-Type + * + * The Content-Type entity-header field indicates the media type of the + * entity-body sent to the recipient or, in the case of the HEAD method, + * the media type that would have been sent had the request been a GET. + * + * Content-Type = "Content-Type" ":" media-type + * + * Media types are defined in section 3.7. An example of the field is + * + * Content-Type: text/html; charset=ISO-8859-4 + * </p> + */ +public class ContentType extends MediaType { + + private static final boolean nocache = Boolean.getBoolean("juneau.nocache"); + private static final ConcurrentHashMap<String,ContentType> cache = new ConcurrentHashMap<String,ContentType>(); + + /** + * Returns a parsed <code>Content-Type</code> header. + * + * @param s The <code>Content-Type</code> header string. + * @return The parsed <code>Content-Type</code> header. + */ + public static ContentType forString(String s) { + if (s == null) + return null; + ContentType mt = cache.get(s); + if (mt == null) { + mt = new ContentType(s); + if (nocache) + return mt; + cache.putIfAbsent(s, mt); + } + return cache.get(s); + } + + private ContentType(String s) { + super(s); + } + + /** + * Given a list of media types, returns the best match for this <code>Content-Type</code> header. + * <p> + * Note that fuzzy matching is allowed on the media types where the <code>Content-Types</code> header may + * contain additional subtype parts. + * <br>For example, given a <code>Content-Type</code> 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 parsers to match when artifacts such as <code>id</code> properties are present + * in the header. + * + * @param mediaTypes The media types to match against. + * @return The index into the array of the best match, or <code>-1</code> if no suitable matches could be found. + */ + public int findMatch(MediaType[] mediaTypes) { + int matchQuant = 0, matchIndex = -1; + + for (int i = 0; i < mediaTypes.length; i++) { + MediaType mt = mediaTypes[i]; + int matchQuant2 = mt.match(this); + if (matchQuant2 > matchQuant) { + matchIndex = i; + } + } + + return matchIndex; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/MediaType.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/MediaType.java b/juneau-core/src/main/java/org/apache/juneau/http/MediaType.java new file mode 100644 index 0000000..74f298f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/MediaType.java @@ -0,0 +1,277 @@ +// *************************************************************************************************************************** +// * 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 java.util.*; +import java.util.concurrent.*; + +import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; +import org.apache.juneau.json.*; + + +/** + * Describes a single media 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). + */ +@BeanIgnore +@SuppressWarnings("unchecked") +public class MediaType { + + private static final boolean nocache = Boolean.getBoolean("juneau.nocache"); + private static final ConcurrentHashMap<String,MediaType> cache = new ConcurrentHashMap<String,MediaType>(); + + /** Reusable predefined media type */ + @SuppressWarnings("javadoc") + public static final MediaType + CSV = forString("text/csv"), + HTML = forString("text/html"), + JSON = forString("application/json"), + MSGPACK = forString("octal/msgpack"), + PLAIN = forString("text/plain"), + UON = forString("text/uon"), + URLENCODING = forString("application/x-www-form-urlencoded"), + XML = forString("text/xml"), + XMLSOAP = forString("text/xml+soap"), + + RDF = forString("text/xml+rdf"), + RDFABBREV = forString("text/xml+rdf+abbrev"), + NTRIPLE = forString("text/n-triple"), + TURTLE = forString("text/turtle"), + N3 = forString("text/n3") + ; + + private final String mediaType; + private final String type; // The media type (e.g. "text" for Accept, "utf-8" for Accept-Charset) + private final String subType; // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset) + private final String[] subTypes; // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset) + private final List<String> subTypesList; // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset) + private final Map<String,Set<String>> parameters; // The media type parameters (e.g. "text/html;level=1"). Does not include q! + + + /** + * Returns the media type for the specified string. + * The same media type strings always return the same objects so that these objects + * can be compared for equality using '=='. + * <p> + * <h5 class='section'>Notes:</h5> + * <ul> + * <li>Spaces are replaced with <js>'+'</js> characters. + * This gets around the issue where passing media type strings with <js>'+'</js> as HTTP GET parameters + * get replaced with spaces by your browser. Since spaces aren't supported by the spec, this + * is doesn't break anything. + * <li>Anything including and following the <js>';'</js> character is ignored (e.g. <js>";charset=X"</js>). + * </ul> + * + * @param s The media type string. Will be lowercased. + * <br>Returns <jk>null</jk> if input is null. + * @return A cached media type object. + */ + public static MediaType forString(String s) { + if (s == null) + return null; + MediaType mt = cache.get(s); + if (mt == null) { + mt = new MediaType(s); + if (nocache) + return mt; + cache.putIfAbsent(s, mt); + } + return cache.get(s); + } + + MediaType(String mt) { + Builder b = new Builder(mt); + this.mediaType = b.mediaType; + this.type = b.type; + this.subType = b.subType; + this.subTypes = b.subTypes; + this.subTypesList = Collections.unmodifiableList(Arrays.asList(subTypes)); + this.parameters = (b.parameters == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(b.parameters)); + } + + private static class Builder { + private String mediaType, type, subType; + private String[] subTypes; + private Map<String,Set<String>> parameters; + + private Builder(String mt) { + mt = mt.trim(); + + int i = mt.indexOf(';'); + if (i == -1) { + this.parameters = Collections.EMPTY_MAP; + } else { + this.parameters = new TreeMap<String,Set<String>>(); + String[] tokens = mt.substring(i+1).split(";"); + + for (int j = 0; j < tokens.length; j++) { + String[] parm = tokens[j].split("="); + if (parm.length == 2) { + String k = parm[0].trim(), v = parm[1].trim(); + if (! parameters.containsKey(k)) + parameters.put(k, new TreeSet<String>()); + parameters.get(k).add(v); + } + } + + mt = mt.substring(0, i); + } + + this.mediaType = mt; + if (mt != null) { + mt = mt.replace(' ', '+'); + i = mt.indexOf('/'); + type = (i == -1 ? mt : mt.substring(0, i)); + subType = (i == -1 ? "*" : mt.substring(i+1)); + } + this.subTypes = StringUtils.split(subType, '+'); + } + } + + /** + * Returns the <js>'type'</js> fragment of the <js>'type/subType'</js> string. + * + * @return The media type. + */ + public String getType() { + return type; + } + + /** + * Returns the <js>'subType'</js> fragment of the <js>'type/subType'</js> string. + * + * @return The media subtype. + */ + public String getSubType() { + return subType; + } + + /** + * Returns the subtypes broken down by fragments delimited by <js>"'"</js>. + * For example, the media type <js>"text/foo+bar"</js> will return a list of + * <code>[<js>'foo'</js>,<js>'bar'</js>]</code> + * + * @return An unmodifiable list of subtype fragments. Never <jk>null</jk>. + */ + public List<String> getSubTypes() { + return subTypesList; + } + + /** + * Returns <jk>true</jk> if this media type is a match for the specified media type. + * <p> + * Matches if any of the following is true: + * <ul> + * <li>Both type and subtype are the same. + * <li>One or both types are <js>'*'</js> and the subtypes are the same. + * <li>One or both subtypes are <js>'*'</js> and the types are the same. + * <li>Either is <js>'*\/*'</js>. + * </ul> + * + * @param o The media type to compare with. + * @return <jk>true</jk> if the media types match. + */ + public final boolean matches(MediaType o) { + return match(o) > 0; + } + + /** + * Returns a match metric against the specified media type where a larger number represents a better match. + * <p> + * <ul> + * <li>Exact matches (e.g. <js>"text/json"<js>/</js>"text/json"</js>) should match + * better than metacharacter matches (e.g. <js>"text/*"<js>/</js>"text/json"</js>) + * <li>The comparison media type can have additional subtype tokens (e.g. <js>"text/json+foo"</js>) + * that will not prevent a match. The reverse is not true, e.g. the comparison media type + * must contain all subtype tokens found in the comparing media type. + * <ul> + * <li>We want the {@link JsonSerializer} (<js>"text/json"</js>) class to be able to handle requests for <js>"text/json+foo"</js>. + * <li>We want to make sure {@link org.apache.juneau.json.JsonSerializer.Simple} (<js>"text/json+simple"</js>) does not handle + * requests for <js>"text/json"</js>. + * </ul> + * More token matches should result in a higher match number. + * </ul> + * + * @param o The media type to compare with. + * @return <jk>true</jk> if the media types match. + */ + public final int match(MediaType o) { + + // Perfect match + if (this == o || (type.equals(o.type) && subType.equals(o.subType))) + return Integer.MAX_VALUE; + + int c1 = 0, c2 = 0; + + if (type.equals(o.type)) + c1 += 10000; + else if ("*".equals(type) || "*".equals(o.type)) + c1 += 5000; + + if (c1 == 0) + return 0; + + // Give type slightly higher comparison value than subtype simply for deterministic results. + if (subType.equals(o.subType)) + return c1 + 9999; + + int c3 = 0; + + for (String st1 : subTypes) { + if ("*".equals(st1)) + c1++; + else if (ArrayUtils.contains(st1, o.subTypes)) + c1 += 100; + else if (ArrayUtils.contains("*", o.subTypes)) + c1 += 10; + else + return 0; + } + + return c1 + c2 + c3; + } + + /** + * Returns the additional parameters on this media type. + * <p> + * For example, given the media type string <js>"text/html;level=1"</js>, will return a map + * with the single entry <code>{level:[<js>'1'</js>]}</code>. + * + * @return The map of additional parameters, or an empty map if there are no parameters. + */ + public Map<String,Set<String>> getParameters() { + return parameters; + } + + @Override /* Object */ + public String toString() { + if (parameters.isEmpty()) + return mediaType; + StringBuilder sb = new StringBuilder(mediaType); + for (Map.Entry<String,Set<String>> e : parameters.entrySet()) + for (String value : e.getValue()) + sb.append(';').append(e.getKey()).append('=').append(value); + return sb.toString(); + } + + @Override /* Object */ + public int hashCode() { + return mediaType.hashCode(); + } + + @Override /* Object */ + public boolean equals(Object o) { + return this == o; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/MediaTypeRange.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/MediaTypeRange.java b/juneau-core/src/main/java/org/apache/juneau/http/MediaTypeRange.java new file mode 100644 index 0000000..9ff7c4a --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/MediaTypeRange.java @@ -0,0 +1,270 @@ +// *************************************************************************************************************************** +// * 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 java.util.*; +import java.util.Map.*; + +import org.apache.juneau.annotation.*; +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). + */ +@BeanIgnore +public final class MediaTypeRange implements Comparable<MediaTypeRange> { + + private static final MediaTypeRange[] DEFAULT = new MediaTypeRange[]{new MediaTypeRange("*/*")}; + + private final MediaType mediaType; + private final Float qValue; + private final Map<String,Set<String>> extensions; + + /** + * Parses an <code>Accept</code> 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 <code>value</code> complies with the syntax described in RFC2616, Section 14.1, as described below: + * <p class='bcode'> + * 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 <code>MediaTypeRange</code> 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; + + if (value.indexOf(',') == -1) + return new MediaTypeRange[]{new MediaTypeRange(value)}; + + Set<MediaTypeRange> ranges = new TreeSet<MediaTypeRange>(); + + for (String r : StringUtils.split(value, ',')) { + r = r.trim(); + + if (r.isEmpty()) + continue; + + ranges.add(new MediaTypeRange(r)); + } + + return ranges.toArray(new MediaTypeRange[ranges.size()]); + } + + @SuppressWarnings("unchecked") + private MediaTypeRange(String token) { + Builder b = new Builder(token); + this.mediaType = b.mediaType; + this.qValue = b.qValue; + this.extensions = (b.extensions == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(b.extensions)); + } + + private static class Builder { + private MediaType mediaType; + private Float qValue = 1f; + private Map<String,Set<String>> extensions; + + private Builder(String token) { + + token = token.trim(); + + int i = token.indexOf(";q="); + + if (i == -1) { + mediaType = MediaType.forString(token); + return; + } + + mediaType = MediaType.forString(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<String,Set<String>>(); + 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 <code>0.0</code> (unacceptable) and <code>1.0</code> (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 <code>Accept</code> header value. + * <p> + * The literal text generated will be all lowercase. + * + * @return A media range suitable for use as an Accept header value, never <code>null</code>. + */ + @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 <code>MediaType</code>, and has the same qValue, type, parameters, and extensions. + * + * @return <jk>true</jk> if object is equivalent. + */ + @Override /* Object */ + public boolean equals(Object o) { + + if (o == null || !(o instanceof MediaTypeRange)) + return false; + + if (this == o) + return true; + + MediaTypeRange o2 = (MediaTypeRange) o; + return qValue.equals(o2.qValue) + && mediaType.equals(o2.mediaType) + && extensions.equals(o2.extensions); + } + + /** + * Returns a hash based on this instance's <code>media-type</code>. + * + * @return A hash based on this instance's <code>media-type</code>. + */ + @Override /* Object */ + public int hashCode() { + return mediaType.hashCode(); + } + + /** + * Compares two MediaRanges for equality. + * <p> + * The values are first compared according to <code>qValue</code> values. + * Should those values be equal, the <code>type</code> is then lexicographically compared (case-insensitive) in ascending order, + * with the <js>"*"</js> type demoted last in that order. + * <code>MediaRanges</code> with the same type but different sub-types are compared - a more specific subtype is + * promoted over the 'wildcard' subtype. + * <code>MediaRanges</code> 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; + } + + /** + * Matches the specified media type against this range and returns a q-value + * between 0 and 1 indicating the quality of the match. + * + * @param o The media type to match against. + * @return A float between 0 and 1. 1 is a perfect match. 0 is no match at all. + */ + public float matches(MediaType o) { + if (this.mediaType == o || mediaType.matches(o)) + return qValue; + return 0; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/http/TypeRange.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/http/TypeRange.java b/juneau-core/src/main/java/org/apache/juneau/http/TypeRange.java new file mode 100644 index 0000000..a05189f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/http/TypeRange.java @@ -0,0 +1,276 @@ +// *************************************************************************************************************************** +// * 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 java.util.*; +import java.util.Map.*; + +import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; + +/** + * Represents a single value in a comma-delimited header value that optionally contains a quality + * metric for comparison and extension parameters. + * <p> + * Similar in concept to {@link MediaTypeRange} except instead of media types (e.g. <js>"text/json"</js>), + * it's a simple type (e.g. <js>"iso-8601"</js>). + * <p> + * An example of a type range is a value in an <code>Accept-Encoding</code> header. + */ +@BeanIgnore +public final class TypeRange implements Comparable<TypeRange> { + + private static final TypeRange[] DEFAULT = new TypeRange[]{new TypeRange("*")}; + + private final String type; + private final Float qValue; + private final Map<String,Set<String>> extensions; + + /** + * Parses a header such as an <code>Accept-Encoding</code> header value into an array of type ranges. + * <p> + * The syntax expected to be found in the referenced <code>value</code> complies with the syntax described in RFC2616, Section 14.1, as described below: + * <p class='bcode'> + * Accept-Encoding = "Accept-Encoding" ":" + * 1#( codings [ ";" "q" "=" qvalue ] ) + * codings = ( content-coding | "*" ) + * </p> + * <p> + * Examples of its use are: + * <p class='bcode'> + * Accept-Encoding: compress, gzip + * Accept-Encoding: + * Accept-Encoding: * + * Accept-Encoding: compress;q=0.5, gzip;q=1.0 + * Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0 + * </p> + * + * @param value The value to parse. If <jk>null</jk> or empty, returns a single <code>TypeRange</code> is returned that represents all types. + * @return The type ranges described by the string. + * <br>The ranges are sorted such that the most acceptable type is available at ordinal position <js>'0'</js>, and the least acceptable at position n-1. + */ + public static TypeRange[] parse(String value) { + + if (value == null || value.length() == 0) + return DEFAULT; + + if (value.indexOf(',') == -1) + return new TypeRange[]{new TypeRange(value)}; + + Set<TypeRange> ranges = new TreeSet<TypeRange>(); + + for (String r : StringUtils.split(value, ',')) { + r = r.trim(); + + if (r.isEmpty()) + continue; + + ranges.add(new TypeRange(r)); + } + + return ranges.toArray(new TypeRange[ranges.size()]); + } + + @SuppressWarnings("unchecked") + private TypeRange(String token) { + Builder b = new Builder(token); + this.type = b.type; + this.qValue = b.qValue; + this.extensions = (b.extensions == null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(b.extensions)); + } + + private static class Builder { + private String type; + private Float qValue = 1f; + private Map<String,Set<String>> extensions; + + private Builder(String token) { + + token = token.trim(); + + int i = token.indexOf(";q="); + + if (i == -1) { + type = token; + return; + } + + type = 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<String,Set<String>>(); + 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 type enclosed by this type range. + * + * <h5 class='section'>Examples:</h5> + * <ul> + * <li><js>"compress"</js> + * <li><js>"gzip"</js> + * <li><js>"*"</js> + * </ul> + * + * @return The type of this type range, lowercased, never <jk>null</jk>. + */ + public String getType() { + return type; + } + + /** + * 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 <code>0.0</code> (unacceptable) and <code>1.0</code> (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 <code>Accept</code> header value. + * <p> + * The literal text generated will be all lowercase. + * + * @return A media range suitable for use as an Accept header value, never <code>null</code>. + */ + @Override /* Object */ + public String toString() { + StringBuffer sb = new StringBuffer().append(type); + + // '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 <code>MediaType</code>, and has the same qValue, type, parameters, and extensions. + * + * @return <jk>true</jk> if object is equivalent. + */ + @Override /* Object */ + public boolean equals(Object o) { + + if (o == null || !(o instanceof TypeRange)) + return false; + + if (this == o) + return true; + + TypeRange o2 = (TypeRange) o; + return qValue.equals(o2.qValue) + && type.equals(o2.type) + && extensions.equals(o2.extensions); + } + + /** + * Returns a hash based on this instance's <code>media-type</code>. + * + * @return A hash based on this instance's <code>media-type</code>. + */ + @Override /* Object */ + public int hashCode() { + return type.hashCode(); + } + + /** + * Compares two MediaRanges for equality. + * <p> + * The values are first compared according to <code>qValue</code> values. + * Should those values be equal, the <code>type</code> is then lexicographically compared (case-insensitive) in ascending order, + * with the <js>"*"</js> type demoted last in that order. + * <code>TypeRanges</code> 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(TypeRange 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.type.toString().compareTo(type.toString()); + return i; + } + + /** + * Checks if the specified type matches this range. + * <p> + * The type will match this range if the range type string is the same or <js>"*"</js>. + * + * @param type The type to match against this range. + * @return <jk>true</jk> if the specified type matches this range. + */ + @SuppressWarnings("hiding") + public boolean matches(String type) { + if (qValue == 0) + return false; + return this.type.equals(type) || this.type.equals("*"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWritable.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWritable.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWritable.java index 5bc7009..b8c504c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWritable.java +++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWritable.java @@ -15,6 +15,7 @@ package org.apache.juneau.ini; import java.io.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; /** * Wraps a {@link ConfigFile} in a {@link Writable} to be rendered as plain text. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java index effd363..5199af1 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java @@ -271,6 +271,39 @@ public final class ArrayUtils { } /** + * Returns <jk>true</jk> if the specified array contains the specified element + * using the {@link String#equals(Object)} method. + * + * @param element The element to check for. + * @param array The array to check. + * @return <jk>true</jk> if the specified array contains the specified element, + * <jk>false</jk> if the array or element is <jk>null</jk>. + */ + public static boolean contains(String element, String[] array) { + return indexOf(element, array) != -1; + } + + /** + * Returns the index position of the element in the specified array + * using the {@link String#equals(Object)} method. + * + * @param element The element to check for. + * @param array The array to check. + * @return The index position of the element in the specified array, or + * <code>-1</code> if the array doesn't contain the element, or the array or element is <jk>null</jk>. + */ + public static int indexOf(String element, String[] array) { + if (element == null) + return -1; + if (array == null) + return -1; + for (int i = 0; i < array.length; i++) + if (element.equals(array[i])) + return i; + return -1; + } + + /** * Converts a primitive wrapper array (e.g. <code>Integer[]</code>) to a primitive array (e.g. <code><jk>int</jk>[]</code>). * * @param o The array to convert. Must be a primitive wrapper array. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java index c413427..6c12246 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java @@ -52,4 +52,36 @@ public class CollectionUtils { l.add(o); return l; } + + /** + * Adds the contents of one list to the other in reverse order. + * <p> + * i.e. add values from 2nd list from end-to-start order to the end of the 1st list. + * + * @param list The list to append to. + * @param append Contains the values to append to the list. + * @return The same list. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List<?> addReverse(List list, List append) { + for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();) + list.add(i.previous()); + return list; + } + + /** + * Adds the contents of the array to the list in reverse order. + * <p> + * i.e. add values from the array from end-to-start order to the end of the list. + * + * @param list The list to append to. + * @param append Contains the values to append to the list. + * @return The same list. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List<?> addReverse(List list, Object[] append) { + for (int i = append.length - 1; i >= 0; i--) + list.add(append[i]); + return list; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java index a5bc00e..bee95d9 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java @@ -480,6 +480,8 @@ public final class StringUtils { return null; if (isEmpty(s)) return new String[0]; + if (s.indexOf(c) == -1) + return new String[]{s}; List<String> l = new LinkedList<String>(); char[] sArray = s.toCharArray(); @@ -522,6 +524,83 @@ public final class StringUtils { } /** + * Splits a list of key-value pairs into an ordered map. + * <p> + * Example: + * <p class='bcode'> + * String in = <js>"foo=1;bar=2"</js>; + * Map m = StringUtils.<jsm>splitMap</jsm>(in, <js>';'</js>, <js>'='</js>, <jk>true</jk>); + * </p> + * + * @param s The string to split. + * @param delim The delimiter between the key-value pairs. + * @param eq The delimiter between the key and value. + * @param trim Trim strings after parsing. + * @return The parsed map. Never <jk>null</jk>. + */ + @SuppressWarnings("unchecked") + public static Map<String,String> splitMap(String s, char delim, char eq, boolean trim) { + + char[] unEscapeChars = new char[]{'\\', delim, eq}; + + if (s == null) + return null; + if (isEmpty(s)) + return Collections.EMPTY_MAP; + + Map<String,String> m = new LinkedHashMap<String,String>(); + + int + S1 = 1, // Found start of key, looking for equals. + S2 = 2; // Found equals, looking for delimiter (or end). + + int state = S1; + + char[] sArray = s.toCharArray(); + int x1 = 0, escapeCount = 0; + String key = null; + for (int i = 0; i < sArray.length + 1; i++) { + char c = i == sArray.length ? delim : sArray[i]; + if (c == '\\') + escapeCount++; + if (escapeCount % 2 == 0) { + if (state == S1) { + if (c == eq) { + key = s.substring(x1, i); + if (trim) + key = trim(key); + key = unEscapeChars(key, unEscapeChars); + state = S2; + x1 = i+1; + } else if (c == delim) { + key = s.substring(x1, i); + if (trim) + key = trim(key); + key = unEscapeChars(key, unEscapeChars); + m.put(key, ""); + state = S1; + x1 = i+1; + } + } else if (state == S2) { + if (c == delim) { + String val = s.substring(x1, i); + if (trim) + val = trim(val); + val = unEscapeChars(val, unEscapeChars); + m.put(key, val); + key = null; + x1 = i+1; + state = S1; + } + } + } + if (c != '\\') escapeCount = 0; + } + + return m; + } + + /** * Returns <jk>true</jk> if specified string is <jk>null</jk> or empty. * * @param s The string to check. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/jso/JsoParserBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/jso/JsoParserBuilder.java b/juneau-core/src/main/java/org/apache/juneau/jso/JsoParserBuilder.java index 2608028..210460b 100644 --- a/juneau-core/src/main/java/org/apache/juneau/jso/JsoParserBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/jso/JsoParserBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.jso; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java index c4d7d40..88d89c5 100644 --- a/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.jso; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java index d362112..c07c8b7 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java @@ -20,6 +20,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.internal.*; import org.apache.juneau.parser.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonParserBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserBuilder.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserBuilder.java index ed550cc..1d6a3ad 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.json; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java index 02ae190..f495bca 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java @@ -17,6 +17,7 @@ import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java index 3e28eab..df974ea 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java @@ -20,6 +20,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerBuilder.java index 9a8616f..eb4a1ea 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.json; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; /** * Builder class for building instances of JSON Schema serializers. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java index 91945d4..71859f4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java @@ -19,6 +19,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java index 97323aa..0715dd1 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java @@ -17,6 +17,7 @@ import static org.apache.juneau.json.JsonSerializerContext.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java index dd8b8a1..ef1a975 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java @@ -18,6 +18,7 @@ import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java index 8895f33..bd9128c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java @@ -19,6 +19,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserBuilder.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserBuilder.java index 3e68801..26df53b 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.msgpack; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java index 24a40e7..57beda3 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java @@ -17,6 +17,7 @@ import java.lang.reflect.*; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.parser.*; /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java index 89e736b..e0108ea 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java @@ -17,6 +17,7 @@ import java.util.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; import org.apache.juneau.transform.*; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/c3609d05/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java index f909e14..2c29538 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java @@ -15,6 +15,7 @@ package org.apache.juneau.msgpack; import java.util.*; import org.apache.juneau.*; +import org.apache.juneau.http.*; import org.apache.juneau.serializer.*; /**
