1 package org.daisy.pipeline.braille.common.saxon.impl; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import net.sf.saxon.expr.XPathContext; 7 import net.sf.saxon.lib.ExtensionFunctionCall; 8 import net.sf.saxon.lib.ExtensionFunctionDefinition; 9 import net.sf.saxon.om.Item; 10 import net.sf.saxon.om.Sequence; 11 import net.sf.saxon.om.SequenceIterator; 12 import net.sf.saxon.om.StructuredQName; 13 import net.sf.saxon.trans.XPathException; 14 import net.sf.saxon.value.SequenceExtent; 15 import net.sf.saxon.value.SequenceType; 16 import net.sf.saxon.value.StringValue; 17 18 import org.daisy.pipeline.braille.common.BrailleTranslator; 19 import org.daisy.pipeline.braille.common.BrailleTranslator.FromStyledTextToBraille; 20 import org.daisy.pipeline.braille.common.BrailleTranslatorRegistry; 21 import org.daisy.pipeline.braille.common.Query; 22 import static org.daisy.pipeline.braille.common.Query.util.query; 23 import static org.daisy.pipeline.braille.common.util.Locales.parseLocale; 24 import org.daisy.pipeline.braille.css.CSSStyledText; 25 26 import org.osgi.service.component.annotations.Component; 27 import org.osgi.service.component.annotations.Reference; 28 import org.osgi.service.component.annotations.ReferenceCardinality; 29 import org.osgi.service.component.annotations.ReferencePolicy; 30 31 import org.slf4j.Logger; 32 import org.slf4j.LoggerFactory; 33 34 @Component( 35 name = "pf:text-transform", 36 service = { ExtensionFunctionDefinition.class } 37 ) 38 @SuppressWarnings("serial") 39 public class TextTransformDefinition extends ExtensionFunctionDefinition { 40 41 private static final StructuredQName funcname = new StructuredQName("pf", 42 "http://www.daisy.org/ns/pipeline/functions", "text-transform"); 43 44 @Reference( 45 name = "BrailleTranslatorRegistry", 46 unbind = "-", 47 service = BrailleTranslatorRegistry.class, 48 cardinality = ReferenceCardinality.MANDATORY, 49 policy = ReferencePolicy.STATIC 50 ) 51 protected void bindBrailleTranslatorRegistry(BrailleTranslatorRegistry registry) { 52 translatorRegistry = registry.withContext(logger); 53 logger.debug("Binding BrailleTranslator registry: {}", registry); 54 } 55 56 private BrailleTranslatorRegistry translatorRegistry; 57 58 public StructuredQName getFunctionQName() { 59 return funcname; 60 } 61 62 @Override 63 public int getMinimumNumberOfArguments() { 64 return 2; 65 } 66 67 @Override 68 public int getMaximumNumberOfArguments() { 69 return 4; 70 } 71 72 public SequenceType[] getArgumentTypes() { 73 return new SequenceType[] { 74 SequenceType.SINGLE_STRING, 75 SequenceType.ATOMIC_SEQUENCE, 76 SequenceType.ATOMIC_SEQUENCE, 77 SequenceType.ATOMIC_SEQUENCE 78 }; 79 } 80 81 public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { 82 return SequenceType.ATOMIC_SEQUENCE; 83 } 84 85 public ExtensionFunctionCall makeCallExpression() { 86 return new ExtensionFunctionCall() { 87 public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { 88 try { 89 Query query = query(arguments[0].head().getStringValue()); 90 List<String> text = sequenceToList(arguments[1]); 91 List<CSSStyledText> styledText = new ArrayList<CSSStyledText>(); 92 if (arguments.length > 2) { 93 List<String> style = sequenceToList(arguments[2]); 94 if (style.size() != text.size()) 95 throw new XPathException("Lengths of text and style sequences must match"); 96 if (arguments.length > 3) { 97 List<String> lang = sequenceToList(arguments[3]); 98 if (lang.size() != text.size()) 99 throw new XPathException("Lengths of text and lang sequences must match"); 100 for (int i = 0; i < text.size(); i++) 101 styledText.add(new CSSStyledText(text.get(i), style.get(i), parseLocale(lang.get(i)))); } 102 else 103 for (int i = 0; i < text.size(); i++) 104 styledText.add(new CSSStyledText(text.get(i), style.get(i))); } 105 else 106 for (int i = 0; i < text.size(); i++) 107 styledText.add(new CSSStyledText(text.get(i))); 108 for (BrailleTranslator t : translatorRegistry.getWithHyphenator(query)) { 109 FromStyledTextToBraille fsttb; 110 try { 111 fsttb = t.fromStyledTextToBraille(); } 112 catch (UnsupportedOperationException e) { 113 logger.trace("Translator does not implement the FromStyledTextToBraille interface: " + t); 114 continue; } 115 try { 116 return iterableToSequence(fsttb.transform(styledText)); } 117 catch (Exception e) { 118 logger.debug("Failed to translate string with translator " + t); 119 throw e; } 120 } 121 throw new XPathException("Could not find a BrailleTranslator for query: " + query); } 122 catch (XPathException e) { 123 throw e; } 124 catch (Throwable e) { 125 throw new XPathException("Unexpected error in pf:text-transform", e); } 126 } 127 }; 128 } 129 130 private static List<String> sequenceToList(Sequence seq) throws XPathException { 131 List<String> list = new ArrayList<String>(); 132 SequenceIterator iterator = seq.iterate(); 133 for (Item item = iterator.next(); item != null; item = iterator.next()) 134 list.add(item.getStringValue()); 135 return list; 136 } 137 138 private static Sequence iterableToSequence(Iterable<String> iterable) { 139 List<StringValue> list = new ArrayList<StringValue>(); 140 for (String s : iterable) 141 list.add(new StringValue(s)); 142 return new SequenceExtent(list); 143 } 144 145 private static final Logger logger = LoggerFactory.getLogger(TextTransformDefinition.class); 146 147 }