1   package nl.dedicon.pipeline.braille.impl;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.Map;
6   import java.net.URI;
7   
8   import com.google.common.base.MoreObjects;
9   import com.google.common.collect.ImmutableList;
10  import com.google.common.collect.ImmutableMap;
11  
12  import org.daisy.common.file.URIs;
13  import org.daisy.common.file.URLs;
14  import org.daisy.pipeline.braille.common.AbstractBrailleTranslator;
15  import org.daisy.pipeline.braille.common.AbstractTransformProvider;
16  import org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Function;
17  import org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Iterables;
18  import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Iterables.concat;
19  import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.Iterables.transform;
20  import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.logCreate;
21  import static org.daisy.pipeline.braille.common.AbstractTransformProvider.util.logSelect;
22  import org.daisy.pipeline.braille.common.BrailleTranslator;
23  import org.daisy.pipeline.braille.common.BrailleTranslatorProvider;
24  import org.daisy.pipeline.braille.common.CSSStyledText;
25  import org.daisy.pipeline.braille.common.Hyphenator;
26  import org.daisy.pipeline.braille.common.HyphenatorProvider;
27  import org.daisy.pipeline.braille.common.Query;
28  import org.daisy.pipeline.braille.common.Query.Feature;
29  import org.daisy.pipeline.braille.common.Query.MutableQuery;
30  import static org.daisy.pipeline.braille.common.Query.util.mutableQuery;
31  import static org.daisy.pipeline.braille.common.Query.util.query;
32  import org.daisy.pipeline.braille.common.TransformProvider;
33  import static org.daisy.pipeline.braille.common.TransformProvider.util.dispatch;
34  import static org.daisy.pipeline.braille.common.TransformProvider.util.memoize;
35  import org.daisy.pipeline.braille.liblouis.LiblouisTranslator;
36  
37  import org.osgi.service.component.annotations.Activate;
38  import org.osgi.service.component.annotations.Component;
39  import org.osgi.service.component.annotations.Reference;
40  import org.osgi.service.component.annotations.ReferenceCardinality;
41  import org.osgi.service.component.annotations.ReferencePolicy;
42  
43  public interface DediconTranslator {
44  
45      @Component(
46              name = "nl.dedicon.pipeline.braille.impl.DediconTranslator.Provider",
47              service = {
48                  TransformProvider.class,
49                  BrailleTranslatorProvider.class
50              }
51      )
52      public class Provider extends AbstractTransformProvider<BrailleTranslator> implements BrailleTranslatorProvider<BrailleTranslator> {
53  
54          private URI href;
55  
56          @Activate
57          void activate(final Map<?, ?> properties) {
58              href = URIs.asURI(URLs.getResourceFromJAR("/xml/xproc/block-translate.xpl", DediconTranslator.class));
59          }
60  
61          private final static Iterable<BrailleTranslator> empty = Iterables.<BrailleTranslator>empty();
62  
63          private final static List<String> supportedInput = ImmutableList.of("css", "text-css", "dtbook", "html");
64          private final static List<String> supportedOutput = ImmutableList.of("css", "braille");
65  
66          /**
67           * Recognized features:
68           *
69           * - translator: Will only match if the value is `dedicon'.
70           * - locale:     Used for selecting hyphenator.
71           *
72           */
73          @Override
74          protected final Iterable<BrailleTranslator> _get(Query query) {
75              // the (type:literary) causes Liblouis to use 6 dots braille instead of 8 dots
76              // @todo determine liblouisTable based on query parameter locale
77              final Query liblouisTable = query("(locale:nl_NL)(type:literary)");
78              Query hyphenationLocale = null;
79              
80              final MutableQuery q = mutableQuery(query);
81              for (Feature f : q.removeAll("input")) {
82                  if (!supportedInput.contains(f.getValue().get())) {
83                      return empty;
84                  }
85              }
86              for (Feature f : q.removeAll("output")) {
87                  if (!supportedOutput.contains(f.getValue().get())) {
88                      return empty;
89                  }
90              }
91              if (q.containsKey("locale")) {
92                  hyphenationLocale = mutableQuery().addAll(q.removeAll("locale"));
93              }
94              if (hyphenationLocale == null) {
95                  return empty;
96              }
97              if (q.containsKey("translator")) {
98                  if ("dedicon".equals(q.removeOnly("translator").getValue().get())) {
99                      if (q.isEmpty()) {
100 
101                         Query hyphenTable = mutableQuery(hyphenationLocale).add("hyphenator", "hyphen");
102                         Query fallbackHyphenationTable = mutableQuery(hyphenationLocale).add("hyphenator", "tex");
103 
104                         Iterable<Hyphenator> hyphenators = concat(
105                                 logSelect(hyphenTable, hyphenatorProvider),
106                                 logSelect(fallbackHyphenationTable, hyphenatorProvider)
107                         );
108                         return concat(
109                                 transform(
110                                         concat(
111                                                 transform(
112                                                         hyphenators,
113                                                         new Function<Hyphenator, String>() {
114                                                             @Override
115                                                             public String _apply(Hyphenator h) {
116                                                                 return h.getIdentifier();
117                                                             }
118                                                         }
119                                                 ),
120                                                 "liblouis"
121                                         ),
122                                         new Function<String, Iterable<BrailleTranslator>>() {
123                                             @Override
124                                             public Iterable<BrailleTranslator> _apply(String hyphenator) {
125                                                 final Query translatorQuery = mutableQuery(liblouisTable).add("hyphenator", hyphenator);
126                                                 return transform(
127                                                         logSelect(translatorQuery, liblouisTranslatorProvider),
128                                                         new Function<LiblouisTranslator, BrailleTranslator>() {
129                                                             @Override
130                                                             public BrailleTranslator _apply(LiblouisTranslator translator) {
131                                                                 return __apply(logCreate(new TransformImpl(translatorQuery.toString(), translator)));
132                                                             }
133                                                         }
134                                                 );
135                                             }
136                                         }
137                                 )
138                         );
139                     }
140                 }
141             }
142             return empty;
143         }
144 
145         private class TransformImpl extends AbstractBrailleTranslator {
146 
147             private final FromStyledTextToBraille translator;
148             private final XProc xproc;
149 
150             private TransformImpl(String translatorQuery, LiblouisTranslator translator) {
151                 Map<String, String> options = ImmutableMap.of("text-transform", mutableQuery().add("id", this.getIdentifier()).toString());
152                 xproc = new XProc(href, null, options);
153                 this.translator = translator.fromStyledTextToBraille();
154             }
155 
156             @Override
157             public XProc asXProc() {
158                 return xproc;
159             }
160 
161             @Override
162             public FromStyledTextToBraille fromStyledTextToBraille() {
163                 return fromStyledTextToBraille;
164             }
165 
166             private final FromStyledTextToBraille fromStyledTextToBraille = new FromStyledTextToBraille() {
167                 @Override
168                 public java.lang.Iterable<String> transform(java.lang.Iterable<CSSStyledText> styledText, int from, int to) {
169                     return translator.transform(styledText, from, to);
170                 }
171             };
172 
173             @Override
174             public String toString() {
175                 return MoreObjects.toStringHelper(DediconTranslator.class.getSimpleName())
176                         .add("id", getIdentifier())
177                         .add("liblouisTranslator", translator)
178                         .toString();
179             }
180         }
181 
182         @Reference(
183                 name = "LiblouisTranslatorProvider",
184                 unbind = "unbindLiblouisTranslatorProvider",
185                 service = LiblouisTranslator.Provider.class,
186                 cardinality = ReferenceCardinality.MULTIPLE,
187                 policy = ReferencePolicy.DYNAMIC
188         )
189         protected void bindLiblouisTranslatorProvider(LiblouisTranslator.Provider provider) {
190             liblouisTranslatorProviders.add(provider);
191         }
192 
193         protected void unbindLiblouisTranslatorProvider(LiblouisTranslator.Provider provider) {
194             liblouisTranslatorProviders.remove(provider);
195             liblouisTranslatorProvider.invalidateCache();
196         }
197 
198         private final List<TransformProvider<LiblouisTranslator>> liblouisTranslatorProviders
199                 = new ArrayList<>();
200         private final TransformProvider.util.MemoizingProvider<LiblouisTranslator> liblouisTranslatorProvider
201                 = memoize(dispatch(liblouisTranslatorProviders));
202 
203         @Reference(
204                 name = "HyphenatorProvider",
205                 unbind = "unbindHyphenatorProvider",
206                 service = HyphenatorProvider.class,
207                 cardinality = ReferenceCardinality.MULTIPLE,
208                 policy = ReferencePolicy.DYNAMIC
209         )
210         @SuppressWarnings(
211                 "unchecked" // safe cast to TransformProvider<Hyphenator>
212         )
213         protected void bindHyphenatorProvider(HyphenatorProvider<?> provider) {
214             hyphenatorProviders.add((TransformProvider<Hyphenator>) provider);
215         }
216 
217         protected void unbindHyphenatorProvider(HyphenatorProvider<?> provider) {
218             hyphenatorProviders.remove((HyphenatorProvider)provider);
219             hyphenatorProvider.invalidateCache();
220         }
221 
222         private final List<TransformProvider<Hyphenator>> hyphenatorProviders
223                 = new ArrayList<>();
224         private final TransformProvider.util.MemoizingProvider<Hyphenator> hyphenatorProvider
225                 = memoize(dispatch(hyphenatorProviders));
226 
227     }
228 }