From 286d0ddc34bdbad601d4c16435a0c39b5604b3c0 Mon Sep 17 00:00:00 2001 From: Xiaozhen Liu Date: Tue, 18 Aug 2020 20:40:49 -0700 Subject: [PATCH] [ggj] feat: support file header (apache license) for outer class definition (#157) * add file header comment * gapic unit tests pass * fix bazel rules * clean up * format code * file header class * simplify create header * format * new line after file header * file header is optional for class * add composer test * multiple line comment * multiple line comment for header * rename apache license * move file header addition to gapic class * format * convert change in Composer * add edge unit test * move header addition to composer class * format * feedback * feedback * clean up --- .../generator/engine/ast/ClassDefinition.java | 10 +++ .../engine/writer/JavaWriterVisitor.java | 2 + .../google/api/generator/gapic/BUILD.bazel | 2 +- .../generator/gapic/composer/Composer.java | 20 +++++- .../GrpcServiceStubClassComposer.java | 7 ++- .../MockServiceImplClassComposer.java | 3 +- .../ResourceNameHelperClassComposer.java | 15 +++-- .../ServiceStubSettingsClassComposer.java | 3 +- .../generator/gapic/utils/ApacheLicense.java | 37 +++++++++++ .../api/generator/gapic/utils/BUILD.bazel | 1 + .../engine/ast/ClassDefinitionTest.java | 22 ++++++- .../engine/writer/JavaWriterVisitorTest.java | 10 ++- .../api/generator/gapic/composer/BUILD.bazel | 1 + .../gapic/composer/ComposerTest.java | 62 +++++++++++++++++++ 14 files changed, 182 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/google/api/generator/gapic/utils/ApacheLicense.java create mode 100644 src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/ClassDefinition.java b/src/main/java/com/google/api/generator/engine/ast/ClassDefinition.java index e9af44692b..65475b8d15 100644 --- a/src/main/java/com/google/api/generator/engine/ast/ClassDefinition.java +++ b/src/main/java/com/google/api/generator/engine/ast/ClassDefinition.java @@ -23,6 +23,8 @@ @AutoValue public abstract class ClassDefinition implements AstNode { + // Optional. + public abstract ImmutableList fileHeader(); // Required. public abstract ScopeNode scope(); // Required. @@ -66,6 +68,7 @@ public void accept(AstNodeVisitor visitor) { public static Builder builder() { return new AutoValue_ClassDefinition.Builder() + .setFileHeader(Collections.emptyList()) .setHeaderCommentStatements(Collections.emptyList()) .setIsNested(false) .setIsFinal(false) @@ -78,8 +81,12 @@ public static Builder builder() { .setNestedClasses(Collections.emptyList()); } + public abstract Builder toBuilder(); + @AutoValue.Builder public abstract static class Builder { + public abstract Builder setFileHeader(List fileHeader); + public abstract Builder setHeaderCommentStatements( List headerCommentStatements); @@ -129,6 +136,9 @@ public ClassDefinition build() { Preconditions.checkState(!classDef.isStatic(), "Outer classes cannot be static"); Preconditions.checkState( !classDef.scope().equals(ScopeNode.PRIVATE), "Outer classes cannot be private"); + } else { + Preconditions.checkState( + classDef.fileHeader().isEmpty(), "Nested classes cannot have a file header"); } // Abstract classes cannot be marked final. diff --git a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java index 365272d9ba..29ded3474b 100644 --- a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java +++ b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java @@ -679,6 +679,8 @@ public void visit(MethodDefinition methodDefinition) { @Override public void visit(ClassDefinition classDefinition) { if (!classDefinition.isNested()) { + statements(classDefinition.fileHeader().stream().collect(Collectors.toList())); + newline(); importWriterVisitor.initialize( classDefinition.packageString(), classDefinition.classIdentifier().name()); buffer.append(String.format("package %s;", classDefinition.packageString())); diff --git a/src/main/java/com/google/api/generator/gapic/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/BUILD.bazel index c1978825f4..4021c8018a 100644 --- a/src/main/java/com/google/api/generator/gapic/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/BUILD.bazel @@ -3,7 +3,7 @@ package(default_visibility = ["//visibility:public"]) filegroup( name = "gapic_files", srcs = glob(["*.java"]) + [ - "//src/test/java/com/google/api/generator/gapic/composer:composer_files", + "//src/main/java/com/google/api/generator/gapic/composer:composer_files", "//src/main/java/com/google/api/generator/gapic/model:model_files", "//src/main/java/com/google/api/generator/gapic/protoparser:protoparser_files", ], diff --git a/src/main/java/com/google/api/generator/gapic/composer/Composer.java b/src/main/java/com/google/api/generator/gapic/composer/Composer.java index ec7c6889d7..15aed08d4d 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/Composer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/Composer.java @@ -22,6 +22,8 @@ import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.ResourceName; import com.google.api.generator.gapic.model.Service; +import com.google.api.generator.gapic.utils.ApacheLicense; +import com.google.common.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -36,7 +38,7 @@ public static List composeServiceClasses(GapicContext context) { clazzes.addAll(generateServiceClasses(service, context.messages())); } clazzes.addAll(generateResourceNameHelperClasses(context.helperResourceNames())); - return clazzes; + return addApacheLicense(clazzes); } public static List generateServiceClasses( @@ -106,4 +108,20 @@ private static GapicClass generateGenericClass(Kind kind, String name, Service s .build(); return GapicClass.create(kind, classDef); } + + @VisibleForTesting + protected static List addApacheLicense(List gapicClassList) { + return gapicClassList.stream() + .map( + gapicClass -> { + ClassDefinition classWithHeader = + gapicClass + .classDefinition() + .toBuilder() + .setFileHeader(ApacheLicense.APACHE_LICENSE_COMMENT_STATEMENT) + .build(); + return GapicClass.create(gapicClass.kind(), classWithHeader); + }) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/com/google/api/generator/gapic/composer/GrpcServiceStubClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/GrpcServiceStubClassComposer.java index b20db179d4..de48c6713e 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/GrpcServiceStubClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/GrpcServiceStubClassComposer.java @@ -235,7 +235,8 @@ private static Statement createMethodDescriptorVariableDecl( return ExprStatement.withExpr( AssignmentExpr.builder() .setVariableExpr( - methodDescriptorVarExpr.toBuilder() + methodDescriptorVarExpr + .toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) @@ -524,7 +525,9 @@ private static List createConstructorMethods( secondCtorExprs.add( AssignmentExpr.builder() .setVariableExpr( - classMemberVarExprs.get("callableFactory").toBuilder() + classMemberVarExprs + .get("callableFactory") + .toBuilder() .setExprReferenceExpr(thisExpr) .build()) .setValueExpr(callableFactoryVarExpr) diff --git a/src/main/java/com/google/api/generator/gapic/composer/MockServiceImplClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/MockServiceImplClassComposer.java index 2ab530b612..2c322a05d4 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/MockServiceImplClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/MockServiceImplClassComposer.java @@ -202,7 +202,8 @@ private static MethodDefinition createSetResponsesMethod(Service service) { Expr responseAssignExpr = AssignmentExpr.builder() .setVariableExpr( - responsesVarExpr.toBuilder() + responsesVarExpr + .toBuilder() .setExprReferenceExpr( ValueExpr.withValue(ThisObjectValue.withType(getThisClassType(service)))) .build()) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ResourceNameHelperClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ResourceNameHelperClassComposer.java index 7cd6307182..717981ddc9 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ResourceNameHelperClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ResourceNameHelperClassComposer.java @@ -180,7 +180,9 @@ private static List createClassStatements( // "projects/{project}/locations/{location}/autoscalingPolicies/{autoscaling_policy}"); for (int i = 0; i < patterns.size(); i++) { VariableExpr varExpr = - templateFinalVarExprs.get(i).toBuilder() + templateFinalVarExprs + .get(i) + .toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) @@ -202,7 +204,9 @@ private static List createClassStatements( } memberVars.add( - FIXED_CLASS_VARS.get("fieldValuesMap").toBuilder() + FIXED_CLASS_VARS + .get("fieldValuesMap") + .toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsVolatile(true) @@ -1244,7 +1248,9 @@ private static ClassDefinition createNestedBuilderClass( .setStaticReferenceType(STATIC_TYPES.get("Objects")) .setMethodName("equals") .setArguments( - FIXED_CLASS_VARS.get("pathTemplate").toBuilder() + FIXED_CLASS_VARS + .get("pathTemplate") + .toBuilder() .setExprReferenceExpr(outerClassVarExpr) .build(), templateFinalVarExpr) @@ -1272,7 +1278,8 @@ private static ClassDefinition createNestedBuilderClass( AssignmentExpr.builder() .setVariableExpr(currClassTokenVarExpr) .setValueExpr( - currClassTokenVarExpr.toBuilder() + currClassTokenVarExpr + .toBuilder() .setExprReferenceExpr(outerClassVarExpr) .build()) .build()); diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index a54164dc85..30e68c260e 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -155,7 +155,8 @@ private static List createClassStatements( // Assign DEFAULT_SERVICE_SCOPES. VariableExpr defaultServiceScopesDeclVarExpr = - DEFAULT_SERVICE_SCOPES_VAR_EXPR.toBuilder() + DEFAULT_SERVICE_SCOPES_VAR_EXPR + .toBuilder() .setIsDecl(true) .setScope(ScopeNode.PRIVATE) .setIsStatic(true) diff --git a/src/main/java/com/google/api/generator/gapic/utils/ApacheLicense.java b/src/main/java/com/google/api/generator/gapic/utils/ApacheLicense.java new file mode 100644 index 0000000000..00de28f7e5 --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/utils/ApacheLicense.java @@ -0,0 +1,37 @@ +// Copyright 2020 Google LLC +// +// 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 com.google.api.generator.gapic.utils; + +import com.google.api.generator.engine.ast.BlockComment; +import com.google.api.generator.engine.ast.CommentStatement; +import java.util.Arrays; +import java.util.List; + +public class ApacheLicense { + private static final String fileHeaderString = + "Copyright 2020 Google LLC\n\n" + + "Licensed under the Apache License, Version 2.0 (the \"License\");\n" + + "you may not use this file except in compliance with the License.\n" + + "You may obtain a copy of the License at\n\n" + + " https://www.apache.org/licenses/LICENSE-2.0\n\n" + + "Unless required by applicable law or agreed to in writing, software\n" + + "distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + "See the License for the specific language governing permissions and\n" + + "limitations under the License."; + + public static final List APACHE_LICENSE_COMMENT_STATEMENT = + Arrays.asList(CommentStatement.withComment(BlockComment.withComment(fileHeaderString))); +} diff --git a/src/main/java/com/google/api/generator/gapic/utils/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/utils/BUILD.bazel index e4a82fdb64..934634f689 100644 --- a/src/main/java/com/google/api/generator/gapic/utils/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/utils/BUILD.bazel @@ -11,6 +11,7 @@ java_library( ":utils_files", ], deps = [ + "//src/main/java/com/google/api/generator/engine/ast", "@com_google_guava_guava//jar", ], ) diff --git a/src/test/java/com/google/api/generator/engine/ast/ClassDefinitionTest.java b/src/test/java/com/google/api/generator/engine/ast/ClassDefinitionTest.java index de898bde36..d2a0db7633 100644 --- a/src/test/java/com/google/api/generator/engine/ast/ClassDefinitionTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/ClassDefinitionTest.java @@ -23,7 +23,7 @@ public class ClassDefinitionTest { @Test - public void validClassDefinition_basicWithComments() { + public void validClassDefinition_basicWithCommentsAndHeader() { LineComment lineComment = LineComment.withComment("AUTO-GENERATED DOCUMENTATION AND CLASS"); JavaDocComment javaDocComment = JavaDocComment.builder() @@ -32,6 +32,7 @@ public void validClassDefinition_basicWithComments() { "This class is for advanced usage and reflects the underlying API directly.") .build(); ClassDefinition.builder() + .setFileHeader(createFileHeader()) .setHeaderCommentStatements( Arrays.asList( CommentStatement.withComment(lineComment), @@ -118,6 +119,21 @@ public void validClassDefinition_statementsAndMethods() { // No exception thrown, we're good. } + @Test + public void invalidClassDefinition_nestedWithFileHeader() { + assertThrows( + IllegalStateException.class, + () -> { + ClassDefinition.builder() + .setPackageString("com.google.example.library.v1.stub") + .setName("LibraryServiceStub") + .setIsNested(true) + .setScope(ScopeNode.PUBLIC) + .setFileHeader(createFileHeader()) + .build(); + }); + } + @Test public void invalidClassDefinition_implementsNullType() { assertThrows( @@ -347,4 +363,8 @@ private static ForStatement createForStatement() { .setBody(Arrays.asList(ExprStatement.withExpr(createAssignmentExpr()))) .build(); } + // Create a simple block comment to stand for the Apache License header. + private static List createFileHeader() { + return Arrays.asList(CommentStatement.withComment(BlockComment.withComment("Apache License"))); + } } diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index 80d8f4b6c6..3cc2beac3e 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -1610,9 +1610,12 @@ public void writeMethodDefinition_templatedReturnTypeAndArguments() { } @Test - public void writeClassDefinition_basic() { + public void writeClassDefinition_basicWithFileHeader() { + List fileHeader = + Arrays.asList(CommentStatement.withComment(BlockComment.withComment("Apache License"))); ClassDefinition classDef = ClassDefinition.builder() + .setFileHeader(fileHeader) .setPackageString("com.google.example.library.v1.stub") .setName("LibraryServiceStub") .setScope(ScopeNode.PUBLIC) @@ -1622,7 +1625,10 @@ public void writeClassDefinition_basic() { assertEquals( writerVisitor.write(), String.format( - createLines(3), + createLines(6), + "/*\n", + " * Apache License\n", + " */\n\n", "package com.google.example.library.v1.stub;\n", "\n", "public class LibraryServiceStub {}\n")); diff --git a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel index cb68dd14f1..1f13280759 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) TESTS = [ + "ComposerTest", "GrpcServiceCallableFactoryClassComposerTest", "GrpcServiceStubClassComposerTest", "MockServiceClassComposerTest", diff --git a/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java new file mode 100644 index 0000000000..e5102b3211 --- /dev/null +++ b/src/test/java/com/google/api/generator/gapic/composer/ComposerTest.java @@ -0,0 +1,62 @@ +// Copyright 2020 Google LLC +// +// 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 com.google.api.generator.gapic.composer; + +import static junit.framework.Assert.assertEquals; + +import com.google.api.generator.engine.ast.ClassDefinition; +import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.writer.JavaWriterVisitor; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.GapicClass.Kind; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; + +public class ComposerTest { + @Test + public void gapicClass_addApacheLicense() { + ClassDefinition classDef = + ClassDefinition.builder() + .setPackageString("com.google.showcase.v1beta1.stub") + .setName("EchoStubSettings") + .setScope(ScopeNode.PUBLIC) + .build(); + List gapicClassWithHeaderList = + Composer.addApacheLicense(Arrays.asList(GapicClass.create(Kind.TEST, classDef))); + JavaWriterVisitor visitor = new JavaWriterVisitor(); + gapicClassWithHeaderList.get(0).classDefinition().accept(visitor); + assertEquals(visitor.write(), EXPECTED_CLASS_STRING); + } + + private static final String EXPECTED_CLASS_STRING = + "/*\n" + + " * Copyright 2020 Google LLC\n" + + " *\n" + + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + + " * you may not use this file except in compliance with the License.\n" + + " * You may obtain a copy of the License at\n" + + " *\n" + + " * https://www.apache.org/licenses/LICENSE-2.0\n" + + " *\n" + + " * Unless required by applicable law or agreed to in writing, software\n" + + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + " * See the License for the specific language governing permissions and\n" + + " * limitations under the License.\n" + + " */\n\n" + + "package com.google.showcase.v1beta1.stub;\n\n" + + "public class EchoStubSettings {}\n"; +}