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

org.apache.juneau.StringRanges Maven / Gradle / Ivy

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

import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.internal.ArrayUtils.copyOf;
import static org.apache.juneau.internal.CollectionUtils.*;

import java.util.*;
import java.util.function.*;

import org.apache.http.*;
import org.apache.http.message.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.common.internal.*;
import org.apache.juneau.internal.*;

/**
 * A parsed Accept-Encoding or similar header value.
 *
 * 

* The returned ranges are sorted such that the most acceptable value is available at ordinal position * '0', and the least acceptable at position n-1. * *

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

* *
See Also:
*/ @BeanIgnore public class StringRanges { //----------------------------------------------------------------------------------------------------------------- // Static //----------------------------------------------------------------------------------------------------------------- /** Represents an empty string ranges object. */ public static final StringRanges EMPTY = new StringRanges(""); private static final Cache CACHE = Cache.of(String.class, StringRanges.class).build(); /** * Returns a parsed string range header value. * * @param value The raw header value. * @return A parsed header value. */ public static StringRanges of(String value) { return isEmpty(value) ? EMPTY : CACHE.get(value, ()->new StringRanges(value)); } /** * Returns a parsed string range header value. * * @param value The raw header value. * @return A parsed header value. */ public static StringRanges of(StringRange...value) { return value == null ? null : new StringRanges(value); } //----------------------------------------------------------------------------------------------------------------- // Instance //----------------------------------------------------------------------------------------------------------------- private final StringRange[] value; private final String string; /** * Constructor. * * @param value The string range header value. */ public StringRanges(String value) { this(parse(value)); } /** * Constructor. * * @param value The string range header value. */ public StringRanges(StringRange...value) { this.string = join(value, ", "); this.value = copyOf(value); } /** * Constructor. * * @param e The parsed string range header value. */ public StringRanges(HeaderElement...e) { value = new StringRange[e.length]; for (int i = 0; i < e.length; i++) value[i] = new StringRange(e[i]); Arrays.sort(value, RANGE_COMPARATOR); this.string = value.length == 1 ? value[0].toString() : StringUtils.join(value, ", "); } /** * Compares two StringRanges 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. */ private static final Comparator RANGE_COMPARATOR = (o1, 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. return o2.toString().compareTo(o1.toString()); }; /** * Given a list of media types, returns the best match for this string range header. * *

* Note that fuzzy matching is allowed on the media types where the string range header may * contain additional subtype parts. *
For example, given identical q-values and an string range value of "text/json+activity", * the media type "text/json" will match if "text/json+activity" or "text/activity+json" * isn't found. *
The purpose for this is to allow serializers to match when artifacts such as id properties are * present in the header. * *

* See ActivityPub / Retrieving Objects * * @param names The names to match against. * @return The index into the array of the best match, or -1 if no suitable matches could be found. */ public int match(List names) { if (string.isEmpty()) return -1; 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 (StringRange mr : value) { float q2 = mr.getQValue(); if (q2 < q || q2 == 0) break; for (int i = 0; i < names.size(); i++) { String mt = names.get(i); int matchQuant2 = mr.match(mt); if (matchQuant2 > matchQuant) { matchIndex = i; matchQuant = matchQuant2; q = q2; } } } return matchIndex; } /** * 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 null if the index is out of range. */ public StringRange getRange(int index) { if (index < 0 || index >= value.length) return null; return value[index]; } /** * Returns the string ranges that make up this object. * * @return The string ranges that make up this object. */ public List toList() { return ulist(value); } /** * Performs an action on the string ranges that make up this object. * * @param action The action to perform. * @return This object. */ public StringRanges forEachRange(Consumer action) { for (StringRange r : value) action.accept(r); return this; } private static HeaderElement[] parse(String value) { return value == null ? null : BasicHeaderValueParser.parseElements(emptyIfNull(trim(value)), null); } @Override /* Object */ public String toString() { return string; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy