From 4c49d22dc0888fe51e8ec65e5e6a10defc0665fe Mon Sep 17 00:00:00 2001 From: Felipe Orozco Date: Mon, 9 Jul 2018 17:56:14 +0100 Subject: [PATCH] generate defensive name for inner union class (#34) * generate defensive name for inner union class * add test case * rename inner class * single underscore --- .../java/com/palantir/product/Union.java | 246 ++++++++++++++++++ .../palantir/product/UnionTypeExample.java | 36 +-- .../conjure/java/types/UnionGenerator.java | 18 +- .../src/test/resources/example-types.yml | 4 + 4 files changed, 282 insertions(+), 22 deletions(-) create mode 100644 conjure-java-core/src/integrationInput/java/com/palantir/product/Union.java diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/Union.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/Union.java new file mode 100644 index 000000000..c740fba38 --- /dev/null +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/Union.java @@ -0,0 +1,246 @@ +package com.palantir.product; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; +import javax.annotation.Generated; + +@Generated("com.palantir.conjure.java.types.UnionGenerator") +public final class Union { + @JsonUnwrapped private final Union_ union; + + @JsonCreator + private Union(Union_ union) { + Objects.requireNonNull(union, "union must not be null"); + this.union = union; + } + + public T accept(Visitor visitor) { + if (union.getFoo().isPresent()) { + return visitor.visitFoo(union.getFoo().get()); + } else if (union.getBar().isPresent()) { + return visitor.visitBar(union.getBar().getAsInt()); + } + return visitor.visitUnknown(union.getType()); + } + + @Override + public boolean equals(Object other) { + return this == other || (other instanceof Union && equalTo((Union) other)); + } + + private boolean equalTo(Union other) { + return this.union.equals(other.union); + } + + @Override + public int hashCode() { + return union.hashCode(); + } + + @Override + public String toString() { + return union.toString(); + } + + public static Union foo(String value) { + return new Union(Union_.builder().type("foo").foo(value).build()); + } + + public static Union bar(int value) { + return new Union(Union_.builder().type("bar").bar(value).build()); + } + + public interface Visitor { + T visitFoo(String value); + + T visitBar(int value); + + T visitUnknown(String unknownType); + } + + @JsonDeserialize(builder = Union_.Builder.class) + @Generated("com.palantir.conjure.java.types.BeanGenerator") + @JsonInclude(JsonInclude.Include.NON_ABSENT) + private static final class Union_ { + private final String type; + + private final Optional foo; + + private final OptionalInt bar; + + private final Map __unknownProperties; + + private volatile int memoizedHashCode; + + private Union_( + String type, + Optional foo, + OptionalInt bar, + Map __unknownProperties) { + validateFields(type, foo, bar); + this.type = type; + this.foo = foo; + this.bar = bar; + this.__unknownProperties = Collections.unmodifiableMap(__unknownProperties); + } + + @JsonProperty("type") + public String getType() { + return this.type; + } + + @JsonProperty("foo") + public Optional getFoo() { + return this.foo; + } + + @JsonProperty("bar") + public OptionalInt getBar() { + return this.bar; + } + + @JsonAnyGetter + Map unknownProperties() { + return __unknownProperties; + } + + @Override + public boolean equals(Object other) { + return this == other || (other instanceof Union_ && equalTo((Union_) other)); + } + + private boolean equalTo(Union_ other) { + return this.type.equals(other.type) + && this.foo.equals(other.foo) + && this.bar.equals(other.bar) + && this.__unknownProperties.equals(other.__unknownProperties); + } + + @Override + public int hashCode() { + if (memoizedHashCode == 0) { + memoizedHashCode = Objects.hash(type, foo, bar, __unknownProperties); + } + return memoizedHashCode; + } + + @Override + public String toString() { + return new StringBuilder("Union_") + .append("{") + .append("type") + .append(": ") + .append(type) + .append(", ") + .append("foo") + .append(": ") + .append(foo) + .append(", ") + .append("bar") + .append(": ") + .append(bar) + .append("}") + .toString(); + } + + private static void validateFields(String type, Optional foo, OptionalInt bar) { + List missingFields = null; + missingFields = addFieldIfMissing(missingFields, type, "type"); + missingFields = addFieldIfMissing(missingFields, foo, "foo"); + missingFields = addFieldIfMissing(missingFields, bar, "bar"); + if (missingFields != null) { + throw new IllegalArgumentException( + "Some required fields have not been set: " + missingFields); + } + } + + private static List addFieldIfMissing( + List prev, Object fieldValue, String fieldName) { + List missingFields = prev; + if (fieldValue == null) { + if (missingFields == null) { + missingFields = new ArrayList<>(3); + } + missingFields.add(fieldName); + } + return missingFields; + } + + public static Builder builder() { + return new Builder(); + } + + @Generated("com.palantir.conjure.java.types.BeanBuilderGenerator") + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private String type; + + private Optional foo = Optional.empty(); + + private OptionalInt bar = OptionalInt.empty(); + + Map __unknownProperties = new LinkedHashMap<>(); + + private Builder() {} + + public Builder from(Union_ other) { + type(other.getType()); + foo(other.getFoo()); + bar(other.getBar()); + return this; + } + + @JsonSetter("type") + public Builder type(String type) { + this.type = Objects.requireNonNull(type, "type cannot be null"); + return this; + } + + @JsonSetter("foo") + public Builder foo(Optional foo) { + this.foo = Objects.requireNonNull(foo, "foo cannot be null"); + return this; + } + + public Builder foo(String foo) { + this.foo = Optional.of(Objects.requireNonNull(foo, "foo cannot be null")); + return this; + } + + @JsonSetter("bar") + public Builder bar(OptionalInt bar) { + this.bar = Objects.requireNonNull(bar, "bar cannot be null"); + return this; + } + + public Builder bar(int bar) { + this.bar = OptionalInt.of(bar); + return this; + } + + public Union_ build() { + return new Union_(type, foo, bar, __unknownProperties); + } + + @JsonAnySetter + private void setUnknownProperties(String key, Object value) { + __unknownProperties.put(key, value); + } + } + } +} diff --git a/conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java b/conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java index 56cffd4dd..d69ba1e1f 100644 --- a/conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java +++ b/conjure-java-core/src/integrationInput/java/com/palantir/product/UnionTypeExample.java @@ -23,10 +23,10 @@ /** A type which can either be a StringExample, a set of strings, or an integer. */ @Generated("com.palantir.conjure.java.types.UnionGenerator") public final class UnionTypeExample { - @JsonUnwrapped private final Union union; + @JsonUnwrapped private final Union_ union; @JsonCreator - private UnionTypeExample(Union union) { + private UnionTypeExample(Union_ union) { Objects.requireNonNull(union, "union must not be null"); this.union = union; } @@ -73,33 +73,33 @@ public String toString() { /** Docs for when UnionTypeExample is of type StringExample. */ public static UnionTypeExample stringExample(StringExample value) { return new UnionTypeExample( - Union.builder().type("stringExample").stringExample(value).build()); + Union_.builder().type("stringExample").stringExample(value).build()); } public static UnionTypeExample set(Set value) { - return new UnionTypeExample(Union.builder().type("set").set(value).build()); + return new UnionTypeExample(Union_.builder().type("set").set(value).build()); } public static UnionTypeExample thisFieldIsAnInteger(int value) { return new UnionTypeExample( - Union.builder().type("thisFieldIsAnInteger").thisFieldIsAnInteger(value).build()); + Union_.builder().type("thisFieldIsAnInteger").thisFieldIsAnInteger(value).build()); } public static UnionTypeExample alsoAnInteger(int value) { return new UnionTypeExample( - Union.builder().type("alsoAnInteger").alsoAnInteger(value).build()); + Union_.builder().type("alsoAnInteger").alsoAnInteger(value).build()); } public static UnionTypeExample if_(int value) { - return new UnionTypeExample(Union.builder().type("if").if_(value).build()); + return new UnionTypeExample(Union_.builder().type("if").if_(value).build()); } public static UnionTypeExample new_(int value) { - return new UnionTypeExample(Union.builder().type("new").new_(value).build()); + return new UnionTypeExample(Union_.builder().type("new").new_(value).build()); } public static UnionTypeExample interface_(int value) { - return new UnionTypeExample(Union.builder().type("interface").interface_(value).build()); + return new UnionTypeExample(Union_.builder().type("interface").interface_(value).build()); } public interface Visitor { @@ -120,10 +120,10 @@ public interface Visitor { T visitUnknown(String unknownType); } - @JsonDeserialize(builder = Union.Builder.class) + @JsonDeserialize(builder = Union_.Builder.class) @Generated("com.palantir.conjure.java.types.BeanGenerator") @JsonInclude(JsonInclude.Include.NON_ABSENT) - private static final class Union { + private static final class Union_ { private final String type; private final Optional stringExample; @@ -144,7 +144,7 @@ private static final class Union { private volatile int memoizedHashCode; - private Union( + private Union_( String type, Optional stringExample, Optional> set, @@ -221,10 +221,10 @@ Map unknownProperties() { @Override public boolean equals(Object other) { - return this == other || (other instanceof Union && equalTo((Union) other)); + return this == other || (other instanceof Union_ && equalTo((Union_) other)); } - private boolean equalTo(Union other) { + private boolean equalTo(Union_ other) { return this.type.equals(other.type) && this.stringExample.equals(other.stringExample) && this.set.equals(other.set) @@ -256,7 +256,7 @@ public int hashCode() { @Override public String toString() { - return new StringBuilder("Union") + return new StringBuilder("Union_") .append("{") .append("type") .append(": ") @@ -357,7 +357,7 @@ public static final class Builder { private Builder() {} - public Builder from(Union other) { + public Builder from(Union_ other) { type(other.getType()); stringExample(other.getStringExample()); set(other.getSet()); @@ -459,8 +459,8 @@ public Builder interface_(int interface_) { return this; } - public Union build() { - return new Union( + public Union_ build() { + return new Union_( type, stringExample, set, diff --git a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java index 3660e3650..9a279b94a 100644 --- a/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java +++ b/conjure-java-core/src/main/java/com/palantir/conjure/java/types/UnionGenerator.java @@ -52,6 +52,7 @@ public final class UnionGenerator { private static final String UNION_FIELD_NAME = "union"; + private static final String INNER_UNION_TYPE_NAME = "Union_"; private static final String TYPE_FIELD_NAME = "type"; private static final String VALUE_PARAM = "value"; private static final String VISIT_METHOD_NAME = "visit"; @@ -107,7 +108,7 @@ private static TypeSpec generateUnionBean(TypeMapper typeMapper, UnionDefinition ClassName unionClass) { return BeanGenerator.generateBeanTypeSpec(typeMapper, ObjectDefinition.builder() .typeName(com.palantir.conjure.spec.TypeName.of( - "Union", typePackage + "." + unionClass.simpleName())) + INNER_UNION_TYPE_NAME, typePackage + "." + unionClass.simpleName())) .fields(FieldDefinition.builder() .fieldName(FieldName.of("type")) .type(Type.primitive(PrimitiveType.STRING)) @@ -129,7 +130,11 @@ private static TypeSpec generateUnionBean(TypeMapper typeMapper, UnionDefinition private static List generateFields(ClassName unionClass) { return ImmutableList.of( - FieldSpec.builder(unionClass.nestedClass("Union"), UNION_FIELD_NAME, Modifier.PRIVATE, Modifier.FINAL) + FieldSpec.builder( + unionClass.nestedClass(INNER_UNION_TYPE_NAME), + UNION_FIELD_NAME, + Modifier.PRIVATE, + Modifier.FINAL) .addAnnotation(JsonUnwrapped.class) .build()); } @@ -138,7 +143,8 @@ private static MethodSpec generateConstructor(TypeMapper typeMapper, ClassName u return MethodSpec.constructorBuilder() .addModifiers(Modifier.PRIVATE) .addAnnotation(AnnotationSpec.builder(JsonCreator.class).build()) - .addParameter(ParameterSpec.builder(unionClass.nestedClass("Union"), UNION_FIELD_NAME).build()) + .addParameter(ParameterSpec.builder( + unionClass.nestedClass(INNER_UNION_TYPE_NAME), UNION_FIELD_NAME).build()) .addStatement(Expressions.requireNonNull(UNION_FIELD_NAME, "union must not be null")) .addStatement("this.$1L = $1L", UNION_FIELD_NAME) .build(); @@ -156,7 +162,11 @@ private static List generateStaticFactories( .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(memberType, VALUE_PARAM) .addStatement("return new $1T($2T.builder().type($3S).$4N($5N).build())", - unionClass, unionClass.nestedClass("Union"), memberName, safeName, VALUE_PARAM) + unionClass, + unionClass.nestedClass(INNER_UNION_TYPE_NAME), + memberName, + safeName, + VALUE_PARAM) .returns(unionClass); memberTypeDef.getDocs() .ifPresent(docs -> builder.addJavadoc("$L", StringUtils.appendIfMissing(docs.get(), "\n"))); diff --git a/conjure-java-core/src/test/resources/example-types.yml b/conjure-java-core/src/test/resources/example-types.yml index 7b0e3faa9..3e960ef7c 100644 --- a/conjure-java-core/src/test/resources/example-types.yml +++ b/conjure-java-core/src/test/resources/example-types.yml @@ -156,3 +156,7 @@ types: type: string memoizedHashCode: type: integer + Union: + union: + foo: string + bar: integer