##### Coverage Report - org.yaml.snakeyaml.constructor.BaseConstructor

Classes in this File | Line Coverage | Branch Coverage | Complexity
BaseConstructor |
92% | 127/137
94% | 51/54
| 2.3666666666666667;2.367
BaseConstructor$RecursiveTuple |
100% | 6/6
N/A | N/A
| 2.3666666666666667;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 |  |
     }