diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fac78ae7..d99d55be5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,17 @@ # Changelog +## 1.0.1 +* Check schema required array before is required flag ([#964](https://github.com/joelittlejohn/jsonschema2pojo/pull/964)) +* Features/builders as inner classes ([#953](https://github.com/joelittlejohn/jsonschema2pojo/pull/953)) + +## 1.0.0 +* Don't register output directory as a set of files, since this breaks Gradle 5.0+ ([#940](https://github.com/joelittlejohn/jsonschema2pojo/pull/940)) +* **Builder method should be named consistently with getter/setter ([#905](https://github.com/joelittlejohn/jsonschema2pojo/issues/905))** +* Objects inside array with name ending with "List" are generated as "SomeNameList" ([#780](https://github.com/joelittlejohn/jsonschema2pojo/issues/780)) + ## 1.0.0-beta1 * Add 'formatTypeMapping' config option to allow overriding types used for formats ([#923](https://github.com/joelittlejohn/jsonschema2pojo/pull/923)) -* **Enable development of context-aware rules. ([#917](https://github.com/joelittlejohn/jsonschema2pojo/pull/917))** +* **Modify Rule#apply signature to include parent node (allows context-aware rules) ([#917](https://github.com/joelittlejohn/jsonschema2pojo/pull/917))** * Add 'javaOptional' extension rule to allow individual fields to use Java Optional on getter ([#913](https://github.com/joelittlejohn/jsonschema2pojo/pull/913)) * Allow schema title to be used as class name (new useTitleAsClassname config option) ([#908](https://github.com/joelittlejohn/jsonschema2pojo/issues/908)) * Incorrect @Nullable JSR305 annotations generated when using 'required' array ([#906](https://github.com/joelittlejohn/jsonschema2pojo/issues/906)) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 65a041251..089430037 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -94,3 +94,6 @@ * Klevis Ramo * Martin Bramwell * Jan Oopkaup +* Michał Szymborski +* Duane Zamrok +* Gleb Averchuk diff --git a/README.md b/README.md index 091e93ba9..e420dcaf6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # jsonschema2pojo [![Build Status](https://travis-ci.org/joelittlejohn/jsonschema2pojo.svg?branch=master)](https://travis-ci.org/joelittlejohn/jsonschema2pojo) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.jsonschema2pojo/jsonschema2pojo/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jsonschema2pojo%22) -Current stable: **0.5.1**
-Current unstable: **1.0.0-beta1**
- _jsonschema2pojo_ generates Java types from JSON Schema (or example JSON) and can annotate those types for data-binding with Jackson 1.x, Jackson 2.x or Gson. +**_*Note:*_ there are breaking changes between 0.5.1 and 1.0.0. Check the [change log](https://github.com/joelittlejohn/jsonschema2pojo/blob/master/CHANGELOG.md). Anything marked in bold in the 1.0.0 alpha, beta and final release is a breaking change.** + ### [Try jsonschema2pojo online](http://jsonschema2pojo.org/)
or `brew install jsonschema2pojo` You can use jsonschema2pojo as a Maven plugin, an Ant task, a command line utility, a Gradle plugin or embedded within your own Java app. The [Getting Started](https://github.com/joelittlejohn/jsonschema2pojo/wiki/Getting-Started) guide will show you how. @@ -14,7 +13,7 @@ A very simple Maven example: org.jsonschema2pojo jsonschema2pojo-maven-plugin - 0.5.1 + 1.0.1 ${basedir}/src/main/resources/schema com.example.types @@ -33,9 +32,9 @@ Useful pages: * **[Getting started](https://github.com/joelittlejohn/jsonschema2pojo/wiki/Getting-Started)** * **[How to contribute](https://github.com/joelittlejohn/jsonschema2pojo/blob/master/CONTRIBUTING.md)** * [Reference](https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference) - * [Latest Javadocs](https://joelittlejohn.github.io/jsonschema2pojo/javadocs/0.5.1/) - * [Documentation for the Maven plugin](https://joelittlejohn.github.io/jsonschema2pojo/site/0.5.1/generate-mojo.html) - * [Documentation for the Ant task](https://joelittlejohn.github.io/jsonschema2pojo/site/0.5.1/Jsonschema2PojoTask.html) + * [Latest Javadocs](https://joelittlejohn.github.io/jsonschema2pojo/javadocs/1.0.1/) + * [Documentation for the Maven plugin](https://joelittlejohn.github.io/jsonschema2pojo/site/1.0.1/generate-mojo.html) + * [Documentation for the Ant task](https://joelittlejohn.github.io/jsonschema2pojo/site/1.0.1/Jsonschema2PojoTask.html) Project resources: * [Downloads](https://github.com/joelittlejohn/jsonschema2pojo/releases) diff --git a/jsonschema2pojo-ant/pom.xml b/jsonschema2pojo-ant/pom.xml index b327f204c..a14e031be 100644 --- a/jsonschema2pojo-ant/pom.xml +++ b/jsonschema2pojo-ant/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-ant diff --git a/jsonschema2pojo-ant/src/main/java/org/jsonschema2pojo/ant/Jsonschema2PojoTask.java b/jsonschema2pojo-ant/src/main/java/org/jsonschema2pojo/ant/Jsonschema2PojoTask.java index 5e0a7f625..e629b9e07 100644 --- a/jsonschema2pojo-ant/src/main/java/org/jsonschema2pojo/ant/Jsonschema2PojoTask.java +++ b/jsonschema2pojo-ant/src/main/java/org/jsonschema2pojo/ant/Jsonschema2PojoTask.java @@ -69,6 +69,10 @@ public class Jsonschema2PojoTask extends Task implements GenerationConfig { private boolean generateBuilders; + private boolean includeTypeInfo = false; + + private boolean useInnerClassBuilders = false; + private boolean includeConstructors = false; private boolean usePrimitives; @@ -300,6 +304,22 @@ public void setGenerateBuilders(boolean generateBuilders) { this.generateBuilders = generateBuilders; } + /** + * Sets the 'includeTypeInfo' property of this class. + * + * @param includeTypeInfo + * Whether to use include json type info information to class. Often required to support polymorphic type deserialization. + *

+ * By default the type information is stored in the @class property, this can be overridden in the deserializationClassProperty of the schema. + *

+ * Default: false. + * + * @see https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization + */ + public void setIncludeTypeInfo(boolean includeTypeInfo) { + this.includeTypeInfo = includeTypeInfo; + } + /** * Sets the 'usePrimitives' property of this class. * @@ -890,6 +910,12 @@ public boolean isGenerateBuilders() { return generateBuilders; } + @Override + public boolean isIncludeTypeInfo() + { + return includeTypeInfo; + } + @Override public boolean isUsePrimitives() { return usePrimitives; @@ -1201,5 +1227,18 @@ public Language getTargetLanguage() { public Map getFormatTypeMapping() { return formatTypeMapping; } - + + @Override + public boolean isUseInnerClassBuilders() { + return useInnerClassBuilders; + } + + /** + * Sets the 'useInnerClassBuilders' property of this class + * + * @param useInnerClassBuilders determines whether builders will be chainable setters or embedded classes when {@link #isGenerateBuilders()} used + */ + public void setUseInnerClassBuilders(boolean useInnerClassBuilders) { + this.useInnerClassBuilders = useInnerClassBuilders; + } } diff --git a/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html b/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html index 5b823df1b..9677d9da9 100644 --- a/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html +++ b/jsonschema2pojo-ant/src/site/Jsonschema2PojoTask.html @@ -156,6 +156,19 @@

Parameters

No (default false) + + includeTypeInfo + +

Whether to include json type information; often required to support polymorphic type handling.

+

By default the type information is stored in the @class property, this can be overridden in the deserializationClassProperty of the schema.

+ + No (default false) + + + useInnerClassBuilders + Determines whether builders will be chainable setters or embedded classes when generateBuilders is used. + No (default false) + includeGetters Whether to include getters or to omit these accessor methods and create public fields instead. diff --git a/jsonschema2pojo-cli/pom.xml b/jsonschema2pojo-cli/pom.xml index 2f61d83cd..19032dd94 100644 --- a/jsonschema2pojo-cli/pom.xml +++ b/jsonschema2pojo-cli/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-cli diff --git a/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java b/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java index f7b5f33de..861c82c72 100644 --- a/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java +++ b/jsonschema2pojo-cli/src/main/java/org/jsonschema2pojo/cli/Arguments.java @@ -63,6 +63,12 @@ public class Arguments implements GenerationConfig { @Parameter(names = { "-b", "--generate-builders" }, description = "Generate builder-style methods as well as setters") private boolean generateBuilderMethods = false; + @Parameter(names = { "--include-type-info" }, description = "Include json type info; required to support polymorphic type handling. https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization") + private boolean includeTypeInfo = false; + + @Parameter(names = { "--use-inner-class-builders" }, description = "Generate an inner class with builder-style methods") + private boolean useInnerClassBuilders = false; + @Parameter(names = { "-c", "--generate-constructors" }, description = "Generate constructors") private boolean generateConstructors = false; @@ -278,6 +284,17 @@ public boolean isGenerateBuilders() { return generateBuilderMethods; } + @Override + public boolean isIncludeTypeInfo() + { + return includeTypeInfo; + } + + @Override + public boolean isUseInnerClassBuilders() { + return useInnerClassBuilders; + } + @Override public boolean isUsePrimitives() { return usePrimitives; diff --git a/jsonschema2pojo-core/pom.xml b/jsonschema2pojo-core/pom.xml index f9ed720b2..f82ffc40e 100644 --- a/jsonschema2pojo-core/pom.xml +++ b/jsonschema2pojo-core/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-core diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractAnnotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractAnnotator.java index 9b17cc303..081cd8f92 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractAnnotator.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractAnnotator.java @@ -41,6 +41,11 @@ public AbstractAnnotator(GenerationConfig generationConfig) { this.generationConfig = generationConfig; } + @Override + public void typeInfo(JDefinedClass clazz, JsonNode schema) { + + } + @Override public void propertyOrder(JDefinedClass clazz, JsonNode propertiesNode) { } @@ -91,6 +96,11 @@ public boolean isAdditionalPropertiesSupported() { public void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, String propertyName) { } + @Override + public boolean isPolymorphicDeserializationSupported(JsonNode node) { + return false; + } + public GenerationConfig getGenerationConfig() { return generationConfig; } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractTypeInfoAwareAnnotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractTypeInfoAwareAnnotator.java new file mode 100644 index 000000000..5c3179c92 --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/AbstractTypeInfoAwareAnnotator.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2010-2017 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; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JDefinedClass; + +public abstract class AbstractTypeInfoAwareAnnotator extends AbstractAnnotator +{ + public AbstractTypeInfoAwareAnnotator(GenerationConfig generationConfig) { + super(generationConfig); + } + + @Override + public void typeInfo(JDefinedClass clazz, JsonNode node) { + if(getGenerationConfig().isIncludeTypeInfo()) { + // Have per-schema JavaTypeInfo configuration override what is defined in generation config; backward comparability + if (node.has("deserializationClassProperty")) { + String annotationName = node.get("deserializationClassProperty").asText(); + addJsonTypeInfoAnnotation(clazz, annotationName); + } else { + addJsonTypeInfoAnnotation(clazz, "@class"); + } + } else { + // per-schema JsonTypeInfo configuration + if (node.has("deserializationClassProperty")) { + String annotationName = node.get("deserializationClassProperty").asText(); + addJsonTypeInfoAnnotation(clazz, annotationName); + } + } + } + + @Override + public boolean isPolymorphicDeserializationSupported(JsonNode node) { + return getGenerationConfig().isIncludeTypeInfo() || node.has("deserializationClassProperty"); + } + + abstract protected void addJsonTypeInfoAnnotation(JDefinedClass clazz, String propertyName); +} diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Annotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Annotator.java index ee6dbf325..65498c7ad 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Annotator.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Annotator.java @@ -32,6 +32,20 @@ */ public interface Annotator { + /** + * Add the necessary annotation to dictate correct type information during + * serialization and deserialization; often required with polymorphic types. + * + * @see Jackson Docs - Polymorphic Type Handling + * + * @param clazz + * a generated pojo class, that is serialized to JSON + * @param schema + * the object schema associated with this clazz + */ + void typeInfo(JDefinedClass clazz, JsonNode schema); + /** * Add the necessary annotation to dictate correct property order during * serialization @@ -195,4 +209,6 @@ public interface Annotator { void timeField(JFieldVar field, JDefinedClass clazz, JsonNode propertyNode); void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, String propertyName); + + boolean isPolymorphicDeserializationSupported(JsonNode node); } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/CompositeAnnotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/CompositeAnnotator.java index 21d680623..393dbb96a 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/CompositeAnnotator.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/CompositeAnnotator.java @@ -43,6 +43,13 @@ public CompositeAnnotator(Annotator... annotators) { this.annotators = annotators; } + @Override + public void typeInfo(JDefinedClass clazz, JsonNode node) { + for (Annotator annotator : annotators) { + annotator.typeInfo(clazz, node); + } + } + @Override public void propertyOrder(JDefinedClass clazz, JsonNode propertiesNode) { for (Annotator annotator : annotators) { @@ -130,7 +137,17 @@ public void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, Stri } } - @Override + @Override + public boolean isPolymorphicDeserializationSupported(JsonNode node) { + for (Annotator annotator : annotators) { + if (!annotator.isPolymorphicDeserializationSupported(node)) { + return false; + } + } + return true; + } + + @Override public void dateTimeField(JFieldVar field, JDefinedClass clazz, JsonNode propertyNode) { for (Annotator annotator : annotators) { annotator.dateTimeField(field, clazz, propertyNode); diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/DefaultGenerationConfig.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/DefaultGenerationConfig.java index d295b2690..ba0886336 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/DefaultGenerationConfig.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/DefaultGenerationConfig.java @@ -38,6 +38,15 @@ public boolean isGenerateBuilders() { return false; } + /** + * @return false + */ + @Override + public boolean isIncludeTypeInfo() + { + return false; + } + /** * @return false */ @@ -444,5 +453,7 @@ public Language getTargetLanguage() { public Map getFormatTypeMapping() { return Collections.emptyMap(); } - + + @Override + public boolean isUseInnerClassBuilders() { return false; } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java index efcf91f5d..48d7f4d75 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/GenerationConfig.java @@ -43,6 +43,16 @@ public interface GenerationConfig { boolean isGenerateBuilders(); /** + * Gets the 'includeTypeInfo' configuration option. + * + * @return whether to include json type information. Commonly used to support polymorphic type deserialization. + * + * @see https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization + * + */ + boolean isIncludeTypeInfo(); + + /** * Gets the 'usePrimitives' configuration option. * * @return whether to use primitives (long, double @@ -571,4 +581,13 @@ public interface GenerationConfig { * fully qualified type name (e.g. 'java.net.URI'). */ Map getFormatTypeMapping(); + + /** + * If set to true, then the gang of four builder pattern will be used to generate builders on generated classes. Note: This property works + * in collaboration with the {@link #isGenerateBuilders()} method. If the {@link #isGenerateBuilders()} is false, + * then this property will not do anything. + * @return whether to include the gang of four builder patter on the generated classes. The default value for this is false. + */ + default boolean isUseInnerClassBuilders(){ return false;} + } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson1Annotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson1Annotator.java index cd5720a1a..bc59af115 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson1Annotator.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson1Annotator.java @@ -20,12 +20,15 @@ import java.util.LinkedHashSet; import java.util.Set; +import com.sun.codemodel.JAnnotationUse; +import org.apache.commons.lang3.StringUtils; import org.codehaus.jackson.annotate.JsonAnyGetter; import org.codehaus.jackson.annotate.JsonAnySetter; import org.codehaus.jackson.annotate.JsonCreator; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonPropertyOrder; +import org.codehaus.jackson.annotate.JsonTypeInfo; import org.codehaus.jackson.annotate.JsonValue; import org.codehaus.jackson.map.annotate.JsonDeserialize; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -42,7 +45,7 @@ * * @see http://jackson.codehaus.org/ */ -public class Jackson1Annotator extends AbstractAnnotator { +public class Jackson1Annotator extends AbstractTypeInfoAwareAnnotator { private final JsonSerialize.Inclusion inclusionLevel; @@ -140,4 +143,14 @@ public void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, Stri field.annotate(JsonIgnore.class); } + protected void addJsonTypeInfoAnnotation(JDefinedClass jclass, String propertyName) { + JAnnotationUse jsonTypeInfo = jclass.annotate(JsonTypeInfo.class); + jsonTypeInfo.param("use", JsonTypeInfo.Id.CLASS); + jsonTypeInfo.param("include", JsonTypeInfo.As.PROPERTY); + + // When not provided it will use default provided by "use" attribute + if(StringUtils.isNotBlank(propertyName)) { + jsonTypeInfo.param("property", propertyName); + } + } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson2Annotator.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson2Annotator.java index 6678e9490..d325e8740 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson2Annotator.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/Jackson2Annotator.java @@ -22,6 +22,9 @@ import java.util.LinkedHashSet; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.sun.codemodel.JAnnotationUse; +import org.apache.commons.lang3.StringUtils; import org.jsonschema2pojo.rules.FormatRule; import com.fasterxml.jackson.annotation.JsonAnyGetter; @@ -49,7 +52,7 @@ * @see https://github.com/FasterXML/jackson-annotations */ -public class Jackson2Annotator extends AbstractAnnotator { +public class Jackson2Annotator extends AbstractTypeInfoAwareAnnotator { private final JsonInclude.Include inclusionLevel; @@ -213,4 +216,15 @@ public void dateTimeField(JFieldVar field, JDefinedClass clazz, JsonNode node) { field.annotate(JsonFormat.class).param("shape", JsonFormat.Shape.STRING).param("pattern", pattern).param("timezone", timezone); } } + + protected void addJsonTypeInfoAnnotation(JDefinedClass jclass, String propertyName) { + JAnnotationUse jsonTypeInfo = jclass.annotate(JsonTypeInfo.class); + jsonTypeInfo.param("use", JsonTypeInfo.Id.CLASS); + jsonTypeInfo.param("include", JsonTypeInfo.As.PROPERTY); + + // When not provided it will use default provided by "use" attribute + if(StringUtils.isNotBlank(propertyName)) { + jsonTypeInfo.param("property", propertyName); + } + } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/ScalaSingleStreamCodeWriter.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/ScalaSingleStreamCodeWriter.java index 1f843c845..17f7691a7 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/ScalaSingleStreamCodeWriter.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/ScalaSingleStreamCodeWriter.java @@ -16,15 +16,14 @@ package org.jsonschema2pojo; -import java.io.ByteArrayOutputStream; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; - import com.mysema.scalagen.ConversionSettings; import com.mysema.scalagen.Converter; import com.sun.codemodel.JPackage; import com.sun.codemodel.writer.SingleStreamCodeWriter; +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; public class ScalaSingleStreamCodeWriter extends SingleStreamCodeWriter { diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java index 3225252d0..aaddb1a75 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/AdditionalPropertiesRule.java @@ -19,6 +19,11 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; +import org.apache.commons.collections15.CollectionUtils; import org.jsonschema2pojo.Schema; import com.fasterxml.jackson.databind.JsonNode; @@ -160,7 +165,19 @@ private JMethod addGetter(JDefinedClass jclass, JFieldVar field) { return getter; } - private void addBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { + private JMethod addBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { + + JMethod result = null; + if(ruleFactory.getGenerationConfig().isUseInnerClassBuilders()) { + result = addInnerBuilder(jclass, propertyType, field); + } else { + result = addLegacyBuilder(jclass, propertyType, field); + } + + return result; + } + + private JMethod addLegacyBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { JMethod builder = jclass.method(JMod.PUBLIC, jclass, "withAdditionalProperty"); JVar nameParam = builder.param(String.class, "name"); @@ -171,6 +188,32 @@ private void addBuilder(JDefinedClass jclass, JType propertyType, JFieldVar fiel mapInvocation.arg(nameParam); mapInvocation.arg(valueParam); body._return(JExpr._this()); + + return builder; + } + + private JMethod addInnerBuilder(JDefinedClass jclass, JType propertyType, JFieldVar field) { + Optional builderClass = StreamSupport + .stream(Spliterators.spliteratorUnknownSize(jclass.classes(), Spliterator.ORDERED), false) + .filter(definedClass -> definedClass.name().equals(getBuilderClassName(jclass))) + .findFirst(); + + JMethod builder = builderClass.get().method(JMod.PUBLIC, builderClass.get(), "withAdditionalProperty"); + + JVar nameParam = builder.param(String.class, "name"); + JVar valueParam = builder.param(propertyType, "value"); + + JBlock body = builder.body(); + JInvocation mapInvocation = body.invoke(JExpr.ref(JExpr.cast(jclass, JExpr._this().ref("instance")), field), "put"); + mapInvocation.arg(nameParam); + mapInvocation.arg(valueParam); + body._return(JExpr._this()); + + return builder; + } + + private String getBuilderClassName(JDefinedClass c) { + return ruleFactory.getNameHelper().getBuilderClassName(c); } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/BuilderRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/BuilderRule.java new file mode 100644 index 000000000..7f5095137 --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/BuilderRule.java @@ -0,0 +1,121 @@ +/** + * Copyright © 2010-2017 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.rules; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JAnnotationUse; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JConditional; +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.JTypeVar; +import com.sun.codemodel.JVar; +import java.util.Objects; +import org.jsonschema2pojo.Schema; +import org.jsonschema2pojo.util.ReflectionHelper; + +public class BuilderRule implements Rule { + + private RuleFactory ruleFactory; + private ReflectionHelper reflectionHelper; + + BuilderRule(RuleFactory ruleFactory, ReflectionHelper reflectionHelper) { + this.ruleFactory = ruleFactory; + this.reflectionHelper = reflectionHelper; + } + + @Override + public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDefinedClass instanceClass, Schema currentSchema) { + + // Create the inner class for the builder + JDefinedClass builderClass; + + try { + String builderName = ruleFactory.getNameHelper().getBuilderClassName(instanceClass); + builderClass = instanceClass._class(JMod.PUBLIC + JMod.STATIC, builderName); + } catch (JClassAlreadyExistsException e) { + return e.getExistingClass(); + } + + // Determine which builder (if any) this builder should inherit from + JClass parentBuilderClass = null; + JClass parentClass = instanceClass._extends(); + if (!(parentClass.isPrimitive() || reflectionHelper.isFinal(parentClass) || Objects.equals(parentClass.fullName(), "java.lang.Object"))) { + parentBuilderClass = reflectionHelper.getBuilderClass(parentClass); + } + + // Determine the generic type 'T' that the builder will create instances of + JTypeVar instanceType = builderClass.generify("T", instanceClass); + + // For new builders we need to create an instance variable and 'build' method + // for inheriting builders we'll receive these from the superType + if (parentBuilderClass == null) { + + // Create the instance variable + JFieldVar instanceField = builderClass.field(JMod.PROTECTED, instanceType, "instance"); + + // Create the actual "build" method + JMethod buildMethod = builderClass.method(JMod.PUBLIC, instanceType, "build"); + + JBlock body = buildMethod.body(); + JVar result = body.decl(instanceType, "result"); + body.assign(result, JExpr._this().ref(instanceField)); + body.assign(JExpr._this().ref(instanceField), JExpr._null()); + body._return(result); + + // Create the noargs builder constructor + generateNoArgsBuilderConstructor(instanceClass, builderClass); + } else { + // Declare the inheritance + builderClass._extends(parentBuilderClass); + + // Create the noargs builder constructor + generateNoArgsBuilderConstructor(instanceClass, builderClass); + } + + return builderClass; + } + + private void generateNoArgsBuilderConstructor(JDefinedClass instanceClass, JDefinedClass builderClass) { + JMethod noargsConstructor = builderClass.constructor(JMod.PUBLIC); + JAnnotationUse warningSuppression = noargsConstructor.annotate(SuppressWarnings.class); + warningSuppression.param("value", "unchecked"); + + JBlock constructorBlock = noargsConstructor.body(); + + JFieldVar instanceField = reflectionHelper.searchClassAndSuperClassesForField("instance", builderClass); + + // Determine if we need to invoke the super() method for our parent builder + JClass parentClass = builderClass._extends(); + if (!(parentClass.isPrimitive() || reflectionHelper.isFinal(parentClass) || Objects.equals(parentClass.fullName(), "java.lang.Object"))) { + constructorBlock.invoke("super"); + } + + // Only initialize the instance if the object being constructed is actually this class + // if it's a subtype then ignore the instance initialization since the subclass will initialize it + constructorBlock.directStatement("// Skip initialization when called from subclass"); + JInvocation comparison = JExpr._this().invoke("getClass").invoke("equals").arg(JExpr.dotclass(builderClass)); + JConditional ifNotSubclass = constructorBlock._if(comparison); + ifNotSubclass._then().assign(JExpr._this().ref(instanceField), JExpr.cast(instanceField.type(), JExpr._new(instanceClass))); + } + +} diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ConstructorRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ConstructorRule.java new file mode 100644 index 000000000..a1fa614fd --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ConstructorRule.java @@ -0,0 +1,240 @@ +/** + * Copyright © 2010-2017 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.rules; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JConditional; +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.JVar; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import org.jsonschema2pojo.GenerationConfig; +import org.jsonschema2pojo.Schema; +import org.jsonschema2pojo.util.NameHelper; +import org.jsonschema2pojo.util.ReflectionHelper; + +public class ConstructorRule implements Rule { + + private final RuleFactory ruleFactory; + private final ReflectionHelper reflectionHelper; + + ConstructorRule(RuleFactory ruleFactory, ReflectionHelper reflectionHelper) { + this.ruleFactory = ruleFactory; + this.reflectionHelper = reflectionHelper; + } + + @Override + public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDefinedClass instanceClass, Schema currentSchema) { + + GenerationConfig generationConfig = ruleFactory.getGenerationConfig(); + + // Determine which properties belong to that class (or its superType/parent) + LinkedHashSet classProperties = getConstructorProperties(node, generationConfig.isConstructorsRequiredPropertiesOnly()); + LinkedHashSet combinedSuperProperties = getSuperTypeConstructorPropertiesRecursive(node, currentSchema, generationConfig.isConstructorsRequiredPropertiesOnly()); + + // no properties to put in the constructor => default constructor is good enough. + if (classProperties.isEmpty() && combinedSuperProperties.isEmpty()) { + return instanceClass; + } + + // Generate the no arguments constructor + generateNoArgsConstructor(instanceClass); + + // Generate the constructor with the properties which were located + JMethod instanceConstructor = generateFieldsConstructor(instanceClass, classProperties, combinedSuperProperties); + + // If we're using InnerClassBuilder implementations then we also need to generate those + if (generationConfig.isGenerateBuilders() && generationConfig.isUseInnerClassBuilders()) { + JDefinedClass builderClass = ruleFactory.getReflectionHelper().getBuilderClass(instanceClass); + generateFieldsBuilderConstructor(builderClass, instanceClass, instanceConstructor); + } + + return instanceClass; + } + + /** + * Retrieve the list of properties to go in the constructor from node. This is all properties listed in node["properties"] if ! onlyRequired, and + * only required properties if onlyRequired. + */ + private LinkedHashSet getConstructorProperties(JsonNode node, boolean onlyRequired) { + + if (!node.has("properties")) { + return new LinkedHashSet<>(); + } + + LinkedHashSet rtn = new LinkedHashSet<>(); + Set draft4RequiredProperties = new HashSet<>(); + + // setup the set of required properties for draft4 style "required" + if (onlyRequired && node.has("required")) { + JsonNode requiredArray = node.get("required"); + if (requiredArray.isArray()) { + for (JsonNode requiredEntry : requiredArray) { + if (requiredEntry.isTextual()) { + draft4RequiredProperties.add(requiredEntry.asText()); + } + } + } + } + + NameHelper nameHelper = ruleFactory.getNameHelper(); + for (Iterator> properties = node.get("properties").fields(); properties.hasNext(); ) { + Map.Entry property = properties.next(); + + JsonNode propertyObj = property.getValue(); + if (onlyRequired) { + // draft3 style + if (propertyObj.has("required") && propertyObj.get("required").asBoolean()) { + rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); + } + + // draft4 style + if (draft4RequiredProperties.contains(property.getKey())) { + rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); + } + } else { + rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); + } + } + return rtn; + } + + /** + * Recursive, walks the schema tree and assembles a list of all properties of this schema's super schemas + */ + private LinkedHashSet getSuperTypeConstructorPropertiesRecursive(JsonNode node, Schema schema, boolean onlyRequired) { + Schema superTypeSchema = reflectionHelper.getSuperSchema(node, schema, true); + + if (superTypeSchema == null) { + return new LinkedHashSet<>(); + } + + JsonNode superSchemaNode = superTypeSchema.getContent(); + + LinkedHashSet rtn = getConstructorProperties(superSchemaNode, onlyRequired); + rtn.addAll(getSuperTypeConstructorPropertiesRecursive(superSchemaNode, superTypeSchema, onlyRequired)); + + return rtn; + } + + private void generateFieldsBuilderConstructor(JDefinedClass builderClass, JDefinedClass instanceClass, JMethod instanceConstructor) { + // Locate the instance field since we'll need it to assign a value + JFieldVar instanceField = reflectionHelper.searchClassAndSuperClassesForField("instance", builderClass); + + // Create a new method to be the builder constructor we're defining + JMethod builderConstructor = builderClass.constructor(JMod.PUBLIC); + builderConstructor.annotate(SuppressWarnings.class).param("value", "unchecked"); + JBlock constructorBlock = builderConstructor.body(); + + // The builder constructor should have the exact same parameters as the instanceConstructor + for(JVar param : instanceConstructor.params()) { + builderConstructor.param(param.type(), param.name()); + } + + // Determine if we need to invoke the super() method for our parent builder + JClass parentClass = builderClass._extends(); + if (!(parentClass.isPrimitive() || reflectionHelper.isFinal(parentClass) || Objects.equals(parentClass.fullName(), "java.lang.Object"))) { + constructorBlock.invoke("super"); + } + + // The constructor invocation will also need all the parameters passed through + JInvocation instanceConstructorInvocation = JExpr._new(instanceClass); + for(JVar param : instanceConstructor.params()) { + instanceConstructorInvocation.arg(param); + } + + // Only initialize the instance if the object being constructed is actually this class + // if it's a subtype then ignore the instance initialization since the subclass will initialize it + constructorBlock.directStatement("// Skip initialization when called from subclass"); + + JInvocation comparison = JExpr._this().invoke("getClass").invoke("equals").arg(JExpr.dotclass(builderClass)); + JConditional ifNotSubclass = constructorBlock._if(comparison); + ifNotSubclass._then().assign(JExpr._this().ref(instanceField), JExpr.cast(instanceField.type(), instanceConstructorInvocation)); + } + + private JMethod generateFieldsConstructor(JDefinedClass jclass, LinkedHashSet classProperties, LinkedHashSet combinedSuperProperties) { + // add the public constructor with property parameters + JMethod fieldsConstructor = jclass.constructor(JMod.PUBLIC); + JBlock constructorBody = fieldsConstructor.body(); + JInvocation superInvocation = constructorBody.invoke("super"); + + Map fields = jclass.fields(); + Map classFieldParams = new HashMap<>(); + + for (String property : classProperties) { + JFieldVar field = fields.get(property); + + if (field == null) { + throw new IllegalStateException("Property " + property + " hasn't been added to JDefinedClass before calling addConstructors"); + } + + fieldsConstructor.javadoc().addParam(property); + JVar param = fieldsConstructor.param(field.type(), field.name()); + constructorBody.assign(JExpr._this().ref(field), param); + classFieldParams.put(property, param); + } + + List superConstructorParams = new ArrayList<>(); + + for (String property : combinedSuperProperties) { + JFieldVar field = reflectionHelper.searchSuperClassesForField(property, jclass); + + if (field == null) { + throw new IllegalStateException("Property " + property + " hasn't been added to JDefinedClass before calling addConstructors"); + } + + JVar param = classFieldParams.get(property); + + if (param == null) { + param = fieldsConstructor.param(field.type(), field.name()); + } + + fieldsConstructor.javadoc().addParam(property); + superConstructorParams.add(param); + } + + for (JVar param : superConstructorParams) { + superInvocation.arg(param); + } + + return fieldsConstructor; + } + + private void generateNoArgsConstructor(JDefinedClass jclass) { + // add a no-args constructor for serialization purposes + JMethod noargsConstructor = jclass.constructor(JMod.PUBLIC); + noargsConstructor.javadoc().add("No args constructor for use in serialization"); + } + + + + +} diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java new file mode 100644 index 000000000..db2129599 --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/DigitsRule.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2010-2017 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.rules; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JAnnotationUse; +import com.sun.codemodel.JFieldVar; +import org.jsonschema2pojo.Schema; + +import javax.validation.constraints.Digits; +import java.util.NoSuchElementException; + +public class DigitsRule implements Rule { + + private final RuleFactory ruleFactory; + + protected DigitsRule(RuleFactory ruleFactory) { + this.ruleFactory = ruleFactory; + } + + @Override + public JFieldVar apply(String nodeName, JsonNode node, JsonNode parent, JFieldVar field, Schema currentSchema) { + + if (ruleFactory.getGenerationConfig().isIncludeJsr303Annotations() + && node.has("integerDigits") && node.has("fractionalDigits")) { + + JAnnotationUse annotation = field.annotate(Digits.class); + + annotation.param("integer", node.get("integerDigits").asInt()); + annotation.param("fraction", node.get("fractionalDigits").asInt()); + } + + return field; + } + +} diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/EnumRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/EnumRule.java index 256a20e89..7d7bf09ea 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/EnumRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/EnumRule.java @@ -134,6 +134,14 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JClassContai addEnumConstants(node.path("enum"), _enum, node.path("javaEnumNames"), backingType); addFactoryMethod(_enum, backingType); + if (node.has("title")) { + ruleFactory.getTitleRule().apply(nodeName, node.get("title"), node, _enum, schema); + } + + if (node.has("description")) { + ruleFactory.getDescriptionRule().apply(nodeName, node.get("description"), node, _enum, schema); + } + return _enum; } 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 8ec982777..d347b1695 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/ObjectRule.java @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.jsonschema2pojo.rules; -import com.fasterxml.jackson.annotation.JsonTypeInfo; +import static org.apache.commons.lang3.StringUtils.substringAfter; +import static org.apache.commons.lang3.StringUtils.substringBefore; +import static org.jsonschema2pojo.rules.PrimitiveTypes.isPrimitive; +import static org.jsonschema2pojo.rules.PrimitiveTypes.primitiveType; +import static org.jsonschema2pojo.util.TypeUtil.resolveType; + 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; @@ -29,40 +32,27 @@ import com.sun.codemodel.JExpression; import com.sun.codemodel.JFieldRef; import com.sun.codemodel.JFieldVar; -import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JOp; import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; - -import org.jsonschema2pojo.AnnotationStyle; -import org.jsonschema2pojo.Schema; -import org.jsonschema2pojo.exception.ClassAlreadyExistsException; -import org.jsonschema2pojo.exception.GenerationException; -import org.jsonschema2pojo.util.MakeUniqueClassName; -import org.jsonschema2pojo.util.NameHelper; -import org.jsonschema2pojo.util.ParcelableHelper; -import org.jsonschema2pojo.util.SerializableHelper; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.commons.lang3.StringUtils.capitalize; -import static org.apache.commons.lang3.StringUtils.substringAfter; -import static org.apache.commons.lang3.StringUtils.substringBefore; -import static org.jsonschema2pojo.rules.PrimitiveTypes.isPrimitive; -import static org.jsonschema2pojo.rules.PrimitiveTypes.primitiveType; -import static org.jsonschema2pojo.util.TypeUtil.resolveType; +import org.jsonschema2pojo.AnnotationStyle; +import org.jsonschema2pojo.Annotator; +import org.jsonschema2pojo.Schema; +import org.jsonschema2pojo.exception.ClassAlreadyExistsException; +import org.jsonschema2pojo.exception.GenerationException; +import org.jsonschema2pojo.util.ParcelableHelper; +import org.jsonschema2pojo.util.ReflectionHelper; +import org.jsonschema2pojo.util.SerializableHelper; /** * Applies the generation steps required for schemas of type "object". @@ -74,11 +64,13 @@ public class ObjectRule implements Rule { private final RuleFactory ruleFactory; + private final ReflectionHelper reflectionHelper; private final ParcelableHelper parcelableHelper; - protected ObjectRule(RuleFactory ruleFactory, ParcelableHelper parcelableHelper) { + protected ObjectRule(RuleFactory ruleFactory, ParcelableHelper parcelableHelper, ReflectionHelper reflectionHelper) { this.ruleFactory = ruleFactory; this.parcelableHelper = parcelableHelper; + this.reflectionHelper = reflectionHelper; } /** @@ -91,9 +83,8 @@ protected ObjectRule(RuleFactory ruleFactory, ParcelableHelper parcelableHelper) @Override public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _package, Schema schema) { - JType superType = getSuperType(nodeName, node, _package, schema); - - if (superType.isPrimitive() || isFinal(superType)) { + JType superType = reflectionHelper.getSuperType(nodeName, node, _package, schema); + if (superType.isPrimitive() || reflectionHelper.isFinal(superType)) { return superType; } @@ -108,10 +99,6 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _pa schema.setJavaTypeIfEmpty(jclass); - if (node.has("deserializationClassProperty")) { - addJsonTypeInfoAnnotation(jclass, node); - } - if (node.has("title")) { ruleFactory.getTitleRule().apply(nodeName, node.get("title"), node, jclass, schema); } @@ -120,6 +107,11 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _pa ruleFactory.getDescriptionRule().apply(nodeName, node.get("description"), node, jclass, schema); } + // Creates the class definition for the builder + if(ruleFactory.getGenerationConfig().isGenerateBuilders() && ruleFactory.getGenerationConfig().isUseInnerClassBuilders()){ + ruleFactory.getBuilderRule().apply(nodeName, node, parent, jclass, schema); + } + ruleFactory.getPropertiesRule().apply(nodeName, node.get("properties"), node, jclass, schema); if (node.has("javaInterfaces")) { @@ -148,7 +140,8 @@ public JType apply(String nodeName, JsonNode node, JsonNode parent, JPackage _pa } if (ruleFactory.getGenerationConfig().isIncludeConstructors()) { - addConstructors(jclass, node, schema, ruleFactory.getGenerationConfig().isConstructorsRequiredPropertiesOnly()); + ruleFactory.getConstructorRule().apply(nodeName, node, parent, jclass, schema); + } if (ruleFactory.getGenerationConfig().isSerializable()) { @@ -173,74 +166,7 @@ private void addParcelSupport(JDefinedClass jclass) { } } - /** - * Retrieve the list of properties to go in the constructor from node. This - * is all properties listed in node["properties"] if ! onlyRequired, and - * only required properties if onlyRequired. - * - * @param node - * @return - */ - private LinkedHashSet getConstructorProperties(JsonNode node, boolean onlyRequired) { - - if (!node.has("properties")) { - return new LinkedHashSet<>(); - } - - LinkedHashSet rtn = new LinkedHashSet<>(); - Set draft4RequiredProperties = new HashSet<>(); - - // setup the set of required properties for draft4 style "required" - if (onlyRequired && node.has("required")) { - JsonNode requiredArray = node.get("required"); - if (requiredArray.isArray()) { - for (JsonNode requiredEntry: requiredArray) { - if (requiredEntry.isTextual()) { - draft4RequiredProperties.add(requiredEntry.asText()); - } - } - } - } - - NameHelper nameHelper = ruleFactory.getNameHelper(); - for (Iterator> properties = node.get("properties").fields(); properties.hasNext();) { - Map.Entry property = properties.next(); - - JsonNode propertyObj = property.getValue(); - if (onlyRequired) { - // draft3 style - if (propertyObj.has("required") && propertyObj.get("required").asBoolean()) { - rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); - } - - // draft4 style - if (draft4RequiredProperties.contains(property.getKey())) { - rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); - } - } else { - rtn.add(nameHelper.getPropertyName(property.getKey(), property.getValue())); - } - } - return rtn; - } - - /** - * Recursive, walks the schema tree and assembles a list of all properties of this schema's super schemas - */ - private LinkedHashSet getSuperTypeConstructorPropertiesRecursive(JsonNode node, Schema schema, boolean onlyRequired) { - Schema superTypeSchema = getSuperSchema(node, schema, true); - - if (superTypeSchema == null) { - return new LinkedHashSet<>(); - } - - JsonNode superSchemaNode = superTypeSchema.getContent(); - LinkedHashSet rtn = getConstructorProperties(superSchemaNode, onlyRequired); - rtn.addAll(getSuperTypeConstructorPropertiesRecursive(superSchemaNode, superTypeSchema, onlyRequired)); - - return rtn; - } /** * Creates a new Java class that will be generated. @@ -265,6 +191,8 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack JDefinedClass newType; + Annotator annotator = ruleFactory.getAnnotator(); + try { if (node.has("existingJavaType")) { String fqn = substringBefore(node.get("existingJavaType").asText(), "<"); @@ -277,7 +205,8 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack throw new ClassAlreadyExistsException(existingClass); } - boolean usePolymorphicDeserialization = usesPolymorphicDeserialization(node); + boolean usePolymorphicDeserialization = annotator.isPolymorphicDeserializationSupported(node); + if (node.has("javaType")) { String fqn = node.path("javaType").asText(); @@ -301,85 +230,22 @@ private JDefinedClass createClass(String nodeName, JsonNode node, JPackage _pack } } else { if (usePolymorphicDeserialization) { - newType = _package._class(JMod.PUBLIC, getClassName(nodeName, node, _package), ClassType.CLASS); + newType = _package._class(JMod.PUBLIC, ruleFactory.getNameHelper().getUniqueClassName(nodeName, node, _package), ClassType.CLASS); } else { - newType = _package._class(getClassName(nodeName, node, _package)); + newType = _package._class(ruleFactory.getNameHelper().getUniqueClassName(nodeName, node, _package)); } } } catch (JClassAlreadyExistsException e) { throw new ClassAlreadyExistsException(e.getExistingClass()); } - ruleFactory.getAnnotator().propertyInclusion(newType, node); + annotator.typeInfo(newType, node); + annotator.propertyInclusion(newType, node); return newType; } - private boolean isFinal(JType superType) { - try { - Class javaClass = Class.forName(superType.fullName()); - return Modifier.isFinal(javaClass.getModifiers()); - } catch (ClassNotFoundException e) { - return false; - } - } - - private JType getSuperType(String nodeName, JsonNode node, JPackage jPackage, Schema schema) { - if (node.has("extends") && node.has("extendsJavaClass")) { - throw new IllegalStateException("'extends' and 'extendsJavaClass' defined simultaneously"); - } - - JType superType = jPackage.owner().ref(Object.class); - Schema superTypeSchema = getSuperSchema(node, schema, false); - if (superTypeSchema != null) { - superType = ruleFactory.getSchemaRule().apply(nodeName + "Parent", node.get("extends"), node, jPackage, superTypeSchema); - } else if (node.has("extendsJavaClass")) { - superType = resolveType(jPackage, node.get("extendsJavaClass").asText()); - } - - return superType; - } - - private Schema getSuperSchema(JsonNode node, Schema schema, boolean followRefs) { - if (node.has("extends")) { - String path; - if (schema.getId().getFragment() == null) { - path = "#extends"; - } else { - path = "#" + schema.getId().getFragment() + "/extends"; - } - - Schema superSchema = ruleFactory.getSchemaStore().create(schema, path, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); - - if (followRefs) { - superSchema = resolveSchemaRefsRecursive(superSchema); - } - - return superSchema; - } - return null; - } - - private Schema resolveSchemaRefsRecursive(Schema schema) { - JsonNode schemaNode = schema.getContent(); - if (schemaNode.has("$ref")) { - schema = ruleFactory.getSchemaStore().create(schema, schemaNode.get("$ref").asText(), ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); - return resolveSchemaRefsRecursive(schema); - } - return schema; - } - - private void addJsonTypeInfoAnnotation(JDefinedClass jclass, JsonNode node) { - if (ruleFactory.getGenerationConfig().getAnnotationStyle() == AnnotationStyle.JACKSON2) { - String annotationName = node.get("deserializationClassProperty").asText(); - JAnnotationUse jsonTypeInfo = jclass.annotate(JsonTypeInfo.class); - jsonTypeInfo.param("use", JsonTypeInfo.Id.CLASS); - jsonTypeInfo.param("include", JsonTypeInfo.As.PROPERTY); - jsonTypeInfo.param("property", annotationName); - } - } - private void addToString(JDefinedClass jclass) { Map fields = jclass.fields(); JMethod toString = jclass.method(JMod.PUBLIC, String.class, "toString"); @@ -575,98 +441,6 @@ private Map removeFieldsExcludedFromEqualsAndHashCode(Map classProperties = getConstructorProperties(node, onlyRequired); - LinkedHashSet combinedSuperProperties = getSuperTypeConstructorPropertiesRecursive(node, schema, onlyRequired); - - // no properties to put in the constructor => default constructor is good enough. - if (classProperties.isEmpty() && combinedSuperProperties.isEmpty()) { - return; - } - - // add a no-args constructor for serialization purposes - JMethod noargsConstructor = jclass.constructor(JMod.PUBLIC); - noargsConstructor.javadoc().add("No args constructor for use in serialization"); - - // add the public constructor with property parameters - JMethod fieldsConstructor = jclass.constructor(JMod.PUBLIC); - JBlock constructorBody = fieldsConstructor.body(); - JInvocation superInvocation = constructorBody.invoke("super"); - - Map fields = jclass.fields(); - Map classFieldParams = new HashMap<>(); - - for (String property : classProperties) { - JFieldVar field = fields.get(property); - - if (field == null) { - throw new IllegalStateException("Property " + property + " hasn't been added to JDefinedClass before calling addConstructors"); - } - - fieldsConstructor.javadoc().addParam(property); - JVar param = fieldsConstructor.param(field.type(), field.name()); - constructorBody.assign(JExpr._this().ref(field), param); - classFieldParams.put(property, param); - } - - List superConstructorParams = new ArrayList<>(); - - - for (String property : combinedSuperProperties) { - JFieldVar field = searchSuperClassesForField(property, jclass); - - if (field == null) { - throw new IllegalStateException("Property " + property + " hasn't been added to JDefinedClass before calling addConstructors"); - } - - JVar param = classFieldParams.get(property); - - if (param == null) { - param = fieldsConstructor.param(field.type(), field.name()); - } - - fieldsConstructor.javadoc().addParam(property); - superConstructorParams.add(param); - } - - for (JVar param : superConstructorParams) { - superInvocation.arg(param); - } - } - - private static JDefinedClass definedClassOrNullFromType(JType type) - { - if (type == null || type.isPrimitive()) - { - return null; - } - JClass fieldClass = type.boxify(); - JPackage jPackage = fieldClass._package(); - return jPackage._getClass(fieldClass.name()); - } - - /** - * This is recursive with searchClassAndSuperClassesForField - */ - private JFieldVar searchSuperClassesForField(String property, JDefinedClass jclass) { - JClass superClass = jclass._extends(); - JDefinedClass definedSuperClass = definedClassOrNullFromType(superClass); - if (definedSuperClass == null) { - return null; - } - return searchClassAndSuperClassesForField(property, definedSuperClass); - } - - private JFieldVar searchClassAndSuperClassesForField(String property, JDefinedClass jclass) { - Map fields = jclass.fields(); - JFieldVar field = fields.get(property); - if (field == null) { - return searchSuperClassesForField(property, jclass); - } - return field; - } - private void addEquals(JDefinedClass jclass, JsonNode node) { Map fields = removeFieldsExcludedFromEqualsAndHashCode(jclass.fields(), node); @@ -736,45 +510,16 @@ private void addInterfaces(JDefinedClass jclass, JsonNode javaInterfaces) { } } - private String getClassName(String nodeName, JsonNode node, JPackage _package) { - String prefix = ruleFactory.getGenerationConfig().getClassNamePrefix(); - String suffix = ruleFactory.getGenerationConfig().getClassNameSuffix(); - String fieldName = ruleFactory.getNameHelper().getClassName(nodeName, node); - String capitalizedFieldName = capitalize(fieldName); - String fullFieldName = createFullFieldName(capitalizedFieldName, prefix, suffix); - - String className = ruleFactory.getNameHelper().replaceIllegalCharacters(fullFieldName); - String normalizedName = ruleFactory.getNameHelper().normalizeName(className); - return makeUnique(normalizedName, _package); - } - - private String createFullFieldName(String nodeName, String prefix, String suffix) { - String returnString = nodeName; - if (prefix != null) { - returnString = prefix + returnString; - } - - if (suffix != null) { - returnString = returnString + suffix; - } + private boolean usesPolymorphicDeserialization(JsonNode node) { - return returnString; - } + AnnotationStyle annotationStyle = ruleFactory.getGenerationConfig().getAnnotationStyle(); - private String makeUnique(String className, JPackage _package) { - try { - JDefinedClass _class = _package._class(className); - _package.remove(_class); - return className; - } catch (JClassAlreadyExistsException e) { - return makeUnique(MakeUniqueClassName.makeUnique(className), _package); + if (annotationStyle == AnnotationStyle.JACKSON + || annotationStyle == AnnotationStyle.JACKSON1 + || annotationStyle == AnnotationStyle.JACKSON2) { + return ruleFactory.getGenerationConfig().isIncludeTypeInfo() || node.has("deserializationClassProperty"); } - } - private boolean usesPolymorphicDeserialization(JsonNode node) { - if (ruleFactory.getGenerationConfig().getAnnotationStyle() == AnnotationStyle.JACKSON2) { - return node.has("deserializationClassProperty"); - } return false; } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertiesRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertiesRule.java index 38f817ef2..f58d2009e 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertiesRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertiesRule.java @@ -93,6 +93,7 @@ private void addOverrideBuilders(JDefinedClass jclass, JDefinedClass parentJclas private void addOverrideBuilder(JDefinedClass thisJDefinedClass, JMethod parentBuilder, JVar parentParam) { + // Confirm that this class doesn't already have a builder method matching the same name as the parentBuilder if (thisJDefinedClass.getMethod(parentBuilder.name(), new JType[] {parentParam.type()}) == null) { JMethod builder = thisJDefinedClass.method(parentBuilder.mods().getValue(), thisJDefinedClass, parentBuilder.name()); diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java index d4490677e..ef19403fc 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java @@ -17,12 +17,18 @@ package org.jsonschema2pojo.rules; import com.fasterxml.jackson.databind.JsonNode; -import com.sun.codemodel.*; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JDocCommentable; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JType; +import com.sun.codemodel.JVar; import org.jsonschema2pojo.GenerationConfig; import org.jsonschema2pojo.Schema; -import static org.apache.commons.lang3.StringUtils.capitalize; - /** * Applies the schema rules that represent a property definition. @@ -90,7 +96,7 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef } if (ruleFactory.getGenerationConfig().isGenerateBuilders()) { - addBuilder(jclass, field, nodeName, node); + addBuilderMethod(jclass, field, nodeName, node); } if (node.has("pattern")) { @@ -105,6 +111,8 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef ruleFactory.getMinLengthMaxLengthRule().apply(nodeName, node, parent, field, schema); + ruleFactory.getDigitsRule().apply(nodeName, node, parent, field, schema); + if (isObject(node) || isArray(node)) { ruleFactory.getValidRule().apply(nodeName, node, parent, field, schema); } @@ -112,16 +120,10 @@ public JDefinedClass apply(String nodeName, JsonNode node, JsonNode parent, JDef return jclass; } - private boolean isRequired(String nodeName, JsonNode node, Schema schema) { - if (node.has("required")) { - final JsonNode requiredNode = node.get("required"); - return requiredNode.asBoolean(); - } - - JsonNode requiredArray = schema.getContent().get("required"); - - if (requiredArray != null) { - for (JsonNode requiredNode : requiredArray) { + private boolean hasEnumerated(Schema schema, String arrayFieldName, String nodeName) { + JsonNode array = schema.getContent().get(arrayFieldName); + if (array != null) { + for (JsonNode requiredNode : array) { if (nodeName.equals(requiredNode.asText())) return true; } @@ -130,22 +132,25 @@ private boolean isRequired(String nodeName, JsonNode node, Schema schema) { return false; } - private boolean useOptional(String nodeName, JsonNode node, Schema schema) { - if (node.has("javaOptional")) { - final JsonNode requiredNode = node.get("javaOptional"); + private boolean hasFlag(JsonNode node, String fieldName) { + if (node.has(fieldName)) { + final JsonNode requiredNode = node.get(fieldName); return requiredNode.asBoolean(); } - JsonNode javaOptionalArray = schema.getContent().get("javaOptional"); + return false; + } - if (javaOptionalArray != null) { - for (JsonNode requiredNode : javaOptionalArray) { - if (nodeName.equals(requiredNode.asText())) - return true; - } - } + private boolean isDeclaredAs(String type, String nodeName, JsonNode node, Schema schema) { + return hasEnumerated(schema, type, nodeName) || hasFlag(node, type); + } - return false; + private boolean isRequired(String nodeName, JsonNode node, Schema schema) { + return isDeclaredAs("required", nodeName, node, schema); + } + + private boolean useOptional(String nodeName, JsonNode node, Schema schema) { + return isDeclaredAs("javaOptional", nodeName, node, schema); } private void propertyAnnotations(String nodeName, JsonNode node, Schema schema, JDocCommentable generatedJavaConstruct) { @@ -235,27 +240,54 @@ private JMethod addSetter(JDefinedClass c, JFieldVar field, String jsonPropertyN return setter; } - private JMethod addBuilder(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) { - JMethod builder = c.method(JMod.PUBLIC, c, getBuilderName(jsonPropertyName, node)); + private JMethod addBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) { + JMethod result = null; + if(ruleFactory.getGenerationConfig().isUseInnerClassBuilders()) { + result = addInnerBuilderMethod(c, field, jsonPropertyName, node); + } else { + result = addLegacyBuilder(c, field, jsonPropertyName, node); + } + return result; + } - JVar param = builder.param(field.type(), field.name()); - JBlock body = builder.body(); - body.assign(JExpr._this().ref(field), param); - body._return(JExpr._this()); + private JMethod addLegacyBuilder(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) { + JMethod builder = c.method(JMod.PUBLIC, c, getBuilderName(jsonPropertyName, node)); - return builder; - } + JVar param = builder.param(field.type(), field.name()); + JBlock body = builder.body(); + body.assign(JExpr._this().ref(field), param); + body._return(JExpr._this()); - private String getBuilderName(String propertyName, JsonNode node) { - return ruleFactory.getNameHelper().getBuilderName(propertyName, node); - } + return builder; + } - private String getSetterName(String propertyName, JsonNode node) { - return ruleFactory.getNameHelper().getSetterName(propertyName, node); - } + private JMethod addInnerBuilderMethod(JDefinedClass c, JFieldVar field, String jsonPropertyName, JsonNode node) { + JDefinedClass builderClass = ruleFactory.getReflectionHelper().getBuilderClass(c); - private String getGetterName(String propertyName, JType type, JsonNode node) { - return ruleFactory.getNameHelper().getGetterName(propertyName, type, node); - } + JMethod builderMethod = builderClass.method(JMod.PUBLIC, builderClass, getBuilderName(jsonPropertyName, node)); + + JVar param = builderMethod.param(field.type(), field.name()); + JBlock body = builderMethod.body(); + body.assign(JExpr.ref(JExpr.cast(c, JExpr._this().ref("instance")), field), param); + body._return(JExpr._this()); + + return builderMethod; + } + + private String getBuilderClassName(JDefinedClass c) { + return ruleFactory.getNameHelper().getBuilderClassName(c); + } + + private String getBuilderName(String propertyName, JsonNode node) { + return ruleFactory.getNameHelper().getBuilderName(propertyName, node); + } + + private String getSetterName(String propertyName, JsonNode node) { + return ruleFactory.getNameHelper().getSetterName(propertyName, node); + } + + private String getGetterName(String propertyName, JType type, JsonNode node) { + return ruleFactory.getNameHelper().getGetterName(propertyName, type, node); + } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java index ca1342e27..d9e904bc4 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/RuleFactory.java @@ -32,6 +32,8 @@ import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; +import org.jsonschema2pojo.util.ReflectionHelper; +import sun.reflect.Reflection; /** * Provides factory/creation methods for the code generation rules. @@ -39,6 +41,7 @@ public class RuleFactory { private NameHelper nameHelper; + private ReflectionHelper reflectionHelper; private GenerationConfig generationConfig; private Annotator annotator; private SchemaStore schemaStore; @@ -61,6 +64,7 @@ public RuleFactory(GenerationConfig generationConfig, Annotator annotator, Schem this.annotator = annotator; this.schemaStore = schemaStore; this.nameHelper = new NameHelper(generationConfig); + this.reflectionHelper = new ReflectionHelper(this); } /** @@ -119,7 +123,17 @@ public Rule getFormatRule() { * @return a schema rule that can handle the "object" declaration. */ public Rule getObjectRule() { - return new ObjectRule(this, new ParcelableHelper()); + return new ObjectRule(this, new ParcelableHelper(), reflectionHelper); + } + + /** + * Provides a rule instance that should be applied to add constructors to a generated type + * + * @return a schema rule that can handle the "object" declaration. + */ + public Rule getConstructorRule() + { + return new ConstructorRule(this, reflectionHelper); } /** @@ -258,6 +272,17 @@ public Rule getMinLengthMaxLengthRule() { return new MinLengthMaxLengthRule(this); } + /** + * Provides a rule instance that should be applied when a property + * declaration is found in the schema, to assign he digits validation + * on that property. + * + * @return a schema rule that can handle the "digits" declaration. + */ + public Rule getDigitsRule() { + return new DigitsRule(this); + } + /** * Provides a rule instance that should be applied when a "pattern" * declaration is found in the schema for a property. @@ -356,6 +381,11 @@ public NameHelper getNameHelper() { return nameHelper; } + public ReflectionHelper getReflectionHelper() { + return reflectionHelper; + } + + /** * Provides a rule instance that should be applied when a "media" * declaration is found in the schema. @@ -374,6 +404,10 @@ public Rule getDynamicPropertiesRule() { return new DynamicPropertiesRule(this); } + public Rule getBuilderRule(){ + return new BuilderRule(this, reflectionHelper); + } + public Rule getJavaNameRule() { return new JavaNameRule(); } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java index 6a7c9c5a0..92f958cb5 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/TypeRule.java @@ -1,17 +1,14 @@ /** * Copyright © 2010-2017 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 + * 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.jsonschema2pojo.rules; @@ -33,155 +30,145 @@ /** * Applies the "type" schema rule. * - * @see http:/ - * /tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1 + * @see http:/ /tools.ietf.org/html/draft-zyp-json-schema-03#section-5.1 */ public class TypeRule implements Rule { - private static final String DEFAULT_TYPE_NAME = "any"; - - private final RuleFactory ruleFactory; - - protected TypeRule(RuleFactory ruleFactory) { - this.ruleFactory = ruleFactory; + private static final String DEFAULT_TYPE_NAME = "any"; + + private final RuleFactory ruleFactory; + + protected TypeRule(RuleFactory ruleFactory) { + this.ruleFactory = ruleFactory; + } + + /** + * Applies this schema rule to take the required code generation steps. + *

+ * When applied, this rule reads the details of the given node to determine the appropriate Java type to return. This may be a newly generated type, + * it may be a primitive type or other type such as {@link java.lang.String} or {@link java.lang.Object}. + *

+ * JSON schema types and their Java type equivalent: + *

    + *
  • "type":"any" => {@link java.lang.Object} + *
  • "type":"array" => Either {@link java.util.Set} or + * {@link java.util.List}, see {@link ArrayRule} + *
  • "type":"boolean" => boolean + *
  • "type":"integer" => int + *
  • "type":"null" => {@link java.lang.Object} + *
  • "type":"number" => double + *
  • "type":"object" => Generated type (see {@link ObjectRule}) + *
  • "type":"string" => {@link java.lang.String} (or alternative based + * on presence of "format", see {@link FormatRule}) + *
+ * + * @param nodeName the name of the node for which this "type" rule applies + * @param node the node for which this "type" rule applies + * @param parent the parent node + * @param jClassContainer the package into which any newly generated type may be placed + * @return the Java type which, after reading the details of the given schema node, most appropriately matches the "type" specified + */ + @Override + public JType apply(String nodeName, JsonNode node, JsonNode parent, JClassContainer jClassContainer, Schema schema) { + + String propertyTypeName = getTypeName(node); + + JType type; + + if (propertyTypeName.equals("object") || node.has("properties") && node.path("properties").size() > 0) { + + type = ruleFactory.getObjectRule().apply(nodeName, node, parent, jClassContainer.getPackage(), schema); + } else if (node.has("existingJavaType")) { + String typeName = node.path("existingJavaType").asText(); + + if (isPrimitive(typeName, jClassContainer.owner())) { + type = primitiveType(typeName, jClassContainer.owner()); + } else { + type = resolveType(jClassContainer, typeName); + } + } else if (propertyTypeName.equals("string")) { + + type = jClassContainer.owner().ref(String.class); + } else if (propertyTypeName.equals("number")) { + + type = getNumberType(jClassContainer.owner(), ruleFactory.getGenerationConfig()); + } else if (propertyTypeName.equals("integer")) { + + type = getIntegerType(jClassContainer.owner(), node, ruleFactory.getGenerationConfig()); + } else if (propertyTypeName.equals("boolean")) { + + type = unboxIfNecessary(jClassContainer.owner().ref(Boolean.class), ruleFactory.getGenerationConfig()); + } else if (propertyTypeName.equals("array")) { + + type = ruleFactory.getArrayRule().apply(nodeName, node, parent, jClassContainer.getPackage(), schema); + } else { + + type = jClassContainer.owner().ref(Object.class); } - /** - * Applies this schema rule to take the required code generation steps. - *

- * When applied, this rule reads the details of the given node to determine - * the appropriate Java type to return. This may be a newly generated type, - * it may be a primitive type or other type such as {@link java.lang.String} - * or {@link java.lang.Object}. - *

- * JSON schema types and their Java type equivalent: - *

    - *
  • "type":"any" => {@link java.lang.Object} - *
  • "type":"array" => Either {@link java.util.Set} or - * {@link java.util.List}, see {@link ArrayRule} - *
  • "type":"boolean" => boolean - *
  • "type":"integer" => int - *
  • "type":"null" => {@link java.lang.Object} - *
  • "type":"number" => double - *
  • "type":"object" => Generated type (see {@link ObjectRule}) - *
  • "type":"string" => {@link java.lang.String} (or alternative based - * on presence of "format", see {@link FormatRule}) - *
- * - * @param nodeName - * the name of the node for which this "type" rule applies - * @param node - * the node for which this "type" rule applies - * @param parent - * the parent node - * @param jClassContainer - * the package into which any newly generated type may be placed - * @return the Java type which, after reading the details of the given - * schema node, most appropriately matches the "type" specified - */ - @Override - public JType apply(String nodeName, JsonNode node, JsonNode parent, JClassContainer jClassContainer, Schema schema) { - - String propertyTypeName = getTypeName(node); - - JType type; - - if (propertyTypeName.equals("object") || node.has("properties") && node.path("properties").size() > 0) { - - type = ruleFactory.getObjectRule().apply(nodeName, node, parent, jClassContainer.getPackage(), schema); - } else if (node.has("existingJavaType")) { - String typeName = node.path("existingJavaType").asText(); - - if (isPrimitive(typeName, jClassContainer.owner())) { - type = primitiveType(typeName, jClassContainer.owner()); - } else { - type = resolveType(jClassContainer, typeName); - } - } else if (propertyTypeName.equals("string")) { - - type = jClassContainer.owner().ref(String.class); - } else if (propertyTypeName.equals("number")) { - - type = getNumberType(jClassContainer.owner(), ruleFactory.getGenerationConfig()); - } else if (propertyTypeName.equals("integer")) { - - type = getIntegerType(jClassContainer.owner(), node, ruleFactory.getGenerationConfig()); - } else if (propertyTypeName.equals("boolean")) { - - type = unboxIfNecessary(jClassContainer.owner().ref(Boolean.class), ruleFactory.getGenerationConfig()); - } else if (propertyTypeName.equals("array")) { - - type = ruleFactory.getArrayRule().apply(nodeName, node, parent, jClassContainer.getPackage(), schema); - } else { - - type = jClassContainer.owner().ref(Object.class); - } - - if (!node.has("javaType") && !node.has("existingJavaType") && node.has("format")) { - type = ruleFactory.getFormatRule().apply(nodeName, node.get("format"), node, type, schema); - } else if (!node.has("javaType") && !node.has("existingJavaType") && propertyTypeName.equals("string") && node.has("media")) { - type = ruleFactory.getMediaRule().apply(nodeName, node.get("media"), node, type, schema); - } - - return type; + if (!node.has("javaType") && !node.has("existingJavaType") && node.has("format")) { + type = ruleFactory.getFormatRule().apply(nodeName, node.get("format"), node, type, schema); + } else if (!node.has("javaType") && !node.has("existingJavaType") && propertyTypeName.equals("string") && node.has("media")) { + type = ruleFactory.getMediaRule().apply(nodeName, node.get("media"), node, type, schema); } - private String getTypeName(JsonNode node) { - if (node.has("type") && node.get("type").isArray() && node.get("type").size() > 0) { - for (JsonNode jsonNode : node.get("type")) { - String typeName = jsonNode.asText(); - if (!typeName.equals("null")) { - return typeName; - } - } - } + return type; + } - if (node.has("type") && node.get("type").isTextual()) { - return node.get("type").asText(); + private String getTypeName(JsonNode node) { + if (node.has("type") && node.get("type").isArray() && node.get("type").size() > 0) { + for (JsonNode jsonNode : node.get("type")) { + String typeName = jsonNode.asText(); + if (!typeName.equals("null")) { + return typeName; } - - return DEFAULT_TYPE_NAME; + } } - private JType unboxIfNecessary(JType type, GenerationConfig config) { - if (config.isUsePrimitives()) { - return type.unboxify(); - } else { - return type; - } + if (node.has("type") && node.get("type").isTextual()) { + return node.get("type").asText(); } - /** - * Returns the JType for an integer field. Handles type lookup and unboxing. - */ - private JType getIntegerType(JCodeModel owner, JsonNode node, GenerationConfig config) { - - if (config.isUseBigIntegers()) { - return unboxIfNecessary(owner.ref(BigInteger.class), config); - } else if (config.isUseLongIntegers() || - node.has("minimum") && node.get("minimum").isLong() || - node.has("maximum") && node.get("maximum").isLong()) { - return unboxIfNecessary(owner.ref(Long.class), config); - } else { - return unboxIfNecessary(owner.ref(Integer.class), config); - } + return DEFAULT_TYPE_NAME; + } + private JType unboxIfNecessary(JType type, GenerationConfig config) { + if (config.isUsePrimitives()) { + return type.unboxify(); + } else { + return type; + } + } + + /** + * Returns the JType for an integer field. Handles type lookup and unboxing. + */ + private JType getIntegerType(JCodeModel owner, JsonNode node, GenerationConfig config) { + + if (config.isUseBigIntegers()) { + return unboxIfNecessary(owner.ref(BigInteger.class), config); + } else if (config.isUseLongIntegers() || node.has("minimum") && node.get("minimum").isLong() || node.has("maximum") && node.get("maximum") + .isLong()) { + return unboxIfNecessary(owner.ref(Long.class), config); + } else { + return unboxIfNecessary(owner.ref(Integer.class), config); } - /** - * Returns the JType for a number field. Handles type lookup and unboxing. - */ - private JType getNumberType(JCodeModel owner, GenerationConfig config) { - - if (config.isUseBigDecimals()) { - return unboxIfNecessary(owner.ref(BigDecimal.class), config); - } else if (config.isUseDoubleNumbers()) { - return unboxIfNecessary(owner.ref(Double.class), config); - } else { - return unboxIfNecessary(owner.ref(Float.class), config); - } + } + + /** + * Returns the JType for a number field. Handles type lookup and unboxing. + */ + private JType getNumberType(JCodeModel owner, GenerationConfig config) { + if (config.isUseBigDecimals()) { + return unboxIfNecessary(owner.ref(BigDecimal.class), config); + } else if (config.isUseDoubleNumbers()) { + return unboxIfNecessary(owner.ref(Double.class), config); + } else { + return unboxIfNecessary(owner.ref(Float.class), config); } + } + } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/NameHelper.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/NameHelper.java index d3a4cb4da..0a519ab36 100644 --- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/NameHelper.java +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/NameHelper.java @@ -16,15 +16,21 @@ package org.jsonschema2pojo.util; -import static java.lang.Character.*; -import static javax.lang.model.SourceVersion.*; -import static org.apache.commons.lang3.StringUtils.*; - -import org.apache.commons.lang3.text.WordUtils; -import org.jsonschema2pojo.GenerationConfig; +import static java.lang.Character.isDigit; +import static java.lang.Character.toLowerCase; +import static javax.lang.model.SourceVersion.isKeyword; +import static org.apache.commons.lang3.StringUtils.capitalize; +import static org.apache.commons.lang3.StringUtils.containsAny; +import static org.apache.commons.lang3.StringUtils.remove; import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; +import org.apache.commons.lang3.text.WordUtils; +import org.jsonschema2pojo.GenerationConfig; public class NameHelper { @@ -213,4 +219,46 @@ private String getPropertyNameForAccessor(String jsonPropertyName, JsonNode node jsonPropertyName = capitalizeTrailingWords(jsonPropertyName); return jsonPropertyName; } + + public String getBuilderClassName(JClass outterClass) { + return outterClass.name() + "Builder"; + } + + public String getUniqueClassName(String nodeName, JsonNode node, JPackage _package) { + return makeUnique(getClassName(nodeName, node, _package), _package); + } + + public String getClassName(String nodeName, JsonNode node, JPackage _package) { + String prefix = generationConfig.getClassNamePrefix(); + String suffix = generationConfig.getClassNameSuffix(); + String fieldName = getClassName(nodeName, node); + String capitalizedFieldName = capitalize(fieldName); + String fullFieldName = createFullFieldName(capitalizedFieldName, prefix, suffix); + + String className = replaceIllegalCharacters(fullFieldName); + return normalizeName(className); + } + + private String createFullFieldName(String nodeName, String prefix, String suffix) { + String returnString = nodeName; + if (prefix != null) { + returnString = prefix + returnString; + } + + if (suffix != null) { + returnString = returnString + suffix; + } + + return returnString; + } + + private String makeUnique(String className, JPackage _package) { + try { + JDefinedClass _class = _package._class(className); + _package.remove(_class); + return className; + } catch (JClassAlreadyExistsException e) { + return makeUnique(MakeUniqueClassName.makeUnique(className), _package); + } + } } diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/ReflectionHelper.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/ReflectionHelper.java new file mode 100644 index 000000000..4fff8313e --- /dev/null +++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/util/ReflectionHelper.java @@ -0,0 +1,162 @@ +/** + * Copyright © 2010-2017 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 static org.jsonschema2pojo.util.TypeUtil.resolveType; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JFieldVar; +import com.sun.codemodel.JPackage; +import com.sun.codemodel.JType; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.LinkedList; +import java.util.Map; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.StreamSupport; +import org.jsonschema2pojo.Schema; +import org.jsonschema2pojo.rules.RuleFactory; + +public class ReflectionHelper { + + private RuleFactory ruleFactory; + + public ReflectionHelper(RuleFactory ruleFactory) { + this.ruleFactory = ruleFactory; + } + + public JType getSuperType(String nodeName, JsonNode node, JPackage jPackage, Schema schema) { + if (node.has("extends") && node.has("extendsJavaClass")) { + throw new IllegalStateException("'extends' and 'extendsJavaClass' defined simultaneously"); + } + + JType superType = jPackage.owner().ref(Object.class); + Schema superTypeSchema = getSuperSchema(node, schema, false); + if (superTypeSchema != null) { + superType = ruleFactory.getSchemaRule().apply(nodeName + "Parent", node.get("extends"), node, jPackage, superTypeSchema); + } else if (node.has("extendsJavaClass")) { + superType = resolveType(jPackage, node.get("extendsJavaClass").asText()); + } + + return superType; + } + + public Schema getSuperSchema(JsonNode node, Schema schema, boolean followRefs) { + if (node.has("extends")) { + String path; + if (schema.getId().getFragment() == null) { + path = "#extends"; + } else { + path = "#" + schema.getId().getFragment() + "/extends"; + } + + Schema superSchema = ruleFactory.getSchemaStore().create(schema, path, ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); + + if (followRefs) { + superSchema = resolveSchemaRefsRecursive(superSchema); + } + + return superSchema; + } + return null; + } + + /** + * This is recursive with searchClassAndSuperClassesForField + */ + public JFieldVar searchSuperClassesForField(String property, JDefinedClass jclass) { + JClass superClass = jclass._extends(); + JDefinedClass definedSuperClass = definedClassOrNullFromType(superClass); + if (definedSuperClass == null) { + return null; + } + return searchClassAndSuperClassesForField(property, definedSuperClass); + } + + public JDefinedClass getBuilderClass(JDefinedClass target) { + String builderClassname = ruleFactory.getNameHelper().getBuilderClassName(target); + + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(target.classes(), Spliterator.ORDERED), false) + .filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst().orElse(null); + } + + public JDefinedClass getBuilderClass(JClass target) { + String builderClassname = ruleFactory.getNameHelper().getBuilderClassName(target); + return getAllPackageClasses(target._package()).stream().filter(definedClass -> definedClass.name().equals(builderClassname)).findFirst() + .orElse(null); + } + + public boolean isFinal(JType superType) { + try { + Class javaClass = Class.forName(superType.fullName()); + return Modifier.isFinal(javaClass.getModifiers()); + } catch (ClassNotFoundException e) { + return false; + } + } + + public JFieldVar searchClassAndSuperClassesForField(String property, JDefinedClass jclass) { + Map fields = jclass.fields(); + JFieldVar field = fields.get(property); + if (field == null) { + return searchSuperClassesForField(property, jclass); + } + return field; + } + + private JDefinedClass definedClassOrNullFromType(JType type) { + if (type == null || type.isPrimitive()) { + return null; + } + JClass fieldClass = type.boxify(); + JPackage jPackage = fieldClass._package(); + return this._getClass(fieldClass.name(), jPackage); + } + + private JDefinedClass _getClass(String name, JPackage _package) { + return getAllPackageClasses(_package).stream().filter(definedClass -> definedClass.name().equals(name)).findFirst() + .orElseThrow(() -> new NoClassDefFoundError(name)); + } + + private Collection getAllPackageClasses(JPackage _package) { + LinkedList result = new LinkedList<>(); + StreamSupport.stream(Spliterators.spliteratorUnknownSize(_package.classes(), Spliterator.ORDERED), false) + .forEach(_class -> result.addAll(getAllClassClasses(_class))); + return result; + } + + private Collection getAllClassClasses(JDefinedClass _class) { + LinkedList result = new LinkedList<>(); + result.add(_class); + + _class.classes().forEachRemaining(result::add); + return result; + } + + private Schema resolveSchemaRefsRecursive(Schema schema) { + JsonNode schemaNode = schema.getContent(); + if (schemaNode.has("$ref")) { + schema = ruleFactory.getSchemaStore() + .create(schema, schemaNode.get("$ref").asText(), ruleFactory.getGenerationConfig().getRefFragmentPathDelimiters()); + return resolveSchemaRefsRecursive(schema); + } + return schema; + } + +} diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PropertyRuleTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PropertyRuleTest.java new file mode 100755 index 000000000..6187cbc19 --- /dev/null +++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/PropertyRuleTest.java @@ -0,0 +1,131 @@ +/** + * Copyright © 2010-2017 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.rules; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.BooleanNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JType; +import org.jsonschema2pojo.GenerationConfig; +import org.jsonschema2pojo.NoopAnnotator; +import org.jsonschema2pojo.Schema; +import org.jsonschema2pojo.SchemaStore; +import org.junit.Before; +import org.junit.Test; + +public class PropertyRuleTest { + private static final String TARGET_CLASS_NAME = PropertyRuleTest.class.getName() + ".DummyClass"; + + private static final String internalFieldName = "internalRequired"; + private static final String targetFieldName = "requiredFoo"; + + private final GenerationConfig config = mock(GenerationConfig.class); + private final PropertyRule rule = new PropertyRule(new RuleFactory(config, new NoopAnnotator(), new SchemaStore())); + + private ObjectMapper mapper; + + @Before + public void setup() { + mapper = new ObjectMapper(); + + when(config.isIncludeGetters()).thenReturn(true); + when(config.isUseOptionalForGetters()).thenReturn(true); + } + + private String getGeneratedMethodTypeName(JDefinedClass jclass) { + return jclass.getMethod("getRequiredFoo", new JType[] {}).type().name(); + } + + private Schema getMockedSchema(ObjectNode parentNode) { + Schema schema = mock(Schema.class); + when(schema.getContent()).thenReturn(parentNode); + when(schema.deriveChildSchema(any())).thenReturn(schema); + return schema; + } + + private JDefinedClass applyRule(ObjectNode propertyNode, ObjectNode parentNode) throws JClassAlreadyExistsException { + JDefinedClass jclass = new JCodeModel()._class(TARGET_CLASS_NAME); + return rule.apply(targetFieldName, propertyNode, parentNode, jclass, getMockedSchema(parentNode)); + } + + @Test + public void applyRequiredByTopArray() throws JClassAlreadyExistsException { + ObjectNode propertyNode = mapper.createObjectNode(); + propertyNode.set("required", mapper.createArrayNode().add(internalFieldName)); + propertyNode.set("properties", mapper.createObjectNode().set(internalFieldName, mapper.createObjectNode())); + + ObjectNode parentNode = mapper.createObjectNode(); + parentNode.set("required", mapper.createArrayNode().add(targetFieldName)); + parentNode.set("properties", mapper.createObjectNode().set(targetFieldName, propertyNode)); + + JDefinedClass jclass = applyRule(propertyNode, parentNode); + + assertThat(jclass, notNullValue()); + assertThat(getGeneratedMethodTypeName(jclass), is("RequiredFoo")); + } + + @Test + public void applyNotRequiredByTopArray() throws JClassAlreadyExistsException { + ObjectNode propertyNode = mapper.createObjectNode(); + propertyNode.set("required", mapper.createArrayNode().add(internalFieldName)); + propertyNode.set("properties", mapper.createObjectNode().set(internalFieldName, mapper.createObjectNode())); + + ObjectNode parentNode = mapper.createObjectNode(); + parentNode.set("properties", mapper.createObjectNode().set(targetFieldName, propertyNode)); + + JDefinedClass jclass = applyRule(propertyNode, parentNode); + + assertThat(jclass, notNullValue()); + assertThat(getGeneratedMethodTypeName(jclass), is("Optional")); + } + + @Test + public void applyRequiredByFlag() throws JClassAlreadyExistsException { + ObjectNode propertyNode = mapper.createObjectNode(); + propertyNode.set("required", BooleanNode.TRUE); + propertyNode.set("properties", mapper.createObjectNode().set(internalFieldName, mapper.createObjectNode())); + + ObjectNode parentNode = mapper.createObjectNode(); + parentNode.set("properties", mapper.createObjectNode().set(targetFieldName, propertyNode)); + + JDefinedClass jclass = applyRule(propertyNode, parentNode); + + assertThat(jclass, notNullValue()); + assertThat(getGeneratedMethodTypeName(jclass), is("RequiredFoo")); + } + + @Test + public void applyNotRequiredByFlag() throws JClassAlreadyExistsException { + ObjectNode propertyNode = mapper.createObjectNode(); + propertyNode.set("properties", mapper.createObjectNode().set(internalFieldName, mapper.createObjectNode())); + + ObjectNode parentNode = mapper.createObjectNode(); + parentNode.set("properties", mapper.createObjectNode().set(targetFieldName, propertyNode)); + + JDefinedClass jclass = applyRule(propertyNode, parentNode); + + assertThat(jclass, notNullValue()); + assertThat(getGeneratedMethodTypeName(jclass), is("Optional")); + } +} diff --git a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/RuleFactoryImplTest.java b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/RuleFactoryImplTest.java index 8c3249618..76a9899b9 100644 --- a/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/RuleFactoryImplTest.java +++ b/jsonschema2pojo-core/src/test/java/org/jsonschema2pojo/rules/RuleFactoryImplTest.java @@ -68,6 +68,8 @@ public void factoryMethodsCreateRules() { assertThat(ruleFactory.getValidRule(), notNullValue()); + assertThat(ruleFactory.getDigitsRule(), notNullValue()); + } @Test diff --git a/jsonschema2pojo-core/src/test/resources/schema/child.json b/jsonschema2pojo-core/src/test/resources/schema/child.json new file mode 100644 index 000000000..7c8c259bc --- /dev/null +++ b/jsonschema2pojo-core/src/test/resources/schema/child.json @@ -0,0 +1,15 @@ +{ + "$id": "https://example.com/child.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description" : "A schema definition for an object which can be inherited", + "javaType" : "com.example.package.GeneratedChildType", + "type" : "object", + "extends" : { + "$ref" : "parent.json" + }, + "properties" : { + "childProperty" : { "type" : "string" } + }, + "dependencies" : { + } +} \ No newline at end of file diff --git a/jsonschema2pojo-core/src/test/resources/schema/parent.json b/jsonschema2pojo-core/src/test/resources/schema/parent.json new file mode 100644 index 000000000..f2a9b7d78 --- /dev/null +++ b/jsonschema2pojo-core/src/test/resources/schema/parent.json @@ -0,0 +1,12 @@ +{ + "$id": "https://example.com/address.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description" : "A schema definition for an object which can be inherited", + "javaType" : "com.example.package.GeneratedParentType", + "type" : "object", + "properties" : { + "parentProperty" : { "type" : "string" } + }, + "dependencies" : { + } +} \ No newline at end of file diff --git a/jsonschema2pojo-gradle-plugin/pom.xml b/jsonschema2pojo-gradle-plugin/pom.xml index 7eea26783..f32cc8f02 100644 --- a/jsonschema2pojo-gradle-plugin/pom.xml +++ b/jsonschema2pojo-gradle-plugin/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-gradle-plugin diff --git a/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy b/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy index 55c68b790..6b2866145 100644 --- a/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy +++ b/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/JsonSchemaExtension.groovy @@ -45,6 +45,8 @@ public class JsonSchemaExtension implements GenerationConfig { Class customAnnotator Class customRuleFactory boolean generateBuilders + boolean includeJsonTypeInfoAnnotation + boolean useInnerClassBuilders boolean includeGetters boolean includeSetters boolean includeAdditionalProperties @@ -95,6 +97,8 @@ public class JsonSchemaExtension implements GenerationConfig { public JsonSchemaExtension() { // See DefaultGenerationConfig generateBuilders = false + includeJsonTypeInfoAnnotation = false + useInnerClassBuilders = false usePrimitives = false sourceFiles = [] targetPackage = '' @@ -148,6 +152,11 @@ public class JsonSchemaExtension implements GenerationConfig { formatTypeMapping = Collections.emptyMap() } + @Override + boolean isIncludeTypeInfo() { + return includeJsonTypeInfoAnnotation + } + @Override public Iterator getSource() { def urlList = [] @@ -201,6 +210,7 @@ public class JsonSchemaExtension implements GenerationConfig { @Override public String toString() { """|generateBuilders = ${generateBuilders} + |includeJsonTypeInfoAnnotation = ${includeJsonTypeInfoAnnotation} |usePrimitives = ${usePrimitives} |source = ${sourceFiles} |targetDirectory = ${targetDirectory} @@ -254,11 +264,12 @@ public class JsonSchemaExtension implements GenerationConfig { |sourceSortOrder = ${sourceSortOrder} |targetLanguage = ${targetLanguage} |formatTypeMapping = ${formatTypeMapping} + |useInnerClassBuilders = ${useInnerClassBuilders} """.stripMargin() } public boolean isFormatDateTimes() { - return formatDateTimes; + return formatDateTimes } } diff --git a/jsonschema2pojo-integration-tests/pom.xml b/jsonschema2pojo-integration-tests/pom.xml index 2b911cd19..5275f830c 100644 --- a/jsonschema2pojo-integration-tests/pom.xml +++ b/jsonschema2pojo-integration-tests/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-integration-tests diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/DescriptionEnumIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/DescriptionEnumIT.java new file mode 100644 index 000000000..6d8c08b28 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/DescriptionEnumIT.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2010-2017 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.integration; + +import com.thoughtworks.qdox.JavaDocBuilder; +import com.thoughtworks.qdox.model.JavaClass; +import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/* + Enums are treated differently to schemas of type object and we want to ensure that a description + added to root-level enums is added to the javadoc. + */ +public class DescriptionEnumIT { + + @ClassRule public static Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule(); + + private static JavaClass classWithDescription; + + @BeforeClass + public static void generateClasses() throws IOException { + + schemaRule.generateAndCompile("/schema/description/descriptionEnum.json", "com.example"); + File generatedJavaFile = schemaRule.generated("com/example/DescriptionEnum.java"); + + JavaDocBuilder javaDocBuilder = new JavaDocBuilder(); + javaDocBuilder.addSource(generatedJavaFile); + + classWithDescription = javaDocBuilder.getClassByName("com.example.DescriptionEnum"); + } + + @Test + public void descriptionAppearsInEnumJavadoc() { + + String javaDocComment = classWithDescription.getComment(); + + assertThat(javaDocComment, containsString("A description for this enum")); + + } + +} diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/PolymorphicIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/PolymorphicIT.java index ea7e49960..8381242d9 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/PolymorphicIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/PolymorphicIT.java @@ -32,7 +32,10 @@ package org.jsonschema2pojo.integration; import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.config; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule; import org.junit.Rule; @@ -46,7 +49,7 @@ public class PolymorphicIT { @Rule public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule(); @Test - public void extendsWithPolymorphicDeserialization() throws ClassNotFoundException { + public void extendsWithPolymorphicDeserializationWithDefaultAnnotationStyle() throws ClassNotFoundException { ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/polymorphic/extendsSchema.json", "com.example"); @@ -54,6 +57,32 @@ public void extendsWithPolymorphicDeserialization() throws ClassNotFoundExceptio Class supertype = subtype.getSuperclass(); assertNotNull(supertype.getAnnotation(JsonTypeInfo.class)); + assertNull(supertype.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void extendsWithPolymorphicDeserializationWithJackson2() throws ClassNotFoundException { + + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/polymorphic/extendsSchema.json", "com.example", + config("annotationStyle", "JACKSON2")); + + Class subtype = resultsClassLoader.loadClass("com.example.ExtendsSchema"); + Class supertype = subtype.getSuperclass(); + + assertNotNull(supertype.getAnnotation(JsonTypeInfo.class)); + assertNull(supertype.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void extendsWithPolymorphicDeserializationWithJackson1() throws ClassNotFoundException { + + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/polymorphic/extendsSchema.json", "com.example", + config("annotationStyle", "JACKSON1")); + + Class subtype = resultsClassLoader.loadClass("com.example.ExtendsSchema"); + Class supertype = subtype.getSuperclass(); + assertNotNull(supertype.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + assertNull(supertype.getAnnotation(JsonTypeInfo.class)); } } diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/TitleEnumIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/TitleEnumIT.java new file mode 100644 index 000000000..579684260 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/TitleEnumIT.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2010-2017 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.integration; + +import com.thoughtworks.qdox.JavaDocBuilder; +import com.thoughtworks.qdox.model.JavaClass; +import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/* + Enums are treated differently to schemas of type object and we want to ensure that a title + added to root-level enums is added to the javadoc. + */ +public class TitleEnumIT { + @ClassRule public static Jsonschema2PojoRule classSchemaRule = new Jsonschema2PojoRule(); + + private static JavaClass classWithTitle; + + @BeforeClass + public static void generateClasses() throws IOException { + + classSchemaRule.generateAndCompile("/schema/title/titleEnum.json", "com.example"); + File generatedJavaFile = classSchemaRule.generated("com/example/TitleEnum.java"); + + JavaDocBuilder javaDocBuilder = new JavaDocBuilder(); + javaDocBuilder.addSource(generatedJavaFile); + + classWithTitle = javaDocBuilder.getClassByName("com.example.TitleEnum"); + } + + @Test + public void descriptionAppearsInEnumJavadoc() { + + String javaDocComment = classWithTitle.getComment(); + + assertThat(javaDocComment, containsString("A title for this enum")); + + } +} diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/CustomAnnotatorIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/CustomAnnotatorIT.java index a38f969ec..993c12921 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/CustomAnnotatorIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/CustomAnnotatorIT.java @@ -111,6 +111,11 @@ public void invalidCustomAnnotatorClassCausesMojoException() { */ public static class DeprecatingAnnotator implements Annotator { + @Override + public void typeInfo(JDefinedClass clazz, JsonNode schema) { + + } + @Override public void propertyOrder(JDefinedClass clazz, JsonNode propertiesNode) { clazz.annotate(Deprecated.class); @@ -172,6 +177,11 @@ public void additionalPropertiesField(JFieldVar field, } + @Override + public boolean isPolymorphicDeserializationSupported(JsonNode node) { + return false; + } + @Override public void dateField(JFieldVar field, JDefinedClass clazz, JsonNode propertyNode) { field.annotate(Deprecated.class); diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java index dda9891aa..1944130e6 100644 --- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java @@ -25,6 +25,7 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -223,6 +224,46 @@ public void jsr303SizeValidationIsAddedForSchemaRuleMaxLength() throws ClassNotF assertNumberOfConstraintViolationsOn(invalidInstance, is(1)); } + @Test + public void jsr303DigitsValidationIsAddedForSchemaRuleDigits() throws ClassNotFoundException { + + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/jsr303/digits.json", "com.example", + config("includeJsr303Annotations", true, "useBigDecimals", true)); + + Class generatedType = resultsClassLoader.loadClass("com.example.Digits"); + + // positive value + Object validInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("12345.1234567890")); + + assertNumberOfConstraintViolationsOn(validInstance, is(0)); + + // negative value + validInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("-12345.0123456789")); + + assertNumberOfConstraintViolationsOn(validInstance, is(0)); + + // zero value + validInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("0.0")); + + assertNumberOfConstraintViolationsOn(validInstance, is(0)); + + // too many integer digits + Object invalidInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("123456.0123456789")); + + assertNumberOfConstraintViolationsOn(invalidInstance, is(1)); + + // too many fractional digits + invalidInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("12345.12345678901")); + + assertNumberOfConstraintViolationsOn(invalidInstance, is(1)); + + // too many integer & fractional digits + invalidInstance = createInstanceWithPropertyValue(generatedType, "decimal", new BigDecimal("123456.12345678901")); + + assertNumberOfConstraintViolationsOn(invalidInstance, is(1)); + + } + @Test public void jsr303ValidAnnotationIsAddedForObject() throws ClassNotFoundException { ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/jsr303/validObject.json", "com.example", diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeTypeInfoIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeTypeInfoIT.java new file mode 100644 index 000000000..bfaa93160 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeTypeInfoIT.java @@ -0,0 +1,227 @@ +/** + * Copyright © 2010-2017 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.integration.config; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule; +import org.junit.Rule; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.config; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; + +public class IncludeTypeInfoIT +{ + @Rule + public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule(); + + @Test + public void defaultConfig() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example"); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void defaultJackson1() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example", + config("annotationStyle", "JACKSON1")); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void defaultJackson1WithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example", + config("annotationStyle", "JACKSON1")); + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNotNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + org.codehaus.jackson.annotate.JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(org.codehaus.jackson.annotate.JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } + + @Test + public void defaultWithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example"); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNotNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } + + @Test + public void disabled() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example", + config("includeTypeInfo", false)); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void disabledJackson1() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example", + config("annotationStyle", "JACKSON1", + "includeTypeInfo", false)); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + } + + @Test + public void disabledJackson1WithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example", + config("annotationStyle", "JACKSON1", + "includeTypeInfo", false)); + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNotNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + org.codehaus.jackson.annotate.JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(org.codehaus.jackson.annotate.JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } + + @Test + public void disabledWithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example", + config("includeTypeInfo", false)); + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNotNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } + + @Test + public void enabled() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example", + config("includeTypeInfo", true)); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNotNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@class")); + } + + @Test + public void enabledJackson1() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfo.json", "com.example", + config("annotationStyle", "JACKSON1", + "includeTypeInfo", true)); + + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfo"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNotNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + org.codehaus.jackson.annotate.JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(org.codehaus.jackson.annotate.JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@class")); + } + + @Test + public void enabledJackson1WithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example", + config("annotationStyle", "JACKSON1", + "includeTypeInfo", true)); + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNotNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + org.codehaus.jackson.annotate.JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(org.codehaus.jackson.annotate.JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(org.codehaus.jackson.annotate.JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } + + @Test + public void enabledWithSchemaProperty() throws ClassNotFoundException + { + + ClassLoader classLoader = schemaRule.generateAndCompile("/schema/typeInfo/typeInfoWithSchemaProperty.json", "com.example", + config("includeTypeInfo", true)); + Class classWithTypeInfo = classLoader.loadClass("com.example.TypeInfoWithSchemaProperty"); + + assertNotNull(classWithTypeInfo.getAnnotation(JsonTypeInfo.class)); + assertNull(classWithTypeInfo.getAnnotation(org.codehaus.jackson.annotate.JsonTypeInfo.class)); + + JsonTypeInfo jsonTypeInfo = classWithTypeInfo.getAnnotation(JsonTypeInfo.class); + assertThat(jsonTypeInfo.use(), is(JsonTypeInfo.Id.CLASS)); + assertThat(jsonTypeInfo.include(), is(JsonTypeInfo.As.PROPERTY)); + assertThat(jsonTypeInfo.property(), is("@clazz")); + } +} diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/UseInnerClassBuildersIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/UseInnerClassBuildersIT.java new file mode 100644 index 000000000..680fd2fb5 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/UseInnerClassBuildersIT.java @@ -0,0 +1,227 @@ +/** + * Copyright © 2010-2017 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.integration.config; + +import static org.hamcrest.Matchers.not; +import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.config; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; +import org.hamcrest.Matcher; +import org.jsonschema2pojo.integration.util.FileSearchMatcher; +import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule; +import org.junit.Rule; +import org.junit.Test; + +@SuppressWarnings("rawtypes") +public class UseInnerClassBuildersIT { + + @Rule + public Jsonschema2PojoRule schemaRule = new Jsonschema2PojoRule(); + + private static Matcher containsText(String searchText) { + return new FileSearchMatcher(searchText); + } + + /** + * This test asserts that no methods containing 'with' appear in the generated code using the default configuration + */ + @Test + public void noBuilderMethodsByDefault() { + File outputDirectory = schemaRule.generate("/schema.useInnerClassBuilders/child.json", "com.example", config()); + + assertThat(outputDirectory, not(containsText("with"))); + } + + /** + * This method confirms that if you choose to generate builders, but don't indicate that useInnerBuilders is true, they will be generated using the + * chaining setters instead of the inner classes + */ + @Test(expected = ClassNotFoundException.class) + public void defaultBuilderIsChainedSetters() throws ClassNotFoundException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true)); + + Class childClass = resultsClassLoader.loadClass("com.example.Child"); + boolean containsWithMethod = Stream.of(childClass.getMethods()) + .map(Method::getName) + .anyMatch(methodName -> StringUtils.contains(methodName, "with")); + + assertTrue("Generated class missing any builders at all", containsWithMethod); + + resultsClassLoader.loadClass("com.example.Child.ChildBuilder"); + } + + /** + * This method confirms that if you choose to use inner class builders then the chaining setters will be removed from the generated class + */ + @Test + public void innerBuildersRemoveChainedSetters() throws ClassNotFoundException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true)); + + Class childClass = resultsClassLoader.loadClass("com.example.Child"); + boolean containsWithMethod = Stream.of(childClass.getMethods()) + .map(Method::getName) + .anyMatch(methodName -> StringUtils.contains(methodName, "with")); + + assertFalse("Generated contains unexpected builders", containsWithMethod); + + assertNotNull(resultsClassLoader.loadClass("com.example.Child$ChildBuilder")); + } + + /** + * This methods confirms that the builders can be constructed using the empty constructor and possess a 'build' method that will return a non-null + * object + */ + @Test + public void innerBuildersInvokeBuild() + throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true)); + + Class builderClass = resultsClassLoader.loadClass("com.example.Child$ChildBuilder"); + Method buildMethod = builderClass.getMethod("build"); + + Object builder = builderClass.newInstance(); + assertNotNull(builder); + + assertNotNull(buildMethod.invoke(builder)); + } + + /** + * This method walks through invoking the various incremental 'with' methods to build out an entire object, then invokes build on the constructed + * object and confirms all values on the object match those provided to the with methods + */ + @Test + public void innerBuildersBuildObjectIncrementally() + throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true)); + + Class builderClass = resultsClassLoader.loadClass("com.example.Child$ChildBuilder"); + Method buildMethod = builderClass.getMethod("build"); + Method withChildProperty = builderClass.getMethod("withChildProperty", Integer.class); + Method withParentProperty = builderClass.getMethod("withParentProperty", String.class); + Method withSharedProperty = builderClass.getMethod("withSharedProperty", String.class); + + int childProperty = 1; + String parentProperty = "parentProperty"; + String sharedProperty = "sharedProperty"; + + Object builder = builderClass.newInstance(); + withChildProperty.invoke(builder, childProperty); + withParentProperty.invoke(builder, parentProperty); + withSharedProperty.invoke(builder, sharedProperty); + Object childObject = buildMethod.invoke(builder); + + Class childClass = resultsClassLoader.loadClass("com.example.Child"); + Method getChildProperty = childClass.getMethod("getChildProperty"); + Method getParentProperty = childClass.getMethod("getParentProperty"); + Method getSharedProperty = childClass.getMethod("getSharedProperty"); + + assertEquals(childProperty, getChildProperty.invoke(childObject)); + assertEquals(parentProperty, getParentProperty.invoke(childObject)); + assertEquals(sharedProperty, getSharedProperty.invoke(childObject)); + } + + /** + * This method confirms that by default the only constructor available to a builder is the empty argument constructor + */ + @Test + public void innerBuilderExtraConstructorsRequireConfig() throws ClassNotFoundException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true)); + + Class builderClass = resultsClassLoader.loadClass("com.example.Child$ChildBuilder"); + assertEquals(1, builderClass.getConstructors().length); + + Constructor constructor = builderClass.getConstructors()[0]; + assertEquals(0, constructor.getParameterCount()); + } + + /** + * This method confirms that if constructors are enabled then a builder constructor which takes all properties will be created + */ + @Test + public void innerBuilderWithAllPropertyConstructor() + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true, "includeConstructors", true)); + + Class builderClass = resultsClassLoader.loadClass("com.example.Child$ChildBuilder"); + Constructor constructor = builderClass.getConstructor(Integer.class, String.class, String.class); + Method buildMethod = builderClass.getMethod("build"); + + int childProperty = 1; + String parentProperty = "parentProperty"; + String sharedProperty = "sharedProperty"; + + Object builder = constructor.newInstance(childProperty, sharedProperty, parentProperty); + Object childObject = buildMethod.invoke(builder); + + Class childClass = resultsClassLoader.loadClass("com.example.Child"); + Method getChildProperty = childClass.getMethod("getChildProperty"); + Method getParentProperty = childClass.getMethod("getParentProperty"); + Method getSharedProperty = childClass.getMethod("getSharedProperty"); + + assertEquals(childProperty, getChildProperty.invoke(childObject)); + assertEquals(parentProperty, getParentProperty.invoke(childObject)); + assertEquals(sharedProperty, getSharedProperty.invoke(childObject)); + } + + /** + * This method confirms that if constructors are enabled and constructorsRequiredPropertiesOnly is set to true then a only a builder constructor + * with the required properties will be created + */ + @Test + public void innerBuilderWithRequiredPropertyConstructor() + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { + ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema.useInnerClassBuilders/child.json", "com.example", + config("generateBuilders", true, "useInnerClassBuilders", true, "includeConstructors", true, "constructorsRequiredPropertiesOnly", true)); + + Class builderClass = resultsClassLoader.loadClass("com.example.Child$ChildBuilder"); + Constructor constructor = builderClass.getConstructor(String.class); + Method buildMethod = builderClass.getMethod("build"); + Method withChildProperty = builderClass.getMethod("withChildProperty", Integer.class); + Method withSharedProperty = builderClass.getMethod("withSharedProperty", String.class); + + int childProperty = 1; + String parentProperty = "parentProperty"; + String sharedProperty = "sharedProperty"; + + Object builder = constructor.newInstance(parentProperty); + withChildProperty.invoke(builder, childProperty); + withSharedProperty.invoke(builder, sharedProperty); + Object childObject = buildMethod.invoke(builder); + + Class childClass = resultsClassLoader.loadClass("com.example.Child"); + Method getChildProperty = childClass.getMethod("getChildProperty"); + Method getParentProperty = childClass.getMethod("getParentProperty"); + Method getSharedProperty = childClass.getMethod("getSharedProperty"); + + assertEquals(childProperty, getChildProperty.invoke(childObject)); + assertEquals(parentProperty, getParentProperty.invoke(childObject)); + assertEquals(sharedProperty, getSharedProperty.invoke(childObject)); + } +} diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/child.json b/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/child.json new file mode 100644 index 000000000..1a71d461e --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/child.json @@ -0,0 +1,10 @@ +{ + "type" : "object", + "extends" : { + "$ref" : "parent.json" + }, + "properties" : { + "childProperty" : { "type" : "integer"}, + "sharedProperty" : { "type" : "string" } + } +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/parent.json b/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/parent.json new file mode 100644 index 000000000..99779bab6 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema.useInnerClassBuilders/parent.json @@ -0,0 +1,8 @@ +{ + "type" : "object", + "properties" : { + "parentProperty" : { "type" : "string" }, + "sharedProperty" : { "type" : "string" } + }, + "required" : ["parentProperty"] +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/description/descriptionEnum.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/description/descriptionEnum.json new file mode 100644 index 000000000..dda38b1e3 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/description/descriptionEnum.json @@ -0,0 +1,5 @@ +{ + "description" : "A description for this enum", + "type":"string", + "enum": ["one", "two"] +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json new file mode 100644 index 000000000..22791f285 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/digits.json @@ -0,0 +1,10 @@ +{ + "type": "object", + "properties": { + "decimal": { + "type": "number", + "integerDigits": 5, + "fractionalDigits": 10 + } + } +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/title/titleEnum.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/title/titleEnum.json new file mode 100644 index 000000000..305861d5e --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/title/titleEnum.json @@ -0,0 +1,5 @@ +{ + "title" : "A title for this enum", + "type":"string", + "enum": ["one", "two"] +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfo.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfo.json new file mode 100644 index 000000000..5c649ee45 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfo.json @@ -0,0 +1,4 @@ +{ + "type" : "object", + "properties" : {} +} \ No newline at end of file diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfoWithSchemaProperty.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfoWithSchemaProperty.json new file mode 100644 index 000000000..b51c7b3a7 --- /dev/null +++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/typeInfo/typeInfoWithSchemaProperty.json @@ -0,0 +1,5 @@ +{ + "type" : "object", + "deserializationClassProperty": "@clazz", + "properties" : {} +} \ No newline at end of file diff --git a/jsonschema2pojo-maven-plugin/pom.xml b/jsonschema2pojo-maven-plugin/pom.xml index 3092044f5..48f3b56e5 100644 --- a/jsonschema2pojo-maven-plugin/pom.xml +++ b/jsonschema2pojo-maven-plugin/pom.xml @@ -5,7 +5,7 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT jsonschema2pojo-maven-plugin diff --git a/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java b/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java index b049cd4aa..755773163 100644 --- a/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java +++ b/jsonschema2pojo-maven-plugin/src/main/java/org/jsonschema2pojo/maven/Jsonschema2PojoMojo.java @@ -112,6 +112,17 @@ public class Jsonschema2PojoMojo extends AbstractMojo implements GenerationConfi */ private boolean generateBuilders = false; + /** + * Whether to include json type information; often required to support polymorphic type handling. + * + * By default the type information is stored in the @class property, this can be overridden in the deserializationClassProperty + * of the schema. + * + * @parameter property="jsonschema2pojo.includeTypeInfo" + * default-value="false" + * @since 1.0.2 + */ + private boolean includeTypeInfo = false; /** * Whether to use primitives (long, double, * boolean) instead of wrapper types where possible when @@ -745,6 +756,13 @@ public class Jsonschema2PojoMojo extends AbstractMojo implements GenerationConfi */ private Map formatTypeMapping = new HashMap<>(); + /** + * @parameter property="jsonschema2pojo.useInnerClassBuilders" + * default-value="false" + * @since 1.0.0 + */ + private boolean useInnerClassBuilders = false; + /** * Executes the plugin, to read the given source and behavioural properties * and generate POJOs. The current implementation acts as a wrapper around @@ -844,6 +862,12 @@ public boolean isGenerateBuilders() { return generateBuilders; } + @Override + public boolean isIncludeTypeInfo() + { + return includeTypeInfo; + } + @Override public File getTargetDirectory() { return outputDirectory; @@ -1159,4 +1183,9 @@ public Language getTargetLanguage() { public Map getFormatTypeMapping() { return formatTypeMapping; } + + @Override + public boolean isUseInnerClassBuilders() { + return useInnerClassBuilders; + } } diff --git a/jsonschema2pojo-maven-plugin/src/test/resources/example/pom.xml b/jsonschema2pojo-maven-plugin/src/test/resources/example/pom.xml index 5d4fca6d0..2849b96b2 100644 --- a/jsonschema2pojo-maven-plugin/src/test/resources/example/pom.xml +++ b/jsonschema2pojo-maven-plugin/src/test/resources/example/pom.xml @@ -40,7 +40,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.7 + 2.9.8 diff --git a/jsonschema2pojo-maven-plugin/src/test/resources/filtered/pom.xml b/jsonschema2pojo-maven-plugin/src/test/resources/filtered/pom.xml index 4cf290f47..3caf0d30d 100644 --- a/jsonschema2pojo-maven-plugin/src/test/resources/filtered/pom.xml +++ b/jsonschema2pojo-maven-plugin/src/test/resources/filtered/pom.xml @@ -43,7 +43,7 @@ com.fasterxml.jackson.core jackson-databind - 2.9.7 + 2.9.8 diff --git a/jsonschema2pojo-scalagen/pom.xml b/jsonschema2pojo-scalagen/pom.xml index 0dc40eb61..8ff8b81a3 100644 --- a/jsonschema2pojo-scalagen/pom.xml +++ b/jsonschema2pojo-scalagen/pom.xml @@ -6,12 +6,12 @@ jsonschema2pojo org.jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT org.jsonschema2pojo jsonschema2pojo-scalagen - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT 4.01 diff --git a/pom.xml b/pom.xml index 199bc9196..3f091283f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.jsonschema2pojo jsonschema2pojo - 1.0.0-beta2-SNAPSHOT + 1.0.2-SNAPSHOT pom jsonschema2pojo @@ -54,8 +54,8 @@ 1.6 2.5 1.5.0 - 2.9.7 - 2.11.6 + 2.9.8 + 2.11.12