org.daisy.pipeline.braille.common.AbstractTransformProvider Maven / Gradle / Ivy
The newest version!
package org.daisy.pipeline.braille.common;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.AbstractIterator;
import org.daisy.pipeline.braille.common.Query.Feature;
import org.daisy.pipeline.braille.common.Query.MutableQuery;
import static org.daisy.pipeline.braille.common.Query.util.mutableQuery;
import org.daisy.pipeline.braille.common.util.Function0;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// FIXME: could this class extend TransformProvider.util.Memoize?
public abstract class AbstractTransformProvider implements TransformProvider {
private Map> transformCache = new HashMap>();
private Map> providerCache = new HashMap>();
protected abstract Iterable _get(Query query);
// WARNING: using "(id:)" in a query where is the result of a
// call to AbstractTransform#getIdentifier() will only work when that
// transformer came from the exact same provider and its cache is not
// cleared in the meantime.
private final java.lang.Iterable get(Query query, Logger context) {
MutableQuery q = mutableQuery(query);
if (q.containsKey("id")) {
Feature f = q.removeOnly("id");
if (q.isEmpty())
return Optional.fromNullable(fromId(f.getValue().get())).asSet(); }
Iterable i;
if (transformCache.containsKey(query))
i = transformCache.get(query);
else {
i = _get(query);
i = util.Iterables.memoize(i);
// don't cache queries that contain "volatile-file" URI
// note that converting them to "file" URIs is the responsibility of the get() method
// also note that the get() method may do some caching of its own (e.g. Liblouis caches compiled tables)
boolean containsVolatileFileURIs = false; {
for (Query.Feature f : query)
try {
if (f.hasValue() && "volatile-file".equals(new URI(f.getValue().get()).getScheme())) {
containsVolatileFileURIs = true;
break; }}
catch (URISyntaxException e) {}}
if (!containsVolatileFileURIs) {
transformCache.put(query, i); }}
// note that we don't cache WithSideEffect objects because we don't want log messages to be
// repeated for (id:...) lookups
return rememberId(i.apply(context));
}
public java.lang.Iterable get(Query query) {
return get(query, null);
}
public final TransformProvider withContext(Logger context) {
if (providerCache.containsKey(context))
return providerCache.get(context);
TransformProvider provider = new DerivativeProvider(context);
providerCache.put(context, provider);
return provider;
}
public void invalidateCache() {
transformCache.clear();
}
public ToStringHelper toStringHelper() {
return MoreObjects.toStringHelper(this);
}
@Override
public String toString() {
return toStringHelper().add("context", null).toString();
}
private class DerivativeProvider implements TransformProvider {
private final Logger context;
private DerivativeProvider(final Logger context) {
this.context = context;
}
public java.lang.Iterable get(Query query) {
return AbstractTransformProvider.this.get(query, context);
}
public TransformProvider withContext(Logger context) {
return AbstractTransformProvider.this.withContext(context);
}
@Override
public String toString() {
return AbstractTransformProvider.this.toStringHelper().add("context", context).toString();
}
}
private Map fromId = new HashMap();
protected T fromId(String id) {
return fromId.get(id);
}
protected T rememberId(T t) {
fromId.put(t.getIdentifier(), t);
return t;
}
protected java.lang.Iterable rememberId(final java.lang.Iterable iterable) {
return new java.lang.Iterable() {
public Iterator iterator() {
return new Iterator() {
Iterator i = iterable.iterator();
public boolean hasNext() {
return i.hasNext();
}
public T next() {
return rememberId(i.next());
}
public void remove() {
i.remove();
}
};
}
};
}
protected Function0 provideTemporarily(T t) {
// assumes t is not in cache yet and is unique to this call
fromId.put(t.getIdentifier(), t);
return () -> {
fromId.remove(t.getIdentifier());
// also remove from transformCache
transformCache.remove(mutableQuery().add("id", t.getIdentifier()));
return null;
};
}
/* -------- */
/* Iterable */
/* -------- */
protected interface Iterable extends WithSideEffect.util.Iterable,
java.lang.Iterable> {}
/* ================== */
/* UTILS */
/* ================== */
public static abstract class util {
/* ------------------------ */
/* logCreate(), logSelect() */
/* ------------------------ */
public static WithSideEffect logCreate(final T t) {
return new WithSideEffect() {
public T _apply() {
__apply(debug("Created " + t));
return t; }};
}
public static Iterable logSelect(final Query query,
final TransformProvider provider) {
return Iterables.concat(
Iterables.of(
new WithSideEffect,Logger>() {
protected Iterable _apply() throws NoSuchElementException {
return __apply(logger -> logSelect(query, provider.withContext(logger).get(query)));
}
}
)
);
}
public static Iterable logSelect(final Query query,
final java.lang.Iterable iterable) {
return Iterables.of(
new java.lang.Iterable>() {
public Iterator> iterator() {
return new Iterator>() {
Iterator i = iterable.iterator();
boolean first = true;
public boolean hasNext() {
if (first)
return true;
return i.hasNext();
}
public WithSideEffect next() {
final T t;
if (first) {
first = false;
try { t = i.next(); }
catch (NoSuchElementException e) {
return new WithSideEffect() {
public T _apply() throws NoSuchElementException {
__apply(debug("No match for query " + query));
throw e;
}
};
}
} else
t = i.next();
return new WithSideEffect() {
public T _apply() {
__apply(debug("Selected " + t + " for query " + query));
return t;
}
};
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
);
}
private static final Logger fallbackLogger = LoggerFactory.getLogger(AbstractTransformProvider.class);
public static com.google.common.base.Function debug(final String message) {
return new com.google.common.base.Function() {
public Void apply(Logger logger) {
if (logger != null)
logger.debug(message);
else
fallbackLogger.debug(message);
return null;
}
};
}
public static com.google.common.base.Function info(final String message) {
return new com.google.common.base.Function() {
public Void apply(Logger logger) {
if (logger != null)
logger.info(message);
else
fallbackLogger.debug(message);
return null;
}
};
}
public static com.google.common.base.Function warn(final String message) {
return new com.google.common.base.Function() {
public Void apply(Logger logger) {
if (logger != null)
logger.warn(message);
else
fallbackLogger.debug(message);
return null;
}
};
}
/* -------- */
/* Function */
/* -------- */
public static abstract class Function extends WithSideEffect.util.Function {}
/* --------- */
/* Iterables */
/* --------- */
public static abstract class Iterables {
/* memoize() */
public static Iterable memoize(Iterable iterable) {
return of(org.daisy.pipeline.braille.common.util.Iterables.memoize(iterable));
}
/* empty() */
public static Iterable empty() {
return of(Optional.>absent().asSet());
}
/* fromNullable() */
public static Iterable fromNullable(T element) {
return of(WithSideEffect.fromNullable(element));
}
/* of() */
public static Iterable of(T element) {
return of(WithSideEffect.of(element));
}
public static Iterable of(WithSideEffect element) {
return of(Optional.of(element).asSet());
}
public static Iterable of(final java.lang.Iterable> iterable) {
return new Of(iterable);
}
private static class Of extends WithSideEffect.util.Iterables.Of implements Iterable {
protected Of(java.lang.Iterable> iterable) {
super(iterable);
}
public Iterator> iterator() {
return iterable.iterator();
}
}
/* transform() */
public static Iterable transform(Iterable from,
final com.google.common.base.Function function) {
return transform(
from,
new Function() {
public T _apply(F from) {
return function.apply(from);
}
}
);
}
public static Iterable transform(Iterable from, final Function function) {
return of(
com.google.common.collect.Iterables.transform(
from,
new Function,T>() {
public T _apply(WithSideEffect from) {
return __apply(function.apply(__apply(from)));
}
}
)
);
}
public static Iterable transform(final java.lang.Iterable from, final Function function) {
return of(
com.google.common.collect.Iterables.transform(from, function)
);
}
/* concat() */
public static Iterable concat(Iterable a, T b) {
return of(
com.google.common.collect.Iterables.concat(a, of(b))
);
}
public static Iterable concat(Iterable a, WithSideEffect b) {
return of(
com.google.common.collect.Iterables.concat(a, of(b))
);
}
public static Iterable concat(Iterable a, Iterable b) {
return of(
com.google.common.collect.Iterables.concat(a, b)
);
}
public static Iterable concat(final java.lang.Iterable extends Iterable> inputs) {
return of(
com.google.common.collect.Iterables.concat(inputs)
);
}
public static Iterable concat(final Iterable extends Iterable> inputs) {
return new Concat() {
protected Iterator extends Iterable> iterator(Logger context) {
return inputs.apply(context).iterator();
}
public Iterator> iterator() {
return new AbstractIterator>() {
Iterator extends WithSideEffect extends Iterable,Logger>> iterableIterator = inputs.iterator();
Iterator> current;
WithSideEffect nextEvaluate;
boolean evaluated = true;
protected WithSideEffect computeNext() {
if (!evaluated)
throw new RuntimeException("Previous element must be evaluated first");
if (current != null && current.hasNext())
return current.next();
else if (!iterableIterator.hasNext())
return endOfData();
nextEvaluate = new WithSideEffect() {
public T _apply() throws NoSuchElementException {
if (nextEvaluate == this)
evaluated = true;
while (current == null || !current.hasNext()) {
WithSideEffect extends Iterable,Logger> nextIterable = iterableIterator.next();
try {
current = __apply(nextIterable).iterator();
break; }
catch (NoSuchElementException e) {
continue; }}
if (current == null)
throw new NoSuchElementException();
T result = __apply(current.next());
return result;
}
};
evaluated = false;
return nextEvaluate;
}
};
}
};
}
protected static abstract class Concat extends WithSideEffect.util.Iterables.Concat
implements Iterable {}
/* intersection() */
public static Iterable intersection(Iterable a, Iterable b) {
return of(
new java.lang.Iterable>() {
public Iterator> iterator() {
return new AbstractIterator>() {
Iterator> itrA = a.iterator();
Iterator> itrB = b.iterator();
Set returned = new HashSet<>();
Set setB = new HashSet<>();
protected WithSideEffect computeNext() {
if (!itrA.hasNext())
return endOfData();
return new WithSideEffect() {
public T _apply() throws NoSuchElementException {
while (itrA.hasNext()) {
T nextA; {
try {
nextA = __apply(itrA.next()); }
catch (NoSuchElementException e) {
continue; }}
if (returned.contains(nextA))
continue;
if (setB.contains(nextA)) {
returned.add(nextA);
return nextA; }
while (itrB.hasNext())
try {
T nextB = __apply(itrB.next());
setB.add(nextB);
if (Objects.equal(nextA, nextB)) {
returned.add(nextA);
return nextA; }}
catch (NoSuchElementException e) {}
}
throw new NoSuchElementException();
}
};
}
};
}
}
);
}
}
}
}