Clover Coverage Report - XWiki Rendering - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 18:03:13 CET
../../../../../../img/srcFileCovDistChart9.png 55% of files have more coverage
247   705   130   4.84
62   548   0.53   17
51     2.55  
3    
 
  XhtmlHandler       Line # 70 83 0% 26 20 82.8% 0.82758623
  XhtmlHandler.TagStack       Line # 72 137 0% 83 18 90.5% 0.9047619
  XhtmlHandler.TagStack.TagContext       Line # 74 27 0% 21 3 94.5% 0.94545454
 
  (219)
 
1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.wikimodel.xhtml.impl;
21   
22    import java.util.ArrayList;
23    import java.util.HashMap;
24    import java.util.List;
25    import java.util.Map;
26    import java.util.Stack;
27   
28    import org.xml.sax.Attributes;
29    import org.xml.sax.SAXException;
30    import org.xml.sax.ext.Attributes2;
31    import org.xml.sax.ext.LexicalHandler;
32    import org.xml.sax.helpers.DefaultHandler;
33    import org.xwiki.rendering.wikimodel.WikiPageUtil;
34    import org.xwiki.rendering.wikimodel.WikiParameter;
35    import org.xwiki.rendering.wikimodel.WikiParameters;
36    import org.xwiki.rendering.wikimodel.impl.WikiScannerContext;
37    import org.xwiki.rendering.wikimodel.xhtml.XhtmlCharacter;
38    import org.xwiki.rendering.wikimodel.xhtml.XhtmlCharacterType;
39    import org.xwiki.rendering.wikimodel.xhtml.handler.BoldTagHandler;
40    import org.xwiki.rendering.wikimodel.xhtml.handler.BreakTagHandler;
41    import org.xwiki.rendering.wikimodel.xhtml.handler.CommentHandler;
42    import org.xwiki.rendering.wikimodel.xhtml.handler.DefinitionDescriptionTagHandler;
43    import org.xwiki.rendering.wikimodel.xhtml.handler.DefinitionTermTagHandler;
44    import org.xwiki.rendering.wikimodel.xhtml.handler.DivisionTagHandler;
45    import org.xwiki.rendering.wikimodel.xhtml.handler.HeaderTagHandler;
46    import org.xwiki.rendering.wikimodel.xhtml.handler.HorizontalLineTagHandler;
47    import org.xwiki.rendering.wikimodel.xhtml.handler.ImgTagHandler;
48    import org.xwiki.rendering.wikimodel.xhtml.handler.ItalicTagHandler;
49    import org.xwiki.rendering.wikimodel.xhtml.handler.ListItemTagHandler;
50    import org.xwiki.rendering.wikimodel.xhtml.handler.ListTagHandler;
51    import org.xwiki.rendering.wikimodel.xhtml.handler.ParagraphTagHandler;
52    import org.xwiki.rendering.wikimodel.xhtml.handler.PreserveTagHandler;
53    import org.xwiki.rendering.wikimodel.xhtml.handler.QuoteTagHandler;
54    import org.xwiki.rendering.wikimodel.xhtml.handler.ReferenceTagHandler;
55    import org.xwiki.rendering.wikimodel.xhtml.handler.SpanTagHandler;
56    import org.xwiki.rendering.wikimodel.xhtml.handler.StrikedOutTagHandler;
57    import org.xwiki.rendering.wikimodel.xhtml.handler.SubScriptTagHandler;
58    import org.xwiki.rendering.wikimodel.xhtml.handler.SuperScriptTagHandler;
59    import org.xwiki.rendering.wikimodel.xhtml.handler.TableDataTagHandler;
60    import org.xwiki.rendering.wikimodel.xhtml.handler.TableRowTagHandler;
61    import org.xwiki.rendering.wikimodel.xhtml.handler.TableTagHandler;
62    import org.xwiki.rendering.wikimodel.xhtml.handler.TagHandler;
63    import org.xwiki.rendering.wikimodel.xhtml.handler.TeletypeTagHandler;
64    import org.xwiki.rendering.wikimodel.xhtml.handler.UnderlineTagHandler;
65   
66    /**
67    * @version $Id: d887b5efc6158740ce7c0227a1e0cf09df004979 $
68    * @since 4.0M1
69    */
 
70    public class XhtmlHandler extends DefaultHandler implements LexicalHandler
71    {
 
72    public static class TagStack
73    {
 
74    public class TagContext
75    {
76    private final WikiParameters fParameters;
77   
78    private String fName;
79   
80    private StringBuffer fContent;
81   
82    public TagHandler fHandler;
83   
84    private final TagContext fParent;
85   
86    TagStack fTagStack;
87   
 
88  1986 toggle public TagContext(
89    TagContext parent,
90    String name,
91    WikiParameters params,
92    TagStack tagStack)
93    {
94  1986 fName = name;
95  1986 fParent = parent;
96  1986 fParameters = params;
97  1986 fTagStack = tagStack;
98    }
99   
 
100  718 toggle public boolean appendContent(String content)
101    {
102  718 if (fHandler == null || !fHandler.isAccumulateContent()) {
103  684 return false;
104    }
105  34 if (fContent == null) {
106  34 fContent = new StringBuffer();
107    }
108  34 fContent.append(content);
109  34 return true;
110    }
111   
 
112  1935 toggle public void beginElement(TagHandler handler)
113    {
114  1935 if (fParent == null) {
115  309 getScannerContext().beginDocument();
116    }
117  1935 fHandler = handler;
118  1935 if (fHandler != null) {
119  1213 fHandler.beginElement(this);
120    }
121    }
122   
 
123  1935 toggle public void endElement()
124    {
125  1935 if (fHandler != null) {
126  1213 fHandler.endElement(this);
127    }
128  1935 if (fParent == null) {
129  309 getScannerContext().endDocument();
130    }
131    }
132   
 
133  4 toggle public String getContent()
134    {
135  4 return fContent != null ? WikiPageUtil.escapeXmlString(fContent
136    .toString()) : "";
137    }
138   
 
139  2515 toggle public String getName()
140    {
141  2515 return fName;
142    }
143   
 
144  1815 toggle public WikiParameters getParams()
145    {
146  1815 return fParameters;
147    }
148   
 
149  493 toggle public TagContext getParent()
150    {
151  493 return fParent;
152    }
153   
 
154  2627 toggle public WikiScannerContext getScannerContext()
155    {
156  2627 return fScannerContext.isEmpty() ? null : fScannerContext
157    .peek();
158    }
159   
 
160  8182 toggle public TagStack getTagStack()
161    {
162  8182 return fTagStack;
163    }
164   
 
165  746 toggle public boolean isContentContainer()
166    {
167  746 return fHandler == null || fHandler.isContentContainer();
168    }
169   
 
170  74 toggle public boolean isTag(String string)
171    {
172  74 return string.equals(fName.toLowerCase());
173    }
174    }
175   
176    private Map<String, TagHandler> fMap = new HashMap<String, TagHandler>();
177   
178    private CommentHandler fCommentHandler;
179   
180    /**
181    * Allow saving parameters. For example we save the number of br
182    * elements if we're outside of a block element so that we can emit an
183    * onEmptyLines event.
184    */
185    private Stack<Map<String, Object>> fStackParameters = new Stack<Map<String, Object>>();
186   
 
187  11742 toggle public void add(String tag, TagHandler handler)
188    {
189  11742 fMap.put(tag, handler);
190    }
191   
 
192  309 toggle public void addAll(Map<String, TagHandler> handlers)
193    {
194  309 fMap.putAll(handlers);
195    }
196   
 
197  309 toggle public void setCommentHandler(CommentHandler handler)
198    {
199  309 fCommentHandler = handler;
200    }
201   
202    private TagContext fPeek;
203   
204    private Stack<WikiScannerContext> fScannerContext = new Stack<WikiScannerContext>();
205   
 
206  309 toggle public TagStack(WikiScannerContext context)
207    {
208    // init stack paramaters
209  309 pushStackParameters();
210   
211  309 fScannerContext.push(context);
212  309 fCommentHandler = new CommentHandler();
213    }
214   
 
215  1986 toggle public void beginElement(String name, WikiParameters params)
216    {
217  1986 fPeek = new TagContext(fPeek, name, params, this);
218  1986 name = fPeek.getName();
219  1986 TagHandler handler = fMap.get(name);
220  1986 boolean ignoreElements = (Boolean) getStackParameter("ignoreElements");
221  1986 if (!ignoreElements) {
222  1935 fPeek.beginElement(handler);
223    }
224    }
225   
 
226  1986 toggle public void endElement()
227    {
228  1986 boolean ignoreElements = (Boolean) getStackParameter("ignoreElements");
229  1986 if (!ignoreElements) {
230  1935 fPeek.endElement();
231    }
232  1986 fPeek = fPeek.fParent;
233    }
234   
 
235  5120 toggle private XhtmlCharacterType getCharacterType(char ch)
236    {
237  5120 XhtmlCharacterType type = XhtmlCharacterType.CHARACTER;
238  5120 switch (ch) {
239  6 case '!':
240  1 case '\'':
241  4 case '#':
242  0 case '$':
243  0 case '%':
244  1 case '&':
245  3 case '(':
246  3 case ')':
247  22 case '*':
248  0 case '+':
249  4 case ',':
250  10 case '-':
251  11 case '.':
252  18 case '/':
253  22 case ':':
254  2 case ';':
255  3 case '<':
256  14 case '=':
257  9 case '>':
258  0 case '?':
259  0 case '@':
260  10 case '[':
261  2 case '\\':
262  5 case ']':
263  4 case '^':
264  4 case '_':
265  0 case '`':
266  23 case '{':
267  7 case '|':
268  20 case '}':
269  3 case '~':
270  3 case '\"':
271  214 type = XhtmlCharacterType.SPECIAL_SYMBOL;
272  214 break;
273  396 case ' ':
274  0 case '\t':
275  35 case 160: // This is a &nbsp;
276  431 type = XhtmlCharacterType.SPACE;
277  431 break;
278  19 case '\n':
279  0 case '\r':
280  19 type = XhtmlCharacterType.NEW_LINE;
281  19 break;
282  4456 default:
283  4456 break;
284    }
285  5120 return type;
286    }
287   
 
288  1718 toggle public WikiScannerContext getScannerContext()
289    {
290  1718 return fScannerContext.isEmpty() ? null : fScannerContext.peek();
291    }
292   
 
293  0 toggle public void setScannerContext(WikiScannerContext context)
294    {
295  0 if (fScannerContext.isEmpty()) {
296  0 pushScannerContext(context);
297    } else {
298  0 fScannerContext.set(fScannerContext.size() - 1, context);
299    }
300    }
301   
 
302  75 toggle public void pushScannerContext(WikiScannerContext context)
303    {
304  75 fScannerContext.push(context);
305    }
306   
 
307  75 toggle public WikiScannerContext popScannerContext()
308    {
309  75 return fScannerContext.pop();
310    }
311   
 
312  684 toggle private void flushStack(Stack<XhtmlCharacter> stack)
313    {
314  2299 while (stack.size() > 0) {
315  1615 XhtmlCharacter character = stack.remove(0);
316  1615 switch (character.getType()) {
317  0 case ESCAPED:
318  0 getScannerContext().onEscape(
319    "" + character.getCharacter());
320  0 break;
321  214 case SPECIAL_SYMBOL:
322  214 getScannerContext().onSpecialSymbol(
323    "" + character.getCharacter());
324  214 break;
325  19 case NEW_LINE:
326  19 getScannerContext().onLineBreak();
327  19 break;
328  410 case SPACE:
329  410 StringBuffer spaceBuffer = new StringBuffer(" ");
330  431 while ((stack.size() > 0)
331    && (stack.firstElement().getType() == XhtmlCharacterType.SPACE))
332    {
333  21 stack.remove(0);
334  21 spaceBuffer.append(' ');
335    }
336  410 getScannerContext().onSpace(spaceBuffer.toString());
337  410 break;
338  972 default:
339  972 StringBuffer charBuffer = new StringBuffer();
340  972 charBuffer.append(character.getCharacter());
341  4456 while ((stack.size() > 0)
342    && (stack.firstElement().getType() == XhtmlCharacterType.CHARACTER))
343    {
344  3484 charBuffer.append(stack
345    .firstElement()
346    .getCharacter());
347  3484 stack.remove(0);
348    }
349  972 getScannerContext()
350    .onWord(
351    WikiPageUtil.escapeXmlString(charBuffer
352    .toString()));
353    }
354    }
355    }
356   
 
357  746 toggle public void onCharacters(String content)
358    {
359   
360  746 if (!fPeek.isContentContainer()) {
361  1 return;
362    }
363  745 boolean ignoreElements = (Boolean) getStackParameter("ignoreElements");
364  745 if (ignoreElements) {
365  27 return;
366    }
367   
368  718 if (!fPeek.appendContent(content)) {
369  684 Stack<XhtmlCharacter> stack = new Stack<XhtmlCharacter>();
370  5804 for (int i = 0; i < content.length(); i++) {
371  5120 char c = content.charAt(i);
372  5120 XhtmlCharacter current = new XhtmlCharacter(
373    c,
374    getCharacterType(c));
375  5120 XhtmlCharacter result = current;
376  5120 stack.push(result);
377    }
378   
379    // Now send the events.
380  684 flushStack(stack);
381    }
382    }
383   
 
384  205 toggle public void onComment(char[] array, int start, int length)
385    {
386  205 fCommentHandler.onComment(new String(array, start, length), this);
387    }
388   
 
389  356 toggle public void pushStackParameters()
390    {
391  356 fStackParameters.push(new HashMap<String, Object>());
392   
393    // Pre-initialize stack parameters for performance reason
394    // (so that we don't have to check all the time if they're
395    // initialized or not)
396  356 setStackParameter("ignoreElements", false);
397  356 setStackParameter("emptyLinesCount", 0);
398  356 setStackParameter("listStyles", new StringBuffer());
399  356 setStackParameter("quoteDepth", new Integer(0));
400  356 setStackParameter("insideBlockElement", new Stack<Boolean>());
401   
402    // Allow each handler to have some initialization
403  356 for (TagHandler tagElementHandler : fMap.values()) {
404  1786 tagElementHandler.initialize(this);
405    }
406    }
407   
 
408  47 toggle public void popStackParameters()
409    {
410  47 fStackParameters.pop();
411    }
412   
 
413  23723 toggle private Map<String, Object> getStackParameters()
414    {
415  23723 return fStackParameters.peek();
416    }
417   
 
418  3524 toggle public void setStackParameter(String name, Object data)
419    {
420  3524 Stack<Object> set = (Stack<Object>) getStackParameters().get(name);
421  3524 if (set == null || set.isEmpty()) {
422  3298 pushStackParameter(name, data);
423    } else {
424  226 set.setElementAt(data, set.size() - 1);
425    }
426    }
427   
 
428  11994 toggle public Object getStackParameter(String name)
429    {
430  11994 Stack<Object> set = (Stack<Object>) getStackParameters().get(name);
431  11994 if (set == null || set.isEmpty()) {
432  1771 return null;
433    } else {
434  10223 return set.peek();
435    }
436    }
437   
 
438  10 toggle public Object getStackParameter(String name, int index)
439    {
440  10 Stack<Object> set = (Stack<Object>) getStackParameters().get(name);
441  10 if (set == null || set.size() <= index) {
442  7 return null;
443    } else {
444  3 return set.get(index);
445    }
446    }
447   
 
448  3942 toggle public void pushStackParameter(String name, Object data)
449    {
450  3942 Stack<Object> set = (Stack<Object>) getStackParameters().get(name);
451  3942 if (set == null) {
452  3609 getStackParameters().put(name, set = new Stack<Object>());
453    }
454   
455  3942 set.push(data);
456    }
457   
 
458  644 toggle public Object popStackParameter(String name)
459    {
460  644 return ((Stack<Object>) getStackParameters().get(name)).pop();
461    }
462    }
463   
464    protected String fDocumentSectionUri;
465   
466    protected String fDocumentUri;
467   
468    protected String fDocumentWikiProperties;
469   
470    TagStack fStack;
471   
 
472  0 toggle public XhtmlHandler(
473    WikiScannerContext context,
474    Map<String, TagHandler> extraHandlers)
475    {
476  0 this(context, extraHandlers, new CommentHandler());
477    }
478   
479    /**
480    * @param context
481    */
 
482  309 toggle public XhtmlHandler(
483    WikiScannerContext context,
484    Map<String, TagHandler> extraHandlers,
485    CommentHandler commentHandler)
486    {
487  309 fStack = new TagStack(context);
488  309 fStack.setCommentHandler(commentHandler);
489   
490    // Register default handlers
491  309 fStack.add("p", new ParagraphTagHandler());
492  309 fStack.add("table", new TableTagHandler());
493  309 fStack.add("tr", new TableRowTagHandler());
494  309 TagHandler handler = new TableDataTagHandler();
495  309 fStack.add("td", handler);
496  309 fStack.add("th", handler);
497  309 handler = new ListTagHandler();
498  309 fStack.add("ul", handler);
499  309 fStack.add("ol", handler);
500  309 fStack.add("dl", handler);
501  309 handler = new ListItemTagHandler();
502  309 fStack.add("li", handler);
503  309 fStack.add("dt", new DefinitionTermTagHandler());
504  309 fStack.add("dd", new DefinitionDescriptionTagHandler());
505  309 handler = new HeaderTagHandler();
506  309 fStack.add("h1", handler);
507  309 fStack.add("h2", handler);
508  309 fStack.add("h3", handler);
509  309 fStack.add("h4", handler);
510  309 fStack.add("h5", handler);
511  309 fStack.add("h6", handler);
512  309 fStack.add("hr", new HorizontalLineTagHandler());
513  309 fStack.add("pre", new PreserveTagHandler());
514  309 handler = new ReferenceTagHandler();
515  309 fStack.add("a", handler);
516  309 handler = new ImgTagHandler();
517  309 fStack.add("img", handler);
518  309 handler = new BoldTagHandler();
519  309 fStack.add("strong", handler);
520  309 fStack.add("b", handler);
521  309 handler = new UnderlineTagHandler();
522  309 fStack.add("ins", handler);
523  309 fStack.add("u", handler);
524  309 handler = new StrikedOutTagHandler();
525  309 fStack.add("del", handler);
526  309 fStack.add("strike", handler);
527  309 fStack.add("s", handler);
528  309 handler = new ItalicTagHandler();
529  309 fStack.add("em", handler);
530  309 fStack.add("i", handler);
531  309 fStack.add("sup", new SuperScriptTagHandler());
532  309 fStack.add("sub", new SubScriptTagHandler());
533  309 fStack.add("tt", new TeletypeTagHandler());
534  309 fStack.add("br", new BreakTagHandler());
535  309 fStack.add("div", new DivisionTagHandler());
536  309 handler = new QuoteTagHandler();
537  309 fStack.add("blockquote", handler);
538  309 fStack.add("quote", handler);
539  309 fStack.add("span", new SpanTagHandler());
540   
541    // Register extra handlers
542  309 fStack.addAll(extraHandlers);
543   
544    // Allow each handler to have some initialization
545  309 for (TagHandler tagElementHandler : fStack.fMap.values()) {
546  11742 tagElementHandler.initialize(fStack);
547    }
548    }
549   
550    /**
551    * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
552    */
 
553  746 toggle @Override
554    public void characters(char[] array, int start, int length)
555    throws SAXException
556    {
557  746 fStack.onCharacters(new String(array, start, length));
558    }
559   
560    /**
561    * @see org.xml.sax.helpers.DefaultHandler#endDocument()
562    */
 
563  309 toggle @Override
564    public void endDocument() throws SAXException
565    {
566  309 TagHandler.sendEmptyLines(fStack.fPeek);
567  309 fStack.endElement();
568    }
569   
570    /**
571    * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
572    * java.lang.String, java.lang.String)
573    */
 
574  1677 toggle @Override
575    public void endElement(String uri, String localName, String qName)
576    throws SAXException
577    {
578  1677 fStack.endElement();
579    }
580   
 
581  0 toggle protected String getHref(Attributes attributes)
582    {
583  0 String value = attributes.getValue("HREF");
584  0 if (value == null) {
585  0 value = attributes.getValue("href");
586    }
587  0 if (value == null) {
588  0 value = attributes.getValue("src");
589    }
590  0 if (value == null) {
591  0 value = attributes.getValue("SRC");
592    }
593  0 return value;
594    }
595   
596    /**
597    * @see org.xml.sax.helpers.DefaultHandler#startDocument()
598    */
 
599  309 toggle @Override
600    public void startDocument() throws SAXException
601    {
602  309 fStack.beginElement(null, null);
603    }
604   
605    /**
606    * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
607    * java.lang.String, java.lang.String, org.xml.sax.Attributes)
608    */
 
609  1677 toggle @Override
610    public void startElement(
611    String uri,
612    String localName,
613    String qName,
614    Attributes attributes) throws SAXException
615    {
616  1677 fStack.beginElement(
617    getLocalName(uri, localName, qName, false),
618    getParameters(attributes));
619    }
620   
621    // Lexical handler methods
622   
 
623  205 toggle public void comment(char[] array, int start, int length)
624    throws SAXException
625    {
626  205 fStack.onComment(array, start, length);
627    }
628   
 
629  3 toggle public void endCDATA() throws SAXException
630    {
631    // Nothing to do
632    }
633   
 
634  219 toggle public void endDTD() throws SAXException
635    {
636    // Nothing to do
637    }
638   
 
639  3760 toggle public void endEntity(String arg0) throws SAXException
640    {
641    // Nothing to do
642    }
643   
 
644  3 toggle public void startCDATA() throws SAXException
645    {
646    // Nothing to do
647    }
648   
 
649  219 toggle public void startDTD(String arg0, String arg1, String arg2)
650    throws SAXException
651    {
652    // Nothing to do
653    }
654   
 
655  3760 toggle public void startEntity(String arg0) throws SAXException
656    {
657    // Nothing to do
658    }
659   
 
660  2138 toggle private String getLocalName(
661    String uri,
662    String localName,
663    String name,
664    boolean upperCase)
665    {
666  2138 String result = (localName != null && !"".equals(localName))
667    ? localName
668    : name;
669  2138 return upperCase ? result.toUpperCase() : result;
670    }
671   
 
672  1677 toggle private WikiParameters getParameters(Attributes attributes)
673    {
674  1677 List<WikiParameter> params = new ArrayList<WikiParameter>();
675  2138 for (int i = 0; i < attributes.getLength(); i++) {
676  461 String key = getLocalName(attributes.getURI(i), attributes
677    .getQName(i), attributes.getLocalName(i), false);
678  461 String value = attributes.getValue(i);
679  461 WikiParameter param = new WikiParameter(key, value);
680   
681    // The XHTML DTD specifies some default value for some attributes.
682    // For example for a TD element
683    // it defines colspan=1 and rowspan=1. Thus we'll get a colspan and
684    // rowspan attribute passed to
685    // the current method even though they are not defined in the source
686    // XHTML content.
687    // However with SAX2 it's possible to check if an attribute is
688    // defined in the source or not using
689    // the Attributes2 class.
690    // See
691    // http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description
692  461 if (attributes instanceof Attributes2) {
693  213 Attributes2 attributes2 = (Attributes2) attributes;
694    // If the attribute is present in the XHTML source file then add
695    // it, otherwise skip it.
696  213 if (attributes2.isSpecified(i)) {
697  213 params.add(param);
698    }
699    } else {
700  248 params.add(param);
701    }
702    }
703  1677 return new WikiParameters(params);
704    }
705    }