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

org.apache.juneau.MediaRanges 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.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 or similar header value.
 *
 * 

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

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

* Accept = "Accept" ":" * #( media-range [ accept-params ] ) * * media-range = ( "*\/*" * | ( type "/" "*" ) * | ( type "/" subtype ) * ) *( ";" parameter ) * accept-params = ";" "q" "=" qvalue *( accept-extension ) * accept-extension = ";" token [ "=" ( token | quoted-string ) ] *

* *
See Also:
*/ @BeanIgnore public class MediaRanges { //----------------------------------------------------------------------------------------------------------------- // Static //----------------------------------------------------------------------------------------------------------------- /** Represents an empty media ranges object. */ public static final MediaRanges EMPTY = new MediaRanges(""); private static final Cache CACHE = Cache.of(String.class, MediaRanges.class).build(); private final MediaRange[] ranges; private final String string; /** * Returns a parsed Accept header value. * * @param value The raw Accept header value. * @return A parsed Accept header value. */ public static MediaRanges of(String value) { return isEmpty(value) ? EMPTY : CACHE.get(value, ()->new MediaRanges(value)); } //----------------------------------------------------------------------------------------------------------------- // Instance //----------------------------------------------------------------------------------------------------------------- /** * Constructor. * * @param value The Accept header value. */ public MediaRanges(String value) { this(parse(value)); } /** * Constructor. * * @param e The parsed header value. */ public MediaRanges(HeaderElement[] e) { ranges = new MediaRange[e.length]; for (int i = 0; i < e.length; i++) ranges[i] = new MediaRange(e[i]); Arrays.sort(ranges, RANGE_COMPARATOR); this.string = ranges.length == 1 ? ranges[0].toString() : StringUtils.join(ranges, ','); } /** * 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. * MediaRanges with the same type but different sub-types are compared - a more specific subtype is * promoted over the 'wildcard' subtype. * MediaRanges with the same types but with extensions are promoted over those same types with no * extensions. */ 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 Accept header. * *

* Note that fuzzy matching is allowed on the media types where the Accept header may * contain additional subtype parts. *
For example, given identical q-values and an Accept 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 mediaTypes The media types 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 mediaTypes) { if (string.isEmpty() || mediaTypes == null) 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 (MediaRange mr : ranges) { float q2 = mr.getQValue(); if (q2 < q || q2 == 0) break; for (int i = 0; i < mediaTypes.size(); i++) { MediaType mt = mediaTypes.get(i); int matchQuant2 = mr.match(mt, false); if (matchQuant2 > matchQuant) { matchIndex = i; matchQuant = matchQuant2; q = q2; } } } return matchIndex; } /** * 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 MediaRange getRange(int index) { if (index < 0 || index >= ranges.length) return null; return ranges[index]; } /** * Convenience method for searching through all of the subtypes of all the media ranges in this header for the * presence of a subtype fragment. * *

* For example, given the header "text/json+activity", calling * hasSubtypePart("activity") returns true. * * @param part The media type subtype fragment. * @return true if subtype fragment exists. */ public boolean hasSubtypePart(String part) { for (MediaRange mr : ranges) if (mr.getQValue() > 0 && mr.getSubTypes().indexOf(part) >= 0) return true; return false; } /** * Returns the media ranges that make up this object. * * @return The media ranges that make up this object. */ public List toList() { return ulist(ranges); } /** * Performs an action on the media ranges that make up this object. * * @param action The action to perform. * @return This object. */ public MediaRanges forEachRange(Consumer action) { for (MediaRange r : ranges) action.accept(r); return this; } private static HeaderElement[] parse(String value) { return BasicHeaderValueParser.parseElements(emptyIfNull(trim(value)), null); } @Override /* Object */ public String toString() { return string; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy