1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.wikimodel.xhtml.impl

File TagStack.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

32
147
37
1
378
292
99
0.67
3.97
37
2.68

Classes

Class Line # Actions
TagStack 44 147 0% 99 22
0.898148189.8%
 

Contributing tests

This file is covered by 322 tests. .

Source view

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.ArrayDeque;
23    import java.util.Deque;
24    import java.util.HashMap;
25    import java.util.Iterator;
26    import java.util.LinkedList;
27    import java.util.Map;
28    import java.util.Queue;
29   
30    import org.xwiki.rendering.wikimodel.WikiPageUtil;
31    import org.xwiki.rendering.wikimodel.WikiParameters;
32    import org.xwiki.rendering.wikimodel.impl.WikiScannerContext;
33    import org.xwiki.rendering.wikimodel.xhtml.XhtmlCharacter;
34    import org.xwiki.rendering.wikimodel.xhtml.XhtmlCharacterType;
35    import org.xwiki.rendering.wikimodel.xhtml.handler.CommentHandler;
36    import org.xwiki.rendering.wikimodel.xhtml.handler.TagHandler;
37   
38    /**
39    * Provides context for the parsing.
40    *
41    * @version $Id: a09af52e338b34ca66ef04c2459a8064ec860d88 $
42    * @since 7.0RC1
43    */
 
44    public class TagStack
45    {
46    private static final String QUOTE_DEPTH = "quoteDepth";
47    private static final String INSIDE_BLOCK_ELEMENT = "insideBlockElement";
48    private static final String LIST_STYLES = "listStyles";
49    private static final String DOCUMENT_PARENT = "documentParent";
50   
51    private final Map<String, TagHandler> fMap;
52   
53    private final CommentHandler fCommentHandler;
54   
55    private TagContext fPeek;
56   
57    private final Deque<WikiScannerContext> fScannerContext = new ArrayDeque<WikiScannerContext>();
58   
59    private final Deque<Map<String, Object>> fStackParameters = new ArrayDeque<Map<String, Object>>();
60   
61    private boolean fIgnoreElements;
62    private int fEmptyLineCount;
63   
 
64  0 toggle public TagStack(WikiScannerContext context, Map<String, TagHandler> handlers)
65    {
66  0 this(context, handlers, new CommentHandler());
67    }
68   
 
69  437 toggle public TagStack(WikiScannerContext context, Map<String, TagHandler> handlers, CommentHandler commentHandler)
70    {
71  437 fMap = handlers;
72  437 fScannerContext.push(context);
73  437 fCommentHandler = commentHandler;
74   
75    // init stack paramaters
76  437 pushStackParameters();
77    }
78   
 
79  5237 toggle public void beginElement(String name, WikiParameters params)
80    {
81  5237 fPeek = new TagContext(fPeek, name, params, this);
82  5237 name = fPeek.getName();
83  5237 TagHandler handler = fMap.get(name);
84  5237 if (!shouldIgnoreElements()) {
85  5151 fPeek.beginElement(handler);
86    }
87    }
88   
 
89  5229 toggle public void endElement()
90    {
91  5229 boolean ignoreElements = shouldIgnoreElements();
92  5229 if (!ignoreElements) {
93  5143 fPeek.endElement();
94    }
95  5229 fPeek = fPeek.getParentContext();
96    }
97   
 
98  23104 toggle private XhtmlCharacterType getCharacterType(char ch)
99    {
100  23104 XhtmlCharacterType type = XhtmlCharacterType.CHARACTER;
101  23104 switch (ch) {
102  11 case '!':
103  44 case '\'':
104  6 case '#':
105  2 case '$':
106  3 case '%':
107  12 case '&':
108  19 case '(':
109  19 case ')':
110  11 case '*':
111  14 case '+':
112  96 case ',':
113  20 case '-':
114  167 case '.':
115  28 case '/':
116  54 case ':':
117  4 case ';':
118  18 case '<':
119  41 case '=':
120  11 case '>':
121  5 case '?':
122  7 case '@':
123  14 case '[':
124  2 case '\\':
125  9 case ']':
126  6 case '^':
127  7 case '_':
128  2 case '`':
129  23 case '{':
130  9 case '|':
131  20 case '}':
132  3 case '~':
133  17 case '\"':
134  704 type = XhtmlCharacterType.SPECIAL_SYMBOL;
135  704 break;
136  3049 case ' ':
137  0 case '\t':
138  174 case 160: // This is a &nbsp;
139  3223 type = XhtmlCharacterType.SPACE;
140  3223 break;
141  17 case '\n':
142  0 case '\r':
143  17 type = XhtmlCharacterType.NEW_LINE;
144  17 break;
145  19160 default:
146  19160 break;
147    }
148  23104 return type;
149    }
150   
 
151  14783 toggle public WikiScannerContext getScannerContext()
152    {
153  14783 return fScannerContext.isEmpty() ? null : fScannerContext.peek();
154    }
155   
 
156  0 toggle public void setScannerContext(WikiScannerContext context)
157    {
158  0 if (!fScannerContext.isEmpty()) {
159  0 fScannerContext.pop();
160    }
161  0 fScannerContext.push(context);
162    }
163   
 
164  178 toggle public void pushScannerContext(WikiScannerContext context)
165    {
166  178 fScannerContext.push(context);
167    }
168   
 
169  178 toggle public WikiScannerContext popScannerContext()
170    {
171  178 return fScannerContext.pop();
172    }
173   
 
174  1941 toggle private void flushStack(Queue<XhtmlCharacter> stack)
175    {
176  10297 while (!stack.isEmpty()) {
177  8356 XhtmlCharacter character = stack.poll();
178  8356 switch (character.getType()) {
179  0 case ESCAPED:
180  0 getScannerContext().onEscape(
181    "" + character.getCharacter());
182  0 break;
183  704 case SPECIAL_SYMBOL:
184  704 getScannerContext().onSpecialSymbol(
185    "" + character.getCharacter());
186  704 break;
187  17 case NEW_LINE:
188  17 getScannerContext().onLineBreak();
189  17 break;
190  3184 case SPACE:
191  3184 StringBuilder spaceBuffer = new StringBuilder(" ");
192  3223 while (!stack.isEmpty() && (stack.element().getType() == XhtmlCharacterType.SPACE)) {
193  39 stack.poll();
194  39 spaceBuffer.append(' ');
195    }
196  3184 getScannerContext().onSpace(spaceBuffer.toString());
197  3184 break;
198  4451 default:
199  4451 StringBuilder charBuffer = new StringBuilder();
200  4451 charBuffer.append(character.getCharacter());
201  19160 while (!stack.isEmpty() && (stack.element().getType() == XhtmlCharacterType.CHARACTER)) {
202  14709 charBuffer.append(stack.poll().getCharacter());
203    }
204  4451 getScannerContext()
205    .onWord(
206    WikiPageUtil.escapeXmlString(charBuffer
207    .toString()));
208    }
209    }
210    }
211   
 
212  2220 toggle public void onCharacters(String content)
213    {
214   
215  2220 if (!fPeek.isContentContainer() || shouldIgnoreElements()) {
216  71 return;
217    }
218   
219  2149 if (!fPeek.appendContent(content)) {
220  1941 Queue<XhtmlCharacter> stack = new ArrayDeque<XhtmlCharacter>();
221  25045 for (int i = 0; i < content.length(); i++) {
222  23104 char c = content.charAt(i);
223  23104 stack.offer(new XhtmlCharacter(c, getCharacterType(c)));
224    }
225   
226    // Now send the events.
227  1941 flushStack(stack);
228    }
229    }
230   
 
231  257 toggle public void onComment(char[] array, int start, int length)
232    {
233  257 fCommentHandler.onComment(new String(array, start, length), this);
234    }
235   
 
236  561 toggle public void pushStackParameters()
237    {
238  561 fStackParameters.push(new HashMap<String, Object>());
239   
240    // Pre-initialize stack parameters for performance reason
241    // (so that we don't have to check all the time if they're
242    // initialized or not)
243  561 setStackParameter(LIST_STYLES, new StringBuffer());
244  561 setQuoteDepth(0);
245  561 getStackParameters().put(INSIDE_BLOCK_ELEMENT, false);
246   
247    // Allow each handler to have some initialization
248  561 for (TagHandler tagElementHandler : fMap.values()) {
249  28503 tagElementHandler.initialize(this);
250    }
251    }
252   
 
253  124 toggle public void popStackParameters()
254    {
255  124 fStackParameters.pop();
256    }
257   
 
258  30159 toggle private Map<String, Object> getStackParameters()
259    {
260  30159 return fStackParameters.peek();
261    }
262   
 
263  3686 toggle public void setStackParameter(String name, Object data)
264    {
265  3686 Deque<Object> set = (Deque<Object>) getStackParameters().get(name);
266  3686 if (set != null && !set.isEmpty()) {
267  224 set.pop();
268    }
269  3686 pushStackParameter(name, data);
270    }
271   
 
272  7284 toggle public Object getStackParameter(String name)
273    {
274  7284 Deque<Object> set = (Deque<Object>) getStackParameters().get(name);
275  7284 return (set == null) ? null : set.peek();
276    }
277   
 
278  0 toggle @Deprecated
279    public Object getStackParameter(String name, int index)
280    {
281  0 Deque<Object> set = (Deque<Object>) getStackParameters().get(name);
282  0 if (set == null || set.size() <= index) {
283  0 return null;
284    }
285  0 return set.toArray()[set.size() - index - 1];
286    }
287   
 
288  8 toggle public Iterator<Object> getStackParameterIterator(String name) {
289  8 Deque<Object> set = (Deque<Object>) getStackParameters().get(name);
290  8 return (set == null) ? null : set.descendingIterator();
291    }
292   
 
293  5393 toggle public void pushStackParameter(String name, Object data)
294    {
295  5393 Deque<Object> set = (Deque<Object>) getStackParameters().get(name);
296  5393 if (set == null) {
297  3917 getStackParameters().put(name, set = new LinkedList<Object>());
298    }
299   
300  5393 set.push(data);
301    }
302   
 
303  1709 toggle public Object popStackParameter(String name)
304    {
305  1709 return ((Deque<Object>) getStackParameters().get(name)).pop();
306    }
307   
 
308  613 toggle public void setQuoteDepth(int depth) {
309  613 setStackParameter(QUOTE_DEPTH, depth);
310    }
311   
 
312  3918 toggle public int getQuoteDepth() {
313  3918 return (int) getStackParameter(QUOTE_DEPTH);
314    }
315   
 
316  1236 toggle public boolean isInsideBlockElement() {
317  1236 return (boolean) getStackParameters().get(INSIDE_BLOCK_ELEMENT);
318    }
319   
 
320  1228 toggle public void setInsideBlockElement() {
321  1228 getStackParameters().put(INSIDE_BLOCK_ELEMENT, true);
322    }
323   
 
324  1321 toggle public void unsetInsideBlockElement() {
325  1321 getStackParameters().put(INSIDE_BLOCK_ELEMENT, false);
326    }
327   
 
328  29 toggle public void setDocumentParent() {
329  29 getStackParameters().put(DOCUMENT_PARENT, fPeek.getParent());
330    }
331   
 
332  3787 toggle public TagContext getDocumentParent() {
333  3787 return (TagContext) getStackParameters().get(DOCUMENT_PARENT);
334    }
335   
 
336  363 toggle public String pushListStyle(char style) {
337  363 StringBuffer listStyles = (StringBuffer) getStackParameter(LIST_STYLES);
338  363 listStyles.append(style);
339  363 return listStyles.toString();
340    }
341   
 
342  363 toggle public void popListStyle() {
343    // We should always have a length greater than 0 but we handle
344    // the case where the user has entered some badly formed HTML
345  363 StringBuffer listStyles = (StringBuffer) getStackParameter(LIST_STYLES);
346  363 if (listStyles.length() > 0) {
347  363 listStyles.setLength(listStyles.length() - 1);
348    }
349    }
350   
 
351  150 toggle public boolean isEndOfList() {
352  150 return ((StringBuffer) getStackParameter(LIST_STYLES)).length() == 0;
353    }
354   
 
355  13 toggle public void resetEmptyLinesCount() {
356  13 fEmptyLineCount = 0;
357    }
358   
 
359  17 toggle public void incrementEmptyLinesCount() {
360  17 fEmptyLineCount += 1;
361    }
362   
 
363  1906 toggle public int getEmptyLinesCount() {
364  1906 return fEmptyLineCount;
365    }
366   
 
367  12968 toggle public boolean shouldIgnoreElements() {
368  12968 return fIgnoreElements;
369    }
370   
 
371  38 toggle public void setIgnoreElements() {
372  38 fIgnoreElements = true;
373    }
374   
 
375  38 toggle public void unsetIgnoreElements() {
376  38 fIgnoreElements = false;
377    }
378    }