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