diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSample.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSample.java new file mode 100644 index 0000000000..9ca1509ab3 --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSample.java @@ -0,0 +1,53 @@ +// Copyright 2021 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.samplecode; + +import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.Statement; +import java.util.List; + +public class ExecutableSample { + private final String samplePackage; + private final String sampleName; + private final List sampleVariableAssignments; + private final List sampleBody; + + public ExecutableSample( + String samplePackage, + String sampleName, + List sampleVariableAssignments, + List sampleBody) { + this.samplePackage = samplePackage; + this.sampleName = sampleName; + this.sampleVariableAssignments = sampleVariableAssignments; + this.sampleBody = sampleBody; + } + + public String getSamplePackage() { + return samplePackage; + } + + public String getSampleName() { + return sampleName; + } + + public List getSampleVariableAssignments() { + return sampleVariableAssignments; + } + + public List getSampleBody() { + return sampleBody; + } +} diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposer.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposer.java new file mode 100644 index 0000000000..5ec355f0ae --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposer.java @@ -0,0 +1,140 @@ +// Copyright 2021 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.samplecode; + +import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.ClassDefinition; +import com.google.api.generator.engine.ast.Expr; +import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.MethodDefinition; +import com.google.api.generator.engine.ast.MethodInvocationExpr; +import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.Statement; +import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.Variable; +import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.utils.JavaStyle; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ExecutableSampleComposer { + public static String createExecutableSample(ExecutableSample executableSample) { + Preconditions.checkNotNull(executableSample); + String sampleMethodName = JavaStyle.toLowerCamelCase(executableSample.getSampleName()); + return SampleCodeWriter.write( + composeExecutableSample( + executableSample.getSamplePackage(), + sampleMethodName, + executableSample.getSampleVariableAssignments(), + executableSample.getSampleBody())); + } + + private static ClassDefinition composeExecutableSample( + String packageName, + String sampleMethodName, + List sampleVariableAssignments, + List sampleBody) { + String sampleClassName = JavaStyle.toUpperCamelCase(sampleMethodName); + List sampleMethodArgs = composeSampleMethodArgs(sampleVariableAssignments); + MethodDefinition mainMethod = + composeMainMethod( + composeMainBody( + sampleVariableAssignments, + composeInvokeMethodStatement(sampleMethodName, sampleMethodArgs))); + MethodDefinition sampleMethod = + composeSampleMethod(sampleMethodName, sampleMethodArgs, sampleBody); + return composeSampleClass(packageName, sampleClassName, mainMethod, sampleMethod); + } + + private static List composeSampleMethodArgs( + List sampleVariableAssignments) { + return sampleVariableAssignments.stream() + .map(v -> v.variableExpr().toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList()); + } + + private static Statement composeInvokeMethodStatement( + String sampleMethodName, List sampleMethodArgs) { + List invokeArgs = + sampleMethodArgs.stream() + .map(arg -> arg.toBuilder().setIsDecl(false).build()) + .collect(Collectors.toList()); + return ExprStatement.withExpr( + MethodInvocationExpr.builder() + .setMethodName(sampleMethodName) + .setArguments(invokeArgs) + .build()); + } + + private static List composeMainBody( + List sampleVariableAssignments, Statement invokeMethod) { + List setVariables = + sampleVariableAssignments.stream() + .map(var -> ExprStatement.withExpr(var)) + .collect(Collectors.toList()); + List body = new ArrayList<>(setVariables); + body.add(invokeMethod); + return body; + } + + private static ClassDefinition composeSampleClass( + String packageName, + String sampleClassName, + MethodDefinition mainMethod, + MethodDefinition sampleMethod) { + return ClassDefinition.builder() + .setScope(ScopeNode.PUBLIC) + .setPackageString(packageName) + .setName(sampleClassName) + .setMethods(ImmutableList.of(mainMethod, sampleMethod)) + .build(); + } + + private static MethodDefinition composeMainMethod(List mainBody) { + return MethodDefinition.builder() + .setScope(ScopeNode.PUBLIC) + .setIsStatic(true) + .setReturnType(TypeNode.VOID) + .setName("main") + .setArguments( + VariableExpr.builder() + .setVariable( + Variable.builder().setType(TypeNode.STRING_ARRAY).setName("args").build()) + .setIsDecl(true) + .build()) + .setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class))) + .setBody(mainBody) + .build(); + } + + private static MethodDefinition composeSampleMethod( + String sampleMethodName, + List sampleMethodArgs, + List sampleMethodBody) { + return MethodDefinition.builder() + .setScope(ScopeNode.PUBLIC) + .setIsStatic(true) + .setReturnType(TypeNode.VOID) + .setName(sampleMethodName) + .setArguments(sampleMethodArgs) + .setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class))) + .setBody(sampleMethodBody) + .build(); + } +} diff --git a/src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeWriter.java b/src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeWriter.java index 07abeb9a17..c2602ba115 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeWriter.java +++ b/src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeWriter.java @@ -14,6 +14,7 @@ package com.google.api.generator.gapic.composer.samplecode; +import com.google.api.generator.engine.ast.ClassDefinition; import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.writer.JavaWriterVisitor; import java.util.Arrays; @@ -34,4 +35,10 @@ public static String write(List statements) { // Escape character "@" in the markdown code block
{@code...} tags.
     return formattedSampleCode.replaceAll("@", "{@literal @}");
   }
+
+  public static String write(ClassDefinition classDefinition) {
+    JavaWriterVisitor visitor = new JavaWriterVisitor();
+    classDefinition.accept(visitor);
+    return visitor.write();
+  }
 }
diff --git a/src/test/java/com/google/api/generator/gapic/composer/samplecode/BUILD.bazel b/src/test/java/com/google/api/generator/gapic/composer/samplecode/BUILD.bazel
index 7a1cdbb61c..81611925e9 100644
--- a/src/test/java/com/google/api/generator/gapic/composer/samplecode/BUILD.bazel
+++ b/src/test/java/com/google/api/generator/gapic/composer/samplecode/BUILD.bazel
@@ -7,6 +7,7 @@ TESTS = [
     "SampleCodeWriterTest",
     "ServiceClientSampleCodeComposerTest",
     "SettingsSampleCodeComposerTest",
+    "ExecutableSampleComposerTest"
 ]
 
 filegroup(
diff --git a/src/test/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposerTest.java
new file mode 100644
index 0000000000..c54d104b4b
--- /dev/null
+++ b/src/test/java/com/google/api/generator/gapic/composer/samplecode/ExecutableSampleComposerTest.java
@@ -0,0 +1,238 @@
+// Copyright 2021 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.samplecode;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.google.api.generator.engine.ast.AssignmentExpr;
+import com.google.api.generator.engine.ast.Expr;
+import com.google.api.generator.engine.ast.ExprStatement;
+import com.google.api.generator.engine.ast.MethodInvocationExpr;
+import com.google.api.generator.engine.ast.NewObjectExpr;
+import com.google.api.generator.engine.ast.PrimitiveValue;
+import com.google.api.generator.engine.ast.Statement;
+import com.google.api.generator.engine.ast.StringObjectValue;
+import com.google.api.generator.engine.ast.TypeNode;
+import com.google.api.generator.engine.ast.ValueExpr;
+import com.google.api.generator.engine.ast.VaporReference;
+import com.google.api.generator.engine.ast.Variable;
+import com.google.api.generator.engine.ast.VariableExpr;
+import com.google.api.generator.testutils.LineFormatter;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import org.junit.Test;
+
+public class ExecutableSampleComposerTest {
+  private final String packageName = "com.google.example";
+  private final String sampleMethodName = "echoClientMethod";
+
+  @Test
+  public void createExecutableSampleNoSample() {
+    assertThrows(
+        NullPointerException.class, () -> ExecutableSampleComposer.createExecutableSample(null));
+  }
+
+  @Test
+  public void createExecutableSampleEmptyStatementSample() {
+    ExecutableSample executableSample =
+        new ExecutableSample(packageName, sampleMethodName, new ArrayList<>(), new ArrayList<>());
+
+    String sampleResult = ExecutableSampleComposer.createExecutableSample(executableSample);
+    String expected =
+        LineFormatter.lines(
+            "package com.google.example;\n",
+            "\n",
+            "public class EchoClientMethod {\n",
+            "\n",
+            "  public static void main(String[] args) throws Exception {\n",
+            "    echoClientMethod();\n",
+            "  }\n",
+            "\n",
+            "  public static void echoClientMethod() throws Exception {}\n",
+            "}\n");
+
+    assertEquals(expected, sampleResult);
+  }
+
+  @Test
+  public void createExecutableSampleMethodArgsNoVar() {
+    Statement sampleBody = ExprStatement.withExpr(systemOutPrint("Testing " + sampleMethodName));
+    ExecutableSample executableSample =
+        new ExecutableSample(
+            packageName, sampleMethodName, new ArrayList<>(), ImmutableList.of(sampleBody));
+
+    String sampleResult = ExecutableSampleComposer.createExecutableSample(executableSample);
+    String expected =
+        LineFormatter.lines(
+            "package com.google.example;\n",
+            "\n",
+            "public class EchoClientMethod {\n",
+            "\n",
+            "  public static void main(String[] args) throws Exception {\n",
+            "    echoClientMethod();\n",
+            "  }\n",
+            "\n",
+            "  public static void echoClientMethod() throws Exception {\n",
+            "    System.out.println(\"Testing echoClientMethod\");\n",
+            "  }\n",
+            "}\n");
+
+    assertEquals(expected, sampleResult);
+  }
+
+  @Test
+  public void createExecutableSampleMethod() {
+    VariableExpr variableExpr =
+        VariableExpr.builder()
+            .setVariable(Variable.builder().setType(TypeNode.STRING).setName("content").build())
+            .setIsDecl(true)
+            .build();
+    AssignmentExpr varAssignment =
+        AssignmentExpr.builder()
+            .setVariableExpr(variableExpr)
+            .setValueExpr(
+                ValueExpr.withValue(StringObjectValue.withValue("Testing " + sampleMethodName)))
+            .build();
+    Statement sampleBody = ExprStatement.withExpr(systemOutPrint(variableExpr));
+    ExecutableSample executableSample =
+        new ExecutableSample(
+            packageName,
+            sampleMethodName,
+            ImmutableList.of(varAssignment),
+            ImmutableList.of(sampleBody));
+
+    String sampleResult = ExecutableSampleComposer.createExecutableSample(executableSample);
+    String expected =
+        LineFormatter.lines(
+            "package com.google.example;\n",
+            "\n",
+            "public class EchoClientMethod {\n",
+            "\n",
+            "  public static void main(String[] args) throws Exception {\n",
+            "    String content = \"Testing echoClientMethod\";\n",
+            "    echoClientMethod(content);\n",
+            "  }\n",
+            "\n",
+            "  public static void echoClientMethod(String content) throws Exception {\n",
+            "    System.out.println(content);\n",
+            "  }\n",
+            "}\n");
+
+    assertEquals(expected, sampleResult);
+  }
+
+  @Test
+  public void createExecutableSampleMethodMultipleStatements() {
+    VariableExpr strVariableExpr =
+        VariableExpr.builder()
+            .setVariable(Variable.builder().setType(TypeNode.STRING).setName("content").build())
+            .setIsDecl(true)
+            .build();
+    VariableExpr intVariableExpr =
+        VariableExpr.builder()
+            .setVariable(Variable.builder().setType(TypeNode.INT).setName("num").build())
+            .setIsDecl(true)
+            .build();
+    VariableExpr objVariableExpr =
+        VariableExpr.builder()
+            .setVariable(Variable.builder().setType(TypeNode.OBJECT).setName("thing").build())
+            .setIsDecl(true)
+            .build();
+    AssignmentExpr strVarAssignment =
+        AssignmentExpr.builder()
+            .setVariableExpr(strVariableExpr)
+            .setValueExpr(
+                ValueExpr.withValue(
+                    StringObjectValue.withValue("Testing ".concat(sampleMethodName))))
+            .build();
+    AssignmentExpr intVarAssignment =
+        AssignmentExpr.builder()
+            .setVariableExpr(intVariableExpr)
+            .setValueExpr(
+                ValueExpr.withValue(
+                    PrimitiveValue.builder().setType(TypeNode.INT).setValue("2").build()))
+            .build();
+    AssignmentExpr objVarAssignment =
+        AssignmentExpr.builder()
+            .setVariableExpr(objVariableExpr)
+            .setValueExpr(NewObjectExpr.builder().setType(TypeNode.OBJECT).build())
+            .build();
+
+    Statement strBodyStatement = ExprStatement.withExpr(systemOutPrint(strVariableExpr));
+    Statement intBodyStatement = ExprStatement.withExpr(systemOutPrint(intVariableExpr));
+    Statement objBodyStatement =
+        ExprStatement.withExpr(
+            systemOutPrint(
+                MethodInvocationExpr.builder()
+                    .setExprReferenceExpr(objVariableExpr.toBuilder().setIsDecl(false).build())
+                    .setMethodName("response")
+                    .build()));
+    ExecutableSample executableSample =
+        new ExecutableSample(
+            packageName,
+            sampleMethodName,
+            ImmutableList.of(strVarAssignment, intVarAssignment, objVarAssignment),
+            ImmutableList.of(strBodyStatement, intBodyStatement, objBodyStatement));
+
+    String sampleResult = ExecutableSampleComposer.createExecutableSample(executableSample);
+    String expected =
+        LineFormatter.lines(
+            "package com.google.example;\n",
+            "\n",
+            "public class EchoClientMethod {\n",
+            "\n",
+            "  public static void main(String[] args) throws Exception {\n",
+            "    String content = \"Testing echoClientMethod\";\n",
+            "    int num = 2;\n",
+            "    Object thing = new Object();\n",
+            "    echoClientMethod(content, num, thing);\n",
+            "  }\n",
+            "\n",
+            "  public static void echoClientMethod(String content, int num, Object thing) throws Exception {\n",
+            "    System.out.println(content);\n",
+            "    System.out.println(num);\n",
+            "    System.out.println(thing.response());\n",
+            "  }\n",
+            "}\n");
+
+    assertEquals(expected, sampleResult);
+  }
+
+  private Expr systemOutPrint(MethodInvocationExpr response) {
+    return composeSystemOutPrint(response);
+  }
+
+  private static MethodInvocationExpr systemOutPrint(String content) {
+    return composeSystemOutPrint(ValueExpr.withValue(StringObjectValue.withValue(content)));
+  }
+
+  private static MethodInvocationExpr systemOutPrint(VariableExpr variableExpr) {
+    return composeSystemOutPrint(variableExpr.toBuilder().setIsDecl(false).build());
+  }
+
+  private static MethodInvocationExpr composeSystemOutPrint(Expr content) {
+    VaporReference out =
+        VaporReference.builder()
+            .setEnclosingClassNames("System")
+            .setName("out")
+            .setPakkage("java.lang")
+            .build();
+    return MethodInvocationExpr.builder()
+        .setStaticReferenceType(TypeNode.withReference(out))
+        .setMethodName("println")
+        .setArguments(content)
+        .build();
+  }
+}