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

org.daisy.pipeline.braille.common.BrailleTranslatorRegistry Maven / Gradle / Ivy

package org.daisy.pipeline.braille.common;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Supplier;
import java.util.List;
import java.util.Map;

import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import org.daisy.pipeline.braille.common.Query.MutableQuery;
import static org.daisy.pipeline.braille.common.Query.util.mutableQuery;
import static org.daisy.pipeline.braille.common.TransformProvider.util.dispatch;
import static org.daisy.pipeline.braille.common.TransformProvider.util.logCreate;
import org.daisy.pipeline.braille.common.TransformProvider.util.Memoize;
import static org.daisy.pipeline.braille.common.util.Locales.parseLocale;
import org.daisy.pipeline.braille.common.util.Strings;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;

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

@Component(
	name = "braille-translator-registry",
	service = { BrailleTranslatorRegistry.class }
)
public class BrailleTranslatorRegistry extends Memoize
                                       implements BrailleTranslatorProvider {

	public BrailleTranslatorRegistry() {
		super(true); // memoize (id:...) lookups only
		providers = new ArrayList<>();
		dispatch = dispatch(providers);
		context = logger;
		unmodifiable = false;
	}

	private BrailleTranslatorRegistry(TransformProvider dispatch,
	                                  HyphenatorRegistry hyphenatorRegistry,
	                                  BrailleTranslatorRegistry from,
	                                  Logger context) {
		super(from);
		this.providers = null;
		this.dispatch = dispatch;
		this.hyphenatorRegistry = hyphenatorRegistry;
		this.context = context;
		this.unmodifiable = true;
	}

	private final List> providers;
	private final TransformProvider dispatch;
	private final Logger context;
	private final boolean unmodifiable;

	@Reference(
		name = "BrailleTranslatorProvider",
		unbind = "-",
		service = BrailleTranslatorProvider.class,
		cardinality = ReferenceCardinality.MULTIPLE,
		policy = ReferencePolicy.STATIC
	)
	/**
	 * @throws UnsupportedOperationException if this object is an unmodifiable view of another
	 * {@link BrailleTranslatorRegistry}.
	 */
	public void addProvider(BrailleTranslatorProvider p) throws UnsupportedOperationException {
		if (unmodifiable)
			throw new UnsupportedOperationException("Unmodifiable");
		providers.add(p);
	}

	public Iterable _get(Query q) {
		return dispatch.get(q);
	}

	@Override
	public BrailleTranslatorRegistry withContext(Logger context) {
		return (BrailleTranslatorRegistry)super.withContext(context);
	}
	
	protected BrailleTranslatorRegistry _withContext(Logger context) {
		if (this.context == context)
			return this;
		return new BrailleTranslatorRegistry(dispatch.withContext(context),
		                                     hyphenatorRegistry.withContext(context),
		                                     this,
		                                     context);
	}

	private HyphenatorRegistry hyphenatorRegistry;

	@Reference(
		name = "HyphenatorRegistry",
		unbind = "-",
		service = HyphenatorRegistry.class,
		cardinality = ReferenceCardinality.MANDATORY,
		policy = ReferencePolicy.STATIC
	)
	/**
	 * @throws UnsupportedOperationException if this object is an unmodifiable view of another
	 * {@link BrailleTranslatorRegistry}.
	 */
	public void bindHyphenatorRegistry(HyphenatorRegistry registry) throws UnsupportedOperationException {
		if (unmodifiable)
			throw new UnsupportedOperationException("Unmodifiable");
		hyphenatorRegistry = registry;
		logger.debug("Binding hyphenator registry: " + registry);
	}

	/**
	 * Select {@link BrailleTranslator}s with {@link Hyphenator} based on a single query.
	 *
	 * Only the {@code hyphenator} and {@code document-locale} features contribute to the hyphenator
	 * selection.
	 *
	 * Like {@code #get(Query)}, returned objects are selectable based on their identifier.
	 */
	public Iterable getWithHyphenator(Query query) {
		return getWithHyphenator(this, hyphenatorRegistry, query, context);
	}

	private static Iterable getWithHyphenator(Provider translatorProvider,
	                                                             Provider hyphenatorProvider,
	                                                             Query query,
	                                                             Logger context) {
		if (query.containsKey("id"))
			return translatorProvider.get(query);
		MutableQuery q = mutableQuery(query);

		// select hyphenator
		MutableQuery hyphenatorQuery = mutableQuery();
		String hyphenator = q.containsKey("hyphenator")
			? q.removeOnly("hyphenator").getValue().get()
			: "auto";
		if (!"auto".equals(hyphenator))
			hyphenatorQuery.add("hyphenator", hyphenator);
		String documentLocale; {
			try {
				documentLocale = q.containsKey("document-locale")
					? parseLocale(q.getOnly("document-locale").getValue().get()).toLanguageTag()
					: null; }
			catch (IllegalArgumentException e) {
				logger.error("Invalid locale", e);
				return Collections.emptyList(); }
		}
		if (documentLocale != null)
			hyphenatorQuery.add("document-locale", documentLocale);
		Iterable hyphenators; {
			Iterable h = hyphenatorProvider.get(hyphenatorQuery.asImmutable());
			if (documentLocale != null && !"auto".equals(hyphenator)) {
				// also search without locale because "hyphenator" feature might be an ID
				hyphenatorQuery.removeAll("document-locale");
				h = Iterables.concat(h, hyphenatorProvider.get(hyphenatorQuery.asImmutable())); }
			hyphenators = h;
		}

		// select translator and bind hyphenator
		Iterable translators = translatorProvider.get(q);
		Iterable translatorsWithHyphenator =
			Iterables.concat(
				Iterables.transform(
					translators,
					t -> Iterables.filter(
						Iterables.transform(
							hyphenators,
							h -> {
								try {
									return logCreate(t.withHyphenator(h), context);
								} catch (UnsupportedOperationException e) {
									logger.debug("Could not set hyphenator: " + e.getMessage());
									return null;
								}}),
						Predicates.notNull())));
		return "auto".equals(hyphenator)
			? Iterables.concat(translatorsWithHyphenator, translators)
			: translatorsWithHyphenator;
	}

	/**
	 * Select {@link BrailleTranslator}s based on a query and a CSS style sheet possibly containing
	 * {@code @text-transform} rules.
	 *
	 * Contrary to {@link #get(Query)}, this method is not memoized, and the returned objects may
	 * not be selectable based on their identifier.
	 *
	 * @param baseURI Base URI for resolving relative paths in CSS against.
	 * @param forceMainTranslator if {@code true}, the translator defined in {@code query} is used
	 *                            even if a default translator has been defined in CSS.
	 */
	public Iterable get(Query query, String style, URI baseURI, boolean forceMainTranslator) {
		if (style != null) {
			Map subQueries = TextTransformParser.getBrailleTranslatorQueries(style, baseURI, query);
			if (subQueries != null && !subQueries.isEmpty()) {
				Query mainQuery = subQueries.remove("auto");
				if (mainQuery == null || forceMainTranslator)
					mainQuery = query;
				if (subQueries.isEmpty())
					return get(mainQuery);
				else {
					Map> subTranslators
						= Maps.transformValues(
							subQueries,
							// using getWithHyphenator() because @text-transform rules may contain "hyphenator"
							// descriptor (though it should be deprecated)
							q -> () -> BrailleTranslatorRegistry.this.getWithHyphenator(q).iterator().next());
					return Iterables.transform(
						get(mainQuery),
						t -> logCreate(new CompoundBrailleTranslator(t, subTranslators), context));
				}
			}
		}
		return get(query);
	}

	/**
	 * Select {@link BrailleTranslator}s with {@link Hyphenator} based on a single query and a CSS
	 * style sheet possibly containing {@code @text-transform} and {@code @hyphenation-resource}
	 * rules.
	 *
	 * Contrary to {@link #get(Query)} and similar to {@link #get(Query, String, URI, boolean)},
	 * this method is not memoized, and the returned objects may not be selectable based on their
	 * identifier.
	 */
	public Iterable getWithHyphenator(Query query, String style, URI baseURI, boolean forceMainTranslator) {
		return getWithHyphenator(
			q -> get(q, style, baseURI, forceMainTranslator),
			q -> hyphenatorRegistry.get(q, style, baseURI),
			query,
			context);
	}

	@Override
	public String toString() {
		return "memoize(dispatch( " + Strings.join(providers, ", ") + " ))";
	}

	private static final Logger logger = LoggerFactory.getLogger(BrailleTranslatorRegistry.class);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy