//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.ToIntFunction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.Integer.MIN_VALUE;

Implements a quoted comma separated list of quality values in accordance with RFC7230 and RFC7231. Values are returned sorted in quality order, with OWS and the quality parameters removed.
See Also:
  • https://tools.ietf.org/html/rfc7230#section-3.2.6
  • https://tools.ietf.org/html/rfc7230#section-7
  • https://tools.ietf.org/html/rfc7231#section-5.3.1
/** * Implements a quoted comma separated list of quality values * in accordance with RFC7230 and RFC7231. * Values are returned sorted in quality order, with OWS and the * quality parameters removed. * * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6" * @see "https://tools.ietf.org/html/rfc7230#section-7" * @see "https://tools.ietf.org/html/rfc7231#section-5.3.1" */
public class QuotedQualityCSV extends QuotedCSV implements Iterable<String> { private static final Logger LOG = LoggerFactory.getLogger(QuotedQualityCSV.class);
Lambda to apply a most specific MIME encoding secondary ordering.
See Also:
  • https://tools.ietf.org/html/rfc7231#section-5.3.2
/** * Lambda to apply a most specific MIME encoding secondary ordering. * * @see "https://tools.ietf.org/html/rfc7231#section-5.3.2" */
public static ToIntFunction<String> MOST_SPECIFIC_MIME_ORDERING = s -> { if ("*/*".equals(s)) return 0; if (s.endsWith("/*")) return 1; if (s.indexOf(';') < 0) return 2; return 3; }; private final List<Double> _quality = new ArrayList<>(); private boolean _sorted = false; private final ToIntFunction<String> _secondaryOrdering;
Sorts values with equal quality according to the length of the value String.
/** * Sorts values with equal quality according to the length of the value String. */
public QuotedQualityCSV() { this((ToIntFunction)null); }
Sorts values with equal quality according to given order.
Params:
  • preferredOrder – Array indicating the preferred order of known values
/** * Sorts values with equal quality according to given order. * * @param preferredOrder Array indicating the preferred order of known values */
public QuotedQualityCSV(String[] preferredOrder) { this((s) -> { for (int i = 0; i < preferredOrder.length; ++i) { if (preferredOrder[i].equals(s)) return preferredOrder.length - i; } if ("*".equals(s)) return preferredOrder.length; return MIN_VALUE; }); }
Orders values with equal quality with the given function.
Params:
  • secondaryOrdering – Function to apply an ordering other than specified by quality
/** * Orders values with equal quality with the given function. * * @param secondaryOrdering Function to apply an ordering other than specified by quality */
public QuotedQualityCSV(ToIntFunction<String> secondaryOrdering) { this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering; } @Override protected void parsedValue(StringBuffer buffer) { super.parsedValue(buffer); // Assume a quality of ONE _quality.add(1.0D); } @Override protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) { if (paramName < 0) { if (buffer.charAt(buffer.length() - 1) == ';') buffer.setLength(buffer.length() - 1); } else if (paramValue >= 0 && buffer.charAt(paramName) == 'q' && paramValue > paramName && buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=') { Double q; try { q = (_keepQuotes && buffer.charAt(paramValue) == '"') ? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1)) : Double.valueOf(buffer.substring(paramValue)); } catch (Exception e) { LOG.trace("IGNORED", e); q = 0.0D; } buffer.setLength(Math.max(0, paramName - 1)); if (q != 1.0D) // replace assumed quality _quality.set(_quality.size() - 1, q); } } @Override public List<String> getValues() { if (!_sorted) sort(); return _values; } @Override public Iterator<String> iterator() { if (!_sorted) sort(); return _values.iterator(); } protected void sort() { _sorted = true; Double last = 0.0D; int lastSecondaryOrder = Integer.MIN_VALUE; for (int i = _values.size(); i-- > 0; ) { String v = _values.get(i); Double q = _quality.get(i); int compare = last.compareTo(q); if (compare > 0 || (compare == 0 && _secondaryOrdering.applyAsInt(v) < lastSecondaryOrder)) { _values.set(i, _values.get(i + 1)); _values.set(i + 1, v); _quality.set(i, _quality.get(i + 1)); _quality.set(i + 1, q); last = 0.0D; lastSecondaryOrder = 0; i = _values.size(); continue; } last = q; lastSecondaryOrder = _secondaryOrdering.applyAsInt(v); } int lastElement = _quality.size(); while (lastElement > 0 && _quality.get(--lastElement).equals(0.0D)) { _quality.remove(lastElement); _values.remove(lastElement); } } }