1 package org.daisy.pipeline.braille.common.impl;
2
3 import java.net.URI;
4 import java.util.HashMap;
5 import java.util.Map;
6
7 import javax.xml.namespace.QName;
8
9 import com.google.common.base.MoreObjects;
10 import com.google.common.base.MoreObjects.ToStringHelper;
11 import com.google.common.collect.ImmutableMap;
12
13 import com.xmlcalabash.core.XProcRuntime;
14 import com.xmlcalabash.runtime.XAtomicStep;
15
16 import org.daisy.common.file.URLs;
17 import org.daisy.common.saxon.SaxonInputValue;
18 import org.daisy.common.transform.InputValue;
19 import org.daisy.common.transform.Mult;
20 import org.daisy.common.transform.SingleInSingleOutXMLTransformer;
21 import org.daisy.common.transform.TransformerException;
22 import org.daisy.common.transform.XMLInputValue;
23 import org.daisy.common.transform.XMLOutputValue;
24 import org.daisy.common.xproc.calabash.XProcBasedTransformer;
25 import org.daisy.common.xproc.calabash.XProcStep;
26 import org.daisy.common.xproc.calabash.XProcStepProvider;
27 import org.daisy.common.xproc.XProcMonitor;
28 import org.daisy.pipeline.braille.common.AbstractBrailleTranslator;
29 import org.daisy.pipeline.braille.common.AbstractTransformProvider;
30 import org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Function;
31 import org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Iterables;
32 import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Iterables.transform;
33 import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.logCreate;
34 import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.logSelect;
35 import org.daisy.pipeline.braille.common.BrailleTranslator;
36 import org.daisy.pipeline.braille.common.BrailleTranslatorProvider;
37 import org.daisy.pipeline.braille.common.BrailleTranslatorRegistry;
38 import org.daisy.pipeline.braille.common.Hyphenator;
39 import org.daisy.pipeline.braille.common.HyphenatorRegistry;
40 import org.daisy.pipeline.braille.common.Query;
41 import org.daisy.pipeline.braille.common.Query.Feature;
42 import org.daisy.pipeline.braille.common.Query.MutableQuery;
43 import static org.daisy.pipeline.braille.common.Query.util.mutableQuery;
44 import org.daisy.pipeline.braille.common.TransformProvider;
45 import org.daisy.pipeline.braille.common.util.Function0;
46 import org.daisy.pipeline.braille.common.util.Functions;
47
48 import org.osgi.service.component.annotations.Activate;
49 import org.osgi.service.component.annotations.Component;
50 import org.osgi.service.component.annotations.Reference;
51 import org.osgi.service.component.annotations.ReferenceCardinality;
52 import org.osgi.service.component.annotations.ReferencePolicy;
53
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import org.w3c.dom.Document;
58 import org.w3c.dom.Node;
59
60
61
62
63
64 public interface CSSBlockTransform {
65
66 @Component(
67 name = "org.daisy.pipeline.braille.common.impl.CSSBlockTransform.Provider",
68 service = {
69 BrailleTranslatorProvider.class,
70 TransformProvider.class
71 }
72 )
73 public class Provider extends AbstractTransformProvider<BrailleTranslator> implements BrailleTranslatorProvider<BrailleTranslator> {
74
75 private URI href;
76
77 @Activate
78 protected void activate(final Map<?,?> properties) {
79 href = URLs.asURI(URLs.getResourceFromJAR("xml/block-translator.xpl", CSSBlockTransform.class));
80 translatorRegistry = translatorRegistry.withContext(logger);
81 }
82
83 private final static Iterable<BrailleTranslator> empty = Iterables.<BrailleTranslator>empty();
84
85 @Override
86 protected Iterable<BrailleTranslator> _get(Query query) {
87 final MutableQuery q = mutableQuery(query);
88 for (Feature f : q.removeAll("input"))
89 if ("html".equals(f.getValue().get())) {}
90 else if (!"css".equals(f.getValue().get()))
91 return empty;
92 boolean braille = false; {
93 for (Feature f : q.removeAll("output"))
94 if ("css".equals(f.getValue().get())) {}
95 else if ("html".equals(f.getValue().get())) {}
96 else if ("braille".equals(f.getValue().get()))
97 braille = true;
98 else
99 return empty;
100 }
101 final String brailleCharset = q.containsKey("braille-charset")
102 ? q.getOnly("braille-charset").getValue().get()
103 : null;
104 final boolean includeBrailleCodeInLanguage = q.containsKey("include-braille-code-in-language")
105 ? q.getOnly("include-braille-code-in-language").getValue().orElse("true").equalsIgnoreCase("true")
106 : false;
107 q.add("input", "text-css");
108 if (braille)
109 q.add("output", "braille");
110 return transform(
111 logSelect(q, translatorRegistry.getWithHyphenator(q)),
112 new Function<BrailleTranslator,BrailleTranslator>() {
113 public BrailleTranslator _apply(BrailleTranslator translator) {
114 return __apply(
115 logCreate(new TransformImpl(translator, false, brailleCharset,
116 includeBrailleCodeInLanguage, q))
117 );
118 }
119 }
120 );
121 }
122
123 @Override
124 public ToStringHelper toStringHelper() {
125 return MoreObjects.toStringHelper("CSSBlockTransform$Provider");
126 }
127
128 private class TransformImpl extends AbstractBrailleTranslator implements XProcStepProvider {
129
130 private final Query mainQuery;
131 private final BrailleTranslator mainTranslator;
132 private final boolean forceMainTranslator;
133 private final Map<String,String> options;
134 private final String brailleCharset;
135
136
137
138
139
140
141
142
143 private TransformImpl(BrailleTranslator mainTranslator, boolean forceMainTranslator,
144 String brailleCharset, boolean includeBrailleCodeInLanguage, Query query) {
145 options = ImmutableMap.of("braille-charset", brailleCharset != null ? brailleCharset : "",
146 "include-braille-code-in-language", "" + includeBrailleCodeInLanguage);
147 this.mainTranslator = mainTranslator;
148 this.forceMainTranslator = forceMainTranslator;
149 mainQuery = query;
150 this.brailleCharset = brailleCharset;
151 }
152
153 private Hyphenator hyphenator = null;
154
155 private TransformImpl(TransformImpl from, BrailleTranslator mainTranslator) {
156 super(from);
157 this.mainQuery = from.mainQuery;
158 this.mainTranslator = mainTranslator;
159 this.forceMainTranslator = from.forceMainTranslator;
160 this.options = from.options;
161 this.brailleCharset = from.brailleCharset;
162 }
163
164
165
166
167
168 @Override
169 public TransformImpl _withHyphenator(Hyphenator hyphenator) throws UnsupportedOperationException {
170 TransformImpl t = new TransformImpl(this, mainTranslator.withHyphenator(hyphenator));
171 Provider.this.rememberId(t);
172 return t;
173 }
174
175 @Override
176 public XProcStep newStep(XProcRuntime runtime, XAtomicStep step, XProcMonitor monitor, Map<String,String> properties) {
177 return XProcStep.of(
178 new SingleInSingleOutXMLTransformer() {
179 public Runnable transform(XMLInputValue<?> source, XMLOutputValue<?> result, InputValue<?> params) {
180 return () -> {
181 if (!(source instanceof SaxonInputValue))
182 throw new IllegalArgumentException();
183 Mult<SaxonInputValue> mult = ((SaxonInputValue)source).mult(2);
184
185
186 Node doc = mult.get().ensureSingleItem().asNodeIterator().next();
187 if (!(doc instanceof Document))
188 throw new TransformerException(new IllegalArgumentException());
189 String style = (((Document)doc).getDocumentElement()).getAttribute("style");
190 URI styleBaseURI = URLs.asURI(((Document)doc).getBaseURI());
191 BrailleTranslator compoundTranslator
192 = translatorRegistry.getWithHyphenator(mainQuery, style, styleBaseURI, forceMainTranslator)
193 .iterator().next();
194 if (hyphenator != null)
195 compoundTranslator = compoundTranslator.withHyphenator(hyphenator);
196 Function0<Void> evictTempTranslator; {
197 if (compoundTranslator != mainTranslator)
198
199 evictTempTranslator = Provider.this.provideTemporarily(compoundTranslator);
200 else
201 evictTempTranslator = Functions.noOp;
202 }
203
204
205 Map<QName,InputValue<?>> options = new HashMap<>();
206 for (String option : TransformImpl.this.options.keySet())
207 options.put(new QName(option), new InputValue<>(TransformImpl.this.options.get(option)));
208 options.put(new QName("text-transform"),
209 new InputValue<>(
210 mutableQuery().add("id", compoundTranslator.getIdentifier()).toString()));
211 new XProcBasedTransformer(
212 href,
213 options
214 ).newStep(runtime, step, monitor, properties).transform(
215 ImmutableMap.of(
216 new QName("source"), mult.get(),
217 new QName("parameters"), params),
218 ImmutableMap.of(
219 new QName("result"), result)
220 ).run();
221 evictTempTranslator.apply();
222 };
223 }
224 },
225 runtime,
226 step
227 );
228 }
229
230 @Override
231 public ToStringHelper toStringHelper() {
232 return MoreObjects.toStringHelper("CSSBlockTransform$Provider$TransformImpl")
233 .add("translator", mainTranslator);
234 }
235 }
236
237
238
239
240
241
242
243 @Reference(
244 name = "BrailleTranslatorProvider",
245 unbind = "-",
246 service = BrailleTranslatorProvider.class,
247 cardinality = ReferenceCardinality.MULTIPLE,
248 policy = ReferencePolicy.STATIC
249 )
250 protected void bindBrailleTranslatorProvider(BrailleTranslatorProvider<?> provider) {
251 translatorRegistry.addProvider(provider);
252 }
253
254 @Reference(
255 name = "HyphenatorRegistry",
256 unbind = "-",
257 service = HyphenatorRegistry.class,
258 cardinality = ReferenceCardinality.MANDATORY,
259 policy = ReferencePolicy.STATIC
260 )
261 protected void bindHyphenatorRegistry(HyphenatorRegistry registry) {
262 translatorRegistry.bindHyphenatorRegistry(registry);
263 }
264
265 private BrailleTranslatorRegistry translatorRegistry = new BrailleTranslatorRegistry();
266
267 private static final Logger logger = LoggerFactory.getLogger(Provider.class);
268
269 }
270 }