1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.properties.converter.collection

File AbstractCollectionConverter.java

 

Coverage histogram

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

Code metrics

28
68
11
1
304
149
30
0.44
6.18
11
2.73

Classes

Class Line # Actions
AbstractCollectionConverter 46 68 0% 30 17
0.841121584.1%
 

Contributing tests

This file is covered by 1 test. .

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.properties.converter.collection;
21   
22    import java.io.IOException;
23    import java.io.StreamTokenizer;
24    import java.io.StringReader;
25    import java.lang.reflect.Array;
26    import java.lang.reflect.ParameterizedType;
27    import java.lang.reflect.Type;
28    import java.util.ArrayList;
29    import java.util.Collection;
30   
31    import javax.inject.Inject;
32   
33    import org.apache.commons.lang3.StringUtils;
34    import org.xwiki.component.util.ReflectionUtils;
35    import org.xwiki.properties.ConverterManager;
36    import org.xwiki.properties.converter.AbstractConverter;
37    import org.xwiki.properties.converter.ConversionException;
38   
39    /**
40    * Base class for all {@link java.util.Collection} converters.
41    *
42    * @param <T> the type in which the provided value has to be converted
43    * @version $Id: 7612d78572c144e5bfcc74d5fee6d93c0a6409ee $
44    * @since 5.2M1
45    */
 
46    public abstract class AbstractCollectionConverter<T extends Collection> extends AbstractConverter<T>
47    {
48    /**
49    * Quote char.
50    */
51    protected static final char QUOTECHAR = '"';
52   
53    /**
54    * Quote string.
55    */
56    protected static final String QUOTESTRING = "\"";
57   
58    /**
59    * Used to convert collection elements.
60    */
61    @Inject
62    private ConverterManager converterManager;
63   
64    /**
65    * @see #setDelimiters(String)
66    */
67    private String delimiters = ", ";
68   
69    /**
70    * @return the converter manager.
71    */
 
72  6 toggle public ConverterManager getConverterManager()
73    {
74  6 return this.converterManager;
75    }
76   
77    /**
78    * Set the delimiter to be used for parsing a delimited String.
79    *
80    * @param delimiter The delimiter [default ", "] since 3.2M3
81    */
 
82  0 toggle public void setDelimiters(String delimiter)
83    {
84  0 this.delimiters = delimiter;
85    }
86   
87    /**
88    * @return the delimiters
89    */
 
90  28 toggle public String getDelimiters()
91    {
92  28 return this.delimiters;
93    }
94   
 
95  26 toggle @Override
96    protected <G extends T> G convertToType(Type targetType, Object value)
97    {
98  26 Type elementType = null;
99  26 if (targetType instanceof ParameterizedType) {
100  14 elementType = ((ParameterizedType) targetType).getActualTypeArguments()[0];
101    }
102   
103  26 if (value instanceof Iterable) {
104  1 return fromIterable(targetType, (Iterable) value, elementType);
105  19 } else if (value.getClass().isArray()) {
106  1 return fromArray(targetType, value, elementType);
107    } else {
108  18 return parseElements(targetType, value.toString(), elementType);
109    }
110    }
111   
112    /**
113    * @param <G> the type in which the provided value has to be converted
114    * @param targetType Data type to which this value should be converted.
115    * @param values the values to be converted (or not) to the target element type
116    * @param elementType the generic type
117    * @return List of parsed elements.
118    * @throws ConversionException if the syntax of <code>value</code> is not syntactically valid
119    * @throws NullPointerException if <code>value</code> is <code>null</code>
120    * @since 7.4.6
121    * @since 8.4.1
122    * @since 9.0RC1
123    */
 
124  1 toggle protected <G extends T> G fromIterable(Type targetType, Iterable<?> values, Type elementType)
125    {
126  1 T collection = newCollection(targetType);
127   
128  1 for (Object value : values) {
129  3 collection.add(this.converterManager.convert(elementType, value));
130    }
131   
132  1 return (G) collection;
133    }
134   
135    /**
136    * @param <G> the type in which the provided value has to be converted
137    * @param targetType Data type to which this value should be converted.
138    * @param values the values to be converted (or not) to the target element type
139    * @param elementType the generic type
140    * @return List of parsed elements.
141    * @throws ConversionException if the syntax of <code>value</code> is not syntactically valid
142    * @throws NullPointerException if <code>value</code> is <code>null</code>
143    * @since 7.4.6
144    * @since 8.4.1
145    * @since 9.0RC1
146    */
 
147  1 toggle protected <G extends T> G fromArray(Type targetType, Object values, Type elementType)
148    {
149  1 T collection = newCollection(targetType);
150   
151  4 for (int i = 0; i < Array.getLength(values); ++i) {
152  3 Object value = Array.get(values, i);
153   
154  3 collection.add(this.converterManager.convert(elementType, value));
155    }
156   
157  1 return (G) collection;
158    }
159   
160    /**
161    * <p>
162    * Parse an incoming String of the form similar to an array initializer in the Java language into a
163    * <code>List</code> individual Strings for each element, according to the following rules.
164    * </p>
165    * <ul>
166    * <li>The string is expected to be a comma-separated list of values.</li>
167    * <li>The string may optionally have matching '{' and '}' delimiters around the list.</li>
168    * <li>Whitespace before and after each element is stripped.</li>
169    * <li>Elements in the list may be delimited by single or double quotes. Within a quoted elements, the normal Java
170    * escape sequences are valid.</li>
171    * </ul>
172    *
173    * @param <G> the type in which the provided value has to be converted
174    * @param targetType Data type to which this value should be converted.
175    * @param value String value to be parsed
176    * @param elementType the generic type
177    * @return List of parsed elements.
178    * @throws ConversionException if the syntax of <code>value</code> is not syntactically valid
179    * @throws NullPointerException if <code>value</code> is <code>null</code>
180    */
 
181  18 toggle protected <G extends T> G parseElements(Type targetType, String value, Type elementType)
182    {
183  18 String cleanedValue = cleanValue(value);
184   
185  18 try {
186    // Set up a StreamTokenizer on the characters in this String
187  18 StreamTokenizer st = createStreamTokenizer(cleanedValue);
188   
189    // Split comma-delimited tokens into a List
190  18 T collection = newCollection(targetType);
191  18 while (true) {
192  69 int ttype = st.nextToken();
193  69 if (ttype == StreamTokenizer.TT_WORD || ttype > 0) {
194  51 if (st.sval != null) {
195  51 Object objValue = st.sval;
196  51 if (elementType != null && elementType != String.class) {
197  33 objValue = this.converterManager.convert(elementType, objValue);
198    }
199   
200  51 collection.add(objValue);
201    }
202  18 } else if (ttype == StreamTokenizer.TT_EOF) {
203  18 break;
204    } else {
205  0 throw new ConversionException("Encountered token of type " + ttype + " parsing elements.");
206    }
207    }
208   
209    // Return the completed list
210  18 return (G) collection;
211    } catch (IOException e) {
212  0 throw new ConversionException("Error converting from String: " + e.getMessage(), e);
213    }
214    }
215   
216    /**
217    * @param <G> the type of the collection to create
218    * @param targetType the type of the collection to create
219    * @return the modifiable {@link Collection} to fill
220    */
 
221  13 toggle protected <G extends T> T newCollection(Type targetType)
222    {
223  13 Class<G> targetClass = ReflectionUtils.getTypeClass(targetType);
224  13 if (targetClass.isAssignableFrom(ArrayList.class)) {
225  13 return (G) new ArrayList();
226    } else {
227  0 try {
228  0 return targetClass.newInstance();
229    } catch (Exception e) {
230  0 throw new ConversionException("Failed to create new instance of target type [" + targetType + "]", e);
231    }
232    }
233    }
234   
235    /**
236    * @param value the string to cleanup
237    * @return the clean version of the string
238    */
 
239  18 toggle private String cleanValue(String value)
240    {
241    // Trim any matching '{' and '}' delimiters
242  18 String cleanedValue = value.trim();
243  18 if (cleanedValue.startsWith("{") && cleanedValue.endsWith("}")) {
244  0 cleanedValue = cleanedValue.substring(1, cleanedValue.length() - 1);
245    }
246   
247  18 return cleanedValue;
248    }
249   
250    /**
251    * Create and initialize a {@link StreamTokenizer} to parse the value.
252    *
253    * @param value the string to parse
254    * @return the {@link StreamTokenizer} used to parse the string
255    */
 
256  18 toggle protected StreamTokenizer createStreamTokenizer(String value)
257    {
258    // Set up a StreamTokenizer on the characters in this String
259  18 StreamTokenizer st = new StreamTokenizer(new StringReader(value));
260   
261    // Everything is word
262  18 st.ordinaryChars(0, 255);
263  18 st.wordChars(0, 255);
264   
265    // Except quote chars
266  18 st.quoteChar('"');
267  18 st.quoteChar('\'');
268   
269    // And delimiters
270  18 for (char c : getDelimiters().toCharArray()) {
271  36 st.whitespaceChars(c, c);
272    }
273   
274  18 return st;
275    }
276   
 
277  2 toggle @Override
278    protected String convertToString(T value)
279    {
280  2 StringBuilder sb = new StringBuilder();
281   
282  2 for (Object element : value) {
283  6 if (sb.length() > 0) {
284  4 sb.append(getDelimiters());
285    }
286   
287  6 String elementString = getConverterManager().convert(String.class, element);
288   
289  6 if (elementString != null) {
290  6 boolean containsDelimiter = StringUtils.contains(elementString, getDelimiters());
291   
292  6 if (containsDelimiter) {
293  0 sb.append(QUOTESTRING);
294    }
295  6 sb.append(elementString.replace("\\", "\\\\").replace(QUOTESTRING, "\\\"").replace("'", "\\'"));
296  6 if (containsDelimiter) {
297  0 sb.append(QUOTESTRING);
298    }
299    }
300    }
301   
302  2 return sb.toString();
303    }
304    }