import patch snakeyaml_recursive.patch from Alex Maslov
diff --git a/src/main/java/org/yaml/snakeyaml/composer/Composer.java b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
index 80f52e2..bc400d0 100644
--- a/src/main/java/org/yaml/snakeyaml/composer/Composer.java
+++ b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
@@ -4,9 +4,11 @@
package org.yaml.snakeyaml.composer;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.yaml.snakeyaml.events.AliasEvent;
import org.yaml.snakeyaml.events.Event;
@@ -35,10 +37,13 @@
private final Resolver resolver;
private final Map<String, Node> anchors;
+ private final Set<Node> recursiveNodes;
+
public Composer(Parser parser, Resolver resolver) {
this.parser = parser;
this.resolver = resolver;
this.anchors = new HashMap<String, Node>();
+ this.recursiveNodes = new HashSet<Node>();
}
public boolean checkNode() {
@@ -86,10 +91,13 @@
// Drop the DOCUMENT-END event.
parser.getEvent();
this.anchors.clear();
+// recursiveNodes = new HashSet<Node>();
+ recursiveNodes.clear();
return node;
}
private Node composeNode(Node parent, Object index) {
+ recursiveNodes.add(parent);
if (parser.checkEvent(AliasEvent.class)) {
AliasEvent event = (AliasEvent) parser.getEvent();
String anchor = event.getAnchor();
@@ -97,7 +105,11 @@
throw new ComposerException(null, null, "found undefined alias " + anchor, event
.getStartMark());
}
- return (Node) anchors.get(anchor);
+ Node result = (Node) anchors.get(anchor);
+ if(recursiveNodes.remove(result)) {
+ result.setTwoStepsConstruction(true);
+ }
+ return result;
}
NodeEvent event = (NodeEvent) parser.peekEvent();
String anchor = null;
@@ -117,6 +129,7 @@
node = composeMappingNode(anchor);
}
// resolver.ascendResolver();
+ recursiveNodes.remove(parent);
return node;
}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java b/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java
new file mode 100644
index 0000000..6060dec
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java
@@ -0,0 +1,12 @@
+package org.yaml.snakeyaml.constructor;
+
+import org.yaml.snakeyaml.nodes.Node;
+
+public abstract class AbstractConstruct implements Construct {
+
+ public void construct2ndStep(Node node, Object data) {
+ assert node.isTwodStepsConstruction();
+ throw new IllegalStateException("Not Implemented in " + getClass().getName());
+ }
+
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
index 5b250ed..0e04396 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -4,10 +4,13 @@
package org.yaml.snakeyaml.constructor;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
import org.yaml.snakeyaml.composer.Composer;
import org.yaml.snakeyaml.nodes.MappingNode;
@@ -23,13 +26,19 @@
private Composer composer;
private final Map<Node, Object> constructedObjects;
- private final Map<Node, Object> recursiveObjects;
+ private final Set<Node> recursiveObjects;
+ private final Stack<Tuple<Node, Object>> toBeConstructedAt2ndStep;
+ private final LinkedList<Tuple<Map<Object, Object>, Tuple<Object, Object>>> maps2fill;
+ private final LinkedList<Tuple<Set<Object>, Object>> sets2fill;
protected Class<? extends Object> rootType;
public BaseConstructor() {
constructedObjects = new HashMap<Node, Object>();
- recursiveObjects = new HashMap<Node, Object>();
+ recursiveObjects = new HashSet<Node>();
+ toBeConstructedAt2ndStep = new Stack<Tuple<Node, Object>>();
+ maps2fill = new LinkedList<Tuple<Map<Object, Object>, Tuple<Object, Object>>>();
+ sets2fill = new LinkedList<Tuple<Set<Object>, Object>>();
rootType = Object.class;
}
@@ -62,8 +71,36 @@
private Object constructDocument(Node node) {
Object data = constructObject(node);
+
+ while (!toBeConstructedAt2ndStep.isEmpty()) {
+ Tuple<Node, Object> toBeProcessed = toBeConstructedAt2ndStep.pop();
+ callPostCreate(toBeProcessed._1(), toBeProcessed._2());
+ }
+
+ if (!maps2fill.isEmpty()) {
+ for (Tuple<Map<Object, Object>, Tuple<Object, Object>> entry : maps2fill) {
+ Tuple<Object, Object> key_value = entry._2();
+ entry._1().put(key_value._1(), key_value._2());
+ }
+ maps2fill.clear();
+ }
+
+ if (!sets2fill.isEmpty()) {
+ for (Tuple<Set<Object>, Object> value : sets2fill) {
+ value._1().add(value._2());
+ }
+ sets2fill.clear();
+ }
+
+ // constructedObjects = new HashMap<Node, Object>();
+ // recursiveObjects = new HashSet<Node>();
+ // toBeConstructedAt2ndStep = new Stack<Tuple<Node,Object>>();
+ // maps2fill = new
+ // LinkedList<Tuple<Map<Object,Object>,Tuple<Object,Object>>>();
+ // sets2fill = new LinkedList<Tuple<Set<Object>,Object>>();
constructedObjects.clear();
recursiveObjects.clear();
+ toBeConstructedAt2ndStep.clear();
return data;
}
@@ -71,11 +108,11 @@
if (constructedObjects.containsKey(node)) {
return constructedObjects.get(node);
}
- if (recursiveObjects.containsKey(node)) {
+ if (recursiveObjects.contains(node)) {
throw new ConstructorException(null, null, "found unconstructable recursive node", node
.getStartMark());
}
- recursiveObjects.put(node, null);
+ recursiveObjects.add(node);
Object data = callConstructor(node);
constructedObjects.put(node, data);
recursiveObjects.remove(node);
@@ -83,18 +120,25 @@
}
protected Object callConstructor(Node node) {
- Object data = null;
- Construct constructor = null;
- constructor = yamlConstructors.get(node.getTag());
- if (constructor == null) {
- constructor = yamlConstructors.get(null);
- data = constructor.construct(node);
- } else {
- data = constructor.construct(node);
+ Object data = getConstructor(node).construct(node);
+ if (node.isTwodStepsConstruction()) {
+ toBeConstructedAt2ndStep.push(new Tuple<Node, Object>(node, data));
}
return data;
}
+ protected void callPostCreate(Node node, Object object) {
+ getConstructor(node).construct2ndStep(node, object);
+ }
+
+ private Construct getConstructor(Node node) {
+ Construct constructor = yamlConstructors.get(node.getTag());
+ if (constructor == null) {
+ return yamlConstructors.get(null);
+ }
+ return constructor;
+ }
+
protected Object constructScalar(ScalarNode node) {
return node.getValue();
}
@@ -104,14 +148,20 @@
}
protected List<? extends Object> constructSequence(SequenceNode node) {
- List<Node> nodeValue = (List<Node>) node.getValue();
- List<Object> result = createDefaultList(nodeValue.size());
- for (Node child : nodeValue) {
- result.add(constructObject(child));
- }
+ List<Object> result = createDefaultList(node.getValue().size());
+ constructSequenceStep2(node, result);
+ // for (Node child : nodeValue) {
+ // result.add(constructObject(child));
+ // }
return result;
}
+ protected void constructSequenceStep2(SequenceNode node, List<Object> list) {
+ for (Node child : node.getValue()) {
+ list.add(constructObject(child));
+ }
+ }
+
protected Map<Object, Object> createDefaultMap() {
// respect order from YAML document
return new LinkedHashMap<Object, Object>();
@@ -119,6 +169,11 @@
protected Map<Object, Object> constructMapping(MappingNode node) {
Map<Object, Object> mapping = createDefaultMap();
+ constructMapping2ndStep(node, mapping);
+ return mapping;
+ }
+
+ protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
List<Node[]> nodeValue = (List<Node[]>) node.getValue();
for (Node[] tuple : nodeValue) {
Node keyNode = tuple[0];
@@ -134,10 +189,48 @@
}
}
Object value = constructObject(valueNode);
- mapping.put(key, value);
+ if (keyNode.isTwodStepsConstruction()) {
+ /*
+ * 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.addFirst(new Tuple<Map<Object, Object>, Tuple<Object, Object>>(mapping,
+ new Tuple<Object, Object>(key, value)));
+ } else {
+ mapping.put(key, value);
+ }
}
- return mapping;
}
+
+ protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
+ List<Node[]> nodeValue = (List<Node[]>) node.getValue();
+ for (Node[] tuple : nodeValue) {
+ Node keyNode = tuple[0];
+ 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[0].getStartMark());
+ }
+ }
+ if (keyNode.isTwodStepsConstruction()) {
+ /*
+ * 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.addFirst(new Tuple<Set<Object>, Object>(set, key));
+ } else {
+ set.add(key);
+ }
+ }
+ }
+
// TODO protected List<Object[]> constructPairs(MappingNode node) {
// List<Object[]> pairs = new LinkedList<Object[]>();
// List<Node[]> nodeValue = (List<Node[]>) node.getValue();
@@ -149,4 +242,9 @@
// }
// return pairs;
// }
+
+ protected void pushToConstruction2ndStep(Node node, Object object) {
+ toBeConstructedAt2ndStep.push(new Tuple<Node, Object>(node, object));
+ }
+
}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Construct.java b/src/main/java/org/yaml/snakeyaml/constructor/Construct.java
index 55e27b0..c566100 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Construct.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Construct.java
@@ -7,4 +7,5 @@
public interface Construct {
public Object construct(Node node);
+ public void construct2ndStep(Node node, Object object);
}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index 3483564..aae6acb 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -14,6 +14,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.error.YAMLException;
@@ -22,6 +23,7 @@
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
@@ -90,7 +92,7 @@
return typeDefinitions.put(definition.getType(), definition);
}
- private class ConstuctYamlObject implements Construct {
+ private class ConstuctYamlObject extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
Object result = null;
@@ -111,7 +113,11 @@
case mapping:
MappingNode mnode = (MappingNode) node;
mnode.setType(cl);
- result = constructMappingNode(mnode);
+ if (node.isTwodStepsConstruction()) {
+ result = createMappingNode(mnode, cl);
+ } else {
+ result = constructMappingNode(mnode);
+ }
break;
case sequence:
SequenceNode seqNode = (SequenceNode) node;
@@ -148,6 +154,16 @@
}
return result;
}
+
+ @Override
+ public void construct2ndStep(Node node, Object object) {
+ assert node.isTwodStepsConstruction();
+
+ if (node.getNodeId() == NodeId.mapping) {
+ constructMappingNode2ndStep((MappingNode) node, object, node.getType());
+ }
+ }
+
}
@Override
@@ -167,12 +183,45 @@
if (Map.class.isAssignableFrom(node.getType())) {
result = super.constructMapping((MappingNode) node);
} else {
- result = constructMappingNode((MappingNode) node);
+ if (node.isTwodStepsConstruction()) {
+ result = createMappingNode((MappingNode) node, node.getType());
+ } else {
+ result = constructMappingNode((MappingNode) node);
+ }
}
}
return result;
}
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void callPostCreate(Node node, Object object) {
+ assert node.isTwodStepsConstruction();
+
+ if (Object.class.equals(node.getType()) || "tag:yaml.org,2002:null".equals(node.getTag())) {
+ super.callPostCreate(node, object);
+
+ } else {
+
+ switch (node.getNodeId()) {
+ case scalar:
+ // result = constructScalarNode((ScalarNode) node);
+ break;
+ case sequence:
+ constructSequenceStep2((SequenceNode) node, (List<Object>) object);
+ break;
+ default:// mapping
+ if (Map.class.isAssignableFrom(node.getType())) {
+ constructMapping2ndStep((MappingNode)node, (Map<Object, Object>) object);
+ } else if(Set.class.isAssignableFrom(node.getType())) {
+ constructSet2ndStep((MappingNode) node, (Set<Object>) object);
+ } else {
+ constructMappingNode2ndStep((MappingNode) node, object, node.getType());
+ }
+ }
+ }
+ };
+
private Object constructScalarNode(ScalarNode node) {
Class<? extends Object> type = node.getType();
Object result;
@@ -256,6 +305,16 @@
return result;
}
+ private Object createMappingNode(MappingNode mnode, Class<?> beanType) {
+ try {
+ return beanType.newInstance();
+ } catch (InstantiationException e) {
+ throw new YAMLException(e);
+ } catch (IllegalAccessException e) {
+ throw new YAMLException(e);
+ }
+ }
+
/**
* Construct JavaBean. If type safe collections are used please look at
* <code>TypeDescription</code>.
@@ -268,14 +327,11 @@
@SuppressWarnings("unchecked")
private Object constructMappingNode(MappingNode node) {
Class<? extends Object> beanType = node.getType();
- Object object;
- try {
- object = beanType.newInstance();
- } catch (InstantiationException e) {
- throw new YAMLException(e);
- } catch (IllegalAccessException e) {
- throw new YAMLException(e);
- }
+ return constructMappingNode2ndStep(node, createMappingNode(node, beanType), beanType);
+ }
+
+ private Object constructMappingNode2ndStep(MappingNode node, Object object,
+ Class<? extends Object> beanType) {
List<Node[]> nodeValue = (List<Node[]>) node.getValue();
for (Node[] tuple : nodeValue) {
ScalarNode keyNode;
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
index 450954a..7120cfe 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
@@ -11,6 +11,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -101,7 +102,17 @@
return super.constructMapping(node);
}
- private class ConstuctYamlNull implements Construct {
+ protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
+ flattenMapping(node);
+ super.constructMapping2ndStep(node, mapping);
+ }
+
+ @Override protected void constructSet2ndStep(MappingNode node, java.util.Set<Object> set) {
+ flattenMapping(node);
+ super.constructSet2ndStep(node, set);
+ };
+
+ private class ConstuctYamlNull extends AbstractConstruct {
public Object construct(Node node) {
constructScalar((ScalarNode) node);
return null;
@@ -118,14 +129,14 @@
BOOL_VALUES.put("off", Boolean.FALSE);
}
- private class ConstuctYamlBool implements Construct {
+ private class ConstuctYamlBool extends AbstractConstruct {
public Object construct(Node node) {
String val = (String) constructScalar((ScalarNode) node);
return BOOL_VALUES.get(val.toLowerCase());
}
}
- private class ConstuctYamlInt implements Construct {
+ private class ConstuctYamlInt extends AbstractConstruct {
public Object construct(Node node) {
String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
int sign = +1;
@@ -183,7 +194,7 @@
return result;
}
- private class ConstuctYamlFloat implements Construct {
+ private class ConstuctYamlFloat extends AbstractConstruct {
public Object construct(Node node) {
String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
int sign = +1;
@@ -219,7 +230,7 @@
}
}
- private class ConstuctYamlBinary implements Construct {
+ private class ConstuctYamlBinary extends AbstractConstruct {
public Object construct(Node node) {
byte[] decoded = Base64Coder.decode(constructScalar((ScalarNode) node).toString()
.toCharArray());
@@ -232,7 +243,7 @@
private final static Pattern YMD_REGEXP = Pattern
.compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$");
- private class ConstuctYamlTimestamp implements Construct {
+ private class ConstuctYamlTimestamp extends AbstractConstruct {
public Object construct(Node node) {
Matcher match = YMD_REGEXP.matcher((String) node.getValue());
if (match.matches()) {
@@ -300,7 +311,7 @@
}
}
- private class ConstuctYamlOmap implements Construct {
+ private class ConstuctYamlOmap extends AbstractConstruct {
public Object construct(Node node) {
// Note: we do not check for duplicate keys, because it's too
// CPU-expensive.
@@ -334,7 +345,7 @@
}
// Note: the same code as `construct_yaml_omap`.
- private class ConstuctYamlPairs implements Construct {
+ private class ConstuctYamlPairs extends AbstractConstruct {
public Object construct(Node node) {
// Note: we do not check for duplicate keys, because it's too
// CPU-expensive.
@@ -366,36 +377,73 @@
}
}
- private class ConstuctYamlSet implements Construct {
+ private class ConstuctYamlSet extends AbstractConstruct {
+
public Object construct(Node node) {
- Map<Object, Object> value = constructMapping((MappingNode) node);
- return value.keySet();
+ if(node.isTwodStepsConstruction()) {
+ return createDefaultMap().keySet();
+ } else {
+ return constructMapping((MappingNode) node).keySet();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void construct2ndStep(Node node, Object object) {
+ assert node.isTwodStepsConstruction();
+ constructSet2ndStep((MappingNode)node, (Set<Object>) object);
}
}
- private class ConstuctYamlStr implements Construct {
+ private class ConstuctYamlStr extends AbstractConstruct {
public Object construct(Node node) {
return (String) constructScalar((ScalarNode) node);
}
}
- private class ConstuctYamlSeq implements Construct {
+ private class ConstuctYamlSeq extends AbstractConstruct {
+ @SuppressWarnings("unchecked")
public Object construct(Node node) {
- return constructSequence((SequenceNode) node);
+ if(node.isTwodStepsConstruction()) {
+ List<Node> nodeValue = (List<Node>) node.getValue();
+ return createDefaultList(nodeValue.size());
+ } else {
+ return constructSequence((SequenceNode) node);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void construct2ndStep(Node node, Object data) {
+ assert node.isTwodStepsConstruction();
+ constructSequenceStep2((SequenceNode) node, (List<Object>) data);
}
}
- private class ConstuctYamlMap implements Construct {
+ private class ConstuctYamlMap extends AbstractConstruct {
public Object construct(Node node) {
- return constructMapping((MappingNode) node);
+ if(node.isTwodStepsConstruction()) {
+ return createDefaultMap();
+ } else {
+ return constructMapping((MappingNode) node);
+ }
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void construct2ndStep(Node node, Object object) {
+ assert node.isTwodStepsConstruction();
+ constructMapping2ndStep((MappingNode)node, (Map<Object, Object>) object);
+ }
+
}
- private class ConstuctUndefined implements Construct {
+ private class ConstuctUndefined extends AbstractConstruct {
public Object construct(Node node) {
throw new ConstructorException(null, null,
"could not determine a constructor for the tag " + node.getTag(), node
.getStartMark());
}
}
+
}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java b/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java
new file mode 100644
index 0000000..45994d0
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Tuple.java
@@ -0,0 +1,21 @@
+package org.yaml.snakeyaml.constructor;
+
+public class Tuple<T, K> {
+
+ private final T _1;
+ private final K _2;
+
+ public Tuple(T _1, K _2) {
+ assert _1 != null && _2 != null;
+ this._1 = _1;
+ this._2 = _2;
+ }
+
+ public K _2() {
+ return _2;
+ }
+
+ public T _1() {
+ return _1;
+ }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Node.java b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
index 6fbd8aa..418b126 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/Node.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
@@ -14,6 +14,7 @@
private Mark startMark;
protected Mark endMark;
private Class<? extends Object> type;
+ private boolean twoStepsConstruction;
public Node(String tag, Object value, Mark startMark, Mark endMark) {
setTag(tag);
@@ -24,6 +25,7 @@
this.startMark = startMark;
this.endMark = endMark;
this.type = Object.class;
+ this.twoStepsConstruction = false;
}
public String getTag() {
@@ -77,4 +79,12 @@
public void setType(Class<? extends Object> type) {
this.type = type;
}
+
+ public void setTwoStepsConstruction(boolean twoStepsConstruction) {
+ this.twoStepsConstruction = twoStepsConstruction;
+ }
+
+ public boolean isTwodStepsConstruction() {
+ return twoStepsConstruction;
+ }
}
diff --git a/src/test/java/examples/DiceExampleTest.java b/src/test/java/examples/DiceExampleTest.java
index 703990f..2b7a761 100644
--- a/src/test/java/examples/DiceExampleTest.java
+++ b/src/test/java/examples/DiceExampleTest.java
@@ -14,7 +14,7 @@
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
@@ -57,7 +57,7 @@
this.yamlConstructors.put("!dice", new ConstructDice());
}
- private class ConstructDice implements Construct {
+ private class ConstructDice extends AbstractConstruct {
public Object construct(Node node) {
String val = (String) constructScalar((ScalarNode) node);
int position = val.indexOf('d');
diff --git a/src/test/java/org/pyyaml/PyRecursiveTest.java b/src/test/java/org/pyyaml/PyRecursiveTest.java
index 000b36f..22c6fea 100644
--- a/src/test/java/org/pyyaml/PyRecursiveTest.java
+++ b/src/test/java/org/pyyaml/PyRecursiveTest.java
@@ -13,7 +13,6 @@
import junit.framework.TestCase;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.ConstructorException;
public class PyRecursiveTest extends TestCase {
@@ -23,27 +22,34 @@
AnInstance instance = new AnInstance(value, value);
value.put(instance, instance);
Yaml yaml = new Yaml();
- try {
- String output1 = yaml.dump(value);
- Map<AnInstance, AnInstance> value2 = (Map<AnInstance, AnInstance>) yaml.load(output1);
- assertEquals(value, value2);
- } catch (ConstructorException e) {
- // TODO recursive objects are not allowed
+ String output1 = yaml.dump(value);
+ Map<AnInstance, AnInstance> value2 = (Map<AnInstance, AnInstance>) yaml.load(output1);
+ assertEquals(value.size(), value2.size());
+ for (AnInstance tmpInstance : value2.values()) {
+ assertSame(tmpInstance.getBar(), tmpInstance.getFoo());
+ assertSame(tmpInstance.getBar(), value2);
+ assertSame(tmpInstance, value2.get(tmpInstance));
}
+ // assertEquals(value,value2);
}
@SuppressWarnings("unchecked")
public void testList() {
List value = new ArrayList();
value.add(value);
+ value.add("test");
+ value.add(new Integer(1));
+
Yaml yaml = new Yaml();
- try {
- String output1 = yaml.dump(value);
- List value2 = (List) yaml.load(output1);
- assertEquals(value, value2);
- } catch (ConstructorException e) {
- // TODO recursive objects are not allowed
- }
+ String output1 = yaml.dump(value);
+ List value2 = (List) yaml.load(output1);
+ assertSame(value2, value2.get(0));
+ // we expect self-reference as 1st element of the list
+ // let's remove self-reference and check other "simple" members of the
+ // list. otherwise assertEquals will lead us to StackOverflow
+ value.remove(0);
+ value2.remove(0);
+ assertEquals(value, value2);
}
@SuppressWarnings("unchecked")
@@ -51,12 +57,16 @@
Set value = new HashSet();
value.add(new AnInstance(value, value));
Yaml yaml = new Yaml();
- try {
- String output1 = yaml.dump(value);
- List value2 = (List) yaml.load(output1);
- assertEquals(value, value2);
- } catch (ConstructorException e) {
- // TODO recursive objects are not allowed
+ String output1 = yaml.dump(value);
+ Set<AnInstance> value2 = (Set<AnInstance>) yaml.load(output1);
+
+ assertEquals(value.size(), value2.size());
+ for (AnInstance tmpInstance : value2) {
+ assertSame(tmpInstance.getBar(), tmpInstance.getFoo());
+ assertSame(tmpInstance.getBar(), value2);
}
+ // assertEquals(value, value2);
}
+
+ // TODO write same more complex tests for recursions. maybe recursion in Arrays, bean properties...
}
diff --git a/src/test/java/org/pyyaml/PyStructureTest.java b/src/test/java/org/pyyaml/PyStructureTest.java
index 56cf7d3..719a712 100644
--- a/src/test/java/org/pyyaml/PyStructureTest.java
+++ b/src/test/java/org/pyyaml/PyStructureTest.java
@@ -14,7 +14,7 @@
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.composer.Composer;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.events.AliasEvent;
import org.yaml.snakeyaml.events.CollectionStartEvent;
@@ -248,7 +248,7 @@
this.yamlConstructors.put(null, new ConstructUndefined());
}
- private class ConstructUndefined implements Construct {
+ private class ConstructUndefined extends AbstractConstruct {
public Object construct(Node node) {
return constructScalar((ScalarNode) node);
}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java
index 2a2ce53..0c01bee 100644
--- a/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java
@@ -15,7 +15,7 @@
import junit.framework.TestCase;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
@@ -121,7 +121,7 @@
this.yamlConstructors.put("!something", new ConstructSomething());
}
- private class ConstructSomething implements Construct {
+ private class ConstructSomething extends AbstractConstruct {
public Object construct(Node node) {
// convert to upper case
String val = (String) constructScalar((ScalarNode) node);
diff --git a/src/test/java/org/yaml/snakeyaml/Example2_24Test.java b/src/test/java/org/yaml/snakeyaml/Example2_24Test.java
index fbb0e21..9d2d7a5 100644
--- a/src/test/java/org/yaml/snakeyaml/Example2_24Test.java
+++ b/src/test/java/org/yaml/snakeyaml/Example2_24Test.java
@@ -10,7 +10,7 @@
import junit.framework.TestCase;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
@@ -33,7 +33,7 @@
this.yamlConstructors.put("tag:clarkevans.com,2002:label", new ConstructLabel());
}
- private class ConstructShape implements Construct {
+ private class ConstructShape extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
SequenceNode snode = (SequenceNode) node;
@@ -43,7 +43,7 @@
}
}
- private class ConstructCircle implements Construct {
+ private class ConstructCircle extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
MappingNode mnode = (MappingNode) node;
@@ -54,7 +54,7 @@
}
}
- private class ConstructLine implements Construct {
+ private class ConstructLine extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
MappingNode mnode = (MappingNode) node;
@@ -64,7 +64,7 @@
}
}
- private class ConstructLabel implements Construct {
+ private class ConstructLabel extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
MappingNode mnode = (MappingNode) node;
diff --git a/src/test/java/org/yaml/snakeyaml/ParallelTest.java b/src/test/java/org/yaml/snakeyaml/ParallelTest.java
index 1de2f2b..015e792 100644
--- a/src/test/java/org/yaml/snakeyaml/ParallelTest.java
+++ b/src/test/java/org/yaml/snakeyaml/ParallelTest.java
@@ -46,14 +46,14 @@
System.out.println("Started: " + id);
Loader loader = new Loader(new Constructor(Invoice.class));
Yaml yaml = new Yaml(loader);
- long time1 = System.currentTimeMillis();
+ long time1 = System.nanoTime();
int cycles = 200;
for (int i = 0; i < cycles; i++) {
Invoice invoice = (Invoice) yaml.load(doc);
assertNotNull(invoice);
}
- long time2 = System.currentTimeMillis();
- float duration = (time2 - time1) / (float) cycles;
+ long time2 = System.nanoTime();
+ float duration = ((time2 - time1) / 1000000) / (float) cycles;
System.out.println("Duration of " + id + " was " + duration + " ms/load.");
progress++;
}
diff --git a/src/test/java/org/yaml/snakeyaml/StressTest.java b/src/test/java/org/yaml/snakeyaml/StressTest.java
index 708366a..bc2e4eb 100644
--- a/src/test/java/org/yaml/snakeyaml/StressTest.java
+++ b/src/test/java/org/yaml/snakeyaml/StressTest.java
@@ -17,42 +17,42 @@
}
public void testPerformance() throws IOException {
- long time1 = System.currentTimeMillis();
+ long time1 = System.nanoTime();
new Yaml(new Loader(new Constructor(Invoice.class)));
- long time2 = System.currentTimeMillis();
- float duration = time2 - time1;
+ long time2 = System.nanoTime();
+ float duration = (time2 - time1) / 1000000;
System.out.println("Init was " + duration + " ms.");
Yaml yaml = new Yaml(new Loader(new Constructor(Invoice.class)));
- time1 = System.currentTimeMillis();
+ time1 = System.nanoTime();
yaml.load(doc);
- time2 = System.currentTimeMillis();
- duration = time2 - time1;
+ time2 = System.nanoTime();
+ duration = (time2 - time1) / 1000000;
System.out.println("\nSingle load was " + duration + " ms.");
yaml = new Yaml(new Loader(new Constructor(Invoice.class)));
int[] range = new int[] { 1000, 2000 };
System.out.println("\nOne instance.");
for (int number : range) {
- time1 = System.currentTimeMillis();
+ time1 = System.nanoTime();
for (int i = 0; i < number; i++) {
yaml.load(doc);
}
- time2 = System.currentTimeMillis();
- duration = (time2 - time1) / (float) number;
+ time2 = System.nanoTime();
+ duration = ((time2 - time1) / 1000000) / (float) number;
System.out.println("Duration for r=" + number + " was " + duration + " ms/load.");
assertTrue("duration=" + duration, duration < 5);
}
System.out.println("\nMany instances.");
for (int number : range) {
- time1 = System.currentTimeMillis();
+ time1 = System.nanoTime();
for (int i = 0; i < number; i++) {
yaml = new Yaml(new Loader(new Constructor(Invoice.class)));
yaml.load(doc);
}
- time2 = System.currentTimeMillis();
- duration = (time2 - time1) / (float) number;
+ time2 = System.nanoTime();
+ duration = ((time2 - time1) / 1000000) / (float) number;
System.out.println("Duration for r=" + number + " was " + duration + " ms/load.");
assertTrue("duration=" + duration, duration < 5);
}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java b/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java
index 7309e1a..d542376 100644
--- a/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java
@@ -7,7 +7,7 @@
import java.util.List;
import java.util.Map;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.events.AliasEvent;
import org.yaml.snakeyaml.events.DocumentEndEvent;
@@ -30,7 +30,7 @@
this.yamlConstructors.put(null, new ConstructEvent());
}
- private class ConstructEvent implements Construct {
+ private class ConstructEvent extends AbstractConstruct {
@SuppressWarnings("unchecked")
public Object construct(Node node) {
diff --git a/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java b/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java
index 89f1c38..bf7b942 100644
--- a/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java
+++ b/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java
@@ -9,7 +9,7 @@
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
@@ -73,7 +73,7 @@
this.yamlConstructors.put("tag:yaml.org,2002:Dice", new ConstuctDice());
}
- private class ConstuctDice implements Construct {
+ private class ConstuctDice extends AbstractConstruct {
public Object construct(Node node) {
String val = (String) constructScalar((ScalarNode) node);
return new CustomBean(val.substring(0, 1), Integer.parseInt(val.substring(2)));
diff --git a/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java
index 8c3d56f..8878293 100644
--- a/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java
+++ b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java
@@ -16,7 +16,7 @@
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Loader;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
@@ -108,7 +108,7 @@
this.yamlConstructors.put("tag:yaml.org,2002:Phone", new ConstuctPhone());
}
- private class ConstuctPhone implements Construct {
+ private class ConstuctPhone extends AbstractConstruct {
public Object construct(Node node) {
String val = (String) constructScalar((ScalarNode) node);
return new Phone(val);