1 package org.daisy.pipeline.css.impl;
2
3 import java.util.Collection;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.xml.namespace.QName;
9 import javax.xml.stream.XMLStreamException;
10 import javax.xml.transform.URIResolver;
11
12 import com.google.common.collect.Iterables;
13
14 import cz.vutbr.web.css.CSSProperty.CounterIncrement;
15 import cz.vutbr.web.css.CSSProperty.CounterReset;
16 import cz.vutbr.web.css.CSSProperty.CounterSet;
17 import cz.vutbr.web.css.NodeData;
18 import cz.vutbr.web.css.RuleFactory;
19 import cz.vutbr.web.css.Selector.PseudoElement;
20 import cz.vutbr.web.css.Term;
21 import cz.vutbr.web.css.TermFunction;
22 import cz.vutbr.web.css.TermIdent;
23 import cz.vutbr.web.css.TermList;
24 import cz.vutbr.web.css.TermPair;
25 import cz.vutbr.web.csskit.antlr.CSSParserFactory;
26 import cz.vutbr.web.csskit.RuleFactoryImpl;
27 import cz.vutbr.web.domassign.StyleMap;
28
29 import org.daisy.braille.css.BrailleCSSParserFactory;
30 import org.daisy.braille.css.BrailleCSSProperty.Content;
31 import org.daisy.braille.css.BrailleCSSProperty.Display;
32 import org.daisy.braille.css.BrailleCSSProperty.ListStyleType;
33 import org.daisy.braille.css.RuleCounterStyle;
34 import org.daisy.braille.css.SupportedBrailleCSS;
35 import org.daisy.common.stax.BaseURIAwareXMLStreamWriter;
36 import org.daisy.common.stax.XMLStreamWriterHelper;
37 import org.daisy.common.transform.XMLTransformer;
38 import org.daisy.pipeline.css.CounterEvaluator;
39 import org.daisy.pipeline.css.CounterStyle;
40 import org.daisy.pipeline.css.CssCascader;
41 import org.daisy.pipeline.css.CssPreProcessor;
42 import org.daisy.pipeline.css.CssSerializer;
43 import org.daisy.pipeline.css.JStyleParserCssCascader;
44 import org.daisy.pipeline.css.Medium;
45 import org.daisy.pipeline.css.XsltProcessor;
46
47 import org.osgi.service.component.annotations.Component;
48
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import org.w3c.dom.Element;
53 import org.w3c.dom.Node;
54
55
56
57
58
59
60
61
62
63
64
65
66 @Component(
67 name = "DefaultCssCascader",
68 service = { CssCascader.class }
69 )
70 public class DefaultCssCascader implements CssCascader {
71
72 public boolean supportsMedium(Medium medium) {
73 switch (medium.getType()) {
74 case PRINT:
75 case SCREEN:
76 return "none".equals(medium.getCustomFeatures().get("counter-support"));
77 default:
78 return false;
79 }
80 }
81
82 public XMLTransformer newInstance(Medium medium,
83 String userStylesheet,
84 URIResolver uriResolver,
85 CssPreProcessor preProcessor,
86 XsltProcessor xsltProcessor,
87 QName attributeName,
88 boolean multipleAttrs) {
89 if (!supportsMedium(medium))
90 throw new IllegalArgumentException("medium not supported: " + medium);
91 return new Transformer(uriResolver, preProcessor, xsltProcessor, userStylesheet, medium,
92 attributeName, multipleAttrs);
93 }
94
95
96 private static final CSSParserFactory parserFactory = new BrailleCSSParserFactory();
97 private static final RuleFactory ruleFactory = RuleFactoryImpl.getInstance();
98 private static final SupportedBrailleCSS supportedCSS = new SupportedBrailleCSS(false, true);
99
100
101
102
103
104
105 private static class Transformer extends JStyleParserCssCascader {
106
107 private Map<String,CounterStyle> namedCounterStyles = null;
108 private CounterEvaluator<Element> evaluator;
109 private final QName markerAttributeName;
110 private final QName markerContentAttributeName;
111
112 private Transformer(URIResolver resolver, CssPreProcessor preProcessor, XsltProcessor xsltProcessor,
113 String userStyleSheet, Medium medium, QName attributeName, boolean multipleAttrs) {
114 super(resolver, preProcessor, xsltProcessor, userStyleSheet, medium, attributeName,
115 parserFactory, ruleFactory, supportedCSS, supportedCSS);
116 if (attributeName == null) {
117 markerAttributeName= null;
118 markerContentAttributeName = null;
119 } else if (multipleAttrs) {
120 markerAttributeName= null;
121 markerContentAttributeName
122 = new QName(attributeName.getNamespaceURI(), "marker-content", attributeName.getPrefix());
123 } else {
124 markerAttributeName= attributeName;
125 markerContentAttributeName = null;
126 }
127 }
128
129 @Override
130 protected void processElement(Element element, StyleMap styleMap, BaseURIAwareXMLStreamWriter writer)
131 throws XMLStreamException, IllegalArgumentException {
132 if (evaluator == null)
133
134 evaluator = new CounterEvaluator<Element>() {
135 @Override
136 protected Collection<TermPair<String,Integer>> getCounterReset(Element element) {
137 NodeData style = styleMap.get(element);
138 if (style.getProperty("counter-reset", false) == CounterReset.list_values) {
139 List<Term<?>> list = style.getValue(TermList.class, "counter-reset", false);
140 if (list != null) {
141 for (Term<?> t : list) {
142 if (!(t instanceof TermPair))
143 throw new IllegalStateException("coding error");
144 TermPair p = (TermPair)t;
145 if (!(p.getKey() instanceof String))
146 throw new IllegalStateException("coding error");
147 if (!(p.getValue() instanceof Integer))
148 throw new IllegalStateException("coding error");
149 }
150 return (List<TermPair<String,Integer>>)(List<?>)list;
151 }
152 }
153 return null;
154 }
155 @Override
156 protected Collection<TermPair<String,Integer>> getCounterSet(Element element) {
157 NodeData style = styleMap.get(element);
158 if (style.getProperty("counter-set", false) == CounterSet.list_values) {
159 List<Term<?>> list = style.getValue(TermList.class, "counter-set", false);
160 if (list != null) {
161 for (Term<?> t : list) {
162 if (!(t instanceof TermPair))
163 throw new IllegalStateException("coding error");
164 TermPair p = (TermPair)t;
165 if (!(p.getKey() instanceof String))
166 throw new IllegalStateException("coding error");
167 if (!(p.getValue() instanceof Integer))
168 throw new IllegalStateException("coding error");
169 }
170 return (List<TermPair<String,Integer>>)(List<?>)list;
171 }
172 }
173 return null;
174 }
175 @Override
176 protected Collection<TermPair<String,Integer>> getCounterIncrement(Element element) {
177 NodeData style = styleMap.get(element);
178 if (style.getProperty("counter-increment", false) == CounterIncrement.list_values) {
179 List<Term<?>> list = style.getValue(TermList.class, "counter-increment", false);
180 if (list != null) {
181 for (Term<?> t : list) {
182 if (!(t instanceof TermPair))
183 throw new IllegalStateException("coding error");
184 TermPair p = (TermPair)t;
185 if (!(p.getKey() instanceof String))
186 throw new IllegalStateException("coding error");
187 if (!(p.getValue() instanceof Integer))
188 throw new IllegalStateException("coding error");
189 }
190 return (List<TermPair<String,Integer>>)(List<?>)list;
191 }
192 }
193 return null;
194 }
195 @Override
196 protected Display getDisplay(Element element) {
197 NodeData style = styleMap.get(element);
198 return style.getProperty("display", false);
199 }
200 @Override
201 protected Collection<Term<?>> getMarkerContent(Element element) {
202 NodeData style = styleMap.get(element);
203 for (PseudoElement pseudo : styleMap.pseudoSet(element)) {
204 if ("marker".equals(pseudo.getName())) {
205 NodeData pseudoStyle = styleMap.get(element, pseudo);
206 if (pseudoStyle.getProperty("content", false) == Content.content_list) {
207 List<Term<?>> content = pseudoStyle.getValue(TermList.class, "content", false);
208 if (content != null)
209 return content;
210 }
211 return Collections.emptyList();
212 }
213 }
214 return null;
215 }
216 @Override
217 protected CounterStyle getListStyleType(Element element, CounterStyle parentListStyleType) {
218 NodeData style = styleMap.get(element);
219 ListStyleType type = style.getProperty("list-style-type", false);
220 if (type == null)
221 return parentListStyleType;
222 else {
223 switch (type) {
224 case INHERIT:
225
226 return parentListStyleType;
227 case INITIAL:
228 return CounterStyle.DISC;
229 case counter_style_name:
230 return getNamedCounterStyle(style.getValue(TermIdent.class, "list-style-type").getValue());
231 case symbols_fn:
232 return CounterStyle.fromSymbolsFunction(style.getValue(TermFunction.class, "list-style-type"));
233 case braille_string:
234 return null;
235 case DECIMAL:
236 return CounterStyle.DECIMAL;
237 case LOWER_ALPHA:
238 return CounterStyle.LOWER_ALPHA;
239 case LOWER_ROMAN:
240 return CounterStyle.LOWER_ROMAN;
241 case UPPER_ALPHA:
242 return CounterStyle.UPPER_ALPHA;
243 case UPPER_ROMAN:
244 return CounterStyle.UPPER_ROMAN;
245 case NONE:
246 default:
247 return null;
248 }
249 }
250 }
251 @Override
252 protected CounterStyle getNamedCounterStyle(String name) {
253 if (namedCounterStyles == null)
254 namedCounterStyles = CounterStyle.parseCounterStyleRules(
255 Iterables.filter(getParsedStyleSheet(), RuleCounterStyle.class));
256 return namedCounterStyles.getOrDefault(name, CounterStyle.DECIMAL);
257 }
258 };
259 XMLStreamWriterHelper.writeStartElement(writer, element);
260 copyAttributes(element, writer);
261 evaluator.startElement(element);
262 String marker = evaluator.generateMarkerContents(element);
263 if (marker != null) {
264 if (markerContentAttributeName != null)
265
266 XMLStreamWriterHelper.writeAttribute(writer, markerContentAttributeName, marker);
267 else if (markerAttributeName != null)
268
269 XMLStreamWriterHelper.writeAttribute(
270 writer, markerAttributeName,
271 String.format("&::marker { content: '%s' }", marker.replaceAll("'", "\\\\'")));
272 else
273
274 writer.writeCharacters(marker);
275 }
276 for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling())
277 traverse(child, styleMap, writer);
278 evaluator.endElement();
279 writer.writeEndElement();
280 }
281
282 protected Map<QName,String> serializeStyle(NodeData mainStyle, Map<PseudoElement,NodeData> pseudoStyles, Element context) {
283 throw new UnsupportedOperationException();
284 }
285
286 protected String serializeValue(Term<?> value) {
287 return CssSerializer.toString(value);
288 }
289 }
290
291 private final static Logger logger = LoggerFactory.getLogger(DefaultCssCascader.class);
292
293 }