1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.filter.xml.internal.parser

File DefaultXMLParser.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

108
171
25
2
520
392
97
0.57
6.84
12.5
3.88

Classes

Class Line # Actions
DefaultXMLParser 64 117 0% 66 37
0.8221153682.2%
DefaultXMLParser.Block 87 54 0% 31 21
0.7812578.1%
 

Contributing tests

This file is covered by 618 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.filter.xml.internal.parser;
21   
22    import java.lang.reflect.InvocationTargetException;
23    import java.lang.reflect.Method;
24    import java.lang.reflect.Type;
25    import java.util.ArrayList;
26    import java.util.Arrays;
27    import java.util.List;
28    import java.util.Stack;
29    import java.util.regex.Matcher;
30   
31    import javax.xml.parsers.ParserConfigurationException;
32   
33    import org.apache.commons.lang3.ArrayUtils;
34    import org.apache.xerces.parsers.XMLParser;
35    import org.slf4j.Logger;
36    import org.slf4j.LoggerFactory;
37    import org.w3c.dom.Element;
38    import org.w3c.dom.Node;
39    import org.w3c.dom.NodeList;
40    import org.xml.sax.Attributes;
41    import org.xml.sax.ContentHandler;
42    import org.xml.sax.SAXException;
43    import org.xml.sax.helpers.DefaultHandler;
44    import org.xwiki.component.util.ReflectionUtils;
45    import org.xwiki.filter.FilterDescriptor;
46    import org.xwiki.filter.FilterElementDescriptor;
47    import org.xwiki.filter.FilterElementParameterDescriptor;
48    import org.xwiki.filter.FilterEventParameters;
49    import org.xwiki.filter.UnknownFilter;
50    import org.xwiki.filter.xml.XMLConfiguration;
51    import org.xwiki.filter.xml.internal.XMLUtils;
52    import org.xwiki.filter.xml.internal.parameter.ParameterManager;
53    import org.xwiki.properties.ConverterManager;
54    import org.xwiki.properties.converter.ConversionException;
55    import org.xwiki.xml.Sax2Dom;
56   
57    /**
58    * Default implementation of {@link XMLParser}.
59    *
60    * @version $Id: 1edc7186f7c21910c18514962e1d95a094e3407f $
61    * @since 5.2M1
62    */
63    // TODO: move from ContentHandler to XMLEventConsumer or XMLEventWriter
 
64    public class DefaultXMLParser extends DefaultHandler implements ContentHandler
65    {
66    /**
67    * Logging helper object.
68    */
69    protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultXMLParser.class);
70   
71    private ParameterManager parameterManager;
72   
73    private ConverterManager stringConverter;
74   
75    private FilterDescriptor filterDescriptor;
76   
77    private Object filter;
78   
79    private Stack<Block> blockStack = new Stack<Block>();
80   
81    private int elementDepth = 0;
82   
83    private StringBuilder content;
84   
85    private XMLConfiguration configuration;
86   
 
87    public static class Block
88    {
89    public String name;
90   
91    public FilterElementDescriptor filterElement;
92   
93    public boolean beginSent = false;
94   
95    public List<Object> parameters = new ArrayList<Object>();
96   
97    public FilterEventParameters namedParameters = new FilterEventParameters();
98   
99    public Sax2Dom parametersDOMBuilder;
100   
101    public int elementDepth;
102   
103    private Object[] parametersTable;
104   
 
105  8066 toggle public Block(String name, FilterElementDescriptor listenerElement, int elementDepth)
106    {
107  8066 this.name = name;
108  8066 this.filterElement = listenerElement;
109  8066 this.elementDepth = elementDepth;
110    }
111   
 
112  16132 toggle public boolean isContainer()
113    {
114  16132 return this.filterElement == null || this.filterElement.getBeginMethod() != null;
115    }
116   
 
117  0 toggle public void setParameter(String name, Object value)
118    {
119  0 this.namedParameters.put(name, value);
120    }
121   
 
122  5593 toggle public void setParameter(int index, Object value)
123    {
124  11232 for (int i = this.parameters.size(); i <= index; ++i) {
125  5639 this.parameters.add(this.filterElement.getParameters()[i].getDefaultValue());
126    }
127   
128  5593 this.parameters.set(index, value);
129  5593 this.parametersTable = null;
130    }
131   
 
132  5015 toggle public List<Object> getParametersList()
133    {
134  5015 return this.parameters;
135    }
136   
 
137  11101 toggle public Object[] getParametersTable()
138    {
139  11101 if (this.parametersTable == null) {
140  8058 if (this.parameters.isEmpty()) {
141  3287 this.parametersTable = ArrayUtils.EMPTY_OBJECT_ARRAY;
142    }
143   
144  8058 this.parametersTable = this.parameters.toArray();
145    }
146   
147  11101 return this.parametersTable;
148    }
149   
 
150  3051 toggle public void fireBeginEvent(Object listener) throws SAXException
151    {
152  3051 if (this.filterElement != null) {
153  3043 fireEvent(this.filterElement.getBeginMethod(), listener);
154  8 } else if (listener instanceof UnknownFilter) {
155  0 try {
156  0 ((UnknownFilter) listener).beginUnknwon(this.name, this.namedParameters);
157    } catch (Exception e) {
158  0 throw new SAXException("Failed to invoke unknown event with name [" + this.name
159    + "] and parameters [" + this.namedParameters + "]", e);
160    }
161    }
162  3051 this.beginSent = true;
163    }
164   
 
165  3051 toggle public void fireEndEvent(Object listener) throws SAXException
166    {
167  3051 if (this.filterElement != null) {
168  3043 fireEvent(this.filterElement.getEndMethod(), listener);
169  8 } else if (listener instanceof UnknownFilter) {
170  0 try {
171  0 ((UnknownFilter) listener).endUnknwon(this.name, this.namedParameters);
172    } catch (Exception e) {
173  0 throw new SAXException("Failed to invoke unknown event with name [" + this.name
174    + "] and parameters [" + this.namedParameters + "]", e);
175    }
176    }
177    }
178   
 
179  5015 toggle public void fireOnEvent(Object listener) throws SAXException
180    {
181  5015 if (this.filterElement != null) {
182  5015 fireEvent(this.filterElement.getOnMethod(), listener);
183  0 } else if (listener instanceof UnknownFilter) {
184  0 try {
185  0 ((UnknownFilter) listener).onUnknwon(this.name, this.namedParameters);
186    } catch (Exception e) {
187  0 throw new SAXException("Failed to invoke unknown event with name [" + this.name
188    + "] and parameters [" + this.namedParameters + "]", e);
189    }
190    }
191    }
192   
 
193  11101 toggle private void fireEvent(Method eventMethod, Object listener) throws SAXException
194    {
195  11101 Object[] parameters = getParametersTable();
196  11101 Class<?>[] methodParameters = eventMethod.getParameterTypes();
197   
198  11101 Object[] properParameters;
199    // Missing parameters
200  11101 if (methodParameters.length > parameters.length) {
201  3820 properParameters = new Object[methodParameters.length];
202  10510 for (int i = 0; i < methodParameters.length; ++i) {
203  6690 if (i < parameters.length) {
204  2426 properParameters[i] = parameters[i];
205    } else {
206  4264 properParameters[i] = this.filterElement.getParameters()[i].getDefaultValue();
207    }
208    }
209    } else {
210  7281 properParameters = parameters;
211    }
212   
213    // Invalid primitive
214  22684 for (int i = 0; i < properParameters.length; ++i) {
215  11583 Object parameter = properParameters[i];
216   
217  11583 if (parameter == null) {
218  162 Class<?> methodParameter = methodParameters[i];
219   
220  162 if (methodParameter.isPrimitive()) {
221  129 properParameters[i] = XMLUtils.emptyValue(methodParameter);
222    }
223    }
224    }
225   
226    // Send event
227  11101 try {
228  11101 eventMethod.invoke(listener, properParameters);
229    } catch (InvocationTargetException e) {
230  0 throw new SAXException("Event [" + eventMethod + "] thrown exception",
231  0 e.getCause() instanceof Exception ? (Exception) e.getCause() : e);
232    } catch (Exception e) {
233  0 throw new SAXException("Failed to invoke event [" + eventMethod + "]", e);
234    }
235    }
236    }
237   
 
238  640 toggle public DefaultXMLParser(Object listener, FilterDescriptor listenerDescriptor, ConverterManager stringConverter,
239    ParameterManager parameterManager, XMLConfiguration configuration)
240    {
241  640 this.filter = listener;
242  640 this.filterDescriptor = listenerDescriptor;
243  640 this.stringConverter = stringConverter;
244  640 this.parameterManager = parameterManager;
245  640 this.configuration = configuration != null ? configuration : new XMLConfiguration();
246    }
247   
 
248  20194 toggle private boolean onBlockChild()
249    {
250  20194 boolean result;
251   
252  20194 if (!this.blockStack.isEmpty()) {
253  20194 Block currentBlock = this.blockStack.peek();
254   
255  20194 return currentBlock.elementDepth == (this.elementDepth - 1);
256    } else {
257  0 result = false;
258    }
259   
260  0 return result;
261    }
262   
 
263  36326 toggle private boolean onBlockElement(String elementName)
264    {
265  36326 boolean result;
266   
267  36326 if (!this.blockStack.isEmpty()) {
268  35686 Block currentBlock = this.blockStack.peek();
269   
270  35686 result = (this.elementDepth - currentBlock.elementDepth <= 1)
271    && !this.configuration.getElementParameters().equals(elementName);
272    } else {
273  640 result = true;
274    }
275   
276  36326 return result;
277    }
278   
 
279  20194 toggle private boolean onParametersElement(String elementName)
280    {
281  20194 return onBlockChild() && this.configuration.getElementParameters().equals(elementName);
282    }
283   
 
284  1 toggle private int extractParameterIndex(String elementName)
285    {
286  1 Matcher matcher = XMLUtils.INDEX_PATTERN.matcher(elementName);
287  1 matcher.find();
288   
289  1 return Integer.valueOf(matcher.group(1));
290    }
291   
 
292  2694 toggle private boolean isReservedBlockAttribute(String attributeName)
293    {
294  2694 return this.configuration.getAttributeBlockName().equals(attributeName)
295    || this.configuration.getAttributeParameterName().equals(attributeName);
296    }
297   
 
298  2695 toggle private void setParameter(Block block, String name, Object value, boolean attribute) throws SAXException
299    {
300  2695 if (XMLUtils.INDEX_PATTERN.matcher(name).matches()) {
301  1 int parameterIndex = extractParameterIndex(name);
302   
303  1 if (block.filterElement != null && block.filterElement.getParameters().length > parameterIndex) {
304  1 FilterElementParameterDescriptor<?> filterParameter =
305    block.filterElement.getParameters()[parameterIndex];
306   
307  1 setParameter(block, filterParameter, value);
308    } else {
309  0 LOGGER.warn("Unknown element parameter [{}] (=[{}]) in block [{}] (available parameters are {})", name,
310    value, block.name, Arrays.asList(block.filterElement.getParameters()));
311   
312  0 block.setParameter(name, value);
313    }
314  2694 } else if (!attribute || !isReservedBlockAttribute(name)) {
315  2694 FilterElementParameterDescriptor<?> filterParameter =
316  2694 block.filterElement != null ? block.filterElement.getParameter(name) : null;
317   
318  2694 if (filterParameter != null) {
319  2694 setParameter(block, filterParameter, value);
320    } else {
321  0 LOGGER.warn("Unknown element parameter [{}] (=[{}]) in block [{}] (available parameters are {})", name,
322    value, block.name, Arrays.asList(block.filterElement.getParameters()));
323   
324  0 block.setParameter(name, value);
325    }
326    }
327    }
328   
 
329  2695 toggle private void setParameter(Block block, FilterElementParameterDescriptor<?> filterParameter, Object value)
330    throws SAXException
331    {
332  2695 Type type = filterParameter.getType();
333   
334  2695 if (value instanceof Element) {
335  955 try {
336  955 block.setParameter(filterParameter.getIndex(), unserializeParameter(type, (Element) value));
337    } catch (ClassNotFoundException e) {
338  0 throw new SAXException("Failed to parse property", e);
339    }
340  1740 } else if (value instanceof String) {
341  1740 String stringValue = (String) value;
342   
343  1740 Class<?> typeClass = ReflectionUtils.getTypeClass(type);
344   
345  1740 if (typeClass == String.class || typeClass == Object.class) {
346  1235 block.setParameter(filterParameter.getIndex(), stringValue);
347    } else {
348  505 try {
349  505 block.setParameter(filterParameter.getIndex(), this.stringConverter.convert(type, value));
350    } catch (ConversionException e) {
351  0 if (stringValue.isEmpty()) {
352  0 block.setParameter(filterParameter.getIndex(), XMLUtils.emptyValue(typeClass));
353    }
354   
355  0 LOGGER.warn("Unsuported conversion to type [{}] for value [{}]", type, value);
356    }
357    }
358    } else {
359  0 LOGGER.warn("Unsuported type [{}] for value [{}]", value.getClass(), value);
360    }
361    }
362   
 
363  955 toggle private Object unserializeParameter(Type type, Element element) throws ClassNotFoundException
364    {
365  955 if (element.hasAttribute(this.configuration.getAttributeParameterType())) {
366  16 String typeString = element.getAttribute(this.configuration.getAttributeParameterType());
367  16 return this.parameterManager
368    .unSerialize(Class.forName(typeString, true, Thread.currentThread().getContextClassLoader()), element);
369    }
370   
371  939 return this.parameterManager.unSerialize(type, element);
372    }
373   
 
374  18163 toggle @Override
375    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
376    {
377  18163 Block currentBlock = this.blockStack.isEmpty() ? null : this.blockStack.peek();
378   
379  18163 if (onBlockElement(qName)) {
380  8066 if (currentBlock != null) {
381    // send previous event
382  7426 if (!currentBlock.beginSent) {
383  2949 currentBlock.fireBeginEvent(this.filter);
384    }
385    }
386   
387    // push new event
388  8066 Block block = getBlock(qName, attributes);
389   
390  8066 this.blockStack.push(block);
391   
392  8066 if (!block.isContainer() && block.filterElement != null && block.filterElement.getParameters().length > 0) {
393  3451 this.content = new StringBuilder();
394    }
395   
396    // Extract simple parameters from attributes
397  9806 for (int i = 0; i < attributes.getLength(); ++i) {
398  1740 String attributeName = attributes.getQName(i);
399   
400  1740 setParameter(block, attributeName, attributes.getValue(i), true);
401    }
402    } else {
403  10097 if (onParametersElement(qName)) {
404    // starting a new block parameter
405  933 if (currentBlock.filterElement != null) {
406  933 try {
407  933 currentBlock.parametersDOMBuilder = new Sax2Dom();
408    } catch (ParserConfigurationException e) {
409  0 throw new SAXException("Failed to create new Sax2Dom handler", e);
410    }
411  933 currentBlock.parametersDOMBuilder.startDocument();
412    }
413    }
414   
415  10097 if (currentBlock.parametersDOMBuilder != null) {
416  10097 currentBlock.parametersDOMBuilder.startElement(uri, localName, qName, attributes);
417    }
418    }
419   
420  18163 ++this.elementDepth;
421    }
422   
 
423  18163 toggle @Override
424    public void endElement(String uri, String localName, String qName) throws SAXException
425    {
426  18163 Block currentBlock = this.blockStack.isEmpty() ? null : this.blockStack.peek();
427   
428  18163 --this.elementDepth;
429   
430  18163 if (onBlockElement(qName)) {
431  8066 Block block = this.blockStack.pop();
432   
433    // Flush pending begin event and send end event or send on event
434  8066 if (block.isContainer()) {
435  3051 if (!block.beginSent) {
436  102 block.fireBeginEvent(this.filter);
437    }
438   
439  3051 block.fireEndEvent(this.filter);
440    } else {
441  5015 if (block.getParametersList().size() == 0
442    && this.filterDescriptor.getElement(qName).getParameters().length > 0) {
443  2944 if (this.content != null && this.content.length() > 0) {
444  2898 block.setParameter(0,
445    this.stringConverter.convert(
446    this.filterDescriptor.getElement(qName).getParameters()[0].getType(),
447    this.content.toString()));
448  2898 this.content = null;
449    }
450    }
451   
452  5015 block.fireOnEvent(this.filter);
453    }
454  10097 } else if (currentBlock.parametersDOMBuilder != null) {
455  10097 currentBlock.parametersDOMBuilder.endElement(uri, localName, qName);
456   
457  10097 if (onParametersElement(qName)) {
458  933 currentBlock.parametersDOMBuilder.endDocument();
459   
460  933 Element rootElement = currentBlock.parametersDOMBuilder.getRootElement();
461  933 NodeList parameterNodes = rootElement.getChildNodes();
462  3617 for (int i = 0; i < parameterNodes.getLength(); ++i) {
463  2684 Node parameterNode = parameterNodes.item(i);
464   
465  2684 if (parameterNode.getNodeType() == Node.ELEMENT_NODE) {
466  955 String nodeName = parameterNode.getLocalName();
467   
468  955 setParameter(currentBlock, nodeName, parameterNode, true);
469    }
470    }
471   
472  933 currentBlock.parametersDOMBuilder = null;
473    }
474    }
475    }
476   
 
477  31943 toggle @Override
478    public void characters(char[] ch, int start, int length) throws SAXException
479    {
480  31943 if (!this.blockStack.isEmpty() && this.blockStack.peek().parametersDOMBuilder != null) {
481  18017 this.blockStack.peek().parametersDOMBuilder.characters(ch, start, length);
482  13926 } else if (this.content != null) {
483  4178 this.content.append(ch, start, length);
484    }
485    }
486   
 
487  0 toggle @Override
488    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
489    {
490  0 if (!this.blockStack.isEmpty() && this.blockStack.peek().parametersDOMBuilder != null) {
491  0 this.blockStack.peek().parametersDOMBuilder.ignorableWhitespace(ch, start, length);
492    }
493    }
494   
 
495  0 toggle @Override
496    public void skippedEntity(String name) throws SAXException
497    {
498  0 if (!this.blockStack.isEmpty() && this.blockStack.peek().parametersDOMBuilder != null) {
499  0 this.blockStack.peek().parametersDOMBuilder.skippedEntity(name);
500    }
501    }
502   
 
503  8066 toggle private Block getBlock(String qName, Attributes attributes)
504    {
505  8066 String blockName;
506  8066 if (this.configuration.getElementBlock().equals(qName)) {
507  0 blockName = attributes.getValue(this.configuration.getAttributeBlockName());
508    } else {
509  8066 blockName = qName;
510    }
511   
512  8066 FilterElementDescriptor element = this.filterDescriptor.getElement(blockName);
513   
514  8066 if (element == null) {
515  8 LOGGER.warn("Uknown filter element [{}]", blockName);
516    }
517   
518  8066 return new Block(qName, element, this.elementDepth);
519    }
520    }