package com.firefly.codec.http2.model;
import static java.lang.Integer.MIN_VALUE;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
/**
* 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 final static Double ZERO = new Double(0.0);
private final static Double ONE = new Double(1.0);
private final List<Double> _quality = new ArrayList<>();
private boolean _sorted = false;
private final Function<String, Integer> secondaryOrderingFunction;
/**
* Sorts values with equal quality according to the length of the value
* String.
*/
public QuotedQualityCSV() {
this((s) -> s.length());
}
/**
* Sorts values with equal quality according to given order.
*/
public QuotedQualityCSV(String[] serverPreferredValueOrder) {
this((s) -> {
for (int i = 0; i < serverPreferredValueOrder.length; ++i)
if (serverPreferredValueOrder[i].equals(s))
return serverPreferredValueOrder.length - i;
if ("*".equals(s))
return serverPreferredValueOrder.length;
return MIN_VALUE;
});
}
/**
* Orders values with equal quality with the given function.
*/
public QuotedQualityCSV(Function<String, Integer> secondaryOrderingFunction) {
this.secondaryOrderingFunction = secondaryOrderingFunction;
}
public void addValue(String value) {
super.addValue(value);
while (_quality.size() < _values.size())
_quality.add(ONE);
}
@Override
protected void parsedValue(StringBuffer buffer) {
super.parsedValue(buffer);
}
@Override
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) {
if (buffer.charAt(paramName) == 'q' && paramValue > paramName && buffer.charAt(paramName + 1) == '=') {
Double q;
try {
q = (_keepQuotes && buffer.charAt(paramValue) == '"')
? new Double(buffer.substring(paramValue + 1, buffer.length() - 1))
: new Double(buffer.substring(paramValue));
} catch (Exception e) {
q = ZERO;
}
buffer.setLength(paramName - 1);
while (_quality.size() < _values.size())
_quality.add(ONE);
_quality.add(q);
}
}
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 = ZERO;
int lastOrderIndex = 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 && secondaryOrderingFunction.apply(v) < lastOrderIndex)) {
_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 = ZERO;
lastOrderIndex = 0;
i = _values.size();
continue;
}
last = q;
lastOrderIndex = secondaryOrderingFunction.apply(v);
}
int last_element = _quality.size();
while (last_element > 0 && _quality.get(--last_element).equals(ZERO)) {
_quality.remove(last_element);
_values.remove(last_element);
}
}
}