Fix issue 64: ClassCastException in Representer when working with ParameterizedType
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2380dc9..4d7c2c8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,9 @@
</properties>
<body>
<release version="1.7" date="in Mercurial" description="development">
+ <action dev="py4fun" type="fix" issue="64" due-to="maxim.moschko">
+ ClassCastException in Representer when working with ParameterizedType (2010-04-25)
+ </action>
<action dev="py4fun" type="update">
Improve toString() method for Node. Since scalars cannot be recursive
they can be printed (2010-04-15)
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
index c0e74fc..618d4d1 100644
--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -21,7 +21,6 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -268,7 +267,7 @@
if (!typeDetected && valueNode.getNodeId() != NodeId.scalar) {
// only if there is no explicit TypeDescription
Type[] arguments = property.getActualTypeArguments();
- if (arguments != null && !(arguments[0] instanceof TypeVariable)) {
+ if (arguments != null) {
// TODO check non Java HotSpot(TM) Server VM
// type safe (generic) collection may contain the
// proper class
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java b/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
index b2cd78a..3f9362d 100644
--- a/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
+++ b/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
@@ -18,6 +18,7 @@
import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -37,6 +38,7 @@
property.getWriteMethod().invoke(object, value);
}
+ @SuppressWarnings("unchecked")
@Override
public Type[] getActualTypeArguments() {
if (List.class.isAssignableFrom(property.getPropertyType())
@@ -45,7 +47,13 @@
if (property.getReadMethod().getGenericReturnType() instanceof ParameterizedType) {
ParameterizedType grt = (ParameterizedType) property.getReadMethod()
.getGenericReturnType();
- return grt.getActualTypeArguments();
+ Type[] result = grt.getActualTypeArguments();
+ if (result == null || (result[0] instanceof TypeVariable)
+ || (result[0] instanceof ParameterizedType)) {
+ return null;
+ } else {
+ return result;
+ }
} else {
return null;
}
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
index 5fb4bb9..dc049cd 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
@@ -21,7 +21,6 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -179,7 +178,7 @@
@SuppressWarnings("unchecked")
protected void checkGlobalTag(Property property, Node node, Object object) {
Type[] arguments = property.getActualTypeArguments();
- if (arguments != null && !(arguments[0] instanceof TypeVariable)) {
+ if (arguments != null) {
if (node.getNodeId() == NodeId.sequence) {
// apply map tag where class is the same
Class<? extends Object> t = (Class<? extends Object>) arguments[0];
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java b/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java
new file mode 100644
index 0000000..96328f9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008-2010 Andrey Somov
+ *
+ * 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
+ *
+ * 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.issues.issue64;
+
+import java.util.List;
+
+public class MethodDesc {
+ private String name;
+ private List<Class<?>> argTypes;
+
+ public MethodDesc() {
+ }
+
+ public MethodDesc(String name, List<Class<?>> argTypes) {
+ this.name = name;
+ this.argTypes = argTypes;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<Class<?>> getArgTypes() {
+ return argTypes;
+ }
+
+ public void setArgTypes(List<Class<?>> argTypes) {
+ this.argTypes = argTypes;
+ }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java
new file mode 100644
index 0000000..8f31985
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2008-2010 Andrey Somov
+ *
+ * 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
+ *
+ * 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.issues.issue64;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Dumper;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Loader;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ParameterizedTypeTest extends TestCase {
+
+ public void testRepresenter() {
+ Yaml yaml = new Yaml(new Loader(new ClassConstructor()), new Dumper(new ClassRepresenter(),
+ new DumperOptions()));
+
+ String methodName = "testMethod";
+ List<Class<?>> argTypes = new LinkedList<Class<?>>();
+ argTypes.add(String.class);
+ argTypes.add(Integer.class);
+ argTypes.add(Boolean.class);
+ MethodDesc methodDesc = new MethodDesc(methodName, argTypes);
+
+ String out = yaml.dump(methodDesc);
+ // System.out.println(out);
+ assertEquals(
+ "!!org.yaml.snakeyaml.issues.issue64.MethodDesc\nargTypes: [!clazz 'String', !clazz 'Integer', !clazz 'Boolean']\nname: testMethod\n",
+ out);
+ MethodDesc parsed = (MethodDesc) yaml.load(out);
+ assertEquals(methodName, parsed.getName());
+ List<Class<?>> argTypes2 = parsed.getArgTypes();
+ assertEquals(3, argTypes2.size());
+ assertEquals(argTypes, argTypes2);
+ }
+
+ static class ClassRepresenter extends Representer {
+ public ClassRepresenter() {
+ this.representers.put(Class.class, new RepresentClass());
+ }
+
+ private class RepresentClass implements Represent {
+ public Node representData(Object data) {
+ Class<?> clazz = (Class<?>) data;
+ return representScalar(new Tag("!clazz"), clazz.getSimpleName());
+ }
+ }
+ }
+
+ static class ClassConstructor extends Constructor {
+ public ClassConstructor() {
+ this.yamlConstructors.put(new Tag("!clazz"), new ConstructClass());
+ }
+
+ private class ConstructClass extends AbstractConstruct {
+
+ public Object construct(Node node) {
+ String clazz = (String) constructScalar((ScalarNode) node);
+ try {
+ return Class.forName("java.lang." + clazz);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+}