• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2008, 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 package org.yaml.snakeyaml.constructor;
17 
18 import java.beans.IntrospectionException;
19 import java.math.BigDecimal;
20 import java.math.BigInteger;
21 import java.util.ArrayList;
22 import java.util.Calendar;
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Properties;
29 import java.util.Set;
30 import java.util.SortedMap;
31 import java.util.SortedSet;
32 import java.util.TreeMap;
33 import java.util.TreeSet;
34 import java.util.UUID;
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 public class Constructor extends SafeConstructor {
51     private final Map<Tag, Class<? extends Object>> typeTags;
52     protected final Map<Class<? extends Object>, TypeDescription> typeDefinitions;
53 
Constructor()54     public Constructor() {
55         this(Object.class);
56     }
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      */
Constructor(Class<? extends Object> theRoot)64     public Constructor(Class<? extends Object> theRoot) {
65         this(new TypeDescription(checkRoot(theRoot)));
66     }
67 
68     /**
69      * Ugly Java way to check the argument in the constructor
70      */
checkRoot(Class<? extends Object> theRoot)71     private static Class<? extends Object> checkRoot(Class<? extends Object> theRoot) {
72         if (theRoot == null) {
73             throw new NullPointerException("Root class must be provided.");
74         } else
75             return theRoot;
76     }
77 
Constructor(TypeDescription theRoot)78     public Constructor(TypeDescription theRoot) {
79         if (theRoot == null) {
80             throw new NullPointerException("Root type must be provided.");
81         }
82         this.yamlConstructors.put(null, new ConstructYamlObject());
83         if (!Object.class.equals(theRoot.getType())) {
84             rootTag = new Tag(theRoot.getType());
85         }
86         typeTags = new HashMap<Tag, Class<? extends Object>>();
87         typeDefinitions = new HashMap<Class<? extends Object>, TypeDescription>();
88         yamlClassConstructors.put(NodeId.scalar, new ConstructScalar());
89         yamlClassConstructors.put(NodeId.mapping, new ConstructMapping());
90         yamlClassConstructors.put(NodeId.sequence, new ConstructSequence());
91         addTypeDescription(theRoot);
92     }
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      */
Constructor(String theRoot)103     public Constructor(String theRoot) throws ClassNotFoundException {
104         this(Class.forName(check(theRoot)));
105     }
106 
check(String s)107     private static final String check(String s) {
108         if (s == null) {
109             throw new NullPointerException("Root type must be provided.");
110         }
111         if (s.trim().length() == 0) {
112             throw new YAMLException("Root type must be provided.");
113         }
114         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      */
addTypeDescription(TypeDescription definition)127     public TypeDescription addTypeDescription(TypeDescription definition) {
128         if (definition == null) {
129             throw new NullPointerException("TypeDescription is required.");
130         }
131         Tag tag = definition.getTag();
132         typeTags.put(tag, definition.getType());
133         return typeDefinitions.put(definition.getType(), definition);
134     }
135 
136     /**
137      * Construct mapping instance (Map, JavaBean) when the runtime class is
138      * known.
139      */
140     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          */
construct(Node node)151         public Object construct(Node node) {
152             MappingNode mnode = (MappingNode) node;
153             if (Properties.class.isAssignableFrom(node.getType())) {
154                 Properties properties = new Properties();
155                 if (!node.isTwoStepsConstruction()) {
156                     constructMapping2ndStep(mnode, properties);
157                 } else {
158                     throw new YAMLException("Properties must not be recursive.");
159                 }
160                 return properties;
161             } else if (SortedMap.class.isAssignableFrom(node.getType())) {
162                 SortedMap<Object, Object> map = new TreeMap<Object, Object>();
163                 if (!node.isTwoStepsConstruction()) {
164                     constructMapping2ndStep(mnode, map);
165                 }
166                 return map;
167             } else if (Map.class.isAssignableFrom(node.getType())) {
168                 if (node.isTwoStepsConstruction()) {
169                     return createDefaultMap();
170                 } else {
171                     return constructMapping(mnode);
172                 }
173             } else if (SortedSet.class.isAssignableFrom(node.getType())) {
174                 SortedSet<Object> set = new TreeSet<Object>();
175                 // XXX why this is not used ?
176                 // if (!node.isTwoStepsConstruction()) {
177                 constructSet2ndStep(mnode, set);
178                 // }
179                 return set;
180             } else if (Collection.class.isAssignableFrom(node.getType())) {
181                 if (node.isTwoStepsConstruction()) {
182                     return createDefaultSet();
183                 } else {
184                     return constructSet(mnode);
185                 }
186             } else {
187                 if (node.isTwoStepsConstruction()) {
188                     return createEmptyJavaBean(mnode);
189                 } else {
190                     return constructJavaBean2ndStep(mnode, createEmptyJavaBean(mnode));
191                 }
192             }
193         }
194 
195         @SuppressWarnings("unchecked")
construct2ndStep(Node node, Object object)196         public void construct2ndStep(Node node, Object object) {
197             if (Map.class.isAssignableFrom(node.getType())) {
198                 constructMapping2ndStep((MappingNode) node, (Map<Object, Object>) object);
199             } else if (Set.class.isAssignableFrom(node.getType())) {
200                 constructSet2ndStep((MappingNode) node, (Set<Object>) object);
201             } else {
202                 constructJavaBean2ndStep((MappingNode) node, object);
203             }
204         }
205 
createEmptyJavaBean(MappingNode node)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                 java.lang.reflect.Constructor<?> c = node.getType().getDeclaredConstructor();
217                 c.setAccessible(true);
218                 return c.newInstance();
219             } catch (Exception e) {
220                 throw new YAMLException(e);
221             }
222         }
223 
constructJavaBean2ndStep(MappingNode node, Object object)224         protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
225             flattenMapping(node);
226             Class<? extends Object> beanType = node.getType();
227             List<NodeTuple> nodeValue = node.getValue();
228             for (NodeTuple tuple : nodeValue) {
229                 ScalarNode keyNode;
230                 if (tuple.getKeyNode() instanceof ScalarNode) {
231                     // key must be scalar
232                     keyNode = (ScalarNode) tuple.getKeyNode();
233                 } else {
234                     throw new YAMLException("Keys must be scalars but found: " + tuple.getKeyNode());
235                 }
236                 Node valueNode = tuple.getValueNode();
237                 // keys can only be Strings
238                 keyNode.setType(String.class);
239                 String key = (String) constructObject(keyNode);
240                 try {
241                     Property property = getProperty(beanType, key);
242                     valueNode.setType(property.getType());
243                     TypeDescription memberDescription = typeDefinitions.get(beanType);
244                     boolean typeDetected = false;
245                     if (memberDescription != null) {
246                         switch (valueNode.getNodeId()) {
247                         case sequence:
248                             SequenceNode snode = (SequenceNode) valueNode;
249                             Class<? extends Object> memberType = memberDescription
250                                     .getListPropertyType(key);
251                             if (memberType != null) {
252                                 snode.setListType(memberType);
253                                 typeDetected = true;
254                             } else if (property.getType().isArray()) {
255                                 snode.setListType(property.getType().getComponentType());
256                                 typeDetected = true;
257                             }
258                             break;
259                         case mapping:
260                             MappingNode mnode = (MappingNode) valueNode;
261                             Class<? extends Object> keyType = memberDescription.getMapKeyType(key);
262                             if (keyType != null) {
263                                 mnode.setTypes(keyType, memberDescription.getMapValueType(key));
264                                 typeDetected = true;
265                             }
266                             break;
267                         default: // scalar
268                         }
269                     }
270                     if (!typeDetected && valueNode.getNodeId() != NodeId.scalar) {
271                         // only if there is no explicit TypeDescription
272                         Class<?>[] arguments = property.getActualTypeArguments();
273                         if (arguments != null && arguments.length > 0) {
274                             // type safe (generic) collection may contain the
275                             // proper class
276                             if (valueNode.getNodeId() == NodeId.sequence) {
277                                 Class<?> t = arguments[0];
278                                 SequenceNode snode = (SequenceNode) valueNode;
279                                 snode.setListType(t);
280                             } else if (valueNode.getTag().equals(Tag.SET)) {
281                                 Class<?> t = arguments[0];
282                                 MappingNode mnode = (MappingNode) valueNode;
283                                 mnode.setOnlyKeyType(t);
284                                 mnode.setUseClassConstructor(true);
285                             } else if (property.getType().isAssignableFrom(Map.class)) {
286                                 Class<?> ketType = arguments[0];
287                                 Class<?> valueType = arguments[1];
288                                 MappingNode mnode = (MappingNode) valueNode;
289                                 mnode.setTypes(ketType, valueType);
290                                 mnode.setUseClassConstructor(true);
291                             } else {
292                                 // the type for collection entries cannot be
293                                 // detected
294                             }
295                         }
296                     }
297 
298                     Object value = constructObject(valueNode);
299                     // Correct when the property expects float but double was
300                     // constructed
301                     if (property.getType() == Float.TYPE || property.getType() == Float.class) {
302                         if (value instanceof Double) {
303                             value = ((Double) value).floatValue();
304                         }
305                     }
306                     // Correct when the property a String but the value is binary
307                     if (property.getType() == String.class && Tag.BINARY.equals(valueNode.getTag()) && value instanceof byte[]) {
308                         value = new String((byte[])value);
309                     }
310 
311                     property.set(object, value);
312                 } catch (Exception e) {
313                     throw new ConstructorException("Cannot create property=" + key
314                             + " for JavaBean=" + object, node.getStartMark(), e.getMessage(),
315                             valueNode.getStartMark(), e);
316                 }
317             }
318             return object;
319         }
320 
getProperty(Class<? extends Object> type, String name)321         protected Property getProperty(Class<? extends Object> type, String name)
322                 throws IntrospectionException {
323             return getPropertyUtils().getProperty(type, name);
324         }
325     }
326 
327     /**
328      * Construct an instance when the runtime class is not known but a global
329      * tag with a class name is defined. It delegates the construction to the
330      * appropriate constructor based on the node kind (scalar, sequence,
331      * mapping)
332      */
333     protected class ConstructYamlObject implements Construct {
334 
getConstructor(Node node)335         private Construct getConstructor(Node node) {
336             Class<?> cl = getClassForNode(node);
337             node.setType(cl);
338             // call the constructor as if the runtime class is defined
339             Construct constructor = yamlClassConstructors.get(node.getNodeId());
340             return constructor;
341         }
342 
construct(Node node)343         public Object construct(Node node) {
344             Object result = null;
345             try {
346                 result = getConstructor(node).construct(node);
347             } catch (ConstructorException e) {
348                 throw e;
349             } catch (Exception e) {
350                 throw new ConstructorException(null, null, "Can't construct a java object for "
351                         + node.getTag() + "; exception=" + e.getMessage(), node.getStartMark(), e);
352             }
353             return result;
354         }
355 
construct2ndStep(Node node, Object object)356         public void construct2ndStep(Node node, Object object) {
357             try {
358                 getConstructor(node).construct2ndStep(node, object);
359             } catch (Exception e) {
360                 throw new ConstructorException(null, null,
361                         "Can't construct a second step for a java object for " + node.getTag()
362                                 + "; exception=" + e.getMessage(), node.getStartMark(), e);
363             }
364         }
365     }
366 
367     /**
368      * Construct scalar instance when the runtime class is known. Recursive
369      * structures are not supported.
370      */
371     protected class ConstructScalar extends AbstractConstruct {
construct(Node nnode)372         public Object construct(Node nnode) {
373             ScalarNode node = (ScalarNode) nnode;
374             Class<?> type = node.getType();
375             Object result;
376             if (type.isPrimitive() || type == String.class || Number.class.isAssignableFrom(type)
377                     || type == Boolean.class || Date.class.isAssignableFrom(type)
378                     || type == Character.class || type == BigInteger.class
379                     || type == BigDecimal.class || Enum.class.isAssignableFrom(type)
380                     || Tag.BINARY.equals(node.getTag()) || Calendar.class.isAssignableFrom(type) || type == UUID.class) {
381                 // standard classes created directly
382                 result = constructStandardJavaInstance(type, node);
383             } else {
384                 // there must be only 1 constructor with 1 argument
385                 java.lang.reflect.Constructor<?>[] javaConstructors = type
386                         .getDeclaredConstructors();
387                 int oneArgCount = 0;
388                 java.lang.reflect.Constructor<?> javaConstructor = null;
389                 for (java.lang.reflect.Constructor<?> c : javaConstructors) {
390                     if (c.getParameterTypes().length == 1) {
391                         oneArgCount++;
392                         javaConstructor = c;
393                     }
394                 }
395                 Object argument;
396                 if (javaConstructor == null) {
397                     throw new YAMLException("No single argument constructor found for " + type);
398                 } else if (oneArgCount == 1) {
399                     argument = constructStandardJavaInstance(
400                             javaConstructor.getParameterTypes()[0], node);
401                 } else {
402                     // TODO it should be possible to use implicit types instead
403                     // of forcing String. Resolver must be available here to
404                     // obtain the implicit tag. Then we can set the tag and call
405                     // callConstructor(node) to create the argument instance.
406                     // On the other hand it may be safer to require a custom
407                     // constructor to avoid guessing the argument class
408                     argument = constructScalar(node);
409                     try {
410                         javaConstructor = type.getDeclaredConstructor(String.class);
411                     } catch (Exception e) {
412                         throw new YAMLException("Can't construct a java object for scalar "
413                                 + node.getTag() + "; No String constructor found. Exception="
414                                 + e.getMessage(), e);
415                     }
416                 }
417                 try {
418                     javaConstructor.setAccessible(true);
419                     result = javaConstructor.newInstance(argument);
420                 } catch (Exception e) {
421                     throw new ConstructorException(null, null,
422                             "Can't construct a java object for scalar " + node.getTag()
423                                     + "; exception=" + e.getMessage(), node.getStartMark(), e);
424                 }
425             }
426             return result;
427         }
428 
429         @SuppressWarnings("unchecked")
constructStandardJavaInstance(@uppressWarnings"rawtypes") Class type, ScalarNode node)430         private Object constructStandardJavaInstance(@SuppressWarnings("rawtypes")
431         Class type, ScalarNode node) {
432             Object result;
433             if (type == String.class) {
434                 Construct stringConstructor = yamlConstructors.get(Tag.STR);
435                 result = stringConstructor.construct(node);
436             } else if (type == Boolean.class || type == Boolean.TYPE) {
437                 Construct boolConstructor = yamlConstructors.get(Tag.BOOL);
438                 result = boolConstructor.construct(node);
439             } else if (type == Character.class || type == Character.TYPE) {
440                 Construct charConstructor = yamlConstructors.get(Tag.STR);
441                 String ch = (String) charConstructor.construct(node);
442                 if (ch.length() == 0) {
443                     result = null;
444                 } else if (ch.length() != 1) {
445                     throw new YAMLException("Invalid node Character: '" + ch + "'; length: "
446                             + ch.length());
447                 } else {
448                     result = Character.valueOf(ch.charAt(0));
449                 }
450             } else if (Date.class.isAssignableFrom(type)) {
451                 Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
452                 Date date = (Date) dateConstructor.construct(node);
453                 if (type == Date.class) {
454                     result = date;
455                 } else {
456                     try {
457                         java.lang.reflect.Constructor<?> constr = type.getConstructor(long.class);
458                         result = constr.newInstance(date.getTime());
459                     } catch (RuntimeException e) {
460                         throw e;
461                     } catch (Exception e) {
462                         throw new YAMLException("Cannot construct: '" + type + "'");
463                     }
464                 }
465             } else if (type == Float.class || type == Double.class || type == Float.TYPE
466                     || type == Double.TYPE || type == BigDecimal.class) {
467                 if (type == BigDecimal.class) {
468                     result = new BigDecimal(node.getValue());
469                 } else {
470                     Construct doubleConstructor = yamlConstructors.get(Tag.FLOAT);
471                     result = doubleConstructor.construct(node);
472                     if (type == Float.class || type == Float.TYPE) {
473                         result = new Float((Double) result);
474                     }
475                 }
476             } else if (type == Byte.class || type == Short.class || type == Integer.class
477                     || type == Long.class || type == BigInteger.class || type == Byte.TYPE
478                     || type == Short.TYPE || type == Integer.TYPE || type == Long.TYPE) {
479                 Construct intConstructor = yamlConstructors.get(Tag.INT);
480                 result = intConstructor.construct(node);
481                 if (type == Byte.class || type == Byte.TYPE) {
482                     result = Byte.valueOf(result.toString());
483                 } else if (type == Short.class || type == Short.TYPE) {
484                     result = Short.valueOf(result.toString());
485                 } else if (type == Integer.class || type == Integer.TYPE) {
486                     result = Integer.parseInt(result.toString());
487                 } else if (type == Long.class || type == Long.TYPE) {
488                     result = Long.valueOf(result.toString());
489                 } else {
490                     // only BigInteger left
491                     result = new BigInteger(result.toString());
492                 }
493             } else if (Enum.class.isAssignableFrom(type)) {
494                 String enumValueName = node.getValue();
495                 try {
496                     result = Enum.valueOf(type, enumValueName);
497                 } catch (Exception ex) {
498                     throw new YAMLException("Unable to find enum value '" + enumValueName
499                             + "' for enum class: " + type.getName());
500                 }
501             } else if (Calendar.class.isAssignableFrom(type)) {
502                 ConstructYamlTimestamp contr = new ConstructYamlTimestamp();
503                 contr.construct(node);
504                 result = contr.getCalendar();
505             } else if (Number.class.isAssignableFrom(type)) {
506                 ConstructYamlNumber contr = new ConstructYamlNumber();
507                 result = contr.construct(node);
508             }  else if (UUID.class == type) {
509                 result = UUID.fromString(node.getValue());
510             } else {
511                 if (yamlConstructors.containsKey(node.getTag())) {
512                     result = yamlConstructors.get(node.getTag()).construct(node);
513                 } else {
514                     throw new YAMLException("Unsupported class: " + type);
515                 }
516             }
517             return result;
518         }
519     }
520 
521     /**
522      * Construct sequence (List, Array, or immutable object) when the runtime
523      * class is known.
524      */
525     protected class ConstructSequence implements Construct {
526         @SuppressWarnings("unchecked")
construct(Node node)527         public Object construct(Node node) {
528             SequenceNode snode = (SequenceNode) node;
529             if (Set.class.isAssignableFrom(node.getType())) {
530                 if (node.isTwoStepsConstruction()) {
531                     throw new YAMLException("Set cannot be recursive.");
532                 } else {
533                     return constructSet(snode);
534                 }
535             } else if (Collection.class.isAssignableFrom(node.getType())) {
536                 if (node.isTwoStepsConstruction()) {
537                     return createDefaultList(snode.getValue().size());
538                 } else {
539                     return constructSequence(snode);
540                 }
541             } else if (node.getType().isArray()) {
542                 if (node.isTwoStepsConstruction()) {
543                     return createArray(node.getType(), snode.getValue().size());
544                 } else {
545                     return constructArray(snode);
546                 }
547             } else {
548                 // create immutable object
549                 List<java.lang.reflect.Constructor<?>> possibleConstructors = new ArrayList<java.lang.reflect.Constructor<?>>(
550                         snode.getValue().size());
551                 for (java.lang.reflect.Constructor<?> constructor : node
552                         .getType().getDeclaredConstructors()) {
553                     if (snode.getValue()
554                             .size() == constructor.getParameterTypes().length) {
555                         possibleConstructors.add(constructor);
556                     }
557                 }
558                 if (!possibleConstructors.isEmpty()) {
559                     if (possibleConstructors.size() == 1) {
560                         Object[] argumentList = new Object[snode.getValue().size()];
561                         java.lang.reflect.Constructor<?> c = possibleConstructors.get(0);
562                         int index = 0;
563                         for (Node argumentNode : snode.getValue()) {
564                             Class<?> type = c.getParameterTypes()[index];
565                             // set runtime classes for arguments
566                             argumentNode.setType(type);
567                             argumentList[index++] = constructObject(argumentNode);
568                         }
569 
570                         try {
571                             c.setAccessible(true);
572                             return c.newInstance(argumentList);
573                         } catch (Exception e) {
574                             throw new YAMLException(e);
575                         }
576                     }
577 
578                     // use BaseConstructor
579                     List<Object> argumentList = (List<Object>) constructSequence(snode);
580                     Class<?>[] parameterTypes = new Class[argumentList.size()];
581                     int index = 0;
582                     for (Object parameter : argumentList) {
583                         parameterTypes[index] = parameter.getClass();
584                         index++;
585                     }
586 
587                     for (java.lang.reflect.Constructor<?> c : possibleConstructors) {
588                         Class<?>[] argTypes = c.getParameterTypes();
589                         boolean foundConstructor = true;
590                         for (int i = 0; i < argTypes.length; i++) {
591                             if (!wrapIfPrimitive(argTypes[i]).isAssignableFrom(parameterTypes[i])) {
592                                 foundConstructor = false;
593                                 break;
594                             }
595                         }
596                         if (foundConstructor) {
597                             try {
598                                 c.setAccessible(true);
599                                 return c.newInstance(argumentList.toArray());
600                             } catch (Exception e) {
601                                 throw new YAMLException(e);
602                             }
603                         }
604                     }
605                 }
606                 throw new YAMLException("No suitable constructor with "
607                         + String.valueOf(snode.getValue().size()) + " arguments found for "
608                         + node.getType());
609 
610             }
611         }
612 
wrapIfPrimitive(Class<?> clazz)613         private final Class<? extends Object> wrapIfPrimitive(Class<?> clazz) {
614             if (!clazz.isPrimitive()) {
615                 return clazz;
616             }
617             if (clazz == Integer.TYPE) {
618                 return Integer.class;
619             }
620             if (clazz == Float.TYPE) {
621                 return Float.class;
622             }
623             if (clazz == Double.TYPE) {
624                 return Double.class;
625             }
626             if (clazz == Boolean.TYPE) {
627                 return Boolean.class;
628             }
629             if (clazz == Long.TYPE) {
630                 return Long.class;
631             }
632             if (clazz == Character.TYPE) {
633                 return Character.class;
634             }
635             if (clazz == Short.TYPE) {
636                 return Short.class;
637             }
638             if (clazz == Byte.TYPE) {
639                 return Byte.class;
640             }
641             throw new YAMLException("Unexpected primitive " + clazz);
642         }
643 
644         @SuppressWarnings("unchecked")
construct2ndStep(Node node, Object object)645         public void construct2ndStep(Node node, Object object) {
646             SequenceNode snode = (SequenceNode) node;
647             if (List.class.isAssignableFrom(node.getType())) {
648                 List<Object> list = (List<Object>) object;
649                 constructSequenceStep2(snode, list);
650             } else if (node.getType().isArray()) {
651                 constructArrayStep2(snode, object);
652             } else {
653                 throw new YAMLException("Immutable objects cannot be recursive.");
654             }
655         }
656     }
657 
getClassForNode(Node node)658     protected Class<?> getClassForNode(Node node) {
659         Class<? extends Object> classForTag = typeTags.get(node.getTag());
660         if (classForTag == null) {
661             String name = node.getTag().getClassName();
662             Class<?> cl;
663             try {
664                 cl = getClassForName(name);
665             } catch (ClassNotFoundException e) {
666                 throw new YAMLException("Class not found: " + name);
667             }
668             typeTags.put(node.getTag(), cl);
669             return cl;
670         } else {
671             return classForTag;
672         }
673     }
674 
getClassForName(String name)675     protected Class<?> getClassForName(String name) throws ClassNotFoundException {
676         try {
677             return Class.forName(name, true, Thread.currentThread().getContextClassLoader());
678         } catch (ClassNotFoundException e) {
679             return Class.forName(name);
680         }
681     }
682 }
683