##### Coverage Report - org.yaml.snakeyaml.constructor.Constructor

Classes in this File | Line Coverage | Branch Coverage | Complexity
Constructor |
97% | 43/44
92% | 13/14
| 8.863636363636363;8.864
Constructor$1 |
100% | 1/1
N/A | N/A
| 8.863636363636363;8.864
Constructor$ConstructMapping |
100% | 94/94
100% | 51/51
| 8.863636363636363;8.864
Constructor$ConstructScalar |
100% | 83/83
97% | 99/102
| 8.863636363636363;8.864
Constructor$ConstructSequence |
83% | 64/77
83% | 45/54
| 8.863636363636363;8.864
Constructor$ConstructYamlObject |
87% | 14/16
N/A | N/A
| 8.863636363636363;8.864

1 |  |
     /**
2 |  |
      * Copyright (c) 2008-2011, http://www.snakeyaml.org
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License");
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 |  |
      * See the License for the specific language governing permissions and
14 |  |
      * limitations under the License.
15 |  |
      */
16 |  |
17 |  |
     package org.yaml.snakeyaml.constructor;
18 |  |
19 |  |
     import java.beans.IntrospectionException;
20 |  |
     import java.math.BigDecimal;
21 |  |
     import java.math.BigInteger;
22 |  |
     import java.util.ArrayList;
23 |  |
     import java.util.Calendar;
24 |  |
     import java.util.Collection;
25 |  |
     import java.util.Date;
26 |  |
     import java.util.HashMap;
27 |  |
     import java.util.List;
28 |  |
     import java.util.Map;
29 |  |
     import java.util.Properties;
30 |  |
     import java.util.Set;
31 |  |
     import java.util.SortedMap;
32 |  |
     import java.util.SortedSet;
33 |  |
     import java.util.TreeMap;
34 |  |
     import java.util.TreeSet;
35 |  |
36 |  |
     import org.yaml.snakeyaml.TypeDescription;
37 |  |
     import org.yaml.snakeyaml.error.YAMLException;
38 |  |
     import org.yaml.snakeyaml.introspector.Property;
39 |  |
     import org.yaml.snakeyaml.nodes.MappingNode;
40 |  |
     import org.yaml.snakeyaml.nodes.Node;
41 |  |
     import org.yaml.snakeyaml.nodes.NodeId;
42 |  |
     import org.yaml.snakeyaml.nodes.NodeTuple;
43 |  |
     import org.yaml.snakeyaml.nodes.ScalarNode;
44 |  |
     import org.yaml.snakeyaml.nodes.SequenceNode;
45 |  |
     import org.yaml.snakeyaml.nodes.Tag;
46 |  |
47 |  |
     /**
48 |  |
      * Construct a custom Java instance.
49 |  |
      */
50 | 2508 |
     public class Constructor extends SafeConstructor {
51 |  |
         private final Map<Tag, Class<? extends Object>> typeTags;
52 |  |
         private final Map<Class<? extends Object>, TypeDescription> typeDefinitions;
53 |  |
54 |  |
         public Constructor() {
55 | 3920 |
             this(Object.class);
56 | 3920 |
         }
57 |  |
58 |  |
         /**
59 |  |
          * Create Constructor for the specified class as the root.
60 |  |
          *
61 |  |
          * @param theRoot
62 |  |
          *            - the class (usually JavaBean) to be constructed
63 |  |
          */
64 |  |
         public Constructor(Class<? extends Object> theRoot) {
65 | 3961 |
             this(new TypeDescription(checkRoot(theRoot)));
66 | 3960 |
         }
67 |  |
68 |  |
         /**
69 |  |
          * Ugly Java way to check the argument in the constructor
70 |  |
          */
71 |  |
         private static Class<? extends Object> checkRoot(Class<? extends Object> theRoot) {
72 | 3961 |
             if (theRoot == null) {
73 | 1 |
                 throw new NullPointerException("Root class must be provided.");
74 |  |
             } else
75 | 3960 |
                 return theRoot;
76 |  |
         }
77 |  |
78 | 3971 |
         public Constructor(TypeDescription theRoot) {
79 | 3971 |
             if (theRoot == null) {
80 | 0 |
                 throw new NullPointerException("Root type must be provided.");
81 |  |
             }
82 | 3971 |
             this.yamlConstructors.put(null, new ConstructYamlObject());
83 | 3971 |
             if (!Object.class.equals(theRoot.getType())) {
84 | 48 |
                 rootTag = new Tag(theRoot.getType());
85 |  |
             }
86 | 3970 |
             typeTags = new HashMap<Tag, Class<? extends Object>>();
87 | 3970 |
             typeDefinitions = new HashMap<Class<? extends Object>, TypeDescription>();
88 | 3970 |
             yamlClassConstructors.put(NodeId.scalar, new ConstructScalar());
89 | 3970 |
             yamlClassConstructors.put(NodeId.mapping, new ConstructMapping());
90 | 3970 |
             yamlClassConstructors.put(NodeId.sequence, new ConstructSequence());
91 | 3970 |
             addTypeDescription(theRoot);
92 | 3970 |
         }
93 |  |
94 |  |
         /**
95 |  |
          * Create Constructor for a class which does not have to be in the classpath
96 |  |
          * or for a definition from a Spring ApplicationContext.
97 |  |
          *
98 |  |
          * @param theRoot
99 |  |
          *            fully qualified class name of the root class (usually
100 |  |
          *            JavaBean)
101 |  |
          * @throws ClassNotFoundException
102 |  |
          */
103 |  |
         public Constructor(String theRoot) throws ClassNotFoundException {
104 | 4 |
             this(Class.forName(check(theRoot)));
105 | 2 |
         }
106 |  |
107 |  |
         private static final String check(String s) {
108 | 4 |
             if (s == null) {
109 | 1 |
                 throw new NullPointerException("Root type must be provided.");
110 |  |
             }
111 | 3 |
             if (s.trim().length() == 0) {
112 | 1 |
                 throw new YAMLException("Root type must be provided.");
113 |  |
             }
114 | 2 |
             return s;
115 |  |
         }
116 |  |
117 |  |
         /**
118 |  |
          * Make YAML aware how to parse a custom Class. If there is no root Class
119 |  |
          * assigned in constructor then the 'root' property of this definition is
120 |  |
          * respected.
121 |  |
          *
122 |  |
          * @param definition
123 |  |
          *            to be added to the Constructor
124 |  |
          * @return the previous value associated with <tt>definition</tt>, or
125 |  |
          *         <tt>null</tt> if there was no mapping for <tt>definition</tt>.
126 |  |
          */
127 |  |
         public TypeDescription addTypeDescription(TypeDescription definition) {
128 | 4010 |
             if (definition == null) {
129 | 2 |
                 throw new NullPointerException("TypeDescription is required.");
130 |  |
             }
131 | 4008 |
             Tag tag = definition.getTag();
132 | 4008 |
             typeTags.put(tag, definition.getType());
133 | 4008 |
             return typeDefinitions.put(definition.getType(), definition);
134 |  |
         }
135 |  |
136 |  |
         /**
137 |  |
          * Construct mapping instance (Map, JavaBean) when the runtime class is
138 |  |
          * known.
139 |  |
          */
140 | 3971 |
         protected class ConstructMapping implements Construct {
141 |  |
142 |  |
             /**
143 |  |
              * Construct JavaBean. If type safe collections are used please look at
144 |  |
              * <code>TypeDescription</code>.
145 |  |
              *
146 |  |
              * @param node
147 |  |
              *            node where the keys are property names (they can only be
148 |  |
              *            <code>String</code>s) and values are objects to be created
149 |  |
              * @return constructed JavaBean
150 |  |
              */
151 |  |
             public Object construct(Node node) {
152 | 1605 |
                 MappingNode mnode = (MappingNode) node;
153 | 1605 |
                 if (Properties.class.isAssignableFrom(node.getType())) {
154 | 2 |
                     Properties properties = new Properties();
155 | 2 |
                     if (!node.isTwoStepsConstruction()) {
156 | 1 |
                         constructMapping2ndStep(mnode, properties);
157 |  |
                     } else {
158 | 1 |
                         throw new YAMLException("Properties must not be recursive.");
159 |  |
                     }
160 | 1 |
                     return properties;
161 | 1603 |
                 } else if (SortedMap.class.isAssignableFrom(node.getType())) {
162 | 2 |
                     SortedMap<Object, Object> map = new TreeMap<Object, Object>();
163 | 2 |
                     if (!node.isTwoStepsConstruction()) {
164 | 1 |
                         constructMapping2ndStep(mnode, map);
165 |  |
                     }
166 | 2 |
                     return map;
167 | 1601 |
                 } else if (Map.class.isAssignableFrom(node.getType())) {
168 | 36 |
                     if (node.isTwoStepsConstruction()) {
169 | 2 |
                         return createDefaultMap();
170 |  |
                     } else {
171 | 34 |
                         return constructMapping(mnode);
172 |  |
                     }
173 | 1565 |
                 } else if (SortedSet.class.isAssignableFrom(node.getType())) {
174 | 9 |
                     SortedSet<Object> set = new TreeSet<Object>();
175 |  |
                     // XXX why this is not used ?
176 |  |
                     // if (!node.isTwoStepsConstruction()) {
177 | 9 |
                     constructSet2ndStep(mnode, set);
178 |  |
                     // }
179 | 9 |
                     return set;
180 | 1556 |
                 } else if (Collection.class.isAssignableFrom(node.getType())) {
181 | 15 |
                     if (node.isTwoStepsConstruction()) {
182 | 1 |
                         return createDefaultSet();
183 |  |
                     } else {
184 | 14 |
                         return constructSet(mnode);
185 |  |
                     }
186 |  |
                 } else {
187 | 1541 |
                     if (node.isTwoStepsConstruction()) {
188 | 32 |
                         return createEmptyJavaBean(mnode);
189 |  |
                     } else {
190 | 1509 |
                         return constructJavaBean2ndStep(mnode, createEmptyJavaBean(mnode));
191 |  |
                     }
192 |  |
                 }
193 |  |
             }
194 |  |
195 |  |
             @SuppressWarnings("unchecked")
196 |  |
             public void construct2ndStep(Node node, Object object) {
197 | 36 |
                 if (Map.class.isAssignableFrom(node.getType())) {
198 | 3 |
                     constructMapping2ndStep((MappingNode) node, (Map<Object, Object>) object);
199 | 33 |
                 } else if (Set.class.isAssignableFrom(node.getType())) {
200 | 1 |
                     constructSet2ndStep((MappingNode) node, (Set<Object>) object);
201 |  |
                 } else {
202 | 32 |
                     constructJavaBean2ndStep((MappingNode) node, object);
203 |  |
                 }
204 | 36 |
             }
205 |  |
206 |  |
             protected Object createEmptyJavaBean(MappingNode node) {
207 |  |
                 try {
208 |  |
                     /**
209 |  |
                      * Using only default constructor. Everything else will be
210 |  |
                      * initialized on 2nd step. If we do here some partial
211 |  |
                      * initialization, how do we then track what need to be done on
212 |  |
                      * 2nd step? I think it is better to get only object here (to
213 |  |
                      * have it as reference for recursion) and do all other thing on
214 |  |
                      * 2nd step.
215 |  |
                      */
216 | 1541 |
                     java.lang.reflect.Constructor<?> c = node.getType().getDeclaredConstructor();
217 | 1538 |
                     c.setAccessible(true);
218 | 1538 |
                     return c.newInstance();
219 | 3 |
                 } catch (Exception e) {
220 | 3 |
                     throw new YAMLException(e);
221 |  |
                 }
222 |  |
             }
223 |  |
224 |  |
             protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
225 | 1537 |
                 flattenMapping(node);
226 | 1537 |
                 Class<? extends Object> beanType = node.getType();
227 | 1537 |
                 List<NodeTuple> nodeValue = node.getValue();
228 | 1537 |
                 for (NodeTuple tuple : nodeValue) {
229 |  |
                     ScalarNode keyNode;
230 | 2519 |
                     if (tuple.getKeyNode() instanceof ScalarNode) {
231 |  |
                         // key must be scalar
232 | 2518 |
                         keyNode = (ScalarNode) tuple.getKeyNode();
233 |  |
                     } else {
234 | 1 |
                         throw new YAMLException("Keys must be scalars but found: " + tuple.getKeyNode());
235 |  |
                     }
236 | 2518 |
                     Node valueNode = tuple.getValueNode();
237 |  |
                     // keys can only be Strings
238 | 2518 |
                     keyNode.setType(String.class);
239 | 2518 |
                     String key = (String) constructObject(keyNode);
240 |  |
                     try {
241 | 2518 |
                         Property property = getProperty(beanType, key);
242 | 2508 |
                         valueNode.setType(property.getType());
243 | 2508 |
                         TypeDescription memberDescription = typeDefinitions.get(beanType);
244 | 2508 |
                         boolean typeDetected = false;
245 | 2508 |
                         if (memberDescription != null) {
246 | 656 |
                             switch (valueNode.getNodeId()) {
247 |  |
                             case sequence:
248 | 47 |
                                 SequenceNode snode = (SequenceNode) valueNode;
249 | 47 |
                                 Class<? extends Object> memberType = memberDescription
250 |  |
                                         .getListPropertyType(key);
251 | 47 |
                                 if (memberType != null) {
252 | 22 |
                                     snode.setListType(memberType);
253 | 22 |
                                     typeDetected = true;
254 | 25 |
                                 } else if (property.getType().isArray()) {
255 | 1 |
                                     snode.setListType(property.getType().getComponentType());
256 | 1 |
                                     typeDetected = true;
257 |  |
                                 }
258 |  |
                                 break;
259 |  |
                             case mapping:
260 | 152 |
                                 MappingNode mnode = (MappingNode) valueNode;
261 | 152 |
                                 Class<? extends Object> keyType = memberDescription.getMapKeyType(key);
262 | 152 |
                                 if (keyType != null) {
263 | 27 |
                                     mnode.setTypes(keyType, memberDescription.getMapValueType(key));
264 | 27 |
                                     typeDetected = true;
265 |  |
                                 }
266 |  |
                                 break;
267 |  |
                             }
268 |  |
                         }
269 | 2508 |
                         if (!typeDetected && valueNode.getNodeId() != NodeId.scalar) {
270 |  |
                             // only if there is no explicit TypeDescription
271 | 298 |
                             Class<?>[] arguments = property.getActualTypeArguments();
272 | 298 |
                             if (arguments != null) {
273 |  |
                                 // type safe (generic) collection may contain the
274 |  |
                                 // proper class
275 | 121 |
                                 if (valueNode.getNodeId() == NodeId.sequence) {
276 | 79 |
                                     Class<?> t = arguments[0];
277 | 79 |
                                     SequenceNode snode = (SequenceNode) valueNode;
278 | 79 |
                                     snode.setListType(t);
279 | 79 |
                                 } else if (valueNode.getTag().equals(Tag.SET)) {
280 | 20 |
                                     Class<?> t = arguments[0];
281 | 20 |
                                     MappingNode mnode = (MappingNode) valueNode;
282 | 20 |
                                     mnode.setOnlyKeyType(t);
283 | 20 |
                                     mnode.setUseClassConstructor(true);
284 | 20 |
                                 } else if (property.getType().isAssignableFrom(Map.class)) {
285 | 18 |
                                     Class<?> ketType = arguments[0];
286 | 18 |
                                     Class<?> valueType = arguments[1];
287 | 18 |
                                     MappingNode mnode = (MappingNode) valueNode;
288 | 18 |
                                     mnode.setTypes(ketType, valueType);
289 | 18 |
                                     mnode.setUseClassConstructor(true);
290 |  |
                                 } else {
291 |  |
                                     // the type for collection entries cannot be
292 |  |
                                     // detected
293 |  |
                                 }
294 |  |
                             }
295 |  |
                         }
296 | 2508 |
                         Object value = constructObject(valueNode);
297 | 2500 |
                         property.set(object, value);
298 | 19 |
                     } catch (Exception e) {
299 | 19 |
                         throw new YAMLException("Cannot create property=" + key + " for JavaBean="
300 |  |
                                 + object + "; " + e.getMessage(), e);
301 | 2499 |
                     }
302 | 2499 |
                 }
303 | 1517 |
                 return object;
304 |  |
             }
305 |  |
306 |  |
             protected Property getProperty(Class<? extends Object> type, String name)
307 |  |
                     throws IntrospectionException {
308 | 2518 |
                 return getPropertyUtils().getProperty(type, name);
309 |  |
             }
310 |  |
         }
311 |  |
312 |  |
         /**
313 |  |
          * Construct an instance when the runtime class is not known but a global
314 |  |
          * tag with a class name is defined. It delegates the construction to the
315 |  |
          * appropriate constructor based on the node kind (scalar, sequence,
316 |  |
          * mapping)
317 |  |
          */
318 | 3971 |
         protected class ConstructYamlObject implements Construct {
319 |  |
320 |  |
             private Construct getConstructor(Node node) {
321 | 1385 |
                 Class<?> cl = getClassForNode(node);
322 | 1379 |
                 node.setType(cl);
323 |  |
                 // call the constructor as if the runtime class is defined
324 | 1379 |
                 Construct constructor = yamlClassConstructors.get(node.getNodeId());
325 | 1379 |
                 return constructor;
326 |  |
             }
327 |  |
328 |  |
             public Object construct(Node node) {
329 | 1367 |
                 Object result = null;
330 |  |
                 try {
331 | 1367 |
                     result = getConstructor(node).construct(node);
332 | 37 |
                 } catch (Exception e) {
333 | 37 |
                     throw new ConstructorException(null, null, "Can't construct a java object for "
334 |  |
                             + node.getTag() + "; exception=" + e.getMessage(), node.getStartMark(), e);
335 | 1330 |
                 }
336 | 1330 |
                 return result;
337 |  |
             }
338 |  |
339 |  |
             public void construct2ndStep(Node node, Object object) {
340 |  |
                 try {
341 | 18 |
                     getConstructor(node).construct2ndStep(node, object);
342 | 0 |
                 } catch (Exception e) {
343 | 0 |
                     throw new ConstructorException(null, null,
344 |  |
                             "Can't construct a second step for a java object for " + node.getTag()
345 |  |
                                     + "; exception=" + e.getMessage(), node.getStartMark(), e);
346 | 18 |
                 }
347 | 18 |
             }
348 |  |
         }
349 |  |
350 |  |
         /**
351 |  |
          * Construct scalar instance when the runtime class is known. Recursive
352 |  |
          * structures are not supported.
353 |  |
          */
354 | 3984 |
         protected class ConstructScalar extends AbstractConstruct {
355 |  |
             public Object construct(Node nnode) {
356 | 4701 |
                 ScalarNode node = (ScalarNode) nnode;
357 | 4701 |
                 Class<?> type = node.getType();
358 |  |
                 Object result;
359 | 4701 |
                 if (type.isPrimitive() || type == String.class || Number.class.isAssignableFrom(type)
360 |  |
                         || type == Boolean.class || Date.class.isAssignableFrom(type)
361 |  |
                         || type == Character.class || type == BigInteger.class
362 |  |
                         || type == BigDecimal.class || Enum.class.isAssignableFrom(type)
363 |  |
                         || Tag.BINARY.equals(node.getTag()) || Calendar.class.isAssignableFrom(type)) {
364 |  |
                     // standard classes created directly
365 | 4683 |
                     result = constructStandardJavaInstance(type, node);
366 |  |
                 } else {
367 |  |
                     // there must be only 1 constructor with 1 argument
368 | 18 |
                     java.lang.reflect.Constructor<?>[] javaConstructors = type.getConstructors();
369 | 18 |
                     int oneArgCount = 0;
370 | 18 |
                     java.lang.reflect.Constructor<?> javaConstructor = null;
371 | 55 |
                     for (java.lang.reflect.Constructor<?> c : javaConstructors) {
372 | 37 |
                         if (c.getParameterTypes().length == 1) {
373 | 23 |
                             oneArgCount++;
374 | 23 |
                             javaConstructor = c;
375 |  |
                         }
376 |  |
                     }
377 |  |
                     Object argument;
378 | 18 |
                     if (javaConstructor == null) {
379 | 1 |
                         throw new YAMLException("No single argument constructor found for " + type);
380 | 17 |
                     } else if (oneArgCount == 1) {
381 | 13 |
                         argument = constructStandardJavaInstance(
382 |  |
                                 javaConstructor.getParameterTypes()[0], node);
383 |  |
                     } else {
384 |  |
                         // TODO it should be possible to use implicit types instead
385 |  |
                         // of forcing String. Resolver must be available here to
386 |  |
                         // obtain the implicit tag. Then we can set the tag and call
387 |  |
                         // callConstructor(node) to create the argument instance.
388 |  |
                         // On the other hand it may be safer to require a custom
389 |  |
                         // constructor to avoid guessing the argument class
390 | 4 |
                         argument = constructScalar(node);
391 |  |
                         try {
392 | 4 |
                             javaConstructor = type.getConstructor(String.class);
393 | 2 |
                         } catch (Exception e) {
394 | 2 |
                             throw new ConstructorException(null, null,
395 |  |
                                     "Can't construct a java object for scalar " + node.getTag()
396 |  |
                                             + "; No String constructor found. Exception="
397 |  |
                                             + e.getMessage(), node.getStartMark(), e);
398 | 2 |
                         }
399 |  |
                     }
400 |  |
                     try {
401 | 14 |
                         result = javaConstructor.newInstance(argument);
402 | 1 |
                     } catch (Exception e) {
403 | 1 |
                         throw new ConstructorException(null, null,
404 |  |
                                 "Can't construct a java object for scalar " + node.getTag()
405 |  |
                                         + "; exception=" + e.getMessage(), node.getStartMark(), e);
406 | 13 |
                     }
407 |  |
                 }
408 | 4692 |
                 return result;
409 |  |
             }
410 |  |
411 |  |
             @SuppressWarnings("unchecked")
412 |  |
             private Object constructStandardJavaInstance(@SuppressWarnings("rawtypes") Class type,
413 |  |
                     ScalarNode node) {
414 |  |
                 Object result;
415 | 4696 |
                 if (type == String.class) {
416 | 3300 |
                     Construct stringConstructor = yamlConstructors.get(Tag.STR);
417 | 3300 |
                     result = stringConstructor.construct(node);
418 | 3300 |
                 } else if (type == Boolean.class || type == Boolean.TYPE) {
419 | 10 |
                     Construct boolConstructor = yamlConstructors.get(Tag.BOOL);
420 | 10 |
                     result = boolConstructor.construct(node);
421 | 10 |
                 } else if (type == Character.class || type == Character.TYPE) {
422 | 9 |
                     Construct charConstructor = yamlConstructors.get(Tag.STR);
423 | 9 |
                     String ch = (String) charConstructor.construct(node);
424 | 9 |
                     if (ch.length() == 0) {
425 | 1 |
                         result = null;
426 | 8 |
                     } else if (ch.length() != 1) {
427 | 1 |
                         throw new YAMLException("Invalid node Character: '" + ch + "'; length: "
428 |  |
                                 + ch.length());
429 |  |
                     } else {
430 | 7 |
                         result = new Character(ch.charAt(0));
431 |  |
                     }
432 | 8 |
                 } else if (Date.class.isAssignableFrom(type)) {
433 | 70 |
                     Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
434 | 70 |
                     Date date = (Date) dateConstructor.construct(node);
435 | 70 |
                     if (type == Date.class) {
436 | 57 |
                         result = date;
437 |  |
                     } else {
438 |  |
                         try {
439 | 13 |
                             java.lang.reflect.Constructor<?> constr = type.getConstructor(long.class);
440 | 13 |
                             result = constr.newInstance(date.getTime());
441 | 1 |
                         } catch (Exception e) {
442 | 1 |
                             throw new YAMLException("Cannot construct: '" + type + "'");
443 | 12 |
                         }
444 |  |
                     }
445 | 69 |
                 } else if (type == Float.class || type == Double.class || type == Float.TYPE
446 |  |
                         || type == Double.TYPE || type == BigDecimal.class) {
447 | 55 |
                     if (type == BigDecimal.class) {
448 | 5 |
                         result = new BigDecimal(node.getValue());
449 |  |
                     } else {
450 | 50 |
                         Construct doubleConstructor = yamlConstructors.get(Tag.FLOAT);
451 | 50 |
                         result = doubleConstructor.construct(node);
452 | 50 |
                         if (type == Float.class || type == Float.TYPE) {
453 | 20 |
                             result = new Float((Double) result);
454 |  |
                         }
455 | 50 |
                     }
456 | 1252 |
                 } else if (type == Byte.class || type == Short.class || type == Integer.class
457 |  |
                         || type == Long.class || type == BigInteger.class || type == Byte.TYPE
458 |  |
                         || type == Short.TYPE || type == Integer.TYPE || type == Long.TYPE) {
459 | 1217 |
                     Construct intConstructor = yamlConstructors.get(Tag.INT);
460 | 1217 |
                     result = intConstructor.construct(node);
461 | 1217 |
                     if (type == Byte.class || type == Byte.TYPE) {
462 | 6 |
                         result = new Byte(result.toString());
463 | 1211 |
                     } else if (type == Short.class || type == Short.TYPE) {
464 | 6 |
                         result = new Short(result.toString());
465 | 1205 |
                     } else if (type == Integer.class || type == Integer.TYPE) {
466 | 1188 |
                         result = new Integer(result.toString());
467 | 17 |
                     } else if (type == Long.class || type == Long.TYPE) {
468 | 14 |
                         result = new Long(result.toString());
469 |  |
                     } else {
470 |  |
                         // only BigInteger left
471 | 3 |
                         result = new BigInteger(result.toString());
472 |  |
                     }
473 | 1217 |
                 } else if (Enum.class.isAssignableFrom(type)) {
474 | 26 |
                     String enumValueName = node.getValue();
475 |  |
                     try {
476 | 26 |
                         result = Enum.valueOf(type, enumValueName);
477 | 1 |
                     } catch (Exception ex) {
478 | 1 |
                         throw new YAMLException("Unable to find enum value '" + enumValueName
479 |  |
                                 + "' for enum class: " + type.getName());
480 | 25 |
                     }
481 | 25 |
                 } else if (Calendar.class.isAssignableFrom(type)) {
482 | 7 |
                     ConstructYamlTimestamp contr = new ConstructYamlTimestamp();
483 | 7 |
                     contr.construct(node);
484 | 7 |
                     result = contr.getCalendar();
485 | 7 |
                 } else {
486 | 2 |
                     throw new YAMLException("Unsupported class: " + type);
487 |  |
                 }
488 | 4691 |
                 return result;
489 |  |
             }
490 |  |
         }
491 |  |
492 |  |
         /**
493 |  |
          * Construct sequence (List, Array, or immutable object) when the runtime
494 |  |
          * class is known.
495 |  |
          */
496 | 3971 |
         protected class ConstructSequence implements Construct {
497 |  |
             @SuppressWarnings("unchecked")
498 |  |
             public Object construct(Node node) {
499 | 139 |
                 SequenceNode snode = (SequenceNode) node;
500 | 139 |
                 if (Set.class.isAssignableFrom(node.getType())) {
501 | 11 |
                     if (node.isTwoStepsConstruction()) {
502 | 2 |
                         throw new YAMLException("Set cannot be recursive.");
503 |  |
                     } else {
504 | 9 |
                         return constructSet(snode);
505 |  |
                     }
506 | 128 |
                 } else if (Collection.class.isAssignableFrom(node.getType())) {
507 | 81 |
                     if (node.isTwoStepsConstruction()) {
508 | 1 |
                         return createDefaultList(snode.getValue().size());
509 |  |
                     } else {
510 | 80 |
                         return constructSequence(snode);
511 |  |
                     }
512 | 47 |
                 } else if (node.getType().isArray()) {
513 | 23 |
                     if (node.isTwoStepsConstruction()) {
514 | 2 |
                         return createArray(node.getType(), snode.getValue().size());
515 |  |
                     } else {
516 | 21 |
                         return constructArray(snode);
517 |  |
                     }
518 |  |
                 } else {
519 |  |
                     // create immutable object
520 | 24 |
                     List<java.lang.reflect.Constructor<?>> possibleConstructors = new ArrayList<java.lang.reflect.Constructor<?>>(
521 |  |
                             snode.getValue().size());
522 |  |
                     for (java.lang.reflect.Constructor<?> constructor : node.getType()
523 | 79 |
                             .getConstructors()) {
524 | 55 |
                         if (snode.getValue().size() == constructor.getParameterTypes().length) {
525 | 33 |
                             possibleConstructors.add(constructor);
526 |  |
                         }
527 |  |
                     }
528 | 24 |
                     if (!possibleConstructors.isEmpty()) {
529 | 23 |
                         if (possibleConstructors.size() == 1) {
530 | 16 |
                             Object[] argumentList = new Object[snode.getValue().size()];
531 | 16 |
                             java.lang.reflect.Constructor<?> c = possibleConstructors.get(0);
532 | 16 |
                             int index = 0;
533 | 16 |
                             for (Node argumentNode : snode.getValue()) {
534 | 38 |
                                 Class<?> type = c.getParameterTypes()[index];
535 |  |
                                 // set runtime classes for arguments
536 | 38 |
                                 argumentNode.setType(type);
537 | 38 |
                                 argumentList[index++] = constructObject(argumentNode);
538 | 38 |
                             }
539 |  |
540 |  |
                             try {
541 | 16 |
                                 return c.newInstance(argumentList);
542 | 0 |
                             } catch (Exception e) {
543 | 0 |
                                 throw new YAMLException(e);
544 |  |
                             }
545 |  |
                         }
546 |  |
547 |  |
                         // use BaseConstructor
548 | 7 |
                         List<Object> argumentList = (List<Object>) constructSequence(snode);
549 | 7 |
                         Class<?>[] parameterTypes = new Class[argumentList.size()];
550 | 7 |
                         int index = 0;
551 | 7 |
                         for (Object parameter : argumentList) {
552 | 21 |
                             parameterTypes[index] = parameter.getClass();
553 | 21 |
                             index++;
554 |  |
                         }
555 |  |
556 | 7 |
                         for (java.lang.reflect.Constructor<?> c : possibleConstructors) {
557 | 12 |
                             Class<?>[] argTypes = c.getParameterTypes();
558 | 12 |
                             boolean foundConstructor = true;
559 | 33 |
                             for (int i = 0; i < argTypes.length; i++) {
560 | 27 |
                                 if (!wrapIfPrimitive(argTypes[i]).isAssignableFrom(parameterTypes[i])) {
561 | 6 |
                                     foundConstructor = false;
562 | 6 |
                                     break;
563 |  |
                                 }
564 |  |
                             }
565 | 12 |
                             if (foundConstructor) {
566 |  |
                                 try {
567 | 6 |
                                     return c.newInstance(argumentList.toArray());
568 | 0 |
                                 } catch (Exception e) {
569 | 0 |
                                     throw new YAMLException(e);
570 |  |
                                 }
571 |  |
                             }
572 | 6 |
                         }
573 |  |
                     }
574 | 2 |
                     throw new YAMLException("No suitable constructor with "
575 |  |
                             + String.valueOf(snode.getValue().size()) + " arguments found for "
576 |  |
                             + node.getType());
577 |  |
578 |  |
                 }
579 |  |
             }
580 |  |
581 |  |
             private final Class<? extends Object> wrapIfPrimitive(Class<?> clazz) {
582 | 27 |
                 if (!clazz.isPrimitive()) {
583 | 5 |
                     return clazz;
584 |  |
                 }
585 | 22 |
                 if (clazz == Integer.TYPE) {
586 | 15 |
                     return Integer.class;
587 |  |
                 }
588 | 7 |
                 if (clazz == Float.TYPE) {
589 | 0 |
                     return Float.class;
590 |  |
                 }
591 | 7 |
                 if (clazz == Double.TYPE) {
592 | 3 |
                     return Double.class;
593 |  |
                 }
594 | 4 |
                 if (clazz == Boolean.TYPE) {
595 | 2 |
                     return Boolean.class;
596 |  |
                 }
597 | 2 |
                 if (clazz == Long.TYPE) {
598 | 2 |
                     return Long.class;
599 |  |
                 }
600 | 0 |
                 if (clazz == Character.TYPE) {
601 | 0 |
                     return Character.class;
602 |  |
                 }
603 | 0 |
                 if (clazz == Short.TYPE) {
604 | 0 |
                     return Short.class;
605 |  |
                 }
606 | 0 |
                 if (clazz == Byte.TYPE) {
607 | 0 |
                     return Byte.class;
608 |  |
                 }
609 | 0 |
                 throw new YAMLException("Unexpected primitive " + clazz);
610 |  |
             }
611 |  |
612 |  |
             @SuppressWarnings("unchecked")
613 |  |
             public void construct2ndStep(Node node, Object object) {
614 | 3 |
                 SequenceNode snode = (SequenceNode) node;
615 | 3 |
                 if (List.class.isAssignableFrom(node.getType())) {
616 | 1 |
                     List<Object> list = (List<Object>) object;
617 | 1 |
                     constructSequenceStep2(snode, list);
618 | 1 |
                 } else if (node.getType().isArray()) {
619 | 2 |
                     constructArrayStep2(snode, object);
620 |  |
                 } else {
621 | 0 |
                     throw new YAMLException("Immutable objects cannot be recursive.");
622 |  |
                 }
623 | 3 |
             }
624 |  |
         }
625 |  |
626 |  |
         protected Class<?> getClassForNode(Node node) {
627 | 1387 |
             Class<? extends Object> classForTag = typeTags.get(node.getTag());
628 | 1387 |
             if (classForTag == null) {
629 | 310 |
                 String name = node.getTag().getClassName();
630 |  |
                 Class<?> cl;
631 |  |
                 try {
632 | 307 |
                     cl = getClassForName(name);
633 | 2 |
                 } catch (ClassNotFoundException e) {
634 | 2 |
                     throw new YAMLException("Class not found: " + name);
635 | 304 |
                 }
636 | 304 |
                 typeTags.put(node.getTag(), cl);
637 | 304 |
                 return cl;
638 |  |
             } else {
639 | 1077 |
                 return classForTag;
640 |  |
             }
641 |  |
         }
642 |  |
643 |  |
         protected Class<?> getClassForName(String name) throws ClassNotFoundException {
644 | 321 |
             return Class.forName(name);
645 |  |
         }
646 |  |
     }