All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.http.StringRange Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.
 *
 * 

* Similar in concept to {@link MediaTypeRange} except instead of media types (e.g. "text/json"), * it's a simple type (e.g. "iso-8601"). * *

* An example of a type range is a value in an Accept-Encoding header. * *

Additional Information
* */ @BeanIgnore public final class StringRange implements Comparable { private static final StringRange[] DEFAULT = new StringRange[]{new StringRange("*")}; private final String type; private final Float qValue; private final Map> extensions; /** * Parses a header such as an Accept-Encoding header value into an array of type ranges. * *

* The syntax expected to be found in the referenced value complies with the syntax described in * RFC2616, Section 14.1, as described below: *

* 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 *

* * @param value * The value to parse. * If null or empty, returns a single TypeRange is returned that represents all types. * @return * The type ranges described by the string. *
The ranges are sorted such that the most acceptable type is available at ordinal position '0', and * the least acceptable at position n-1. */ public static StringRange[] parse(String value) { if (value == null || value.length() == 0) return DEFAULT; if (value.indexOf(',') == -1) return new StringRange[]{new StringRange(value)}; Set ranges = new TreeSet(); for (String r : StringUtils.split(value)) { r = r.trim(); if (r.isEmpty()) continue; ranges.add(new StringRange(r)); } return ranges.toArray(new StringRange[ranges.size()]); } @SuppressWarnings("unchecked") private StringRange(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> 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>(); if (! extensions.containsKey(k)) extensions.put(k, new TreeSet()); extensions.get(k).add(v); } else if (k.equals("q")) { qValue = new Float(v); isInExtensions = true; } } } } } } /** * Returns the type enclosed by this type range. * *
Examples:
*
    *
  • "compress" *
  • "gzip" *
  • "*" *
* * @return The type of this type range, lowercased, never null. */ public String getType() { return type; } /** * Returns the 'q' (quality) value for this type, as described in Section 3.9 of RFC2616. * *

* The quality value is a float between 0.0 (unacceptable) and 1.0 (most acceptable). * *

* If 'q' value doesn't make sense for the context (e.g. this range was extracted from a "content-*" * header, as opposed to "accept-*" header, its value will always be "1". * * @return The 'q' value for this type, never null. */ public Float getQValue() { return qValue; } /** * Returns the optional set of custom extensions defined for this type. * *

* Values are lowercase and never null. * * @return The optional list of extensions, never null. */ public Map> getExtensions() { return extensions; } /** * Provides a string representation of this media range, suitable for use as an Accept header value. * *

* The literal text generated will be all lowercase. * * @return A media range suitable for use as an Accept header value, never null. */ @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> 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> e : extensions.entrySet()) { String k = e.getKey(); for (String v : e.getValue()) sb.append(';').append(k).append('=').append(v); } } return sb.toString(); } /** * Returns true if the specified object is also a MediaType, and has the same qValue, type, * parameters, and extensions. * * @return true if object is equivalent. */ @Override /* Object */ public boolean equals(Object o) { if (o == null || !(o instanceof StringRange)) return false; if (this == o) return true; StringRange o2 = (StringRange) o; return qValue.equals(o2.qValue) && type.equals(o2.type) && extensions.equals(o2.extensions); } /** * Returns a hash based on this instance's media-type. * * @return A hash based on this instance's media-type. */ @Override /* Object */ public int hashCode() { return type.hashCode(); } /** * Compares two MediaRanges for equality. * *

* The values are first compared according to qValue values. * Should those values be equal, the type is then lexicographically compared (case-insensitive) in * ascending order, with the "*" type demoted last in that order. * TypeRanges with the same types but with extensions are promoted over those same types with no * extensions. * * @param o The range to compare to. Never null. */ @Override /* Comparable */ public int compareTo(StringRange 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. * *

* The type will match this range if the range type string is the same or "*". * * @param type The type to match against this range. * @return true if the specified type matches this range. */ public boolean matches(String type) { if (qValue == 0) return false; return this.type.equals(type) || this.type.equals("*"); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy