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

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> inputs) {
				return of(
					com.google.common.collect.Iterables.concat(inputs)
				);
			}
			
			public static  Iterable concat(final Iterable> inputs) {
				return new Concat() {
					protected Iterator> iterator(Logger context) {
						return inputs.apply(context).iterator();
					}
					public Iterator> iterator() {
						return new AbstractIterator>() {
							Iterator,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,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();
										}
									};
								}
							};
						}
					}
				);
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy