From 72055d8449804aba74b9c50ceb056dbcf7f997d3 Mon Sep 17 00:00:00 2001 From: Joe Littlejohn Date: Mon, 9 Feb 2015 22:36:09 +0000 Subject: [PATCH] Support nested generics in javaType Closes #196 --- jsonschema2pojo-core/pom.xml | 4 ++ .../org/jsonschema2pojo/rules/ObjectRule.java | 46 ++++++------ .../org/jsonschema2pojo/util/TypeUtil.java | 70 +++++++++++++++++++ .../jsonschema2pojo/integration/TypeIT.java | 18 ++++- .../src/test/resources/ant/build.xml | 2 +- .../schema/type/genericJavaType.json | 4 ++ pom.xml | 7 +- 7 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/TypeUtil.java diff --git a/jsonschema2pojo-core/pom.xml b/jsonschema2pojo-core/pom.xml index 4e4177cc3..41f123f40 100644 --- a/jsonschema2pojo-core/pom.xml +++ b/jsonschema2pojo-core/pom.xml @@ -17,6 +17,10 @@ com.fasterxml.jackson.core jackson-databind + + com.google.code.javaparser + javaparser + com.sun.codemodel codemodel diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java index a4610ea77..acbd01491 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java @@ -16,6 +16,7 @@ package org.jsonschema2pojo.rules; +import static org.apache.commons.lang3.StringUtils.*; import static org.jsonschema2pojo.rules.PrimitiveTypes.*; import java.io.Serializable; @@ -25,19 +26,31 @@ import java.util.List; import java.util.Map; -import static org.apache.commons.lang3.StringUtils.*; -import static org.apache.commons.lang3.ArrayUtils.*; - import javax.annotation.Generated; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -import com.sun.codemodel.*; +import org.jsonschema2pojo.AnnotationStyle; import org.jsonschema2pojo.Schema; import org.jsonschema2pojo.SchemaMapper; import org.jsonschema2pojo.exception.ClassAlreadyExistsException; -import org.jsonschema2pojo.AnnotationStyle; +import org.jsonschema2pojo.util.TypeUtil; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.ClassType; +import com.sun.codemodel.JAnnotationUse; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JClassContainer; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JPackage; +import com.sun.codemodel.JType; +import com.sun.codemodel.JVar; /** * Applies the generation steps required for schemas of type "object". @@ -182,7 +195,6 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack boolean usePolymorphicDeserialization = usesPolymorphicDeserialization(node); if (node.has("javaType")) { String fqn = substringBefore(node.get("javaType").asText(), "<"); - String[] genericArguments = split(substringBetween(node.get("javaType").asText(), "<", ">"), ","); if (isPrimitive(fqn, _package.owner())) { throw new ClassAlreadyExistsException(primitiveType(fqn, _package.owner())); @@ -194,11 +206,8 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack } try { - JClass existingClass = _package.owner().ref(Thread.currentThread().getContextClassLoader().loadClass(fqn)); - - if (isNotEmpty(genericArguments)) { - existingClass = addGenericArguments(_package, existingClass, genericArguments); - } + _package.owner().ref(Thread.currentThread().getContextClassLoader().loadClass(fqn)); + JClass existingClass = TypeUtil.resolveType(_package, fqn + (node.get("javaType").asText().contains("<") ? "<" + substringAfter(node.get("javaType").asText(), "<") : "")); throw new ClassAlreadyExistsException(existingClass); } catch (ClassNotFoundException e) { @@ -226,15 +235,6 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack } - private JClass addGenericArguments(JPackage _package, JClass existingClass, String[] genericArgumentClassNames) { - JClass[] genericArgumentClasses = new JClass[genericArgumentClassNames.length]; - for (int i=0; i javaClass = Class.forName(superType.fullName()); diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/TypeUtil.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/TypeUtil.java new file mode 100644 index 000000000..b302733ea --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/TypeUtil.java @@ -0,0 +1,70 @@ +/** + * Copyright © 2010-2014 Nokia + * + * 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.jsonschema2pojo.util; + +import java.util.List; + +import org.jsonschema2pojo.exception.GenerationException; + +import japa.parser.JavaParser; +import japa.parser.ParseException; +import japa.parser.ast.body.FieldDeclaration; +import japa.parser.ast.type.ClassOrInterfaceType; +import japa.parser.ast.type.ReferenceType; +import japa.parser.ast.type.Type; + +import com.sun.codemodel.JClass; +import com.sun.codemodel.JPackage; + +public class TypeUtil { + + public static JClass resolveType(JPackage _package, String typeDefinition) { + try { + FieldDeclaration fieldDeclaration = (FieldDeclaration) JavaParser.parseBodyDeclaration(typeDefinition + " foo;"); + ClassOrInterfaceType c = (ClassOrInterfaceType) fieldDeclaration.getType().getChildrenNodes().get(0); + + return buildClass(_package, c); + } catch (ParseException e) { + throw new GenerationException(e); + } + } + + private static JClass buildClass(JPackage _package, ClassOrInterfaceType c) { + final String packagePrefix = (c.getScope() != null) ? c.getScope().toString() + "." : ""; + + JClass _class; + try { + _class = _package.owner().ref(Thread.currentThread().getContextClassLoader().loadClass(packagePrefix + c.getName())); + } catch (ClassNotFoundException e) { + _class = _package.owner().ref(packagePrefix + c.getName()); + } + + List typeArgs = c.getTypeArgs(); + if (typeArgs != null && typeArgs.size() > 0) { + JClass[] genericArgumentClasses = new JClass[typeArgs.size()]; + + for (int i=0; i classWithNameConflict = generateAndCompile("/schema/type/genericJavaType.json", "com.example").loadClass("com.example.GenericJavaType"); Method getterMethod = classWithNameConflict.getMethod("getA"); + assertThat((Class) getterMethod.getReturnType(), is(equalTo(Map.class))); + assertThat(getterMethod.getGenericReturnType(), is(instanceOf(ParameterizedType.class))); + + Type[] typeArguments = ((ParameterizedType) getterMethod.getGenericReturnType()).getActualTypeArguments(); + assertThat(typeArguments[0], is(equalTo((Type)String.class))); + assertThat(typeArguments[1], is(equalTo((Type)Integer.class))); + getterMethod = classWithNameConflict.getMethod("getB"); assertThat((Class) getterMethod.getReturnType(), is(equalTo(Map.class))); assertThat(getterMethod.getGenericReturnType(), is(instanceOf(ParameterizedType.class))); - assertThat(((ParameterizedType)getterMethod.getGenericReturnType()).getActualTypeArguments()[0], is(equalTo((Type)String.class))); - assertThat(((ParameterizedType)getterMethod.getGenericReturnType()).getActualTypeArguments()[1], is(equalTo((Type)Integer.class))); + + typeArguments = ((ParameterizedType) getterMethod.getGenericReturnType()).getActualTypeArguments(); + assertThat(typeArguments[0], is(instanceOf(ParameterizedType.class))); + assertThat(((ParameterizedType)typeArguments[0]).getActualTypeArguments().length, is(2)); + assertThat(((ParameterizedType)typeArguments[0]).getActualTypeArguments()[0], is((Type)String.class)); + assertThat(((ParameterizedType)typeArguments[0]).getActualTypeArguments()[1], is((Type)Integer.class)); + assertThat(typeArguments[1], is(instanceOf(ParameterizedType.class))); + assertThat(((ParameterizedType)typeArguments[1]).getActualTypeArguments().length, is(1)); + assertThat(((ParameterizedType)typeArguments[1]).getActualTypeArguments()[0], is(instanceOf(ParameterizedType.class))); } } diff --git a/jsonschema2pojo-integration-tests/src/test/resources/ant/build.xml b/jsonschema2pojo-integration-tests/src/test/resources/ant/build.xml index 70915bdf9..befcd1f90 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/ant/build.xml +++ b/jsonschema2pojo-integration-tests/src/test/resources/ant/build.xml @@ -44,7 +44,7 @@ - + diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/type/genericJavaType.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/type/genericJavaType.json index e0933e410..3059e098b 100644 --- a/jsonschema2pojo-integration-tests/src/test/resources/schema/type/genericJavaType.json +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/type/genericJavaType.json @@ -4,6 +4,10 @@ "a" : { "type" : "object", "javaType" : "java.util.Map" + }, + "b" : { + "type" : "object", + "javaType" : "java.util.Map, java.util.List>>" } } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 275a6aadd..65c0b1b99 100644 --- a/pom.xml +++ b/pom.xml @@ -278,11 +278,16 @@ jackson-databind 2.2.0 - + com.google.code.gson gson ${gson.version} + + com.google.code.javaparser + javaparser + 1.0.11 + com.sun.codemodel codemodel