aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java')
-rw-r--r--src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java895
1 files changed, 534 insertions, 361 deletions
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
index 3a504cbb..a4ed1f50 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -1,21 +1,20 @@
/**
- * Copyright (c) 2008, http://www.snakeyaml.org
+ * Copyright (c) 2008, SnakeYAML
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
*/
package org.yaml.snakeyaml.constructor;
import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
@@ -25,12 +24,19 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Set;
-
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.composer.Composer;
import org.yaml.snakeyaml.composer.ComposerException;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.nodes.CollectionNode;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
@@ -40,411 +46,578 @@ import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
public abstract class BaseConstructor {
- /**
- * It maps the node kind to the the Construct implementation. When the
- * runtime class is known then the implicit tag is ignored.
- */
- protected final Map<NodeId, Construct> yamlClassConstructors = new EnumMap<NodeId, Construct>(
- NodeId.class);
- /**
- * It maps the (explicit or implicit) tag to the Construct implementation.
- * It is used: <br/>
- * 1) explicit tag - if present. <br/>
- * 2) implicit tag - when the runtime class of the instance is unknown (the
- * node has the Object.class)
- */
- protected final Map<Tag, Construct> yamlConstructors = new HashMap<Tag, Construct>();
- /**
- * It maps the (explicit or implicit) tag to the Construct implementation.
- * It is used when no exact match found.
- */
- protected final Map<String, Construct> yamlMultiConstructors = new HashMap<String, Construct>();
-
- protected Composer composer;
- private final Map<Node, Object> constructedObjects;
- private final Set<Node> recursiveObjects;
- private final ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>> maps2fill;
- private final ArrayList<RecursiveTuple<Set<Object>, Object>> sets2fill;
-
- protected Tag rootTag;
- private PropertyUtils propertyUtils;
- private boolean explicitPropertyUtils;
-
- public BaseConstructor() {
- constructedObjects = new HashMap<Node, Object>();
- recursiveObjects = new HashSet<Node>();
- maps2fill = new ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>>();
- sets2fill = new ArrayList<RecursiveTuple<Set<Object>, Object>>();
- rootTag = null;
- explicitPropertyUtils = false;
- }
- public void setComposer(Composer composer) {
- this.composer = composer;
+ /**
+ * An instance returned by newInstance methods when instantiation has not been performed.
+ */
+ protected static final Object NOT_INSTANTIATED_OBJECT = new Object();
+
+ /**
+ * It maps the node kind to the the Construct implementation. When the runtime class is known then
+ * the implicit tag is ignored.
+ */
+ protected final Map<NodeId, Construct> yamlClassConstructors =
+ new EnumMap<NodeId, Construct>(NodeId.class);
+ /**
+ * It maps the (explicit or implicit) tag to the Construct implementation. It is used: 1) explicit
+ * tag - if present. 2) implicit tag - when the runtime class of the instance is unknown (the node
+ * has the Object.class)
+ */
+ protected final Map<Tag, Construct> yamlConstructors = new HashMap<Tag, Construct>();
+ /**
+ * It maps the (explicit or implicit) tag to the Construct implementation. It is used when no
+ * exact match found.
+ */
+ protected final Map<String, Construct> yamlMultiConstructors = new HashMap<String, Construct>();
+
+ protected Composer composer;
+ final Map<Node, Object> constructedObjects;
+ private final Set<Node> recursiveObjects;
+ private final ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>> maps2fill;
+ private final ArrayList<RecursiveTuple<Set<Object>, Object>> sets2fill;
+
+ protected Tag rootTag;
+ private PropertyUtils propertyUtils;
+ private boolean explicitPropertyUtils;
+ private boolean allowDuplicateKeys = true;
+ private boolean wrappedToRootException = false;
+
+ private boolean enumCaseSensitive = false;
+
+ protected final Map<Class<? extends Object>, TypeDescription> typeDefinitions;
+ protected final Map<Tag, Class<? extends Object>> typeTags;
+
+ protected LoaderOptions loadingConfig;
+
+ public BaseConstructor() {
+ this(new LoaderOptions());
+ }
+
+ public BaseConstructor(LoaderOptions loadingConfig) {
+ constructedObjects = new HashMap<Node, Object>();
+ recursiveObjects = new HashSet<Node>();
+ maps2fill =
+ new ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>>();
+ sets2fill = new ArrayList<RecursiveTuple<Set<Object>, Object>>();
+ typeDefinitions = new HashMap<Class<? extends Object>, TypeDescription>();
+ typeTags = new HashMap<Tag, Class<? extends Object>>();
+
+ rootTag = null;
+ explicitPropertyUtils = false;
+
+ typeDefinitions.put(SortedMap.class,
+ new TypeDescription(SortedMap.class, Tag.OMAP, TreeMap.class));
+ typeDefinitions.put(SortedSet.class,
+ new TypeDescription(SortedSet.class, Tag.SET, TreeSet.class));
+ this.loadingConfig = loadingConfig;
+ }
+
+ public void setComposer(Composer composer) {
+ this.composer = composer;
+ }
+
+ /**
+ * Check if more documents available
+ *
+ * @return true when there are more YAML documents in the stream
+ */
+ public boolean checkData() {
+ // If there are more documents available?
+ return composer.checkNode();
+ }
+
+ /**
+ * Construct and return the next document
+ *
+ * @return constructed instance
+ */
+ public Object getData() throws NoSuchElementException {
+ // Construct and return the next document.
+ if (!composer.checkNode()) {
+ throw new NoSuchElementException("No document is available.");
}
-
- /**
- * Check if more documents available
- *
- * @return true when there are more YAML documents in the stream
- */
- public boolean checkData() {
- // If there are more documents available?
- return composer.checkNode();
+ Node node = composer.getNode();
+ if (rootTag != null) {
+ node.setTag(rootTag);
}
-
- /**
- * Construct and return the next document
- *
- * @return constructed instance
- */
- public Object getData() {
- // Construct and return the next document.
- composer.checkNode();
- Node node = composer.getNode();
- if (rootTag != null) {
- node.setTag(rootTag);
- }
- return constructDocument(node);
+ return constructDocument(node);
+ }
+
+ /**
+ * Ensure that the stream contains a single document and construct it
+ *
+ * @param type the class of the instance being created
+ * @return constructed instance
+ * @throws ComposerException in case there are more documents in the stream
+ */
+ public Object getSingleData(Class<?> type) {
+ // Ensure that the stream contains a single document and construct it
+ final Node node = composer.getSingleNode();
+ if (node != null && !Tag.NULL.equals(node.getTag())) {
+ if (Object.class != type) {
+ node.setTag(new Tag(type));
+ } else if (rootTag != null) {
+ node.setTag(rootTag);
+ }
+ return constructDocument(node);
+ } else {
+ Construct construct = yamlConstructors.get(Tag.NULL);
+ return construct.construct(node);
}
-
- /**
- * Ensure that the stream contains a single document and construct it
- *
- * @return constructed instance
- * @throws ComposerException
- * in case there are more documents in the stream
- */
- public Object getSingleData(Class<?> type) {
- // Ensure that the stream contains a single document and construct it
- Node node = composer.getSingleNode();
- if (node != null) {
- if (Object.class != type) {
- node.setTag(new Tag(type));
- } else if (rootTag != null) {
- node.setTag(rootTag);
- }
- return constructDocument(node);
- }
- return null;
+ }
+
+ /**
+ * Construct complete YAML document. Call the second step in case of recursive structures. At the
+ * end cleans all the state.
+ *
+ * @param node root Node
+ * @return Java instance
+ */
+ protected final Object constructDocument(Node node) {
+ try {
+ Object data = constructObject(node);
+ fillRecursive();
+ return data;
+ } catch (RuntimeException e) {
+ if (wrappedToRootException && !(e instanceof YAMLException)) {
+ throw new YAMLException(e);
+ } else {
+ throw e;
+ }
+ } finally {
+ // clean up resources
+ constructedObjects.clear();
+ recursiveObjects.clear();
}
-
- /**
- * Construct complete YAML document. Call the second step in case of
- * recursive structures. At the end cleans all the state.
- *
- * @param node
- * root Node
- * @return Java instance
- */
- protected final Object constructDocument(Node node) {
- Object data = constructObject(node);
- fillRecursive();
- constructedObjects.clear();
- recursiveObjects.clear();
- return data;
+ }
+
+ /**
+ * Fill the recursive structures and clean the internal collections
+ */
+ private void fillRecursive() {
+ if (!maps2fill.isEmpty()) {
+ for (RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>> entry : maps2fill) {
+ RecursiveTuple<Object, Object> key_value = entry._2();
+ entry._1().put(key_value._1(), key_value._2());
+ }
+ maps2fill.clear();
}
-
- private void fillRecursive() {
- if (!maps2fill.isEmpty()) {
- for (RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>> entry : maps2fill) {
- RecursiveTuple<Object, Object> key_value = entry._2();
- entry._1().put(key_value._1(), key_value._2());
- }
- maps2fill.clear();
- }
- if (!sets2fill.isEmpty()) {
- for (RecursiveTuple<Set<Object>, Object> value : sets2fill) {
- value._1().add(value._2());
- }
- sets2fill.clear();
- }
+ if (!sets2fill.isEmpty()) {
+ for (RecursiveTuple<Set<Object>, Object> value : sets2fill) {
+ value._1().add(value._2());
+ }
+ sets2fill.clear();
}
-
- /**
- * Construct object from the specified Node. Return existing instance if the
- * node is already constructed.
- *
- * @param node
- * Node to be constructed
- * @return Java instance
- */
- protected Object constructObject(Node node) {
- if (constructedObjects.containsKey(node)) {
- return constructedObjects.get(node);
- }
- if (recursiveObjects.contains(node)) {
- throw new ConstructorException(null, null, "found unconstructable recursive node",
- node.getStartMark());
- }
- recursiveObjects.add(node);
- Construct constructor = getConstructor(node);
- Object data = constructor.construct(node);
- constructedObjects.put(node, data);
- recursiveObjects.remove(node);
- if (node.isTwoStepsConstruction()) {
- constructor.construct2ndStep(node, data);
- }
- return data;
+ }
+
+ /**
+ * Construct object from the specified Node. Return existing instance if the node is already
+ * constructed.
+ *
+ * @param node Node to be constructed
+ * @return Java instance
+ */
+ protected Object constructObject(Node node) {
+ if (constructedObjects.containsKey(node)) {
+ return constructedObjects.get(node);
}
+ return constructObjectNoCheck(node);
+ }
- /**
- * Get the constructor to construct the Node. For implicit tags if the
- * runtime class is known a dedicated Construct implementation is used.
- * Otherwise the constructor is chosen by the tag.
- *
- * @param node
- * Node to be constructed
- * @return Construct implementation for the specified node
- */
- protected Construct getConstructor(Node node) {
- if (node.useClassConstructor()) {
- return yamlClassConstructors.get(node.getNodeId());
- } else {
- Construct constructor = yamlConstructors.get(node.getTag());
- if (constructor == null) {
- for (String prefix : yamlMultiConstructors.keySet()) {
- if (node.getTag().startsWith(prefix)) {
- return yamlMultiConstructors.get(prefix);
- }
- }
- return yamlConstructors.get(null);
- }
- return constructor;
+ protected Object constructObjectNoCheck(Node node) {
+ if (recursiveObjects.contains(node)) {
+ throw new ConstructorException(null, null, "found unconstructable recursive node",
+ node.getStartMark());
+ }
+ recursiveObjects.add(node);
+ Construct constructor = getConstructor(node);
+ Object data = (constructedObjects.containsKey(node)) ? constructedObjects.get(node)
+ : constructor.construct(node);
+
+ finalizeConstruction(node, data);
+ constructedObjects.put(node, data);
+ recursiveObjects.remove(node);
+ if (node.isTwoStepsConstruction()) {
+ constructor.construct2ndStep(node, data);
+ }
+ return data;
+ }
+
+ /**
+ * Get the constructor to construct the Node. For implicit tags if the runtime class is known a
+ * dedicated Construct implementation is used. Otherwise the constructor is chosen by the tag.
+ *
+ * @param node {@link Node} to construct an instance from
+ * @return {@link Construct} implementation for the specified node
+ */
+ protected Construct getConstructor(Node node) {
+ if (node.useClassConstructor()) {
+ return yamlClassConstructors.get(node.getNodeId());
+ } else {
+ Construct constructor = yamlConstructors.get(node.getTag());
+ if (constructor == null) {
+ for (String prefix : yamlMultiConstructors.keySet()) {
+ if (node.getTag().startsWith(prefix)) {
+ return yamlMultiConstructors.get(prefix);
+ }
}
+ return yamlConstructors.get(null);
+ }
+ return constructor;
}
+ }
- protected Object constructScalar(ScalarNode node) {
- return node.getValue();
- }
+ protected String constructScalar(ScalarNode node) {
+ return node.getValue();
+ }
- protected List<Object> createDefaultList(int initSize) {
- return new ArrayList<Object>(initSize);
- }
+ // >>>> DEFAULTS >>>>
+ protected List<Object> createDefaultList(int initSize) {
+ return new ArrayList<Object>(initSize);
+ }
- protected Set<Object> createDefaultSet(int initSize) {
- return new LinkedHashSet<Object>(initSize);
- }
+ protected Set<Object> createDefaultSet(int initSize) {
+ return new LinkedHashSet<Object>(initSize);
+ }
- protected Object createArray(Class<?> type, int size) {
- return Array.newInstance(type.getComponentType(), size);
- }
+ protected Map<Object, Object> createDefaultMap(int initSize) {
+ // respect order from YAML document
+ return new LinkedHashMap<Object, Object>(initSize);
+ }
- @SuppressWarnings("unchecked")
- protected List<? extends Object> constructSequence(SequenceNode node) {
- List<Object> result;
- if (List.class.isAssignableFrom(node.getType()) && !node.getType().isInterface()) {
- // the root class may be defined (Vector for instance)
- try {
- result = (List<Object>) node.getType().newInstance();
- } catch (Exception e) {
- throw new YAMLException(e);
- }
- } else {
- result = createDefaultList(node.getValue().size());
- }
- constructSequenceStep2(node, result);
- return result;
+ protected Object createArray(Class<?> type, int size) {
+ return Array.newInstance(type.getComponentType(), size);
+ }
- }
+ // <<<< DEFAULTS <<<<
- @SuppressWarnings("unchecked")
- protected Set<? extends Object> constructSet(SequenceNode node) {
- Set<Object> result;
- if (!node.getType().isInterface()) {
- // the root class may be defined
- try {
- result = (Set<Object>) node.getType().newInstance();
- } catch (Exception e) {
- throw new YAMLException(e);
- }
- } else {
- result = createDefaultSet(node.getValue().size());
+ protected Object finalizeConstruction(Node node, Object data) {
+ final Class<? extends Object> type = node.getType();
+ if (typeDefinitions.containsKey(type)) {
+ return typeDefinitions.get(type).finalizeConstruction(data);
+ }
+ return data;
+ }
+
+ // >>>> NEW instance
+ protected Object newInstance(Node node) {
+ return newInstance(Object.class, node);
+ }
+
+ protected final Object newInstance(Class<?> ancestor, Node node) {
+ return newInstance(ancestor, node, true);
+ }
+
+ /**
+ * Tries to create a new object for the node.
+ *
+ * @param ancestor expected ancestor of the {@code node.getType()}
+ * @param node for which to create a corresponding java object
+ * @param tryDefault should default constructor to be tried when there is no corresponding
+ * {@code TypeDescription} or {@code TypeDescription.newInstance(node)} returns
+ * {@code null}.
+ *
+ * @return - a new object created for {@code node.getType()} by using corresponding
+ * TypeDescription.newInstance or default constructor. - {@code NOT_INSTANTIATED_OBJECT}
+ * in case no object has been created
+ */
+ protected Object newInstance(Class<?> ancestor, Node node, boolean tryDefault) {
+ try {
+ final Class<? extends Object> type = node.getType();
+ if (typeDefinitions.containsKey(type)) {
+ TypeDescription td = typeDefinitions.get(type);
+ final Object instance = td.newInstance(node);
+ if (instance != null) {
+ return instance;
}
- constructSequenceStep2(node, result);
- return result;
-
+ }
+
+ if (tryDefault) {
+ /*
+ * Removed <code> have InstantiationException in case of abstract type
+ */
+ if (ancestor.isAssignableFrom(type) && !Modifier.isAbstract(type.getModifiers())) {
+ java.lang.reflect.Constructor<?> c = type.getDeclaredConstructor();
+ c.setAccessible(true);
+ return c.newInstance();
+ }
+ }
+ } catch (Exception e) {
+ throw new YAMLException(e);
}
- protected Object constructArray(SequenceNode node) {
- return constructArrayStep2(node, createArray(node.getType(), node.getValue().size()));
- }
+ return NOT_INSTANTIATED_OBJECT;
+ }
- protected void constructSequenceStep2(SequenceNode node, Collection<Object> collection) {
- for (Node child : node.getValue()) {
- collection.add(constructObject(child));
- }
+ @SuppressWarnings("unchecked")
+ protected Set<Object> newSet(CollectionNode<?> node) {
+ Object instance = newInstance(Set.class, node);
+ if (instance != NOT_INSTANTIATED_OBJECT) {
+ return (Set<Object>) instance;
+ } else {
+ return createDefaultSet(node.getValue().size());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<Object> newList(SequenceNode node) {
+ Object instance = newInstance(List.class, node);
+ if (instance != NOT_INSTANTIATED_OBJECT) {
+ return (List<Object>) instance;
+ } else {
+ return createDefaultList(node.getValue().size());
}
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Map<Object, Object> newMap(MappingNode node) {
+ Object instance = newInstance(Map.class, node);
+ if (instance != NOT_INSTANTIATED_OBJECT) {
+ return (Map<Object, Object>) instance;
+ } else {
+ return createDefaultMap(node.getValue().size());
+ }
+ }
+
+ // <<<< NEW instance
+
+ // >>>> Construct => NEW, 2ndStep(filling)
+ protected List<? extends Object> constructSequence(SequenceNode node) {
+ List<Object> result = newList(node);
+ constructSequenceStep2(node, result);
+ return result;
+ }
+
+ protected Set<? extends Object> constructSet(SequenceNode node) {
+ Set<Object> result = newSet(node);
+ constructSequenceStep2(node, result);
+ return result;
+ }
+
+ protected Object constructArray(SequenceNode node) {
+ return constructArrayStep2(node, createArray(node.getType(), node.getValue().size()));
+ }
+
+ protected void constructSequenceStep2(SequenceNode node, Collection<Object> collection) {
+ for (Node child : node.getValue()) {
+ collection.add(constructObject(child));
+ }
+ }
- protected Object constructArrayStep2(SequenceNode node, Object array) {
- final Class<?> componentType = node.getType().getComponentType();
+ protected Object constructArrayStep2(SequenceNode node, Object array) {
+ final Class<?> componentType = node.getType().getComponentType();
- int index = 0;
- for (Node child : node.getValue()) {
- // Handle multi-dimensional arrays...
- if (child.getType() == Object.class) {
- child.setType(componentType);
- }
+ int index = 0;
+ for (Node child : node.getValue()) {
+ // Handle multi-dimensional arrays...
+ if (child.getType() == Object.class) {
+ child.setType(componentType);
+ }
- final Object value = constructObject(child);
+ final Object value = constructObject(child);
- if (componentType.isPrimitive()) {
- // Null values are disallowed for primitives
- if (value == null) {
- throw new NullPointerException("Unable to construct element value for " + child);
- }
+ if (componentType.isPrimitive()) {
+ // Null values are disallowed for primitives
+ if (value == null) {
+ throw new NullPointerException("Unable to construct element value for " + child);
+ }
- // Primitive arrays require quite a lot of work.
- if (byte.class.equals(componentType)) {
- Array.setByte(array, index, ((Number) value).byteValue());
+ // Primitive arrays require quite a lot of work.
+ if (byte.class.equals(componentType)) {
+ Array.setByte(array, index, ((Number) value).byteValue());
- } else if (short.class.equals(componentType)) {
- Array.setShort(array, index, ((Number) value).shortValue());
+ } else if (short.class.equals(componentType)) {
+ Array.setShort(array, index, ((Number) value).shortValue());
- } else if (int.class.equals(componentType)) {
- Array.setInt(array, index, ((Number) value).intValue());
+ } else if (int.class.equals(componentType)) {
+ Array.setInt(array, index, ((Number) value).intValue());
- } else if (long.class.equals(componentType)) {
- Array.setLong(array, index, ((Number) value).longValue());
+ } else if (long.class.equals(componentType)) {
+ Array.setLong(array, index, ((Number) value).longValue());
- } else if (float.class.equals(componentType)) {
- Array.setFloat(array, index, ((Number) value).floatValue());
+ } else if (float.class.equals(componentType)) {
+ Array.setFloat(array, index, ((Number) value).floatValue());
- } else if (double.class.equals(componentType)) {
- Array.setDouble(array, index, ((Number) value).doubleValue());
+ } else if (double.class.equals(componentType)) {
+ Array.setDouble(array, index, ((Number) value).doubleValue());
- } else if (char.class.equals(componentType)) {
- Array.setChar(array, index, ((Character) value).charValue());
+ } else if (char.class.equals(componentType)) {
+ Array.setChar(array, index, ((Character) value).charValue());
- } else if (boolean.class.equals(componentType)) {
- Array.setBoolean(array, index, ((Boolean) value).booleanValue());
+ } else if (boolean.class.equals(componentType)) {
+ Array.setBoolean(array, index, ((Boolean) value).booleanValue());
- } else {
- throw new YAMLException("unexpected primitive type");
- }
+ } else {
+ throw new YAMLException("unexpected primitive type");
+ }
- } else {
- // Non-primitive arrays can simply be assigned:
- Array.set(array, index, value);
- }
+ } else {
+ // Non-primitive arrays can simply be assigned:
+ Array.set(array, index, value);
+ }
- ++index;
+ ++index;
+ }
+ return array;
+ }
+
+ protected Set<Object> constructSet(MappingNode node) {
+ final Set<Object> set = newSet(node);
+ constructSet2ndStep(node, set);
+ return set;
+ }
+
+ protected Map<Object, Object> constructMapping(MappingNode node) {
+ final Map<Object, Object> mapping = newMap(node);
+ constructMapping2ndStep(node, mapping);
+ return mapping;
+ }
+
+ protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
+ List<NodeTuple> nodeValue = node.getValue();
+ for (NodeTuple tuple : nodeValue) {
+ Node keyNode = tuple.getKeyNode();
+ Node valueNode = tuple.getValueNode();
+ Object key = constructObject(keyNode);
+ if (key != null) {
+ try {
+ key.hashCode();// check circular dependencies
+ } catch (Exception e) {
+ throw new ConstructorException("while constructing a mapping", node.getStartMark(),
+ "found unacceptable key " + key, tuple.getKeyNode().getStartMark(), e);
}
- return array;
+ }
+ Object value = constructObject(valueNode);
+ if (keyNode.isTwoStepsConstruction()) {
+ if (loadingConfig.getAllowRecursiveKeys()) {
+ postponeMapFilling(mapping, key, value);
+ } else {
+ throw new YAMLException(
+ "Recursive key for mapping is detected but it is not configured to be allowed.");
+ }
+ } else {
+ mapping.put(key, value);
+ }
}
-
- protected Map<Object, Object> createDefaultMap() {
- // respect order from YAML document
- return new LinkedHashMap<Object, Object>();
+ }
+
+ /*
+ * if keyObject is created it 2 steps we should postpone putting it in map because it may have
+ * different hash after initialization compared to clean just created one. And map of course does
+ * not observe key hashCode changes.
+ */
+ protected void postponeMapFilling(Map<Object, Object> mapping, Object key, Object value) {
+ maps2fill.add(0, new RecursiveTuple(mapping, new RecursiveTuple(key, value)));
+ }
+
+ protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
+ List<NodeTuple> nodeValue = node.getValue();
+ for (NodeTuple tuple : nodeValue) {
+ Node keyNode = tuple.getKeyNode();
+ Object key = constructObject(keyNode);
+ if (key != null) {
+ try {
+ key.hashCode();// check circular dependencies
+ } catch (Exception e) {
+ throw new ConstructorException("while constructing a Set", node.getStartMark(),
+ "found unacceptable key " + key, tuple.getKeyNode().getStartMark(), e);
+ }
+ }
+ if (keyNode.isTwoStepsConstruction()) {
+ postponeSetFilling(set, key);
+ } else {
+ set.add(key);
+ }
}
-
- protected Set<Object> createDefaultSet() {
- // respect order from YAML document
- return new LinkedHashSet<Object>();
+ }
+
+ /*
+ * if keyObject is created it 2 steps we should postpone putting it into the set because it may
+ * have different hash after initialization compared to clean just created one. And set of course
+ * does not observe value hashCode changes.
+ */
+ protected void postponeSetFilling(Set<Object> set, Object key) {
+ sets2fill.add(0, new RecursiveTuple<Set<Object>, Object>(set, key));
+ }
+
+ public void setPropertyUtils(PropertyUtils propertyUtils) {
+ this.propertyUtils = propertyUtils;
+ explicitPropertyUtils = true;
+ Collection<TypeDescription> tds = typeDefinitions.values();
+ for (TypeDescription typeDescription : tds) {
+ typeDescription.setPropertyUtils(propertyUtils);
}
+ }
- protected Set<Object> constructSet(MappingNode node) {
- Set<Object> set = createDefaultSet();
- constructSet2ndStep(node, set);
- return set;
+ public final PropertyUtils getPropertyUtils() {
+ if (propertyUtils == null) {
+ propertyUtils = new PropertyUtils();
}
-
- protected Map<Object, Object> constructMapping(MappingNode node) {
- Map<Object, Object> mapping = createDefaultMap();
- constructMapping2ndStep(node, mapping);
- return mapping;
+ return propertyUtils;
+ }
+
+ /**
+ * Make YAML aware how to parse a custom Class. If there is no root Class assigned in constructor
+ * then the 'root' property of this definition is respected.
+ *
+ * @param definition to be added to the Constructor
+ * @return the previous value associated with <code>definition</code>, or <code>null</code> if
+ * there was no mapping for <code>definition</code>.
+ */
+ public TypeDescription addTypeDescription(TypeDescription definition) {
+ if (definition == null) {
+ throw new NullPointerException("TypeDescription is required.");
}
+ Tag tag = definition.getTag();
+ typeTags.put(tag, definition.getType());
+ definition.setPropertyUtils(getPropertyUtils());
+ return typeDefinitions.put(definition.getType(), definition);
+ }
- protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
- List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
- for (NodeTuple tuple : nodeValue) {
- Node keyNode = tuple.getKeyNode();
- Node valueNode = tuple.getValueNode();
- Object key = constructObject(keyNode);
- if (key != null) {
- try {
- key.hashCode();// check circular dependencies
- } catch (Exception e) {
- throw new ConstructorException("while constructing a mapping",
- node.getStartMark(), "found unacceptable key " + key, tuple
- .getKeyNode().getStartMark(), e);
- }
- }
- Object value = constructObject(valueNode);
- if (keyNode.isTwoStepsConstruction()) {
- /*
- * if keyObject is created it 2 steps we should postpone putting
- * it in map because it may have different hash after
- * initialization compared to clean just created one. And map of
- * course does not observe key hashCode changes.
- */
- maps2fill.add(0,
- new RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>(
- mapping, new RecursiveTuple<Object, Object>(key, value)));
- } else {
- mapping.put(key, value);
- }
- }
- }
+ private static class RecursiveTuple<T, K> {
- protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
- List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
- for (NodeTuple tuple : nodeValue) {
- Node keyNode = tuple.getKeyNode();
- Object key = constructObject(keyNode);
- if (key != null) {
- try {
- key.hashCode();// check circular dependencies
- } catch (Exception e) {
- throw new ConstructorException("while constructing a Set", node.getStartMark(),
- "found unacceptable key " + key, tuple.getKeyNode().getStartMark(), e);
- }
- }
- if (keyNode.isTwoStepsConstruction()) {
- /*
- * if keyObject is created it 2 steps we should postpone putting
- * it into the set because it may have different hash after
- * initialization compared to clean just created one. And set of
- * course does not observe value hashCode changes.
- */
- sets2fill.add(0, new RecursiveTuple<Set<Object>, Object>(set, key));
- } else {
- set.add(key);
- }
- }
+ private final T _1;
+ private final K _2;
+
+ public RecursiveTuple(T _1, K _2) {
+ this._1 = _1;
+ this._2 = _2;
}
- public void setPropertyUtils(PropertyUtils propertyUtils) {
- this.propertyUtils = propertyUtils;
- explicitPropertyUtils = true;
+ public K _2() {
+ return _2;
}
- public final PropertyUtils getPropertyUtils() {
- if (propertyUtils == null) {
- propertyUtils = new PropertyUtils();
- }
- return propertyUtils;
+ public T _1() {
+ return _1;
}
+ }
- private static class RecursiveTuple<T, K> {
- private final T _1;
- private final K _2;
+ public final boolean isExplicitPropertyUtils() {
+ return explicitPropertyUtils;
+ }
- public RecursiveTuple(T _1, K _2) {
- this._1 = _1;
- this._2 = _2;
- }
+ public boolean isAllowDuplicateKeys() {
+ return allowDuplicateKeys;
+ }
- public K _2() {
- return _2;
- }
+ public void setAllowDuplicateKeys(boolean allowDuplicateKeys) {
+ this.allowDuplicateKeys = allowDuplicateKeys;
+ }
- public T _1() {
- return _1;
- }
- }
+ public boolean isWrappedToRootException() {
+ return wrappedToRootException;
+ }
- public final boolean isExplicitPropertyUtils() {
- return explicitPropertyUtils;
- }
+ public void setWrappedToRootException(boolean wrappedToRootException) {
+ this.wrappedToRootException = wrappedToRootException;
+ }
+
+ public boolean isEnumCaseSensitive() {
+ return enumCaseSensitive;
+ }
+
+ public void setEnumCaseSensitive(boolean enumCaseSensitive) {
+ this.enumCaseSensitive = enumCaseSensitive;
+ }
}