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