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