Coverage Report - org.yaml.snakeyaml.constructor.BaseConstructor
 
Classes in this File Line Coverage Branch Coverage Complexity
BaseConstructor
92%
127/137
94%
51/54
2.367
BaseConstructor$RecursiveTuple
100%
6/6
N/A
2.367
 
 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.lang.reflect.Array;
 20  
 import java.util.ArrayList;
 21  
 import java.util.Collection;
 22  
 import java.util.EnumMap;
 23  
 import java.util.HashMap;
 24  
 import java.util.HashSet;
 25  
 import java.util.LinkedHashMap;
 26  
 import java.util.LinkedHashSet;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 
 31  
 import org.yaml.snakeyaml.composer.Composer;
 32  
 import org.yaml.snakeyaml.composer.ComposerException;
 33  
 import org.yaml.snakeyaml.error.YAMLException;
 34  
 import org.yaml.snakeyaml.introspector.PropertyUtils;
 35  
 import org.yaml.snakeyaml.nodes.MappingNode;
 36  
 import org.yaml.snakeyaml.nodes.Node;
 37  
 import org.yaml.snakeyaml.nodes.NodeId;
 38  
 import org.yaml.snakeyaml.nodes.NodeTuple;
 39  
 import org.yaml.snakeyaml.nodes.ScalarNode;
 40  
 import org.yaml.snakeyaml.nodes.SequenceNode;
 41  
 import org.yaml.snakeyaml.nodes.Tag;
 42  
 
 43  
 public abstract class BaseConstructor {
 44  
     /**
 45  
      * It maps the node kind to the the Construct implementation. When the
 46  
      * runtime class is known then the implicit tag is ignored.
 47  
      */
 48  3984
     protected final Map<NodeId, Construct> yamlClassConstructors = new EnumMap<NodeId, Construct>(
 49  
             NodeId.class);
 50  
     /**
 51  
      * It maps the (explicit or implicit) tag to the Construct implementation.
 52  
      * It is used: <br/>
 53  
      * 1) explicit tag - if present. <br/>
 54  
      * 2) implicit tag - when the runtime class of the instance is unknown (the
 55  
      * node has the Object.class)
 56  
      */
 57  3984
     protected final Map<Tag, Construct> yamlConstructors = new HashMap<Tag, Construct>();
 58  
     /**
 59  
      * It maps the (explicit or implicit) tag to the Construct implementation.
 60  
      * It is used when no exact match found.
 61  
      */
 62  3984
     protected final Map<String, Construct> yamlMultiConstructors = new HashMap<String, Construct>();
 63  
 
 64  
     private Composer composer;
 65  
     private final Map<Node, Object> constructedObjects;
 66  
     private final Set<Node> recursiveObjects;
 67  
     private final ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>> maps2fill;
 68  
     private final ArrayList<RecursiveTuple<Set<Object>, Object>> sets2fill;
 69  
 
 70  
     protected Tag rootTag;
 71  
     private PropertyUtils propertyUtils;
 72  
     private boolean explicitPropertyUtils;
 73  
 
 74  3984
     public BaseConstructor() {
 75  3984
         constructedObjects = new HashMap<Node, Object>();
 76  3984
         recursiveObjects = new HashSet<Node>();
 77  3984
         maps2fill = new ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>>();
 78  3984
         sets2fill = new ArrayList<RecursiveTuple<Set<Object>, Object>>();
 79  3984
         rootTag = null;
 80  3984
         explicitPropertyUtils = false;
 81  3984
     }
 82  
 
 83  
     public void setComposer(Composer composer) {
 84  921
         this.composer = composer;
 85  921
     }
 86  
 
 87  
     /**
 88  
      * Check if more documents available
 89  
      * 
 90  
      * @return true when there are more YAML documents in the stream
 91  
      */
 92  
     public boolean checkData() {
 93  
         // If there are more documents available?
 94  398
         return composer.checkNode();
 95  
     }
 96  
 
 97  
     /**
 98  
      * Construct and return the next document
 99  
      * 
 100  
      * @return constructed instance
 101  
      */
 102  
     public Object getData() {
 103  
         // Construct and return the next document.
 104  355
         composer.checkNode();
 105  355
         Node node = composer.getNode();
 106  290
         if (rootTag != null) {
 107  5
             node.setTag(rootTag);
 108  
         }
 109  290
         return constructDocument(node);
 110  
     }
 111  
 
 112  
     /**
 113  
      * Ensure that the stream contains a single document and construct it
 114  
      * 
 115  
      * @return constructed instance
 116  
      * @throws ComposerException
 117  
      *             in case there are more documents in the stream
 118  
      */
 119  
     public Object getSingleData(Class<?> type) {
 120  
         // Ensure that the stream contains a single document and construct it
 121  563
         Node node = composer.getSingleNode();
 122  553
         if (node != null) {
 123  549
             if (Object.class != type) {
 124  71
                 node.setTag(new Tag(type));
 125  478
             } else if (rootTag != null) {
 126  40
                 node.setTag(rootTag);
 127  
             }
 128  548
             return constructDocument(node);
 129  
         }
 130  4
         return null;
 131  
     }
 132  
 
 133  
     /**
 134  
      * Construct complete YAML document. Call the second step in case of
 135  
      * recursive structures. At the end cleans all the state.
 136  
      * 
 137  
      * @param node
 138  
      *            root Node
 139  
      * @return Java instance
 140  
      */
 141  
     private Object constructDocument(Node node) {
 142  838
         Object data = constructObject(node);
 143  768
         fillRecursive();
 144  768
         constructedObjects.clear();
 145  768
         recursiveObjects.clear();
 146  768
         return data;
 147  
     }
 148  
 
 149  
     private void fillRecursive() {
 150  768
         if (!maps2fill.isEmpty()) {
 151  2
             for (RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>> entry : maps2fill) {
 152  2
                 RecursiveTuple<Object, Object> key_value = entry._2();
 153  2
                 entry._1().put(key_value._1(), key_value._2());
 154  2
             }
 155  2
             maps2fill.clear();
 156  
         }
 157  768
         if (!sets2fill.isEmpty()) {
 158  5
             for (RecursiveTuple<Set<Object>, Object> value : sets2fill) {
 159  5
                 value._1().add(value._2());
 160  
             }
 161  5
             sets2fill.clear();
 162  
         }
 163  768
     }
 164  
 
 165  
     /**
 166  
      * Construct object from the specified Node. Return existing instance if the
 167  
      * node is already constructed.
 168  
      * 
 169  
      * @param node
 170  
      *            Node to be constructed
 171  
      * @return Java instance
 172  
      */
 173  
     protected Object constructObject(Node node) {
 174  386579
         if (constructedObjects.containsKey(node)) {
 175  1397
             return constructedObjects.get(node);
 176  
         }
 177  385182
         if (recursiveObjects.contains(node)) {
 178  0
             throw new ConstructorException(null, null, "found unconstructable recursive node",
 179  
                     node.getStartMark());
 180  
         }
 181  385182
         recursiveObjects.add(node);
 182  385182
         Construct constructor = getConstructor(node);
 183  385182
         Object data = constructor.construct(node);
 184  385097
         constructedObjects.put(node, data);
 185  385097
         recursiveObjects.remove(node);
 186  385097
         if (node.isTwoStepsConstruction()) {
 187  67
             constructor.construct2ndStep(node, data);
 188  
         }
 189  385097
         return data;
 190  
     }
 191  
 
 192  
     /**
 193  
      * Get the constructor to construct the Node. For implicit tags if the
 194  
      * runtime class is known a dedicated Construct implementation is used.
 195  
      * Otherwise the constructor is chosen by the tag.
 196  
      * 
 197  
      * @param node
 198  
      *            Node to be constructed
 199  
      * @return Construct implementation for the specified node
 200  
      */
 201  
     protected Construct getConstructor(Node node) {
 202  385162
         if (node.useClassConstructor()) {
 203  5087
             return yamlClassConstructors.get(node.getNodeId());
 204  
         } else {
 205  380075
             Construct constructor = yamlConstructors.get(node.getTag());
 206  380075
             if (constructor == null) {
 207  2082
                 for (String prefix : yamlMultiConstructors.keySet()) {
 208  430
                     if (node.getTag().startsWith(prefix)) {
 209  429
                         return yamlMultiConstructors.get(prefix);
 210  
                     }
 211  
                 }
 212  1653
                 return yamlConstructors.get(null);
 213  
             }
 214  377993
             return constructor;
 215  
         }
 216  
     }
 217  
 
 218  
     protected Object constructScalar(ScalarNode node) {
 219  351130
         return node.getValue();
 220  
     }
 221  
 
 222  
     protected List<Object> createDefaultList(int initSize) {
 223  10691
         return new ArrayList<Object>(initSize);
 224  
     }
 225  
 
 226  
     protected Set<Object> createDefaultSet(int initSize) {
 227  5
         return new LinkedHashSet<Object>(initSize);
 228  
     }
 229  
 
 230  
     @SuppressWarnings("unchecked")
 231  
     protected <T> T[] createArray(Class<T> type, int size) {
 232  23
         return (T[]) Array.newInstance(type.getComponentType(), size);
 233  
     }
 234  
 
 235  
     @SuppressWarnings("unchecked")
 236  
     protected List<? extends Object> constructSequence(SequenceNode node) {
 237  
         List<Object> result;
 238  10691
         if (List.class.isAssignableFrom(node.getType()) && !node.getType().isInterface()) {
 239  
             // the root class may be defined (Vector for instance)
 240  
             try {
 241  2
                 result = (List<Object>) node.getType().newInstance();
 242  0
             } catch (Exception e) {
 243  0
                 throw new YAMLException(e);
 244  2
             }
 245  
         } else {
 246  10689
             result = createDefaultList(node.getValue().size());
 247  
         }
 248  10691
         constructSequenceStep2(node, result);
 249  10688
         return result;
 250  
 
 251  
     }
 252  
 
 253  
     @SuppressWarnings("unchecked")
 254  
     protected Set<? extends Object> constructSet(SequenceNode node) {
 255  
         Set<Object> result;
 256  9
         if (!node.getType().isInterface()) {
 257  
             // the root class may be defined
 258  
             try {
 259  4
                 result = (Set<Object>) node.getType().newInstance();
 260  0
             } catch (Exception e) {
 261  0
                 throw new YAMLException(e);
 262  4
             }
 263  
         } else {
 264  5
             result = createDefaultSet(node.getValue().size());
 265  
         }
 266  9
         constructSequenceStep2(node, result);
 267  9
         return result;
 268  
 
 269  
     }
 270  
 
 271  
     protected Object constructArray(SequenceNode node) {
 272  21
         return constructArrayStep2(node, createArray(node.getType(), node.getValue().size()));
 273  
     }
 274  
 
 275  
     protected void constructSequenceStep2(SequenceNode node, Collection<Object> collection) {
 276  10705
         for (Node child : node.getValue()) {
 277  113813
             collection.add(constructObject(child));
 278  
         }
 279  10702
     }
 280  
 
 281  
     protected Object constructArrayStep2(SequenceNode node, Object array) {
 282  23
         int index = 0;
 283  23
         for (Node child : node.getValue()) {
 284  41
             Array.set(array, index++, constructObject(child));
 285  
         }
 286  23
         return array;
 287  
     }
 288  
 
 289  
     protected Map<Object, Object> createDefaultMap() {
 290  
         // respect order from YAML document
 291  21317
         return new LinkedHashMap<Object, Object>();
 292  
     }
 293  
 
 294  
     protected Set<Object> createDefaultSet() {
 295  
         // respect order from YAML document
 296  34
         return new LinkedHashSet<Object>();
 297  
     }
 298  
 
 299  
     protected Set<Object> constructSet(MappingNode node) {
 300  27
         Set<Object> set = createDefaultSet();
 301  27
         constructSet2ndStep(node, set);
 302  27
         return set;
 303  
     }
 304  
 
 305  
     protected Map<Object, Object> constructMapping(MappingNode node) {
 306  21308
         Map<Object, Object> mapping = createDefaultMap();
 307  21308
         constructMapping2ndStep(node, mapping);
 308  21300
         return mapping;
 309  
     }
 310  
 
 311  
     protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
 312  21328
         List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
 313  21328
         for (NodeTuple tuple : nodeValue) {
 314  133301
             Node keyNode = tuple.getKeyNode();
 315  133301
             Node valueNode = tuple.getValueNode();
 316  133301
             Object key = constructObject(keyNode);
 317  133301
             if (key != null) {
 318  
                 try {
 319  133291
                     key.hashCode();// check circular dependencies
 320  0
                 } catch (Exception e) {
 321  0
                     throw new ConstructorException("while constructing a mapping",
 322  
                             node.getStartMark(), "found unacceptable key " + key, tuple
 323  
                                     .getKeyNode().getStartMark(), e);
 324  133291
                 }
 325  
             }
 326  133301
             Object value = constructObject(valueNode);
 327  133297
             if (keyNode.isTwoStepsConstruction()) {
 328  
                 /*
 329  
                  * if keyObject is created it 2 steps we should postpone putting
 330  
                  * it in map because it may have different hash after
 331  
                  * initialization compared to clean just created one. And map of
 332  
                  * course does not observe key hashCode changes.
 333  
                  */
 334  2
                 maps2fill.add(0,
 335  
                         new RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>(
 336  
                                 mapping, new RecursiveTuple<Object, Object>(key, value)));
 337  
             } else {
 338  133295
                 mapping.put(key, value);
 339  
             }
 340  133297
         }
 341  21324
     }
 342  
 
 343  
     protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
 344  43
         List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
 345  43
         for (NodeTuple tuple : nodeValue) {
 346  72
             Node keyNode = tuple.getKeyNode();
 347  72
             Object key = constructObject(keyNode);
 348  72
             if (key != null) {
 349  
                 try {
 350  72
                     key.hashCode();// check circular dependencies
 351  0
                 } catch (Exception e) {
 352  0
                     throw new ConstructorException("while constructing a Set", node.getStartMark(),
 353  
                             "found unacceptable key " + key, tuple.getKeyNode().getStartMark(), e);
 354  72
                 }
 355  
             }
 356  72
             if (keyNode.isTwoStepsConstruction()) {
 357  
                 /*
 358  
                  * if keyObject is created it 2 steps we should postpone putting
 359  
                  * it into the set because it may have different hash after
 360  
                  * initialization compared to clean just created one. And set of
 361  
                  * course does not observe value hashCode changes.
 362  
                  */
 363  5
                 sets2fill.add(0, new RecursiveTuple<Set<Object>, Object>(set, key));
 364  
             } else {
 365  67
                 set.add(key);
 366  
             }
 367  72
         }
 368  43
     }
 369  
 
 370  
     // TODO protected List<Object[]> constructPairs(MappingNode node) {
 371  
     // List<Object[]> pairs = new LinkedList<Object[]>();
 372  
     // List<Node[]> nodeValue = (List<Node[]>) node.getValue();
 373  
     // for (Iterator<Node[]> iter = nodeValue.iterator(); iter.hasNext();) {
 374  
     // Node[] tuple = iter.next();
 375  
     // Object key = constructObject(Object.class, tuple[0]);
 376  
     // Object value = constructObject(Object.class, tuple[1]);
 377  
     // pairs.add(new Object[] { key, value });
 378  
     // }
 379  
     // return pairs;
 380  
     // }
 381  
 
 382  
     public void setPropertyUtils(PropertyUtils propertyUtils) {
 383  3963
         this.propertyUtils = propertyUtils;
 384  3963
         explicitPropertyUtils = true;
 385  3963
     }
 386  
 
 387  
     public final PropertyUtils getPropertyUtils() {
 388  2601
         if (propertyUtils == null) {
 389  0
             propertyUtils = new PropertyUtils();
 390  
         }
 391  2601
         return propertyUtils;
 392  
     }
 393  
 
 394  
     private static class RecursiveTuple<T, K> {
 395  
         private final T _1;
 396  
         private final K _2;
 397  
 
 398  9
         public RecursiveTuple(T _1, K _2) {
 399  9
             this._1 = _1;
 400  9
             this._2 = _2;
 401  9
         }
 402  
 
 403  
         public K _2() {
 404  9
             return _2;
 405  
         }
 406  
 
 407  
         public T _1() {
 408  9
             return _1;
 409  
         }
 410  
     }
 411  
 
 412  
     public final boolean isExplicitPropertyUtils() {
 413  3963
         return explicitPropertyUtils;
 414  
     }
 415  
 }