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