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