1   package org.daisy.pipeline.braille.dotify.impl;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import java.util.Locale;
6   import java.util.Map;
7   
8   import cz.vutbr.web.css.CSSProperty;
9   import cz.vutbr.web.css.Term;
10  import cz.vutbr.web.css.TermFunction;
11  import cz.vutbr.web.css.TermIdent;
12  import cz.vutbr.web.css.TermList;
13  
14  import org.daisy.braille.css.SimpleInlineStyle;
15  import org.daisy.braille.css.BrailleCSSProperty.ListStyleType;
16  import org.daisy.braille.css.BrailleCSSProperty.TextTransform;
17  import org.daisy.pipeline.braille.common.AbstractBrailleTranslator;
18  import org.daisy.pipeline.braille.common.BrailleTranslator;
19  import org.daisy.pipeline.braille.common.BrailleTranslator.LineBreakingFromStyledText;
20  import org.daisy.pipeline.braille.common.Hyphenator;
21  import org.daisy.pipeline.braille.css.CSSStyledText;
22  import org.daisy.pipeline.css.CounterStyle;
23  
24  /**
25   * {@link BrailleTranslator} that handles the <code>text-transform</code> value
26   * "<code>-dotify-counter</code>", which causes numbers to be formatted according to the
27   * value of the <code>-dotify-counter-style</code>
28   * property. <code>-dotify-counter-style</code> can be the name of a custom counter style,
29   * or a <code>symbols()</code> function.
30   */
31  public class CounterHandlingBrailleTranslator extends AbstractBrailleTranslator implements LineBreakingFromStyledText {
32  
33  	private final BrailleTranslator backingTranslator;
34  	private final LineBreakingFromStyledText backingLineBreakingTranslator;
35  	private final Map<String,CounterStyle> customCounterStyles;
36  
37  	public CounterHandlingBrailleTranslator(BrailleTranslator backingTranslator,
38  	                                        Map<String,CounterStyle> customCounterStyles) {
39  		this.backingTranslator = backingTranslator;
40  		this.backingLineBreakingTranslator = backingTranslator.lineBreakingFromStyledText();
41  		this.customCounterStyles = customCounterStyles;
42  	}
43  
44  	private CounterHandlingBrailleTranslator(CounterHandlingBrailleTranslator from, BrailleTranslator backingTranslator) {
45  		super(from);
46  		this.backingTranslator = backingTranslator;
47  		this.backingLineBreakingTranslator = backingTranslator.lineBreakingFromStyledText();
48  		this.customCounterStyles = from.customCounterStyles;
49  	}
50  
51  	/**
52  	 * @throws UnsupportedOperationException if {@code backingTranslator.withHyphenator()} throws
53  	 *                                       UnsupportedOperationException
54  	 */
55  	@Override
56  	public CounterHandlingBrailleTranslator _withHyphenator(Hyphenator hyphenator) throws UnsupportedOperationException {
57  		return new CounterHandlingBrailleTranslator(this, backingTranslator.withHyphenator(hyphenator));
58  	}
59  
60  	@Override
61  	public LineBreakingFromStyledText lineBreakingFromStyledText() {
62  		return this;
63  	}
64  
65  	public LineIterator transform(Iterable<CSSStyledText> styledText, int from, int to) {
66  		return backingLineBreakingTranslator.transform(handleCounterStyles(styledText), from, to);
67  	}
68  
69  	private Iterable<CSSStyledText> handleCounterStyles(Iterable<CSSStyledText> styledText) {
70  		List<CSSStyledText> segments = new ArrayList<CSSStyledText>();
71  		String segment = null;
72  		SimpleInlineStyle style = null;
73  		Locale lang = null;
74  		Map<String,String> attrs = null;
75  		for (CSSStyledText st : styledText) {
76  			String t = st.getText();
77  			SimpleInlineStyle s = st.getStyle();
78  			Locale l = st.getLanguage();
79  			Map<String,String> a = st.getTextAttributes();
80  			if (s != null) {
81  				if (s.getProperty("text-transform") == TextTransform.list_values) {
82  					TermList list = s.getValue(TermList.class, "text-transform");
83  					if (((TermIdent)list.get(0)).getValue().equals("-dotify-counter")) {
84  						if (list.size() == 1)
85  							s.removeProperty("text-transform");
86  						else
87  							list.remove(0);
88  						if ("??".equals(t)) {
89  							// If input text is "??", it will be used for creating a placeholder for content that
90  							// can not be computed yet (see org.daisy.dotify.formatter.impl.row.SegmentProcessor).
91  						} else {
92  							int counterValue = Integer.parseInt(t);
93  							ListStyleType counterStyle = s.getProperty("-dotify-counter-style");
94  							if (counterStyle != null)
95  								switch (counterStyle) {
96  								case symbols_fn:
97  									Term<?> symbolsFunction = s.getValue("-dotify-counter-style");
98  									if (symbolsFunction != null && symbolsFunction instanceof TermFunction) {
99  										try {
100 											t = CounterStyle.fromSymbolsFunction(symbolsFunction).format(counterValue);
101 										} catch (IllegalArgumentException e) {
102 										}
103 									}
104 									break;
105 								case counter_style_name:
106 									if (customCounterStyles != null) {
107 										Term<?> counterStyleName = s.getValue("-dotify-counter-style");
108 										if (counterStyleName != null && counterStyleName instanceof TermIdent)
109 											if (customCounterStyles.containsKey(counterStyleName.getValue()))
110 												t = customCounterStyles.get(counterStyleName.getValue()).format(counterValue);
111 									}
112 									break;
113 								default:
114 								}
115 						}
116 					}
117 				}
118 				s.removeProperty("-dotify-counter-style"); }
119 			if (segment != null)
120 				segments.add(new CSSStyledText(segment, style, lang, attrs));
121 			segment = t;
122 			style = s;
123 			lang = l;
124 			attrs = a; }
125 		if (segment != null)
126 			segments.add(new CSSStyledText(segment, style, lang, attrs));
127 		return segments;
128 	}
129 }