![JAR search and dependency download from the Maven repository](/logo.png)
org.daisy.pipeline.braille.common.Query Maven / Gradle / Ivy
package org.daisy.pipeline.braille.common;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import static com.xmlcalabash.core.XProcConstants.NS_XPROC_STEP;
import static org.daisy.common.stax.XMLStreamWriterHelper.writeAttribute;
import static org.daisy.common.stax.XMLStreamWriterHelper.writeStartElement;
import org.unbescape.css.CssEscape;
public interface Query extends Iterable {
@Override
public String toString();
public boolean containsKey(String key);
public Iterable get(String key);
public Feature getOnly(String key) throws IllegalStateException;
public boolean isEmpty();
public static interface Feature {
public String getKey();
public boolean hasValue();
public Optional getValue();
public Optional getLiteral();
@Override public String toString();
}
public static interface MutableQuery extends Query {
public MutableQuery add(String key);
public MutableQuery add(String key, String value);
public MutableQuery add(String key, Optional value);
public boolean add(Feature feature);
public MutableQuery addAll(Iterable features);
public boolean remove(Feature feature);
public Iterable removeAll(String key);
public Feature removeOnly(String key) throws IllegalStateException;
public Query asImmutable();
}
/* ================== */
/* UTILS */
/* ================== */
public static abstract class util {
/* query() */
public static Query query(String query) throws IllegalArgumentException {
if (FEATURES_RE.matcher(query).matches()) {
MutableQuery mq = mutableQuery();
Matcher m = FEATURE_RE.matcher(query);
while(m.find()) {
String key = m.group("key");
String value = m.group("value");
boolean isString = false;
if (value != null) {
Matcher m2 = VALUE_RE.matcher(value);
if (!m2.matches())
throw new RuntimeException("Coding error");
String ident = m2.group("ident");
String string = m2.group("string");
String integer = m2.group("integer");
if (ident != null)
value = ident;
else if (string != null && !string.equals("")) {
value = CssEscape.unescapeCss(string.substring(1, string.length() - 1));
isString = true; }
else if (integer != null && !integer.equals(""))
value = integer;
else
throw new RuntimeException("Coding error"); }
mq.add(new FeatureImpl(key, Optional.ofNullable(value), isString)); }
return mq.asImmutable(); }
throw new IllegalArgumentException("Could not parse query: " + query);
}
public static Query query(XMLStreamReader query) throws XMLStreamException {
return unmarshallQuery(query);
}
/* mutableQuery() */
public static MutableQuery mutableQuery() {
return new MutableQueryImpl();
}
public static MutableQuery mutableQuery(Query copyOf) {
MutableQuery q = new MutableQueryImpl();
q.addAll(copyOf);
return q;
}
/* marshallQuery & unmarshallQuery */
private static final QName C_PARAM_SET = new QName(NS_XPROC_STEP, "param-set", "c");
private static final QName C_PARAM = new QName(NS_XPROC_STEP, "param", "c");
private static final QName _NAME = new QName("name");
private static final QName _NAMESPACE = new QName("namespace");
private static final QName _VALUE = new QName("value");
/**
* Convert query to c:param-set document
*/
public static void marshallQuery(Query query, XMLStreamWriter writer) throws XMLStreamException {
writeStartElement(writer, C_PARAM_SET);
for (Feature f : query) {
writeStartElement(writer, C_PARAM);
writeAttribute(writer, _NAME, f.getKey());
writeAttribute(writer, _NAMESPACE, "");
writeAttribute(writer, _VALUE, f.getValue().orElse("true"));
writer.writeEndElement();
}
writer.writeEndElement();
}
/**
* Convert c:param-set document to query
*/
public static Query unmarshallQuery(XMLStreamReader reader) throws XMLStreamException {
MutableQuery query = mutableQuery();
int depth = 0;
int event = reader.next();
while (true)
try {
switch (event) {
case START_ELEMENT:
if (depth == 0 && C_PARAM.equals(reader.getName())) {
String name = null;
String namespace = null;
String value = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
QName attrName = reader.getAttributeName(i);
String attrValue = reader.getAttributeValue(i);
if (_NAME.equals(attrName))
name = attrValue;
else if (_NAMESPACE.equals(attrName))
namespace = attrValue;
else if (_VALUE.equals(attrName))
value = attrValue;
}
if ((namespace == null || "".equals(namespace)) && name != null && value != null)
query.add(name, value);
}
depth++;
break;
case END_ELEMENT:
depth--;
break;
default:
}
event = reader.next();
} catch (NoSuchElementException e) {
break;
}
return query.asImmutable();
}
private static abstract class AbstractQueryImpl extends AbstractCollection implements Query {
protected final List list;
protected AbstractQueryImpl() {
this(new ArrayList());
}
protected AbstractQueryImpl(List list) {
this.list = list;
}
public int size() {
return list.size();
}
public Iterator iterator() {
return list.iterator();
}
public boolean containsKey(String key) {
return get(key).iterator().hasNext();
}
public Iterable get(final String key) {
final ListIterator features = list.listIterator();
while (features.hasNext()) {
Feature next = features.next();
if (next.getKey().equals(key)) {
features.previous();
return new Iterable() {
public Iterator iterator() {
return new Iterator() {
private boolean canRemove = false;
public boolean hasNext() {
return features.hasNext() && list.get(features.nextIndex()).getKey().equals(key);
}
public Feature next() {
Feature next = features.next();
if (next.getKey().equals(key)) {
canRemove = true;
return next; }
else
throw new NoSuchElementException();
}
public void remove() {
features.remove();
canRemove = false;
}
};
}
};
}
}
return Collections.emptyList();
}
public Feature getOnly(String key) throws IllegalStateException {
Iterator features = get(key).iterator();
if (!features.hasNext())
throw new IllegalStateException();
Feature f = features.next();
if (features.hasNext())
throw new IllegalStateException();
return f;
}
public String toString() {
StringBuilder b = new StringBuilder();
for (Feature f : this)
b.append(f);
return b.toString();
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Query))
return false;
return Iterables.elementsEqual(this, (Query)obj);
}
}
private static class MutableQueryImpl extends AbstractQueryImpl implements MutableQuery {
public boolean add(Feature feature) {
int index = 0;
String key = feature.getKey();
for (Feature f : list) {
int cmp = f.getKey().compareTo(key);
if (cmp > 0) {
break;
} else if (cmp > 0) {
index++;
break;
}
index++;
}
list.add(index, feature);
return true;
}
public MutableQuery add(String key) {
add(key, Optional.empty());
return this;
}
public MutableQuery add(String key, String value) {
add(key, Optional.ofNullable(value));
return this;
}
public MutableQuery add(String key, Optional value) {
add(new FeatureImpl(key, value));
return this;
}
public MutableQuery addAll(Iterable features) {
for (Feature f : features)
add(f);
return this;
}
public boolean remove(Feature feature) {
return super.remove(feature);
}
public Iterable removeAll(final String key) {
ImmutableList.Builder list = new ImmutableList.Builder();
Iterator features = iterator();
while (features.hasNext()) {
Feature next = features.next();
if (next.getKey().equals(key)) {
features.remove();
list.add(next); }}
return list.build();
}
public Feature removeOnly(String key) throws IllegalStateException {
Iterator features = get(key).iterator();
if (!features.hasNext())
throw new IllegalStateException();
Feature f = features.next();
if (features.hasNext())
throw new IllegalStateException();
features.remove();
return f;
}
/**
* Returned value is a snapshot, i.e. will not change after the call.
*/
public Query asImmutable() {
return new ImmutableQueryImpl(this);
}
}
private static class ImmutableQueryImpl extends AbstractQueryImpl {
private ImmutableQueryImpl(MutableQueryImpl query) {
super(ImmutableList.copyOf(query.list));
}
}
private static class FeatureImpl implements Feature {
final String key;
final Optional value;
final Optional literal;
private FeatureImpl(String key, Optional value) {
this(key, value, false);
}
private FeatureImpl(String key, Optional value, boolean specifiedAsString) {
this.key = key;
this.value = value;
if (value.isPresent()) {
String v = value.get();
if (!specifiedAsString && (v.matches(IDENT_RE) || v.matches(INTEGER_RE)))
this.literal = Optional.of(v);
else
this.literal = Optional.of("\"" + v.replace("\n", "\\A ").replace("\"","\\22 ") + "\"");
} else
this.literal = Optional.empty();
}
public String getKey() {
return key;
}
public boolean hasValue() {
return getValue().isPresent();
}
public Optional getValue() {
return value;
}
public Optional getLiteral() {
return literal;
}
public String toString() {
StringBuilder b = new StringBuilder();
String k = getKey();
if (!k.matches(IDENT_RE))
throw new RuntimeException();
b.append("(" + k);
if (hasValue()) {
b.append(":");
b.append(getLiteral().get());
}
b.append(")");
return b.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Feature other = (Feature)obj;
if (key == null) {
if (other.getKey() != null)
return false;
} else if (!key.equals(other.getKey()))
return false;
if (value == null) {
if (other.getValue() != null)
return false;
} else if (!value.equals(other.getValue()))
return false;
return true;
}
}
private static final String IDENT_RE = "[_a-zA-Z][_a-zA-Z0-9-]*";
private static final String STRING_RE = "'[^']*'|\"[^\"]*\"";
private static final String INTEGER_RE = "0|-?[1-9][0-9]*";
private static final Pattern VALUE_RE = Pattern.compile(
"(?" + IDENT_RE + ")|(?" + STRING_RE + ")|(?" + INTEGER_RE + ")"
);
private static final Pattern FEATURE_RE = Pattern.compile(
"\\(\\s*(?" + IDENT_RE+ ")(?:\\s*\\:\\s*(?" + VALUE_RE.pattern() + "))?\\s*\\)"
);
private static final Pattern FEATURES_RE = Pattern.compile(
"\\s*(?:" + FEATURE_RE.pattern() + "\\s*)*"
);
public static final Pattern QUERY = FEATURES_RE;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy