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