Coverage Report - org.yaml.snakeyaml.constructor.SafeConstructor
 
Classes in this File Line Coverage Branch Coverage Complexity
SafeConstructor
98%
77/78
94%
18/19
4
SafeConstructor$1
100%
1/1
N/A
4
SafeConstructor$ConstructUndefined
100%
2/2
N/A
4
SafeConstructor$ConstructYamlBinary
100%
3/3
N/A
4
SafeConstructor$ConstructYamlBool
100%
3/3
N/A
4
SafeConstructor$ConstructYamlFloat
100%
24/24
100%
14/14
4
SafeConstructor$ConstructYamlInt
100%
31/31
100%
16/16
4
SafeConstructor$ConstructYamlMap
87%
7/8
75%
3/4
4
SafeConstructor$ConstructYamlNull
100%
3/3
N/A
4
SafeConstructor$ConstructYamlOmap
100%
18/18
100%
8/8
4
SafeConstructor$ConstructYamlPairs
100%
18/18
100%
8/8
4
SafeConstructor$ConstructYamlSeq
88%
8/9
75%
3/4
4
SafeConstructor$ConstructYamlSet
87%
7/8
75%
3/4
4
SafeConstructor$ConstructYamlStr
100%
2/2
N/A
4
SafeConstructor$ConstructYamlTimestamp
97%
45/46
90%
9/10
4
 
 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.math.BigInteger;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Calendar;
 21  
 import java.util.Collections;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.LinkedHashMap;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 import java.util.TimeZone;
 29  
 import java.util.regex.Matcher;
 30  
 import java.util.regex.Pattern;
 31  
 
 32  
 import org.yaml.snakeyaml.error.YAMLException;
 33  
 import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
 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  
 /**
 43  
  * Construct standard Java classes
 44  
  */
 45  102535
 public class SafeConstructor extends BaseConstructor {
 46  
 
 47  1
     public static final ConstructUndefined undefinedConstructor = new ConstructUndefined();
 48  
 
 49  132957
     public SafeConstructor() {
 50  132957
         this.yamlConstructors.put(Tag.NULL, new ConstructYamlNull());
 51  132957
         this.yamlConstructors.put(Tag.BOOL, new ConstructYamlBool());
 52  132957
         this.yamlConstructors.put(Tag.INT, new ConstructYamlInt());
 53  132957
         this.yamlConstructors.put(Tag.FLOAT, new ConstructYamlFloat());
 54  132957
         this.yamlConstructors.put(Tag.BINARY, new ConstructYamlBinary());
 55  132957
         this.yamlConstructors.put(Tag.TIMESTAMP, new ConstructYamlTimestamp());
 56  132957
         this.yamlConstructors.put(Tag.OMAP, new ConstructYamlOmap());
 57  132957
         this.yamlConstructors.put(Tag.PAIRS, new ConstructYamlPairs());
 58  132957
         this.yamlConstructors.put(Tag.SET, new ConstructYamlSet());
 59  132957
         this.yamlConstructors.put(Tag.STR, new ConstructYamlStr());
 60  132957
         this.yamlConstructors.put(Tag.SEQ, new ConstructYamlSeq());
 61  132957
         this.yamlConstructors.put(Tag.MAP, new ConstructYamlMap());
 62  132957
         this.yamlConstructors.put(null, undefinedConstructor);
 63  132957
         this.yamlClassConstructors.put(NodeId.scalar, undefinedConstructor);
 64  132957
         this.yamlClassConstructors.put(NodeId.sequence, undefinedConstructor);
 65  132957
         this.yamlClassConstructors.put(NodeId.mapping, undefinedConstructor);
 66  132957
     }
 67  
 
 68  
     protected void flattenMapping(MappingNode node) {
 69  
         // perform merging only on nodes containing merge node(s)
 70  22921
         if (node.isMerged()) {
 71  30
             node.setValue(mergeNode(node, true, new HashMap<Object, Integer>(),
 72  
                     new ArrayList<NodeTuple>()));
 73  
         }
 74  22917
     }
 75  
 
 76  
     /**
 77  
      * Does merge for supplied mapping node.
 78  
      * 
 79  
      * @param node
 80  
      *            where to merge
 81  
      * @param isPreffered
 82  
      *            true if keys of node should take precedence over others...
 83  
      * @param key2index
 84  
      *            maps already merged keys to index from values
 85  
      * @param values
 86  
      *            collects merged NodeTuple
 87  
      * @return list of the merged NodeTuple (to be set as value for the
 88  
      *         MappingNode)
 89  
      */
 90  
     private List<NodeTuple> mergeNode(MappingNode node, boolean isPreffered,
 91  
             Map<Object, Integer> key2index, List<NodeTuple> values) {
 92  74
         List<NodeTuple> nodeValue = node.getValue();
 93  
         // reversed for http://code.google.com/p/snakeyaml/issues/detail?id=139
 94  74
         Collections.reverse(nodeValue);
 95  74
         for (Iterator<NodeTuple> iter = nodeValue.iterator(); iter.hasNext();) {
 96  136
             final NodeTuple nodeTuple = iter.next();
 97  136
             final Node keyNode = nodeTuple.getKeyNode();
 98  136
             final Node valueNode = nodeTuple.getValueNode();
 99  136
             if (keyNode.getTag().equals(Tag.MERGE)) {
 100  30
                 iter.remove();
 101  30
                 switch (valueNode.getNodeId()) {
 102  
                 case mapping:
 103  16
                     MappingNode mn = (MappingNode) valueNode;
 104  16
                     mergeNode(mn, false, key2index, values);
 105  16
                     break;
 106  
                 case sequence:
 107  12
                     SequenceNode sn = (SequenceNode) valueNode;
 108  12
                     List<Node> vals = sn.getValue();
 109  12
                     for (Node subnode : vals) {
 110  30
                         if (!(subnode instanceof MappingNode)) {
 111  2
                             throw new ConstructorException("while constructing a mapping",
 112  
                                     node.getStartMark(),
 113  
                                     "expected a mapping for merging, but found "
 114  
                                             + subnode.getNodeId(), subnode.getStartMark());
 115  
                         }
 116  28
                         MappingNode mnode = (MappingNode) subnode;
 117  28
                         mergeNode(mnode, false, key2index, values);
 118  28
                     }
 119  10
                     break;
 120  
                 default:
 121  2
                     throw new ConstructorException("while constructing a mapping",
 122  
                             node.getStartMark(),
 123  
                             "expected a mapping or list of mappings for merging, but found "
 124  
                                     + valueNode.getNodeId(), valueNode.getStartMark());
 125  
                 }
 126  
             } else {
 127  
                 // we need to construct keys to avoid duplications
 128  106
                 Object key = constructObject(keyNode);
 129  106
                 if (!key2index.containsKey(key)) { // 1st time merging key
 130  84
                     values.add(nodeTuple);
 131  
                     // keep track where tuple for the key is
 132  84
                     key2index.put(key, values.size() - 1);
 133  22
                 } else if (isPreffered) { // there is value for the key, but we
 134  
                                           // need to override it
 135  
                     // change value for the key using saved position
 136  0
                     values.set(key2index.get(key), nodeTuple);
 137  
                 }
 138  
             }
 139  132
         }
 140  70
         return values;
 141  
     }
 142  
 
 143  
     protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
 144  21311
         flattenMapping(node);
 145  21307
         super.constructMapping2ndStep(node, mapping);
 146  21304
     }
 147  
 
 148  
     @Override
 149  
     protected void constructSet2ndStep(MappingNode node, java.util.Set<Object> set) {
 150  43
         flattenMapping(node);
 151  43
         super.constructSet2ndStep(node, set);
 152  43
     }
 153  
 
 154  132957
     public class ConstructYamlNull extends AbstractConstruct {
 155  
         public Object construct(Node node) {
 156  247
             constructScalar((ScalarNode) node);
 157  247
             return null;
 158  
         }
 159  
     }
 160  
 
 161  1
     private final static Map<String, Boolean> BOOL_VALUES = new HashMap<String, Boolean>();
 162  
     static {
 163  1
         BOOL_VALUES.put("yes", Boolean.TRUE);
 164  1
         BOOL_VALUES.put("no", Boolean.FALSE);
 165  1
         BOOL_VALUES.put("true", Boolean.TRUE);
 166  1
         BOOL_VALUES.put("false", Boolean.FALSE);
 167  1
         BOOL_VALUES.put("on", Boolean.TRUE);
 168  1
         BOOL_VALUES.put("off", Boolean.FALSE);
 169  
     }
 170  
 
 171  132957
     public class ConstructYamlBool extends AbstractConstruct {
 172  
         public Object construct(Node node) {
 173  159
             String val = (String) constructScalar((ScalarNode) node);
 174  159
             return BOOL_VALUES.get(val.toLowerCase());
 175  
         }
 176  
     }
 177  
 
 178  132957
     public class ConstructYamlInt extends AbstractConstruct {
 179  
         public Object construct(Node node) {
 180  102168
             String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
 181  102168
             int sign = +1;
 182  102168
             char first = value.charAt(0);
 183  102168
             if (first == '-') {
 184  18
                 sign = -1;
 185  18
                 value = value.substring(1);
 186  102150
             } else if (first == '+') {
 187  4
                 value = value.substring(1);
 188  
             }
 189  102168
             int base = 10;
 190  102168
             if ("0".equals(value)) {
 191  35
                 return Integer.valueOf(0);
 192  102133
             } else if (value.startsWith("0b")) {
 193  1
                 value = value.substring(2);
 194  1
                 base = 2;
 195  102132
             } else if (value.startsWith("0x")) {
 196  3
                 value = value.substring(2);
 197  3
                 base = 16;
 198  102129
             } else if (value.startsWith("0")) {
 199  3
                 value = value.substring(1);
 200  3
                 base = 8;
 201  102126
             } else if (value.indexOf(':') != -1) {
 202  4
                 String[] digits = value.split(":");
 203  4
                 int bes = 1;
 204  4
                 int val = 0;
 205  16
                 for (int i = 0, j = digits.length; i < j; i++) {
 206  12
                     val += (Long.parseLong(digits[(j - i) - 1]) * bes);
 207  12
                     bes *= 60;
 208  
                 }
 209  4
                 return createNumber(sign, String.valueOf(val), 10);
 210  
             } else {
 211  102122
                 return createNumber(sign, value, 10);
 212  
             }
 213  7
             return createNumber(sign, value, base);
 214  
         }
 215  
     }
 216  
 
 217  
     private Number createNumber(int sign, String number, int radix) {
 218  
         Number result;
 219  102133
         if (sign < 0) {
 220  17
             number = "-" + number;
 221  
         }
 222  
         try {
 223  102133
             result = Integer.valueOf(number, radix);
 224  13
         } catch (NumberFormatException e) {
 225  
             try {
 226  13
                 result = Long.valueOf(number, radix);
 227  4
             } catch (NumberFormatException e1) {
 228  4
                 result = new BigInteger(number, radix);
 229  9
             }
 230  102120
         }
 231  102133
         return result;
 232  
     }
 233  
 
 234  132957
     public class ConstructYamlFloat extends AbstractConstruct {
 235  
         public Object construct(Node node) {
 236  197
             String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
 237  197
             int sign = +1;
 238  197
             char first = value.charAt(0);
 239  197
             if (first == '-') {
 240  5
                 sign = -1;
 241  5
                 value = value.substring(1);
 242  192
             } else if (first == '+') {
 243  4
                 value = value.substring(1);
 244  
             }
 245  197
             String valLower = value.toLowerCase();
 246  197
             if (".inf".equals(valLower)) {
 247  6
                 return new Double(sign == -1 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
 248  191
             } else if (".nan".equals(valLower)) {
 249  3
                 return new Double(Double.NaN);
 250  188
             } else if (value.indexOf(':') != -1) {
 251  2
                 String[] digits = value.split(":");
 252  2
                 int bes = 1;
 253  2
                 double val = 0.0;
 254  7
                 for (int i = 0, j = digits.length; i < j; i++) {
 255  5
                     val += (Double.parseDouble(digits[(j - i) - 1]) * bes);
 256  5
                     bes *= 60;
 257  
                 }
 258  2
                 return new Double(sign * val);
 259  
             } else {
 260  186
                 Double d = Double.valueOf(value);
 261  185
                 return new Double(d.doubleValue() * sign);
 262  
             }
 263  
         }
 264  
     }
 265  
 
 266  132957
     public class ConstructYamlBinary extends AbstractConstruct {
 267  
         public Object construct(Node node) {
 268  18
             byte[] decoded = Base64Coder.decode(constructScalar((ScalarNode) node).toString()
 269  
                     .toCharArray());
 270  15
             return decoded;
 271  
         }
 272  
     }
 273  
 
 274  1
     private final static Pattern TIMESTAMP_REGEXP = Pattern
 275  
             .compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(?:Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?))?)?$");
 276  1
     private final static Pattern YMD_REGEXP = Pattern
 277  
             .compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$");
 278  
 
 279  132967
     public static class ConstructYamlTimestamp extends AbstractConstruct {
 280  
         private Calendar calendar;
 281  
 
 282  
         public Calendar getCalendar() {
 283  7
             return calendar;
 284  
         }
 285  
 
 286  
         public Object construct(Node node) {
 287  131
             ScalarNode scalar = (ScalarNode) node;
 288  131
             String nodeValue = scalar.getValue();
 289  131
             Matcher match = YMD_REGEXP.matcher(nodeValue);
 290  131
             if (match.matches()) {
 291  19
                 String year_s = match.group(1);
 292  19
                 String month_s = match.group(2);
 293  19
                 String day_s = match.group(3);
 294  19
                 calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
 295  19
                 calendar.clear();
 296  19
                 calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
 297  
                 // Java's months are zero-based...
 298  19
                 calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // x
 299  19
                 calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
 300  19
                 return calendar.getTime();
 301  
             } else {
 302  112
                 match = TIMESTAMP_REGEXP.matcher(nodeValue);
 303  112
                 if (!match.matches()) {
 304  0
                     throw new YAMLException("Unexpected timestamp: " + nodeValue);
 305  
                 }
 306  112
                 String year_s = match.group(1);
 307  112
                 String month_s = match.group(2);
 308  112
                 String day_s = match.group(3);
 309  112
                 String hour_s = match.group(4);
 310  112
                 String min_s = match.group(5);
 311  
                 // seconds and milliseconds
 312  112
                 String seconds = match.group(6);
 313  112
                 String millis = match.group(7);
 314  112
                 if (millis != null) {
 315  41
                     seconds = seconds + "." + millis;
 316  
                 }
 317  112
                 double fractions = Double.parseDouble(seconds);
 318  112
                 int sec_s = (int) Math.round(Math.floor(fractions));
 319  112
                 int usec = (int) Math.round(((fractions - sec_s) * 1000));
 320  
                 // timezone
 321  112
                 String timezoneh_s = match.group(8);
 322  112
                 String timezonem_s = match.group(9);
 323  
                 TimeZone timeZone;
 324  112
                 if (timezoneh_s != null) {
 325  23
                     String time = timezonem_s != null ? ":" + timezonem_s : "00";
 326  23
                     timeZone = TimeZone.getTimeZone("GMT" + timezoneh_s + time);
 327  23
                 } else {
 328  
                     // no time zone provided
 329  89
                     timeZone = TimeZone.getTimeZone("UTC");
 330  
                 }
 331  112
                 calendar = Calendar.getInstance(timeZone);
 332  112
                 calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
 333  
                 // Java's months are zero-based...
 334  112
                 calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1);
 335  112
                 calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
 336  112
                 calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour_s));
 337  112
                 calendar.set(Calendar.MINUTE, Integer.parseInt(min_s));
 338  112
                 calendar.set(Calendar.SECOND, sec_s);
 339  112
                 calendar.set(Calendar.MILLISECOND, usec);
 340  112
                 return calendar.getTime();
 341  
             }
 342  
         }
 343  
     }
 344  
 
 345  132957
     public class ConstructYamlOmap extends AbstractConstruct {
 346  
         public Object construct(Node node) {
 347  
             // Note: we do not check for duplicate keys, because it's too
 348  
             // CPU-expensive.
 349  9
             Map<Object, Object> omap = new LinkedHashMap<Object, Object>();
 350  9
             if (!(node instanceof SequenceNode)) {
 351  2
                 throw new ConstructorException("while constructing an ordered map",
 352  
                         node.getStartMark(), "expected a sequence, but found " + node.getNodeId(),
 353  
                         node.getStartMark());
 354  
             }
 355  7
             SequenceNode snode = (SequenceNode) node;
 356  7
             for (Node subnode : snode.getValue()) {
 357  17
                 if (!(subnode instanceof MappingNode)) {
 358  2
                     throw new ConstructorException("while constructing an ordered map",
 359  
                             node.getStartMark(), "expected a mapping of length 1, but found "
 360  
                                     + subnode.getNodeId(), subnode.getStartMark());
 361  
                 }
 362  15
                 MappingNode mnode = (MappingNode) subnode;
 363  15
                 if (mnode.getValue().size() != 1) {
 364  2
                     throw new ConstructorException("while constructing an ordered map",
 365  
                             node.getStartMark(), "expected a single mapping item, but found "
 366  
                                     + mnode.getValue().size() + " items", mnode.getStartMark());
 367  
                 }
 368  13
                 Node keyNode = mnode.getValue().get(0).getKeyNode();
 369  13
                 Node valueNode = mnode.getValue().get(0).getValueNode();
 370  13
                 Object key = constructObject(keyNode);
 371  13
                 Object value = constructObject(valueNode);
 372  13
                 omap.put(key, value);
 373  13
             }
 374  3
             return omap;
 375  
         }
 376  
     }
 377  
 
 378  
     // Note: the same code as `construct_yaml_omap`.
 379  132957
     public class ConstructYamlPairs extends AbstractConstruct {
 380  
         public Object construct(Node node) {
 381  
             // Note: we do not check for duplicate keys, because it's too
 382  
             // CPU-expensive.
 383  8
             if (!(node instanceof SequenceNode)) {
 384  2
                 throw new ConstructorException("while constructing pairs", node.getStartMark(),
 385  
                         "expected a sequence, but found " + node.getNodeId(), node.getStartMark());
 386  
             }
 387  6
             SequenceNode snode = (SequenceNode) node;
 388  6
             List<Object[]> pairs = new ArrayList<Object[]>(snode.getValue().size());
 389  6
             for (Node subnode : snode.getValue()) {
 390  14
                 if (!(subnode instanceof MappingNode)) {
 391  2
                     throw new ConstructorException("while constructingpairs", node.getStartMark(),
 392  
                             "expected a mapping of length 1, but found " + subnode.getNodeId(),
 393  
                             subnode.getStartMark());
 394  
                 }
 395  12
                 MappingNode mnode = (MappingNode) subnode;
 396  12
                 if (mnode.getValue().size() != 1) {
 397  2
                     throw new ConstructorException("while constructing pairs", node.getStartMark(),
 398  
                             "expected a single mapping item, but found " + mnode.getValue().size()
 399  
                                     + " items", mnode.getStartMark());
 400  
                 }
 401  10
                 Node keyNode = mnode.getValue().get(0).getKeyNode();
 402  10
                 Node valueNode = mnode.getValue().get(0).getValueNode();
 403  10
                 Object key = constructObject(keyNode);
 404  10
                 Object value = constructObject(valueNode);
 405  10
                 pairs.add(new Object[] { key, value });
 406  10
             }
 407  2
             return pairs;
 408  
         }
 409  
     }
 410  
 
 411  132957
     public class ConstructYamlSet implements Construct {
 412  
         public Object construct(Node node) {
 413  19
             if (node.isTwoStepsConstruction()) {
 414  6
                 return createDefaultSet();
 415  
             } else {
 416  13
                 return constructSet((MappingNode) node);
 417  
             }
 418  
         }
 419  
 
 420  
         @SuppressWarnings("unchecked")
 421  
         public void construct2ndStep(Node node, Object object) {
 422  6
             if (node.isTwoStepsConstruction()) {
 423  6
                 constructSet2ndStep((MappingNode) node, (Set<Object>) object);
 424  
             } else {
 425  0
                 throw new YAMLException("Unexpected recursive set structure. Node: " + node);
 426  
             }
 427  6
         }
 428  
     }
 429  
 
 430  132957
     public class ConstructYamlStr extends AbstractConstruct {
 431  
         public Object construct(Node node) {
 432  377342
             return constructScalar((ScalarNode) node);
 433  
         }
 434  
     }
 435  
 
 436  132957
     public class ConstructYamlSeq implements Construct {
 437  
         public Object construct(Node node) {
 438  10602
             SequenceNode seqNode = (SequenceNode) node;
 439  10600
             if (node.isTwoStepsConstruction()) {
 440  3
                 return createDefaultList((seqNode.getValue()).size());
 441  
             } else {
 442  10597
                 return constructSequence(seqNode);
 443  
             }
 444  
         }
 445  
 
 446  
         @SuppressWarnings("unchecked")
 447  
         public void construct2ndStep(Node node, Object data) {
 448  3
             if (node.isTwoStepsConstruction()) {
 449  3
                 constructSequenceStep2((SequenceNode) node, (List<Object>) data);
 450  
             } else {
 451  0
                 throw new YAMLException("Unexpected recursive sequence structure. Node: " + node);
 452  
             }
 453  3
         }
 454  
     }
 455  
 
 456  132960
     public class ConstructYamlMap implements Construct {
 457  
         public Object construct(Node node) {
 458  21175
             if (node.isTwoStepsConstruction()) {
 459  19
                 return createDefaultMap();
 460  
             } else {
 461  21156
                 return constructMapping((MappingNode) node);
 462  
             }
 463  
         }
 464  
 
 465  
         @SuppressWarnings("unchecked")
 466  
         public void construct2ndStep(Node node, Object object) {
 467  19
             if (node.isTwoStepsConstruction()) {
 468  19
                 constructMapping2ndStep((MappingNode) node, (Map<Object, Object>) object);
 469  
             } else {
 470  0
                 throw new YAMLException("Unexpected recursive mapping structure. Node: " + node);
 471  
             }
 472  19
         }
 473  
     }
 474  
 
 475  1
     public static final class ConstructUndefined extends AbstractConstruct {
 476  
         public Object construct(Node node) {
 477  2
             throw new ConstructorException(null, null,
 478  
                     "could not determine a constructor for the tag " + node.getTag(),
 479  
                     node.getStartMark());
 480  
         }
 481  
     }
 482  
 }