Skip to content

Commit

Permalink
Support nested generics in javaType
Browse files Browse the repository at this point in the history
Closes #196
  • Loading branch information
joelittlejohn committed Feb 9, 2015
1 parent 179616f commit 72055d8
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 27 deletions.
4 changes: 4 additions & 0 deletions jsonschema2pojo-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.javaparser</groupId>
<artifactId>javaparser</artifactId>
</dependency>
<dependency>
<groupId>com.sun.codemodel</groupId>
<artifactId>codemodel</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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".
Expand Down Expand Up @@ -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()));
Expand All @@ -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) {
Expand Down Expand Up @@ -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<genericArgumentClasses.length; i++) {
genericArgumentClasses[i] = _package.owner().ref(genericArgumentClassNames[i]);
}

return existingClass.narrow(genericArgumentClasses);
}

private boolean isFinal(JType superType) {
try {
Class<?> javaClass = Class.forName(superType.fullName());
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Type> typeArgs = c.getTypeArgs();
if (typeArgs != null && typeArgs.size() > 0) {
JClass[] genericArgumentClasses = new JClass[typeArgs.size()];

for (int i=0; i<typeArgs.size(); i++) {
genericArgumentClasses[i] = buildClass(_package, (ClassOrInterfaceType) ((ReferenceType) typeArgs.get(i)).getType());
}

_class = _class.narrow(genericArgumentClasses);
}

return _class;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,25 @@ public void genericTypeCanBeIncludedInJavaType() throws ClassNotFoundException,
Class<?> classWithNameConflict = generateAndCompile("/schema/type/genericJavaType.json", "com.example").loadClass("com.example.GenericJavaType");

Method getterMethod = classWithNameConflict.getMethod("getA");
assertThat((Class<Map>) 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<Map>) 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)));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<fileset dir="${project.build.directory}/custom-libs">
<include name="**/*.jar" />
</fileset>
</classpath>
</classpath>
</jsonschema2pojo>

<!-- use json input -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"a" : {
"type" : "object",
"javaType" : "java.util.Map<String,Integer>"
},
"b" : {
"type" : "object",
"javaType" : "java.util.Map<java.util.Map<String,Integer>, java.util.List<java.util.List<Boolean>>>"
}
}
}
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,16 @@
<artifactId>jackson-databind</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.javaparser</groupId>
<artifactId>javaparser</artifactId>
<version>1.0.11</version>
</dependency>
<dependency>
<groupId>com.sun.codemodel</groupId>
<artifactId>codemodel</artifactId>
Expand Down

0 comments on commit 72055d8

Please sign in to comment.