Skip to content

Commit b7cf105

Browse files
authored
feat: SampleComposer, Sample, Region Tag (pt1) (#933)
* feat: SampleComposer, Sample, Region Tag * chore: update sample formatter name * refactor: writing * refactor: include sync/async region tag attribute * refactor: naming updates * refactor: update naming - tests * chore: update formatter regex Security Hotspot * chore: update regex be more explicit
1 parent 8d15cfb commit b7cf105

File tree

14 files changed

+1072
-32
lines changed

14 files changed

+1072
-32
lines changed

src/main/java/com/google/api/generator/engine/ast/ClassDefinition.java

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.api.generator.engine.ast;
1616

17+
import com.google.api.generator.gapic.model.RegionTag;
1718
import com.google.auto.value.AutoValue;
1819
import com.google.common.base.Preconditions;
1920
import com.google.common.collect.ImmutableList;
@@ -26,6 +27,9 @@
2627
public abstract class ClassDefinition implements AstNode {
2728
// Optional.
2829
public abstract ImmutableList<CommentStatement> fileHeader();
30+
// Required for samples classes.
31+
@Nullable
32+
public abstract RegionTag regionTag();
2933
// Required.
3034
public abstract ScopeNode scope();
3135
// Required.
@@ -92,6 +96,8 @@ public Builder setFileHeader(CommentStatement... headerComments) {
9296

9397
public abstract Builder setFileHeader(List<CommentStatement> fileHeader);
9498

99+
public abstract Builder setRegionTag(RegionTag regionTag);
100+
95101
public Builder setHeaderCommentStatements(CommentStatement... comments) {
96102
return setHeaderCommentStatements(Arrays.asList(comments));
97103
}

src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java

-7
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
import com.google.api.generator.engine.ast.VaporReference;
6161
import com.google.api.generator.engine.ast.VariableExpr;
6262
import com.google.api.generator.engine.ast.WhileStatement;
63-
import com.google.common.base.Preconditions;
6463
import com.google.common.base.Strings;
6564
import java.util.ArrayList;
6665
import java.util.Arrays;
@@ -357,13 +356,7 @@ public void visit(TryCatchStatement tryCatchStatement) {
357356
if (tryCatchStatement.tryResourceExpr() != null) {
358357
tryCatchStatement.tryResourceExpr().accept(this);
359358
}
360-
361359
statements(tryCatchStatement.tryBody());
362-
363-
Preconditions.checkState(
364-
!tryCatchStatement.isSampleCode() && !tryCatchStatement.catchVariableExprs().isEmpty(),
365-
"Import generation should not be invoked on sample code, but was found when visiting a"
366-
+ " try-catch block");
367360
for (int i = 0; i < tryCatchStatement.catchVariableExprs().size(); i++) {
368361
tryCatchStatement.catchVariableExprs().get(i).accept(this);
369362
statements(tryCatchStatement.catchBlocks().get(i));

src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java

+28-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.google.api.generator.engine.ast.Variable;
6464
import com.google.api.generator.engine.ast.VariableExpr;
6565
import com.google.api.generator.engine.ast.WhileStatement;
66+
import com.google.api.generator.gapic.model.RegionTag;
6667
import java.util.Arrays;
6768
import java.util.Iterator;
6869
import java.util.List;
@@ -910,6 +911,15 @@ public void visit(ClassDefinition classDefinition) {
910911
newline();
911912
}
912913

914+
String regionTagReplace = "REPLACE_REGION_TAG";
915+
if (classDefinition.regionTag() != null) {
916+
statements(
917+
Arrays.asList(
918+
classDefinition
919+
.regionTag()
920+
.generateTag(RegionTag.RegionTagRegion.START, regionTagReplace)));
921+
}
922+
913923
// This must go first, so that we can check for type collisions.
914924
classDefinition.accept(importWriterVisitor);
915925
if (!classDefinition.isNested()) {
@@ -974,10 +984,27 @@ public void visit(ClassDefinition classDefinition) {
974984
classes(classDefinition.nestedClasses());
975985

976986
rightBrace();
987+
if (classDefinition.regionTag() != null) {
988+
statements(
989+
Arrays.asList(
990+
classDefinition
991+
.regionTag()
992+
.generateTag(RegionTag.RegionTagRegion.END, regionTagReplace)));
993+
}
977994

978995
// We should have valid Java by now, so format it.
979996
if (!classDefinition.isNested()) {
980-
buffer.replace(0, buffer.length(), JavaFormatter.format(buffer.toString()));
997+
String formattedClazz = JavaFormatter.format(buffer.toString());
998+
999+
// fixing region tag after formatting
1000+
// formatter splits long region tags on multiple lines and moves the end tag up - doesn't meet
1001+
// tag requirements
1002+
if (classDefinition.regionTag() != null) {
1003+
formattedClazz =
1004+
formattedClazz.replaceAll(regionTagReplace, classDefinition.regionTag().generate());
1005+
formattedClazz = formattedClazz.replaceAll("} // \\[END", "}\n// \\[END");
1006+
}
1007+
buffer.replace(0, buffer.length(), formattedClazz);
9811008
}
9821009
}
9831010

src/main/java/com/google/api/generator/gapic/composer/comment/CommentComposer.java

+12
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import com.google.api.generator.engine.ast.BlockComment;
1818
import com.google.api.generator.engine.ast.CommentStatement;
1919
import com.google.api.generator.engine.ast.LineComment;
20+
import com.google.api.generator.engine.ast.Statement;
21+
import java.util.Arrays;
22+
import java.util.List;
2023

2124
public class CommentComposer {
2225
private static final String APACHE_LICENSE_STRING =
@@ -52,4 +55,13 @@ public class CommentComposer {
5255
public static final CommentStatement AUTO_GENERATED_METHOD_COMMENT =
5356
CommentStatement.withComment(
5457
LineComment.withComment(AUTO_GENERATED_METHOD_DISCLAIMER_STRING));
58+
59+
public static final List<Statement> AUTO_GENERATED_SAMPLE_COMMENT =
60+
Arrays.asList(
61+
CommentStatement.withComment(
62+
LineComment.withComment(
63+
"This snippet has been automatically generated for illustrative purposes only.")),
64+
CommentStatement.withComment(
65+
LineComment.withComment(
66+
"It may require modifications to work in your environment.")));
5567
}

src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeJavaFormatter.java src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeBodyJavaFormatter.java

+14-7
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@
1717
import com.google.common.annotations.VisibleForTesting;
1818
import com.google.googlejavaformat.java.Formatter;
1919
import com.google.googlejavaformat.java.FormatterException;
20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
2022

21-
public final class SampleCodeJavaFormatter {
23+
public final class SampleCodeBodyJavaFormatter {
2224

23-
private SampleCodeJavaFormatter() {}
25+
private SampleCodeBodyJavaFormatter() {}
2426

2527
private static final Formatter FORMATTER = new Formatter();
2628

27-
private static final String FAKE_CLASS_TITLE = "public class FakeClass { void fakeMethod() {";
29+
private static final String FAKE_CLASS_TITLE = "public class FakeClass { void fakeMethod() {\n";
2830
private static final String FAKE_CLASS_CLOSE = "}}";
2931

3032
/**
@@ -52,10 +54,15 @@ public static String format(String sampleCode) {
5254
// 1. Removing the first and last two lines.
5355
// 2. Delete the first 4 space for each line.
5456
// 3. Trim the last new empty line.
55-
return formattedString
56-
.replaceAll("^([^\n]*\n){2}|([^\n]*\n){2}$", "")
57-
.replaceAll("(?m)^ {4}", "")
58-
.trim();
57+
Pattern pattern = Pattern.compile("(^([^\n]*\n){2})|(([^\n]*\n){2}$)");
58+
Matcher matcher = pattern.matcher(formattedString);
59+
formattedString = matcher.replaceAll("");
60+
61+
pattern = Pattern.compile("(?m)^ {4}");
62+
matcher = pattern.matcher(formattedString);
63+
formattedString = matcher.replaceAll("");
64+
65+
return formattedString.trim();
5966
}
6067

6168
@VisibleForTesting

src/main/java/com/google/api/generator/gapic/composer/samplecode/SampleCodeWriter.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,43 @@
1414

1515
package com.google.api.generator.gapic.composer.samplecode;
1616

17+
import com.google.api.generator.engine.ast.ClassDefinition;
1718
import com.google.api.generator.engine.ast.Statement;
1819
import com.google.api.generator.engine.writer.JavaWriterVisitor;
20+
import com.google.api.generator.gapic.model.Sample;
21+
import com.google.common.annotations.VisibleForTesting;
1922
import java.util.Arrays;
2023
import java.util.List;
2124

2225
public final class SampleCodeWriter {
2326

24-
public static String write(Statement... statement) {
25-
return write(Arrays.asList(statement));
27+
public static String writeInlineSample(List<Statement> statements) {
28+
return write(SampleComposer.composeInlineSample(statements));
29+
}
30+
31+
public static String writeExecutableSample(Sample sample, String packkage) {
32+
return write(SampleComposer.composeExecutableSample(sample, packkage));
2633
}
2734

35+
@VisibleForTesting
2836
public static String write(List<Statement> statements) {
2937
JavaWriterVisitor visitor = new JavaWriterVisitor();
3038
for (Statement statement : statements) {
3139
statement.accept(visitor);
3240
}
33-
String formattedSampleCode = SampleCodeJavaFormatter.format(visitor.write());
41+
String formattedSampleCode = SampleCodeBodyJavaFormatter.format(visitor.write());
3442
// Escape character "@" in the markdown code block <pre>{@code...} tags.
3543
return formattedSampleCode.replaceAll("@", "{@literal @}");
3644
}
45+
46+
@VisibleForTesting
47+
public static String write(ClassDefinition classDefinition) {
48+
JavaWriterVisitor visitor = new JavaWriterVisitor();
49+
classDefinition.accept(visitor);
50+
return visitor.write();
51+
}
52+
53+
public static String write(Statement... statement) {
54+
return write(Arrays.asList(statement));
55+
}
3756
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.api.generator.gapic.composer.samplecode;
16+
17+
import com.google.api.generator.engine.ast.AssignmentExpr;
18+
import com.google.api.generator.engine.ast.ClassDefinition;
19+
import com.google.api.generator.engine.ast.CommentStatement;
20+
import com.google.api.generator.engine.ast.Expr;
21+
import com.google.api.generator.engine.ast.ExprStatement;
22+
import com.google.api.generator.engine.ast.MethodDefinition;
23+
import com.google.api.generator.engine.ast.MethodInvocationExpr;
24+
import com.google.api.generator.engine.ast.ScopeNode;
25+
import com.google.api.generator.engine.ast.Statement;
26+
import com.google.api.generator.engine.ast.TypeNode;
27+
import com.google.api.generator.engine.ast.Variable;
28+
import com.google.api.generator.engine.ast.VariableExpr;
29+
import com.google.api.generator.gapic.composer.comment.CommentComposer;
30+
import com.google.api.generator.gapic.model.RegionTag;
31+
import com.google.api.generator.gapic.model.Sample;
32+
import com.google.api.generator.gapic.utils.JavaStyle;
33+
import com.google.common.collect.ImmutableList;
34+
import java.util.ArrayList;
35+
import java.util.Arrays;
36+
import java.util.List;
37+
import java.util.stream.Collectors;
38+
39+
public class SampleComposer {
40+
static List<Statement> composeInlineSample(List<Statement> sampleBody) {
41+
return bodyWithComment(CommentComposer.AUTO_GENERATED_SAMPLE_COMMENT, sampleBody);
42+
}
43+
44+
// "Executable" meaning it includes the necessary code to execute java code,
45+
// still may require additional configuration to actually execute generated sample code
46+
static ClassDefinition composeExecutableSample(Sample sample, String pakkage) {
47+
return createExecutableSample(
48+
sample.fileHeader(),
49+
pakkage,
50+
sample.name(),
51+
sample.variableAssignments(),
52+
bodyWithComment(CommentComposer.AUTO_GENERATED_SAMPLE_COMMENT, sample.body()),
53+
sample.regionTag());
54+
}
55+
56+
private static List<Statement> bodyWithComment(
57+
List<Statement> autoGeneratedComment, List<Statement> sampleBody) {
58+
List<Statement> bodyWithComment = new ArrayList<>(autoGeneratedComment);
59+
bodyWithComment.addAll(sampleBody);
60+
return bodyWithComment;
61+
}
62+
63+
private static ClassDefinition createExecutableSample(
64+
List<CommentStatement> fileHeader,
65+
String packageName,
66+
String sampleClassName,
67+
List<AssignmentExpr> sampleVariableAssignments,
68+
List<Statement> sampleBody,
69+
RegionTag regionTag) {
70+
71+
String sampleMethodName = JavaStyle.toLowerCamelCase(sampleClassName);
72+
List<VariableExpr> sampleMethodArgs = composeSampleMethodArgs(sampleVariableAssignments);
73+
MethodDefinition mainMethod =
74+
composeMainMethod(
75+
composeMainBody(
76+
sampleVariableAssignments,
77+
composeInvokeMethodStatement(sampleMethodName, sampleMethodArgs)));
78+
MethodDefinition sampleMethod =
79+
composeSampleMethod(sampleMethodName, sampleMethodArgs, sampleBody);
80+
return composeSampleClass(
81+
fileHeader, packageName, sampleClassName, mainMethod, sampleMethod, regionTag);
82+
}
83+
84+
private static List<VariableExpr> composeSampleMethodArgs(
85+
List<AssignmentExpr> sampleVariableAssignments) {
86+
return sampleVariableAssignments.stream()
87+
.map(v -> v.variableExpr().toBuilder().setIsDecl(true).build())
88+
.collect(Collectors.toList());
89+
}
90+
91+
private static Statement composeInvokeMethodStatement(
92+
String sampleMethodName, List<VariableExpr> sampleMethodArgs) {
93+
List<Expr> invokeArgs =
94+
sampleMethodArgs.stream()
95+
.map(arg -> arg.toBuilder().setIsDecl(false).build())
96+
.collect(Collectors.toList());
97+
return ExprStatement.withExpr(
98+
MethodInvocationExpr.builder()
99+
.setMethodName(sampleMethodName)
100+
.setArguments(invokeArgs)
101+
.build());
102+
}
103+
104+
private static List<Statement> composeMainBody(
105+
List<AssignmentExpr> sampleVariableAssignments, Statement invokeMethod) {
106+
List<ExprStatement> setVariables =
107+
sampleVariableAssignments.stream()
108+
.map(var -> ExprStatement.withExpr(var))
109+
.collect(Collectors.toList());
110+
List<Statement> body = new ArrayList<>(setVariables);
111+
body.add(invokeMethod);
112+
return body;
113+
}
114+
115+
private static ClassDefinition composeSampleClass(
116+
List<CommentStatement> fileHeader,
117+
String packageName,
118+
String sampleClassName,
119+
MethodDefinition mainMethod,
120+
MethodDefinition sampleMethod,
121+
RegionTag regionTag) {
122+
return ClassDefinition.builder()
123+
.setFileHeader(fileHeader)
124+
.setRegionTag(regionTag)
125+
.setScope(ScopeNode.PUBLIC)
126+
.setPackageString(packageName)
127+
.setName(sampleClassName)
128+
.setMethods(ImmutableList.of(mainMethod, sampleMethod))
129+
.build();
130+
}
131+
132+
private static MethodDefinition composeMainMethod(List<Statement> mainBody) {
133+
return MethodDefinition.builder()
134+
.setScope(ScopeNode.PUBLIC)
135+
.setIsStatic(true)
136+
.setReturnType(TypeNode.VOID)
137+
.setName("main")
138+
.setArguments(
139+
VariableExpr.builder()
140+
.setVariable(
141+
Variable.builder().setType(TypeNode.STRING_ARRAY).setName("args").build())
142+
.setIsDecl(true)
143+
.build())
144+
.setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class)))
145+
.setBody(mainBody)
146+
.build();
147+
}
148+
149+
private static MethodDefinition composeSampleMethod(
150+
String sampleMethodName,
151+
List<VariableExpr> sampleMethodArgs,
152+
List<Statement> sampleMethodBody) {
153+
return MethodDefinition.builder()
154+
.setScope(ScopeNode.PUBLIC)
155+
.setIsStatic(true)
156+
.setReturnType(TypeNode.VOID)
157+
.setName(sampleMethodName)
158+
.setArguments(sampleMethodArgs)
159+
.setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class)))
160+
.setBody(sampleMethodBody)
161+
.build();
162+
}
163+
}

0 commit comments

Comments
 (0)