From 4ccbab3039030c1be27b728f9ed6f7fb9ba7a8a6 Mon Sep 17 00:00:00 2001 From: Thomas Farr Date: Fri, 30 Aug 2024 12:13:45 +1200 Subject: [PATCH] Invert abstractness of generated vs "handwritten" client classes (#1170) * Invert abstractness of generated vs "handwritten" client classes Signed-off-by: Thomas Farr * Address review comments Signed-off-by: Thomas Farr --------- Signed-off-by: Thomas Farr (cherry picked from commit 42e2928e3edb8aceded93a9ff9e5b21dd2cbbd70) --- .../client/codegen/model/Namespace.java | 45 +++++--- .../client/codegen/model/RequestShape.java | 2 +- .../client/codegen/model/Shape.java | 6 +- .../codegen/model/ShapeRenderingContext.java | 17 ++- .../client/codegen/model/SpecTransformer.java | 4 +- .../opensearch/client/codegen/model/Type.java | 69 +++++++----- .../model/TypeParameterDefinition.java | 71 ++++++++++++ .../codegen/model/TypeParameterDiamond.java | 78 +++++++++++++ .../client/codegen/model/Types.java | 105 +++++++++--------- .../client/codegen/openapi/OpenApiSchema.java | 17 ++- .../codegen/renderer/JavaCodeFormatter.java | 17 ++- .../renderer/TemplateGlobalContext.java | 17 ++- .../codegen/renderer/TemplateLoader.java | 17 ++- .../codegen/renderer/TemplateRenderer.java | 17 ++- .../renderer/TemplateValueFormatter.java | 16 ++- .../client/codegen/utils/ObjectBuilder.java | 16 +++ .../codegen/utils/ObjectBuilderBase.java | 34 ++++++ .../client/codegen/templates/Client.mustache | 4 + .../Partials/ClassDeclaration.mustache | 2 +- 19 files changed, 413 insertions(+), 141 deletions(-) create mode 100644 java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDefinition.java create mode 100644 java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDiamond.java create mode 100644 java-codegen/src/main/java/org/opensearch/client/codegen/utils/ObjectBuilder.java create mode 100644 java-codegen/src/main/java/org/opensearch/client/codegen/utils/ObjectBuilderBase.java diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Namespace.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Namespace.java index e0f52eb63d..68c6a4feb5 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Namespace.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Namespace.java @@ -82,8 +82,10 @@ public void render(ShapeRenderingContext ctx) throws RenderException { if (operations.isEmpty()) return; - new Client(this, false, operations).render(ctx); - new Client(this, true, operations).render(ctx); + var asBaseClass = "".equals(name); + + new Client(this, false, asBaseClass, operations).render(ctx); + new Client(this, true, asBaseClass, operations).render(ctx); } private Collection getOperationsForClient() { @@ -95,32 +97,37 @@ private String getClientClassName(boolean async, boolean base) { return "OpenSearch" + Strings.toPascalCase(name) + (async ? "Async" : "") + "Client" + (base ? "Base" : ""); } - private Type getClientType(boolean async, boolean base) { - var type = Type.builder().pkg(getPackageName()).name(getClientClassName(async, base)); - if (base) { - type.typeParams(getClientType(async, false)); - } - return type.build(); + private Type getClientType(boolean async) { + return Type.builder().withPackage(getPackageName()).withName(getClientClassName(async, false)).build(); } private static class Client extends Shape { private final boolean async; + private final boolean base; private final Collection operations; - private Client(Namespace parent, boolean async, Collection operations) { - super(parent, parent.getClientClassName(async, false), null, "Client for the " + parent.name + " namespace."); + private Client(Namespace parent, boolean async, boolean base, Collection operations) { + super(parent, parent.getClientClassName(async, base), null, "Client for the " + parent.name + " namespace."); this.async = async; + this.base = base; this.operations = operations; } + @Override + public TypeParameterDiamond getTypeParameters() { + if (!base) return null; + var thisType = getType().withTypeParams(Type.builder().withName("Self").build()); + return TypeParameterDiamond.builder() + .withParams(TypeParameterDefinition.builder().withName("Self").withExtends(thisType).build()) + .build(); + } + @Override public Type getExtendsType() { - switch (parent.name) { - case "": - return parent.getClientType(async, true); - default: - return Types.Client.ApiClient(Types.Client.Transport.OpenSearchTransport, getType()); - } + return Types.Client.ApiClient( + Types.Client.Transport.OpenSearchTransport, + !base ? getType() : Type.builder().withName("Self").build() + ); } public String getName() { @@ -139,6 +146,10 @@ public boolean isAsync() { return this.async; } + public boolean isBase() { + return this.base; + } + @Override public String toString() { return new ToStringBuilder(this).append("type", getType()).toString(); @@ -149,7 +160,7 @@ private static class ClientRef { private final String name; public ClientRef(Namespace namespace, boolean async) { - this.type = namespace.getClientType(async, false); + this.type = namespace.getClientType(async); this.name = namespace.name; } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/RequestShape.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/RequestShape.java index 53972b826c..2de1320733 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/model/RequestShape.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/RequestShape.java @@ -78,7 +78,7 @@ public void addSupportedHttpMethod(String method) { } public Type getResponseType() { - return Type.builder().pkg(getPackageName()).name(responseClassName(operationGroup)).build(); + return Type.builder().withPackage(getPackageName()).withName(responseClassName(operationGroup)).build(); } public boolean canBeSingleton() { diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Shape.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Shape.java index bb59217c11..8f6eaa5c04 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Shape.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Shape.java @@ -61,6 +61,10 @@ public Collection getAnnotations() { return Collections.emptyList(); } + public TypeParameterDiamond getTypeParameters() { + return null; + } + public Type getExtendsType() { return null; } @@ -70,7 +74,7 @@ public Collection getImplementsTypes() { } public Type getType() { - return Type.builder().pkg(getPackageName()).name(className).build(); + return Type.builder().withPackage(getPackageName()).withName(className).build(); } public Namespace getParent() { diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/ShapeRenderingContext.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/ShapeRenderingContext.java index 0a99f20042..097dee806c 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/model/ShapeRenderingContext.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/ShapeRenderingContext.java @@ -16,6 +16,7 @@ import org.opensearch.client.codegen.renderer.TemplateLoader; import org.opensearch.client.codegen.renderer.TemplateRenderer; import org.opensearch.client.codegen.renderer.TemplateValueFormatter; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; import org.opensearch.client.codegen.utils.Strings; public final class ShapeRenderingContext implements AutoCloseable { @@ -71,12 +72,21 @@ public static Builder builder() { return new Builder(); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private File outputDir; private TemplateLoader templateLoader; private JavaCodeFormatter javaCodeFormatter; private boolean ownedJavaCodeFormatter; + private Builder() { + super(ShapeRenderingContext::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withOutputDir(@Nonnull File outputDir) { this.outputDir = Objects.requireNonNull(outputDir, "outputDir must not be null"); @@ -118,10 +128,5 @@ public Builder withJavaCodeFormatter(@Nonnull Function PRIMITIVES = Set.of( @@ -44,37 +46,41 @@ public static Builder builder() { return new Builder(); } - private final String pkg; + @Nullable + private final String packageName; + @Nonnull private final String name; + @Nullable private final Type[] typeParams; private final boolean isEnum; private Type(Builder builder) { - this.pkg = builder.pkg; - this.name = builder.name; + this.packageName = builder.packageName; + this.name = Strings.requireNonBlank(builder.name, "name must not be blank"); this.typeParams = builder.typeParams; this.isEnum = builder.isEnum; } public Builder toBuilder() { - return new Builder().pkg(pkg).name(name).typeParams(typeParams).isEnum(isEnum); + return new Builder().withPackage(packageName).withName(name).withTypeParameters(typeParams).isEnum(isEnum); } @Override public String toString() { - String str = name; - if (typeParams != null && typeParams.length > 0) { - str += "<"; - str += Arrays.stream(typeParams).map(Type::toString).collect(Collectors.joining(", ")); - str += ">"; + var out = new StringBuilder(); + out.append(name); + if (typeParams != null) { + TypeParameterDiamond.builder().withParams(typeParams).build().toString(out); } - return str; + return out.toString(); } + @Nonnull public String getName() { return name; } + @Nonnull public Type getBoxed() { switch (name) { case "char": @@ -171,7 +177,7 @@ public Type getBuilderFnType() { } public Type getNestedType(String name) { - return builder().pkg(pkg).name(this.name + "." + name).build(); + return builder().withPackage(packageName).withName(this.name + "." + name).build(); } public Mustache.Lambda serializer() { @@ -183,9 +189,9 @@ public Mustache.Lambda directSerializer() { } public void getRequiredImports(Set imports, String currentPkg) { - if (pkg != null && !pkg.equals(Java.Lang.PACKAGE) && !pkg.equals(currentPkg)) { + if (packageName != null && !packageName.equals(Java.Lang.PACKAGE) && !packageName.equals(currentPkg)) { var dotIdx = name.indexOf('.'); - imports.add(pkg + '.' + (dotIdx > 0 ? name.substring(0, dotIdx) : name)); + imports.add(packageName + '.' + (dotIdx > 0 ? name.substring(0, dotIdx) : name)); } if (typeParams != null) { for (Type arg : typeParams) { @@ -195,7 +201,7 @@ public void getRequiredImports(Set imports, String currentPkg) { } public Type withTypeParams(Type... typeParams) { - return toBuilder().typeParams(typeParams).build(); + return toBuilder().withTypeParameters(typeParams).build(); } public Mustache.Lambda queryParamify() { @@ -206,34 +212,43 @@ public Mustache.Lambda isDefined() { return new TypeIsDefinedLambda(this); } - public static final class Builder { - private String pkg; + public static final class Builder extends ObjectBuilderBase { + private String packageName; private String name; private Type[] typeParams; private boolean isEnum; - public Builder pkg(String pkg) { - this.pkg = pkg; + private Builder() { + super(Type::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + + @Nonnull + public Builder withPackage(@Nullable String packageName) { + this.packageName = packageName; return this; } - public Builder name(String name) { - this.name = name; + @Nonnull + public Builder withName(@Nonnull String name) { + this.name = Strings.requireNonBlank(name, "name must not be blank"); return this; } - public Builder typeParams(Type... typeParams) { + @Nonnull + public Builder withTypeParameters(@Nullable Type... typeParams) { this.typeParams = typeParams; return this; } + @Nonnull public Builder isEnum(boolean isEnum) { this.isEnum = isEnum; return this; } - - public Type build() { - return new Type(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDefinition.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDefinition.java new file mode 100644 index 0000000000..1fb3e9d656 --- /dev/null +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDefinition.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.codegen.model; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; +import org.opensearch.client.codegen.utils.Strings; + +public final class TypeParameterDefinition { + @Nonnull + private final String name; + @Nullable + private final Type extendsType; + + private TypeParameterDefinition(Builder builder) { + this.name = Strings.requireNonBlank(builder.name, "name must not be blank"); + this.extendsType = builder.extendsType; + } + + @Nonnull + public String getName() { + return name; + } + + @Nullable + public Type getExtends() { + return extendsType; + } + + @Override + public String toString() { + return name + (extendsType != null ? " extends " + extendsType : ""); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder extends ObjectBuilderBase { + private String name; + private Type extendsType; + + private Builder() { + super(TypeParameterDefinition::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + + @Nonnull + public Builder withName(@Nonnull String name) { + this.name = Strings.requireNonBlank(name, "name must not be blank"); + return this; + } + + @Nonnull + public Builder withExtends(@Nullable Type type) { + this.extendsType = type; + return this; + } + } +} diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDiamond.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDiamond.java new file mode 100644 index 0000000000..94885e5831 --- /dev/null +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/TypeParameterDiamond.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.codegen.model; + +import java.util.Objects; +import java.util.function.Function; +import javax.annotation.Nonnull; +import org.opensearch.client.codegen.utils.Either; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; + +public final class TypeParameterDiamond { + @Nonnull + private final Either params; + + private TypeParameterDiamond(Builder builder) { + this.params = Objects.requireNonNull(builder.params, "params must not be null"); + } + + public void toString(@Nonnull StringBuilder out) { + Objects.requireNonNull(out, "out must not be null"); + var params = this.params.fold(Function.identity(), Function.identity()); + out.append("<"); + for (var i = 0; i < params.length; i++) { + if (i > 0) { + out.append(", "); + } + out.append(params[i].toString()); + } + out.append(">"); + } + + public Type[] getParamTypes() { + return params.getLeft(); + } + + @Override + public @Nonnull String toString() { + var out = new StringBuilder(); + toString(out); + return out.toString(); + } + + @Nonnull + public static Builder builder() { + return new Builder(); + } + + public static class Builder extends ObjectBuilderBase { + private Either params; + + private Builder() { + super(TypeParameterDiamond::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + + @Nonnull + public Builder withParams(@Nonnull Type... params) { + this.params = Either.left(Objects.requireNonNull(params, "params must not be null")); + return this; + } + + @Nonnull + public Builder withParams(@Nonnull TypeParameterDefinition... params) { + this.params = Either.right(Objects.requireNonNull(params, "params must not be null")); + return this; + } + } +} diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Types.java b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Types.java index 1ce5a4806f..a0c3af44b0 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/model/Types.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/model/Types.java @@ -40,11 +40,11 @@ private static Map asMap(Class clazz) { } public static final class Primitive { - public static final Type Boolean = Type.builder().name("boolean").build(); - public static final Type Int = Type.builder().name("int").build(); - public static final Type Long = Type.builder().name("long").build(); - public static final Type Float = Type.builder().name("float").build(); - public static final Type Double = Type.builder().name("double").build(); + public static final Type Boolean = Type.builder().withName("boolean").build(); + public static final Type Int = Type.builder().withName("int").build(); + public static final Type Long = Type.builder().withName("long").build(); + public static final Type Float = Type.builder().withName("float").build(); + public static final Type Double = Type.builder().withName("double").build(); } public static final class Java { @@ -52,57 +52,57 @@ public static final class Java { public static final class Io { public static final String PACKAGE = Java.PACKAGE + ".io"; - public static final Type IOException = Type.builder().pkg(PACKAGE).name("IOException").build(); + public static final Type IOException = Type.builder().withPackage(PACKAGE).withName("IOException").build(); } public static final class Lang { public static final String PACKAGE = Java.PACKAGE + ".lang"; - public static final Type String = Type.builder().pkg(PACKAGE).name("String").build(); - public static final Type Character = Type.builder().pkg(PACKAGE).name("Character").build(); - public static final Type Boolean = Type.builder().pkg(PACKAGE).name("Boolean").build(); - public static final Type Byte = Type.builder().pkg(PACKAGE).name("Byte").build(); - public static final Type Short = Type.builder().pkg(PACKAGE).name("Short").build(); - public static final Type Integer = Type.builder().pkg(PACKAGE).name("Integer").build(); - public static final Type Long = Type.builder().pkg(PACKAGE).name("Long").build(); - public static final Type Float = Type.builder().pkg(PACKAGE).name("Float").build(); - public static final Type Double = Type.builder().pkg(PACKAGE).name("Double").build(); - public static final Type Object = Type.builder().pkg(PACKAGE).name("Object").build(); + public static final Type String = Type.builder().withPackage(PACKAGE).withName("String").build(); + public static final Type Character = Type.builder().withPackage(PACKAGE).withName("Character").build(); + public static final Type Boolean = Type.builder().withPackage(PACKAGE).withName("Boolean").build(); + public static final Type Byte = Type.builder().withPackage(PACKAGE).withName("Byte").build(); + public static final Type Short = Type.builder().withPackage(PACKAGE).withName("Short").build(); + public static final Type Integer = Type.builder().withPackage(PACKAGE).withName("Integer").build(); + public static final Type Long = Type.builder().withPackage(PACKAGE).withName("Long").build(); + public static final Type Float = Type.builder().withPackage(PACKAGE).withName("Float").build(); + public static final Type Double = Type.builder().withPackage(PACKAGE).withName("Double").build(); + public static final Type Object = Type.builder().withPackage(PACKAGE).withName("Object").build(); } public static final class Util { public static final String PACKAGE = Java.PACKAGE + ".util"; - public static final Type HashMap = Type.builder().pkg(PACKAGE).name("HashMap").build(); + public static final Type HashMap = Type.builder().withPackage(PACKAGE).withName("HashMap").build(); public static Type Map(Type keyType, Type valueType) { return Map.withTypeParams(keyType, valueType); } - public static final Type Map = Type.builder().pkg(PACKAGE).name("Map").build(); + public static final Type Map = Type.builder().withPackage(PACKAGE).withName("Map").build(); public static Type MapEntry(Type keyType, Type valueType) { - return Type.builder().pkg(PACKAGE).name("Map.Entry").typeParams(keyType, valueType).build(); + return Type.builder().withPackage(PACKAGE).withName("Map.Entry").withTypeParameters(keyType, valueType).build(); } public static Type List(Type valueType) { - return Type.builder().pkg(PACKAGE).name("List").typeParams(valueType).build(); + return Type.builder().withPackage(PACKAGE).withName("List").withTypeParameters(valueType).build(); } public static final class Concurrent { public static final String PACKAGE = Util.PACKAGE + ".concurrent"; - public static final Type CompletableFuture = Type.builder().pkg(PACKAGE).name("CompletableFuture").build(); + public static final Type CompletableFuture = Type.builder().withPackage(PACKAGE).withName("CompletableFuture").build(); } public static final class Function { public static final String PACKAGE = Util.PACKAGE + ".function"; public static Type Function(Type argType, Type returnType) { - return Type.builder().pkg(PACKAGE).name("Function").typeParams(argType, returnType).build(); + return Type.builder().withPackage(PACKAGE).withName("Function").withTypeParameters(argType, returnType).build(); } } public static final class Stream { public static final String PACKAGE = Util.PACKAGE + ".stream"; - public static final Type Collectors = Type.builder().pkg(PACKAGE).name("Collectors").build(); + public static final Type Collectors = Type.builder().withPackage(PACKAGE).withName("Collectors").build(); } } } @@ -112,8 +112,8 @@ public static final class Javax { public static final class Annotation { public static final String PACKAGE = Javax.PACKAGE + ".annotation"; - public static final Type Generated = Type.builder().pkg(PACKAGE).name("Generated").build(); - public static final Type Nullable = Type.builder().pkg(PACKAGE).name("Nullable").build(); + public static final Type Generated = Type.builder().withPackage(PACKAGE).withName("Generated").build(); + public static final Type Nullable = Type.builder().withPackage(PACKAGE).withName("Nullable").build(); } } @@ -124,20 +124,23 @@ public static Type ApiClient(Type transport, Type client) { return ApiClient.withTypeParams(transport, client); } - public static final Type ApiClient = Type.builder().pkg(PACKAGE).name("ApiClient").build(); + public static final Type ApiClient = Type.builder().withPackage(PACKAGE).withName("ApiClient").build(); public static final class Json { public static final String PACKAGE = Client.PACKAGE + ".json"; - public static final Type JsonData = Type.builder().pkg(PACKAGE).name("JsonData").build(); - public static final Type JsonpDeserializable = Type.builder().pkg(PACKAGE).name("JsonpDeserializable").build(); - public static final Type JsonpDeserializer = Type.builder().pkg(PACKAGE).name("JsonpDeserializer").build(); - public static final Type JsonEnum = Type.builder().pkg(PACKAGE).name("JsonEnum").build(); - public static final Type JsonpMapper = Type.builder().pkg(PACKAGE).name("JsonpMapper").build(); - public static final Type JsonpSerializable = Type.builder().pkg(PACKAGE).name("JsonpSerializable").build(); - public static final Type ObjectBuilderDeserializer = Type.builder().pkg(PACKAGE).name("ObjectBuilderDeserializer").build(); - public static final Type ObjectDeserializer = Type.builder().pkg(PACKAGE).name("ObjectDeserializer").build(); - public static final Type PlainJsonSerializable = Type.builder().pkg(PACKAGE).name("PlainJsonSerializable").build(); - public static final Type UnionDeserializer = Type.builder().pkg(PACKAGE).name("UnionDeserializer").build(); + public static final Type JsonData = Type.builder().withPackage(PACKAGE).withName("JsonData").build(); + public static final Type JsonpDeserializable = Type.builder().withPackage(PACKAGE).withName("JsonpDeserializable").build(); + public static final Type JsonpDeserializer = Type.builder().withPackage(PACKAGE).withName("JsonpDeserializer").build(); + public static final Type JsonEnum = Type.builder().withPackage(PACKAGE).withName("JsonEnum").build(); + public static final Type JsonpMapper = Type.builder().withPackage(PACKAGE).withName("JsonpMapper").build(); + public static final Type JsonpSerializable = Type.builder().withPackage(PACKAGE).withName("JsonpSerializable").build(); + public static final Type ObjectBuilderDeserializer = Type.builder() + .withPackage(PACKAGE) + .withName("ObjectBuilderDeserializer") + .build(); + public static final Type ObjectDeserializer = Type.builder().withPackage(PACKAGE).withName("ObjectDeserializer").build(); + public static final Type PlainJsonSerializable = Type.builder().withPackage(PACKAGE).withName("PlainJsonSerializable").build(); + public static final Type UnionDeserializer = Type.builder().withPackage(PACKAGE).withName("UnionDeserializer").build(); } public static final class OpenSearch { @@ -145,48 +148,48 @@ public static final class OpenSearch { public static final class _Types { public static final String PACKAGE = OpenSearch.PACKAGE + "._types"; - public static final Type ErrorResponse = Type.builder().pkg(PACKAGE).name("ErrorResponse").build(); - public static final Type OpenSearchException = Type.builder().pkg(PACKAGE).name("OpenSearchException").build(); - public static final Type RequestBase = Type.builder().pkg(PACKAGE).name("RequestBase").build(); - public static final Type Time = Type.builder().pkg(PACKAGE).name("Time").build(); + public static final Type ErrorResponse = Type.builder().withPackage(PACKAGE).withName("ErrorResponse").build(); + public static final Type OpenSearchException = Type.builder().withPackage(PACKAGE).withName("OpenSearchException").build(); + public static final Type RequestBase = Type.builder().withPackage(PACKAGE).withName("RequestBase").build(); + public static final Type Time = Type.builder().withPackage(PACKAGE).withName("Time").build(); } } public static final class Transport { public static final String PACKAGE = Client.PACKAGE + ".transport"; - public static final Type Endpoint = Type.builder().pkg(PACKAGE).name("Endpoint").build(); + public static final Type Endpoint = Type.builder().withPackage(PACKAGE).withName("Endpoint").build(); public static Type JsonEndpoint(Type requestType, Type responseType, Type errorType) { return JsonEndpoint.withTypeParams(requestType, responseType, errorType); } - public static final Type JsonEndpoint = Type.builder().pkg(PACKAGE).name("JsonEndpoint").build(); - public static final Type OpenSearchTransport = Type.builder().pkg(PACKAGE).name("OpenSearchTransport").build(); - public static final Type TransportOptions = Type.builder().pkg(PACKAGE).name("TransportOptions").build(); + public static final Type JsonEndpoint = Type.builder().withPackage(PACKAGE).withName("JsonEndpoint").build(); + public static final Type OpenSearchTransport = Type.builder().withPackage(PACKAGE).withName("OpenSearchTransport").build(); + public static final Type TransportOptions = Type.builder().withPackage(PACKAGE).withName("TransportOptions").build(); public static final class Endpoints { public static final String PACKAGE = Transport.PACKAGE + ".endpoints"; - public static final Type SimpleEndpoint = Type.builder().pkg(PACKAGE).name("SimpleEndpoint").build(); + public static final Type SimpleEndpoint = Type.builder().withPackage(PACKAGE).withName("SimpleEndpoint").build(); } } public static final class Util { public static final String PACKAGE = Client.PACKAGE + ".util"; - public static final Type ApiTypeHelper = Type.builder().pkg(PACKAGE).name("ApiTypeHelper").build(); + public static final Type ApiTypeHelper = Type.builder().withPackage(PACKAGE).withName("ApiTypeHelper").build(); public static Type ObjectBuilder(Type type) { return ObjectBuilder.withTypeParams(type); } - public static final Type ObjectBuilder = Type.builder().pkg(PACKAGE).name("ObjectBuilder").build(); - public static final Type ObjectBuilderBase = Type.builder().pkg(PACKAGE).name("ObjectBuilderBase").build(); + public static final Type ObjectBuilder = Type.builder().withPackage(PACKAGE).withName("ObjectBuilder").build(); + public static final Type ObjectBuilderBase = Type.builder().withPackage(PACKAGE).withName("ObjectBuilderBase").build(); public static Type TaggedUnion(Type tagType, Type baseType) { return TaggedUnion.withTypeParams(tagType, baseType); } - public static final Type TaggedUnion = Type.builder().pkg(PACKAGE).name("TaggedUnion").build(); - public static final Type TaggedUnionUtils = Type.builder().pkg(PACKAGE).name("TaggedUnionUtils").build(); + public static final Type TaggedUnion = Type.builder().withPackage(PACKAGE).withName("TaggedUnion").build(); + public static final Type TaggedUnionUtils = Type.builder().withPackage(PACKAGE).withName("TaggedUnionUtils").build(); } } @@ -198,7 +201,7 @@ public static final class Json { public static final class Stream { public static final String PACKAGE = Json.PACKAGE + ".stream"; - public static final Type JsonGenerator = Type.builder().pkg(PACKAGE).name("JsonGenerator").build(); + public static final Type JsonGenerator = Type.builder().withPackage(PACKAGE).withName("JsonGenerator").build(); } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/openapi/OpenApiSchema.java b/java-codegen/src/main/java/org/opensearch/client/codegen/openapi/OpenApiSchema.java index d48d1140e7..b301160c53 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/openapi/OpenApiSchema.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/openapi/OpenApiSchema.java @@ -23,6 +23,7 @@ import javax.annotation.Nullable; import org.opensearch.client.codegen.utils.Lists; import org.opensearch.client.codegen.utils.Maps; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; import org.opensearch.client.codegen.utils.Sets; import org.opensearch.client.codegen.utils.Versions; import org.semver4j.Semver; @@ -311,7 +312,7 @@ public static Builder builder() { return new Builder(); } - public static class Builder { + public static class Builder extends ObjectBuilderBase { @Nullable private OpenApiElement parent; @Nullable @@ -353,6 +354,15 @@ public static class Builder { @Nullable private Semver versionRemoved; + private Builder() { + super(OpenApiSchema::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withPointer(@Nonnull JsonPointer pointer) { this.pointer = Objects.requireNonNull(pointer, "pointer must not be null"); @@ -380,10 +390,5 @@ public Builder withAllOf(@Nullable List allOf) { this.allOf = allOf; return this; } - - @Nonnull - public OpenApiSchema build() { - return new OpenApiSchema(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/JavaCodeFormatter.java b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/JavaCodeFormatter.java index 6f5421627f..f4589c7ec7 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/JavaCodeFormatter.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/JavaCodeFormatter.java @@ -25,6 +25,7 @@ import javax.annotation.Nonnull; import org.opensearch.client.codegen.exceptions.JavaFormatterException; import org.opensearch.client.codegen.utils.MavenArtifactResolver; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; public class JavaCodeFormatter implements AutoCloseable { private final Formatter formatter; @@ -75,10 +76,19 @@ public static Builder builder() { return new Builder(); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private Path rootDir; private File eclipseFormatterConfig; + private Builder() { + super(JavaCodeFormatter::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withRootDir(@Nonnull Path rootDir) { this.rootDir = Objects.requireNonNull(rootDir, "rootDir must not be null"); @@ -90,10 +100,5 @@ public Builder withEclipseFormatterConfig(@Nonnull File eclipseFormatterConfig) this.eclipseFormatterConfig = Objects.requireNonNull(eclipseFormatterConfig, "eclipseFormatterConfig must not be null"); return this; } - - @Nonnull - public JavaCodeFormatter build() { - return new JavaCodeFormatter(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateGlobalContext.java b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateGlobalContext.java index 69e121a2fa..745d5680fe 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateGlobalContext.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateGlobalContext.java @@ -17,6 +17,7 @@ import org.apache.commons.text.StringEscapeUtils; import org.opensearch.client.codegen.model.Types; import org.opensearch.client.codegen.renderer.lambdas.TemplateStringLambda; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; import org.opensearch.client.codegen.utils.Strings; public final class TemplateGlobalContext implements Mustache.CustomContext { @@ -53,10 +54,19 @@ public static Builder builder() { .withValue("TYPES", Types.TYPES_MAP); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private final Map values = new HashMap<>(); private TemplateRenderer renderer; + private Builder() { + super(TemplateGlobalContext::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withLambda(@Nonnull String name, @Nonnull TemplateStringLambda lambda) { return withLambda(name, TemplateStringLambda.asMustacheLambda(lambda)); @@ -80,10 +90,5 @@ public Builder withRenderer(@Nonnull TemplateRenderer renderer) { this.renderer = Objects.requireNonNull(renderer, "renderer must not be null"); return this; } - - @Nonnull - public TemplateGlobalContext build() { - return new TemplateGlobalContext(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateLoader.java b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateLoader.java index 5bff6f12fd..7988c6219a 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateLoader.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateLoader.java @@ -17,6 +17,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; import org.apache.commons.io.IOUtils; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; import org.opensearch.client.codegen.utils.Strings; public final class TemplateLoader implements Mustache.TemplateLoader { @@ -57,9 +58,18 @@ public static Builder builder() { return new Builder(); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private String templatesResourceSubPath; + private Builder() { + super(TemplateLoader::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withTemplatesResourceSubPath(@Nonnull String templatesResourceSubPath) { Strings.requireNonBlank(templatesResourceSubPath, "templatesResourceSubPath must not be blank"); @@ -72,10 +82,5 @@ public Builder withTemplatesResourceSubPath(@Nonnull String templatesResourceSub this.templatesResourceSubPath = templatesResourceSubPath; return this; } - - @Nonnull - public TemplateLoader build() { - return new TemplateLoader(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateRenderer.java b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateRenderer.java index e957c528d9..1b22b4ede1 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateRenderer.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateRenderer.java @@ -23,6 +23,7 @@ import org.opensearch.client.codegen.exceptions.JavaFormatterException; import org.opensearch.client.codegen.exceptions.RenderException; import org.opensearch.client.codegen.model.Shape; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; public final class TemplateRenderer { @Nonnull @@ -82,11 +83,20 @@ public static Builder builder() { return new Builder(); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private TemplateValueFormatter valueFormatter; private TemplateLoader templateLoader; private JavaCodeFormatter javaCodeFormatter; + private Builder() { + super(TemplateRenderer::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withValueFormatter(@Nonnull Function configurator) { this.valueFormatter = Objects.requireNonNull(configurator, "configurator must not be null") @@ -106,10 +116,5 @@ public Builder withJavaCodeFormatter(@Nonnull JavaCodeFormatter javaCodeFormatte this.javaCodeFormatter = Objects.requireNonNull(javaCodeFormatter, "javaCodeFormatter must not be null"); return this; } - - @Nonnull - public TemplateRenderer build() { - return new TemplateRenderer(this); - } } } diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateValueFormatter.java b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateValueFormatter.java index 26e75de95a..12a41d10f4 100644 --- a/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateValueFormatter.java +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/renderer/TemplateValueFormatter.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Objects; import javax.annotation.Nonnull; +import org.opensearch.client.codegen.utils.ObjectBuilderBase; public final class TemplateValueFormatter implements Mustache.Formatter { @Nonnull @@ -50,9 +51,18 @@ public static Builder builder() { return new Builder(); } - public static final class Builder { + public static final class Builder extends ObjectBuilderBase { private final Map, Formatter> formatters = new HashMap<>(); + private Builder() { + super(TemplateValueFormatter::new); + } + + @Override + protected @Nonnull Builder self() { + return this; + } + @Nonnull public Builder withFormatter(@Nonnull Class clazz, @Nonnull Formatter formatter) { Objects.requireNonNull(clazz, "clazz must not be null"); @@ -60,9 +70,5 @@ public Builder withFormatter(@Nonnull Class clazz, @Nonnull Formatter { + @Nonnull + T build(); +} diff --git a/java-codegen/src/main/java/org/opensearch/client/codegen/utils/ObjectBuilderBase.java b/java-codegen/src/main/java/org/opensearch/client/codegen/utils/ObjectBuilderBase.java new file mode 100644 index 0000000000..31955ba570 --- /dev/null +++ b/java-codegen/src/main/java/org/opensearch/client/codegen/utils/ObjectBuilderBase.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.codegen.utils; + +import java.util.Objects; +import java.util.function.Function; +import javax.annotation.Nonnull; + +public abstract class ObjectBuilderBase> implements ObjectBuilder { + private final Function ctor; + private boolean _used = false; + + protected ObjectBuilderBase(Function ctor) { + this.ctor = Objects.requireNonNull(ctor, "ctor must not be null"); + } + + @Nonnull + protected abstract Self self(); + + @Override + public @Nonnull T build() { + if (this._used) { + throw new IllegalStateException("Object builders can only be used once"); + } + this._used = true; + return ctor.apply(self()); + } +} diff --git a/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Client.mustache b/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Client.mustache index 54b946985c..8547a4e05e 100644 --- a/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Client.mustache +++ b/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Client.mustache @@ -1,16 +1,20 @@ {{>Partials/ClassDeclaration}} { +{{^base}} public {{className}}({{TYPES.Client.Transport.OpenSearchTransport}} transport) { super(transport, null); } +{{/base}} public {{className}}({{TYPES.Client.Transport.OpenSearchTransport}} transport, @{{TYPES.Javax.Annotation.Nullable}} {{TYPES.Client.Transport.TransportOptions}} transportOptions) { super(transport, transportOptions); } +{{^base}} @Override public {{className}} withTransportOptions(@{{TYPES.Javax.Annotation.Nullable}} {{TYPES.Client.Transport.TransportOptions}} transportOptions) { return new {{className}}(this.transport, transportOptions); } +{{/base}} {{#children}} {{#-first}} diff --git a/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Partials/ClassDeclaration.mustache b/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Partials/ClassDeclaration.mustache index fdb7a05eea..5fa66eb9ee 100644 --- a/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Partials/ClassDeclaration.mustache +++ b/java-codegen/src/main/resources/org/opensearch/client/codegen/templates/Partials/ClassDeclaration.mustache @@ -11,4 +11,4 @@ @{{.}} {{/annotations}} @{{TYPES.Javax.Annotation.Generated}}("org.opensearch.client.codegen.CodeGenerator") -public {{#abstract}}abstract {{/abstract}}{{classKind}} {{className}}{{#extendsType}} extends {{.}}{{/extendsType}}{{#implementsTypes}}{{#-first}} implements{{/-first}} {{.}}{{^-last}},{{/-last}}{{/implementsTypes}} +public {{#abstract}}abstract {{/abstract}}{{classKind}} {{className}}{{#typeParameters}}{{.}}{{/typeParameters}}{{#extendsType}} extends {{.}}{{/extendsType}}{{#implementsTypes}}{{#-first}} implements{{/-first}} {{.}}{{^-last}},{{/-last}}{{/implementsTypes}}