1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.filter.xml.internal.serializer

File DefaultXMLSerializer.java

 

Coverage histogram

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

Code metrics

78
129
16
1
382
271
64
0.5
8.06
16
4

Classes

Class Line # Actions
DefaultXMLSerializer 57 129 0% 64 27
0.878923887.9%
 

Contributing tests

This file is covered by 295 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.serializer;
21   
22    import java.io.Closeable;
23    import java.io.IOException;
24    import java.lang.reflect.InvocationHandler;
25    import java.lang.reflect.Method;
26    import java.lang.reflect.Type;
27    import java.util.Arrays;
28    import java.util.List;
29    import java.util.Objects;
30    import java.util.regex.Pattern;
31   
32    import javax.xml.stream.FactoryConfigurationError;
33    import javax.xml.stream.XMLOutputFactory;
34    import javax.xml.stream.XMLStreamException;
35    import javax.xml.stream.XMLStreamWriter;
36    import javax.xml.transform.Result;
37   
38    import org.xwiki.component.util.ReflectionUtils;
39    import org.xwiki.filter.FilterDescriptor;
40    import org.xwiki.filter.FilterElementDescriptor;
41    import org.xwiki.filter.FilterElementParameterDescriptor;
42    import org.xwiki.filter.internal.DefaultFilterDescriptorManager;
43    import org.xwiki.filter.xml.XMLConfiguration;
44    import org.xwiki.filter.xml.internal.XMLUtils;
45    import org.xwiki.filter.xml.internal.parameter.ParameterManager;
46    import org.xwiki.properties.ConverterManager;
47    import org.xwiki.xml.stax.StAXUtils;
48   
49    import com.ctc.wstx.api.WstxOutputProperties;
50   
51    /**
52    * Proxy called as an event filter to produce SAX events.
53    *
54    * @version $Id: 2aca53455999f0cfab598baeb626d3f6d291f97c $
55    * @since 5.2M1
56    */
 
57    public class DefaultXMLSerializer implements InvocationHandler, Closeable
58    {
59    private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance();
60   
 
61  15 toggle static {
62    // Allow producing XML with several root elements (there is no constraint on events to have a single root
63    // begin/end event)
64  15 XML_OUTPUT_FACTORY.setProperty(WstxOutputProperties.P_OUTPUT_VALIDATE_STRUCTURE, false);
65    }
66   
67    private static final Pattern VALID_ELEMENTNAME = Pattern.compile("[A-Za-z][A-Za-z0-9:_.-]*");
68   
69    private final XMLStreamWriter xmlStreamWriter;
70   
71    private final ParameterManager parameterManager;
72   
73    private final FilterDescriptor descriptor;
74   
75    private final ConverterManager converter;
76   
77    private final XMLConfiguration configuration;
78   
 
79  529 toggle public DefaultXMLSerializer(Result result, ParameterManager parameterManager, FilterDescriptor descriptor,
80    ConverterManager converter, XMLConfiguration configuration) throws XMLStreamException, FactoryConfigurationError
81    {
82  529 this.parameterManager = parameterManager;
83  529 this.descriptor = descriptor;
84  529 this.converter = converter;
85  529 this.configuration = configuration != null ? configuration : new XMLConfiguration();
86   
87  529 this.xmlStreamWriter = StAXUtils.getXMLStreamWriter(XML_OUTPUT_FACTORY, result);
88    }
89   
 
90  10150 toggle private boolean isValidBlockElementName(String blockName)
91    {
92  10150 return VALID_ELEMENTNAME.matcher(blockName).matches()
93    && !this.configuration.getElementParameters().equals(blockName);
94    }
95   
 
96  4278 toggle private boolean isValidParameterElementName(String parameterName)
97    {
98  4278 return VALID_ELEMENTNAME.matcher(parameterName).matches()
99    && !XMLUtils.INDEX_PATTERN.matcher(parameterName).matches();
100    }
101   
 
102  3359 toggle private boolean isValidParameterAttributeName(String parameterName)
103    {
104  3359 return isValidParameterElementName(parameterName)
105    && !this.configuration.getAttributeParameterName().equals(parameterName);
106    }
107   
 
108  0 toggle private String getBlockName(String eventName)
109    {
110  0 String blockName = Character.toLowerCase(eventName.charAt(0)) + eventName.substring(1);
111   
112  0 return blockName;
113    }
114   
 
115  3287 toggle private void writeInlineParameters(List<Object> parameters, FilterElementDescriptor element)
116    throws XMLStreamException
117    {
118  9302 for (int i = 0; i < parameters.size(); ++i) {
119  6015 Object parameterValue = parameters.get(i);
120   
121  6015 if (parameterValue != null) {
122  3359 FilterElementParameterDescriptor<?> filterParameter = element.getParameters()[i];
123   
124  3359 if (!Objects.equals(filterParameter.getDefaultValue(), parameterValue)) {
125  3359 Class<?> typeClass = ReflectionUtils.getTypeClass(filterParameter.getType());
126   
127  3359 String attributeName;
128   
129  3359 if (filterParameter.getName() != null) {
130  3359 if (isValidParameterAttributeName(filterParameter.getName())) {
131  3359 attributeName = filterParameter.getName();
132    } else {
133  0 attributeName = null;
134    }
135    } else {
136  0 attributeName = "_" + filterParameter.getIndex();
137    }
138   
139  3359 if (attributeName != null) {
140  3359 if (parameterValue instanceof String) {
141  2027 this.xmlStreamWriter.writeAttribute(attributeName, (String) parameterValue);
142   
143  2027 parameters.set(filterParameter.getIndex(), null);
144  1332 } else if (XMLUtils.isSimpleType(typeClass)) {
145  356 this.xmlStreamWriter.writeAttribute(attributeName,
146    this.converter.<String>convert(String.class, parameterValue));
147   
148  356 parameters.set(filterParameter.getIndex(), null);
149  976 } else if (Objects.equals(XMLUtils.emptyValue(typeClass), parameterValue)) {
150  57 this.xmlStreamWriter.writeAttribute(attributeName, "");
151   
152  57 parameters.set(filterParameter.getIndex(), null);
153    }
154    }
155    }
156    }
157    }
158    }
159   
 
160  3387 toggle private void writeStartAttributes(String blockName, List<Object> parameters) throws XMLStreamException
161    {
162  3387 if (!isValidBlockElementName(blockName)) {
163  0 this.xmlStreamWriter.writeAttribute(this.configuration.getAttributeBlockName(), blockName);
164    }
165   
166  3387 if (parameters != null) {
167  3287 FilterElementDescriptor element = this.descriptor.getElement(blockName);
168   
169  3287 writeInlineParameters(parameters, element);
170    }
171    }
172   
 
173  6763 toggle private void removeDefaultParameters(List<Object> parameters, FilterElementDescriptor descriptor)
174    {
175  6763 if (parameters != null) {
176  13662 for (int i = 0; i < parameters.size(); ++i) {
177  8195 Object value = parameters.get(i);
178   
179  8195 if (!shouldWriteParameter(value, descriptor.getParameters()[i])) {
180  2698 parameters.set(i, null);
181    }
182    }
183    }
184    }
185   
 
186  2554 toggle private void beginEvent(Method method, Object[] parameters) throws XMLStreamException
187    {
188  2554 String blockName = DefaultFilterDescriptorManager.getElementName(method, true);
189   
190  2554 FilterElementDescriptor element = this.descriptor.getElement(blockName);
191   
192  2554 List<Object> elementParameters = parameters != null ? Arrays.asList(parameters) : null;
193   
194    // Remove useless parameters
195  2554 removeDefaultParameters(elementParameters, element);
196   
197    // Get proper element name
198  2554 String elementName;
199  2554 if (isValidBlockElementName(blockName)) {
200  2554 elementName = blockName;
201    } else {
202  0 elementName = this.configuration.getElementBlock();
203    }
204   
205    // Print start element
206  2554 this.xmlStreamWriter.writeStartElement(elementName);
207   
208    // Put as attributes parameters which are simple enough to not require full XML serialization
209  2554 writeStartAttributes(blockName, elementParameters);
210   
211    // Write complex parameters
212  2554 writeParameters(elementParameters, element);
213    }
214   
 
215  2554 toggle private void endEvent() throws XMLStreamException
216    {
217  2554 this.xmlStreamWriter.writeEndElement();
218    }
219   
 
220  4209 toggle private void onEvent(Method method, Object[] parameters) throws XMLStreamException
221    {
222  4209 String blockName = DefaultFilterDescriptorManager.getElementName(method, true);
223   
224  4209 FilterElementDescriptor element = this.descriptor.getElement(blockName);
225   
226  4209 List<Object> elementParameters = parameters != null ? Arrays.asList(parameters) : null;
227   
228    // Remove useless parameters
229  4209 removeDefaultParameters(elementParameters, element);
230   
231    // Get proper element name
232  4209 String elementName;
233  4209 if (isValidBlockElementName(blockName)) {
234  4209 elementName = blockName;
235    } else {
236  0 elementName = this.configuration.getElementBlock();
237    }
238   
239    // Write start element
240  4209 this.xmlStreamWriter.writeStartElement(elementName);
241   
242    // Put as attributes parameters which are simple enough to not require full XML serialization
243  4209 if (elementParameters != null && elementParameters.size() > 1) {
244  833 writeStartAttributes(blockName, Arrays.asList(parameters));
245    }
246   
247    // Write complex parameters
248  4209 if (parameters != null && parameters.length == 1
249    && XMLUtils.isSimpleType(element.getParameters()[0].getType())) {
250  2146 Object parameter = parameters[0];
251  2146 if (parameter != null && !Objects.equals(element.getParameters()[0].getDefaultValue(), parameter)) {
252  2138 String value = parameter.toString();
253  2138 this.xmlStreamWriter.writeCharacters(value);
254    }
255    } else {
256  2063 writeParameters(elementParameters, element);
257    }
258   
259    // Write end element
260  4209 this.xmlStreamWriter.writeEndElement();
261    }
262   
 
263  14244 toggle private boolean shouldWriteParameter(Object value, FilterElementParameterDescriptor<?> filterParameter)
264    {
265  14244 boolean write;
266   
267  14244 if (value != null && !Objects.equals(filterParameter.getDefaultValue(), value)) {
268  6486 write = true;
269   
270  6486 Type type = filterParameter.getType();
271   
272  6486 if (type instanceof Class) {
273  6428 Class<?> typeClass = (Class<?>) type;
274  6428 try {
275  6428 if (typeClass.isPrimitive()) {
276  390 write = !XMLUtils.emptyValue(typeClass).equals(value);
277    }
278    } catch (Exception e) {
279    // Should never happen
280    }
281    }
282    } else {
283  7758 write = false;
284    }
285   
286  14244 return write;
287    }
288   
 
289  4617 toggle private void writeParameters(List<Object> parameters, FilterElementDescriptor descriptor) throws XMLStreamException
290    {
291  4617 if (parameters != null && !parameters.isEmpty()) {
292  3321 boolean writeContainer = false;
293   
294  3321 for (Object parameter : parameters) {
295  5859 if (parameter != null) {
296  899 writeContainer = true;
297  899 break;
298    }
299    }
300   
301  3321 if (writeContainer) {
302  899 this.xmlStreamWriter.writeStartElement(this.configuration.getElementParameters());
303    }
304   
305  9370 for (int i = 0; i < parameters.size(); ++i) {
306  6049 Object parameterValue = parameters.get(i);
307   
308  6049 FilterElementParameterDescriptor<?> filterParameter = descriptor.getParameters()[i];
309   
310  6049 if (shouldWriteParameter(parameterValue, filterParameter)) {
311  919 String elementName;
312  919 String attributeName = null;
313  919 String attributeValue = null;
314   
315  919 if (filterParameter.getName() != null) {
316  919 if (isValidParameterElementName(filterParameter.getName())) {
317  919 elementName = filterParameter.getName();
318    } else {
319  0 elementName = "_" + filterParameter.getIndex();
320  0 attributeName = this.configuration.getAttributeParameterName();
321  0 attributeValue = filterParameter.getName();
322    }
323    } else {
324  0 elementName = "_" + filterParameter.getIndex();
325    }
326   
327  919 this.xmlStreamWriter.writeStartElement(elementName);
328   
329  919 if (attributeName != null) {
330  0 this.xmlStreamWriter.writeAttribute(attributeName, attributeValue);
331    }
332  919 if (descriptor.getParameters()[i].getType() == Object.class
333    && parameterValue.getClass() != String.class) {
334  10 this.xmlStreamWriter.writeAttribute(this.configuration.getAttributeParameterType(),
335    parameterValue.getClass().getCanonicalName());
336    }
337   
338  919 this.parameterManager.serialize(descriptor.getParameters()[i].getType(), parameterValue,
339    this.xmlStreamWriter);
340   
341  919 this.xmlStreamWriter.writeEndElement();
342    }
343    }
344   
345  3321 if (writeContainer) {
346  899 this.xmlStreamWriter.writeEndElement();
347    }
348    }
349    }
350   
 
351  9359 toggle @Override
352    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
353    {
354  9359 Object result = null;
355   
356  9359 if (method.getDeclaringClass() == Closeable.class) {
357  42 close();
358    } else {
359  9317 if (method.getName().startsWith("begin")) {
360  2554 beginEvent(method, args);
361  6763 } else if (method.getName().startsWith("end")) {
362  2554 endEvent();
363  4209 } else if (method.getName().startsWith("on")) {
364  4209 onEvent(method, args);
365    } else {
366  0 throw new NoSuchMethodException(method.toGenericString());
367    }
368    }
369   
370  9359 return result;
371    }
372   
 
373  42 toggle @Override
374    public void close() throws IOException
375    {
376  42 try {
377  42 this.xmlStreamWriter.close();
378    } catch (XMLStreamException e) {
379  0 throw new IOException(e);
380    }
381    }
382    }