1 package org.daisy.pipeline.braille.common;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.FileReader;
6 import java.io.IOException;
7 import java.net.URI;
8 import java.util.ArrayList;
9 import java.util.Collections;
10 import java.util.function.Supplier;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.NoSuchElementException;
14
15 import com.google.common.collect.Iterables;
16 import com.google.common.collect.Maps;
17
18 import org.daisy.braille.css.LanguageRange;
19 import org.daisy.common.file.URLs;
20 import org.daisy.pipeline.braille.common.Query.MutableQuery;
21 import static org.daisy.pipeline.braille.common.Query.util.mutableQuery;
22 import static org.daisy.pipeline.braille.common.Query.util.query;
23 import static org.daisy.pipeline.braille.common.TransformProvider.util.dispatch;
24 import static org.daisy.pipeline.braille.common.TransformProvider.util.logCreate;
25 import org.daisy.pipeline.braille.common.TransformProvider.util.Memoize;
26 import org.daisy.pipeline.braille.common.util.Files;
27 import org.daisy.pipeline.braille.common.util.Strings;
28
29 import org.osgi.service.component.annotations.Component;
30 import org.osgi.service.component.annotations.Reference;
31 import org.osgi.service.component.annotations.ReferenceCardinality;
32 import org.osgi.service.component.annotations.ReferencePolicy;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 @Component(
38 name = "hyphenator-registry",
39 service = { HyphenatorRegistry.class }
40 )
41 public class HyphenatorRegistry extends Memoize<Hyphenator> implements HyphenatorProvider<Hyphenator> {
42
43 public HyphenatorRegistry() {
44 super(true);
45 providers = new ArrayList<>();
46 dispatch = dispatch(providers);
47 context = logger;
48 unmodifiable = false;
49 }
50
51 private HyphenatorRegistry(TransformProvider<Hyphenator> dispatch,
52 HyphenatorRegistry from,
53 Logger context) {
54 super(from);
55 this.providers = null;
56 this.dispatch = dispatch;
57 this.context = context;
58 this.unmodifiable = true;
59 }
60
61 private final List<TransformProvider<Hyphenator>> providers;
62 private final TransformProvider<Hyphenator> dispatch;
63 private final Logger context;
64 private final boolean unmodifiable;
65
66 @Reference(
67 name = "HyphenatorProvider",
68 unbind = "-",
69 service = HyphenatorProvider.class,
70 cardinality = ReferenceCardinality.MULTIPLE,
71 policy = ReferencePolicy.STATIC
72 )
73
74
75
76
77 public void addProvider(HyphenatorProvider p) {
78 if (unmodifiable)
79 throw new UnsupportedOperationException("Unmodifiable");
80 providers.add(p);
81 }
82
83 public Iterable<Hyphenator> _get(Query q) {
84 if (q.containsKey("exception-words")) {
85 MutableQuery baseQuery = mutableQuery(q);
86 String exceptionsFilePath = baseQuery.removeOnly("exception-words").getValue().get();
87 FileReader exceptionsFile;
88 try {
89 URI u = URLs.asURI(exceptionsFilePath);
90 if ("volatile-file".equals(u.getScheme()))
91
92
93 try {
94 u = new URI("file", u.getSchemeSpecificPart(), u.getFragment());
95 } catch (Exception e) {
96
97 throw new IllegalStateException(e);
98 }
99 File f = Files.asFile(u);
100 if (!f.exists())
101 throw new FileNotFoundException("File does not exist: " + f);
102 exceptionsFile = new FileReader(f);
103 } catch (Exception e) {
104 context.debug("'exception-words' could not be resolved to a file'", e);
105 return Collections.emptyList();
106 }
107 return Iterables.transform(
108 _get(baseQuery),
109
110 h -> {
111 try {
112 return logCreate(new HyphenatorWithExceptions(h, exceptionsFile), context);
113 } catch (IOException e) {
114 throw new NoSuchElementException();
115 }
116 });
117 }
118 if (q.containsKey("document-locale")) {
119 MutableQuery fallbackQuery = mutableQuery(q);
120 fallbackQuery.removeAll("document-locale");
121 fallbackQuery.addAll(FALLBACK_QUERY);
122 return Iterables.concat(
123 dispatch.get(q),
124 dispatch.get(fallbackQuery));
125 } else
126 return dispatch.get(q);
127 }
128
129 private final static Query FALLBACK_QUERY = query("(document-locale:und)");
130
131 @Override
132 public HyphenatorRegistry withContext(Logger context) {
133 return (HyphenatorRegistry)super.withContext(context);
134 }
135
136 protected HyphenatorRegistry _withContext(Logger context) {
137 if (this.context == context)
138 return this;
139 return new HyphenatorRegistry(dispatch.withContext(context), this, context);
140 }
141
142
143
144
145
146
147
148
149
150
151 public Iterable<Hyphenator> get(Query query, String style, URI baseURI) {
152 if (style != null) {
153 Map<LanguageRange,Query> subQueries = HyphenationResourceParser.getHyphenatorQueries(style, baseURI, query);
154 if (subQueries != null && !subQueries.isEmpty()) {
155 Map<LanguageRange,Supplier<Hyphenator>> subHyphenators
156 = Maps.transformValues(
157 subQueries,
158 q -> () -> HyphenatorRegistry.this.get(q).iterator().next());
159 Iterable<Hyphenator> hyphenators = Iterables.transform(
160 get(query),
161 t -> logCreate(new CompoundHyphenator(subHyphenators, t), context));
162 if (query.isEmpty())
163 hyphenators = Iterables.concat(
164 hyphenators,
165 LazyValue.from(() -> logCreate(new CompoundHyphenator(subHyphenators, null), context)));
166 return hyphenators;
167 }
168 }
169 return get(query);
170 }
171
172 @Override
173 public String toString() {
174 return "memoize(dispatch( " + Strings.join(providers, ", ") + " ))";
175 }
176
177 private static final Logger logger = LoggerFactory.getLogger(HyphenatorRegistry.class);
178 }