1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.job.internal.xstream

File SafeReflectionConverter.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart5.png
74% of files have more coverage

Code metrics

60
91
8
2
262
203
41
0.45
11.38
4
5.12

Classes

Class Line # Actions
SafeReflectionConverter 53 87 0% 40 92
0.402597440.3%
SafeReflectionConverter.FieldInfo 60 4 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 185 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.job.internal.xstream;
21   
22    import java.lang.reflect.Field;
23    import java.util.ArrayList;
24    import java.util.Collection;
25    import java.util.Collections;
26    import java.util.HashMap;
27    import java.util.HashSet;
28    import java.util.Iterator;
29    import java.util.List;
30    import java.util.Map;
31    import java.util.Set;
32   
33    import org.slf4j.Logger;
34    import org.slf4j.LoggerFactory;
35   
36    import com.thoughtworks.xstream.converters.ConversionException;
37    import com.thoughtworks.xstream.converters.MarshallingContext;
38    import com.thoughtworks.xstream.converters.SingleValueConverter;
39    import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
40    import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
41    import com.thoughtworks.xstream.core.ReferencingMarshallingContext;
42    import com.thoughtworks.xstream.core.util.ArrayIterator;
43    import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
44    import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
45    import com.thoughtworks.xstream.mapper.Mapper;
46   
47    /**
48    * A {@link ReflectionConverter} which never fail whatever value is provided.
49    *
50    * @version $Id: 19a1f48f31a123a9aa78ee0f9eb41434635d6e71 $
51    * @since 5.4M1
52    */
 
53    public class SafeReflectionConverter extends ReflectionConverter
54    {
55    /**
56    * The logger.
57    */
58    private static final Logger LOGGER = LoggerFactory.getLogger(SafeTreeMarshaller.class);
59   
 
60    private static class FieldInfo
61    {
62    final String fieldName;
63   
64    final Class type;
65   
66    final Class definedIn;
67   
68    final Object value;
69   
 
70  32078 toggle FieldInfo(String fieldName, Class type, Class definedIn, Object value)
71    {
72  32077 this.fieldName = fieldName;
73  32077 this.type = type;
74  32077 this.definedIn = definedIn;
75  32077 this.value = value;
76    }
77    }
78   
79    private final SafeXStream xstream;
80   
 
81  463 toggle public SafeReflectionConverter(SafeXStream xstream)
82    {
83  463 super(xstream.getMapper(), xstream.getReflectionProvider());
84   
85  463 this.xstream = xstream;
86    }
87   
88    // Most of the content is copied from AbstractReflectionConverter#doMarshal because it's not designed to be overwritten.
89    // It's using the exact same formatting to make easier to upgrade.
 
90  5495 toggle @Override
91    protected void doMarshal(final Object source, final HierarchicalStreamWriter writer,
92    final MarshallingContext context) {
93  5494 final List fields = new ArrayList();
94  5495 final Map defaultFieldDefinition = new HashMap();
95   
96    // Attributes might be preferred to child elements ...
97  5493 reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
98    final Set writtenAttributes = new HashSet();
99   
 
100  32078 toggle public void visit(String fieldName, Class type, Class definedIn, Object value) {
101  32080 if (!mapper.shouldSerializeMember(definedIn, fieldName)) {
102  0 return;
103    }
104  32080 if (!defaultFieldDefinition.containsKey(fieldName)) {
105  32075 Class lookupType = source.getClass();
106    // See XSTR-457 and OmitFieldsTest
107  32076 if (definedIn != source.getClass()
108    && !mapper.shouldSerializeMember(lookupType, fieldName)) {
109  0 lookupType = definedIn;
110    }
111  32079 defaultFieldDefinition.put(
112    fieldName, reflectionProvider.getField(lookupType, fieldName));
113    }
114   
115  32074 SingleValueConverter converter = mapper.getConverterFromItemType(
116    fieldName, type, definedIn);
117  32076 if (converter != null) {
118  0 final String attribute = mapper.aliasForAttribute(mapper.serializedMember(
119    definedIn, fieldName));
120  0 if (value != null) {
121  0 if (writtenAttributes.contains(fieldName)) { // TODO: use attribute
122  0 throw new ConversionException("Cannot write field with name '"
123    + fieldName
124    + "' twice as attribute for object of type "
125    + source.getClass().getName());
126    }
127  0 final String str = converter.toString(value);
128  0 if (str != null) {
129  0 writer.addAttribute(attribute, str);
130    }
131    }
132  0 writtenAttributes.add(fieldName); // TODO: use attribute
133    } else {
134  32079 fields.add(new FieldInfo(fieldName, type, definedIn, value));
135    }
136    }
137    });
138   
139  5495 new Object() {
 
140  5494 toggle {
141  37577 for (Iterator fieldIter = fields.iterator(); fieldIter.hasNext();) {
142  32082 FieldInfo info = (FieldInfo)fieldIter.next();
143  32082 if (info.value != null) {
144  25862 Mapper.ImplicitCollectionMapping mapping = mapper
145    .getImplicitCollectionDefForFieldName(
146    source.getClass(), info.fieldName);
147  25865 if (mapping != null) {
148  0 if (context instanceof ReferencingMarshallingContext) {
149  0 if (info.value != Collections.EMPTY_LIST
150    && info.value != Collections.EMPTY_SET
151    && info.value != Collections.EMPTY_MAP) {
152  0 ReferencingMarshallingContext refContext = (ReferencingMarshallingContext)context;
153  0 refContext.registerImplicit(info.value);
154    }
155    }
156  0 final boolean isCollection = info.value instanceof Collection;
157  0 final boolean isMap = info.value instanceof Map;
158  0 final boolean isEntry = isMap && mapping.getKeyFieldName() == null;
159  0 final boolean isArray = info.value.getClass().isArray();
160  0 for (Iterator iter = isArray
161    ? new ArrayIterator(info.value)
162  0 : isCollection ? ((Collection)info.value).iterator() : isEntry
163    ? ((Map)info.value).entrySet().iterator()
164  0 : ((Map)info.value).values().iterator(); iter.hasNext();) {
165  0 Object obj = iter.next();
166  0 final String itemName;
167  0 final Class itemType;
168  0 if (obj == null) {
169  0 itemType = Object.class;
170  0 itemName = mapper.serializedClass(null);
171  0 } else if (isEntry) {
172  0 final String entryName = mapping.getItemFieldName() != null
173    ? mapping.getItemFieldName()
174    : mapper.serializedClass(Map.Entry.class);
175  0 Map.Entry entry = (Map.Entry)obj;
176  0 ExtendedHierarchicalStreamWriterHelper.startNode(
177    writer, entryName, entry.getClass());
178  0 writeItem(entry.getKey(), context, writer);
179  0 writeItem(entry.getValue(), context, writer);
180  0 writer.endNode();
181  0 continue;
182  0 } else if (mapping.getItemFieldName() != null) {
183  0 itemType = mapping.getItemType();
184  0 itemName = mapping.getItemFieldName();
185    } else {
186  0 itemType = obj.getClass();
187  0 itemName = mapper.serializedClass(itemType);
188    }
189    // Custom call
190  0 safeWriteField(
191    info.fieldName, itemName, itemType, info.definedIn, obj);
192    }
193    } else {
194    // Custom call
195  25865 safeWriteField(
196    info.fieldName, null, info.type, info.definedIn, info.value);
197    }
198    }
199    }
200   
201    }
202   
203    // Custom method
 
204  25864 toggle void safeWriteField(String fieldName, String aliasName, Class fieldType, Class definedIn, Object newObj)
205    {
206  25864 if (XStreamUtils.isSerializable(newObj)) {
207  25832 writeField(fieldName, aliasName, fieldType, definedIn, newObj);
208    }
209    }
210   
 
211  25830 toggle void writeField(String fieldName, String aliasName, Class fieldType,
212    Class definedIn, Object newObj) {
213  25830 Class actualType = newObj != null ? newObj.getClass() : fieldType;
214  25830 ExtendedHierarchicalStreamWriterHelper.startNode(writer, aliasName != null
215    ? aliasName
216    : mapper.serializedMember(source.getClass(), fieldName), actualType);
217   
218  25831 if (newObj != null) {
219  25832 Class defaultType = mapper.defaultImplementationOf(fieldType);
220  25832 if (!actualType.equals(defaultType)) {
221  2497 String serializedClassName = mapper.serializedClass(actualType);
222  2497 if (!serializedClassName.equals(mapper.serializedClass(defaultType))) {
223  2497 String attributeName = mapper.aliasForSystemAttribute("class");
224  2497 if (attributeName != null) {
225  2497 writer.addAttribute(attributeName, serializedClassName);
226    }
227    }
228    }
229   
230  25832 final Field defaultField = (Field)defaultFieldDefinition.get(fieldName);
231  25831 if (defaultField.getDeclaringClass() != definedIn) {
232  0 String attributeName = mapper.aliasForSystemAttribute("defined-in");
233  0 if (attributeName != null) {
234  0 writer.addAttribute(
235    attributeName, mapper.serializedClass(definedIn));
236    }
237    }
238   
239  25831 Field field = reflectionProvider.getField(definedIn, fieldName);
240  25832 marshallField(context, newObj, field);
241    }
242  25829 writer.endNode();
243    }
244   
 
245  0 toggle void writeItem(Object item, MarshallingContext context,
246    HierarchicalStreamWriter writer) {
247  0 if (item == null) {
248  0 String name = mapper.serializedClass(null);
249  0 ExtendedHierarchicalStreamWriterHelper.startNode(
250    writer, name, Mapper.Null.class);
251  0 writer.endNode();
252    } else {
253  0 String name = mapper.serializedClass(item.getClass());
254  0 ExtendedHierarchicalStreamWriterHelper.startNode(
255    writer, name, item.getClass());
256  0 context.convertAnother(item);
257  0 writer.endNode();
258    }
259    }
260    };
261    }
262    }