Home | History | Annotate | Download | only in constructor
      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 
     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      */
     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      */
     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 
     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      */
    103     public Constructor(String theRoot) throws ClassNotFoundException {
    104         this(Class.forName(check(theRoot)));
    105     }
    106 
    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      */
    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          */
    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")
    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 
    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 
    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 
    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 
    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 
    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 
    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 {
    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")
    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")
    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 
    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")
    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 
    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 
    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