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