Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,8 @@

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;

@AutoValue
Expand All @@ -31,10 +33,8 @@ private static TypeNode annotationType(Class<?> clazz) {

public abstract TypeNode type();

// TODO(unsupported): Any args that do not consist of a single string. However, this can easily be
// extended to enable such support.
@Nullable
public abstract Expr descriptionExpr();
public abstract List<Expr> descriptionExprs();

@Override
public void accept(AstNodeVisitor visitor) {
Expand All @@ -45,6 +45,10 @@ public static AnnotationNode withTypeAndDescription(TypeNode type, String descri
return AnnotationNode.builder().setType(type).setDescription(description).build();
}

public static AnnotationNode withTypeAndDescription(TypeNode type, List<Expr> exprList) {
return AnnotationNode.builder().setType(type).setDescriptions(exprList).build();
}

public static AnnotationNode withSuppressWarnings(String description) {
return withTypeAndDescription(annotationType(SuppressWarnings.class), description);
}
Expand All @@ -62,12 +66,16 @@ public abstract static class Builder {
public abstract Builder setType(TypeNode type);

public Builder setDescription(String description) {
return setDescriptionExpr(ValueExpr.withValue(StringObjectValue.withValue(description)));
return setDescriptionExprs(
Arrays.asList(ValueExpr.withValue(StringObjectValue.withValue(description))));
}

public Builder setDescriptions(List<Expr> exprList) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this improvement in general, but we should be careful exposing a setter that can set descriptions to any Expr, you can technically pass a NewObjectExpr or a ReturnExpr and everything will work until we try to compile the generated code, which could be too late to find an issue.
Correct me if I'm wrong, I think we only allow ValueExpr, AssignmentExpr, or VariableExpr(for clazz if you remove CLazzValue). So we could either add some type checks in the build() method, or only expose methods to add descriptions for the above types, like addDescription(ValueExpr expr), addDescription(AssignmentExpr expr) and addDescription(VariableExpr expr).
Basically anything that could restrict people from passing any types of Expr would be great.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, let me refactor this part and expose methods for each expr type.

return setDescriptionExprs(exprList);
}

// This will never be anything other than a ValueExpr-wrapped StringObjectValue because
// this setter is private, and called only by setDescription above.
abstract Builder setDescriptionExpr(Expr descriptionExpr);
// this setter is private, and called only by setDescription() and setDescriptions() above.
abstract Builder setDescriptionExprs(List<Expr> descriptionExprs);

abstract AnnotationNode autoBuild();

Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/google/api/generator/engine/ast/ClazzValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright 2022 Google LLC
*
* <p>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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.engine.ast;

import com.google.auto.value.AutoValue;

@AutoValue
public abstract class ClazzValue implements ObjectValue {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a way to achieve the same thing, see the code here and the golden test file here. I think it's better not to have different ways to achieve the same thing, it would create more maintenance burden in the future. If you want, you could extract the common logic to a util method/class, something like createClazzValueExpr or createClazzVariableExpr, which only takes type as an argument.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks for the tip. I couldn't wrap my head around needing a Value instead of Variable.

@Override
public abstract TypeNode type();

@Override
public abstract String value();

public static Builder builder() {
return new AutoValue_ClazzValue.Builder();
}

@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setType(TypeNode type);

abstract Builder setValue(String value);

public abstract TypeNode type();

abstract ClazzValue autoBuild();

public ClazzValue build() {
setValue(type().reference().name() + ".class");
return autoBuild();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 Google LLC
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -169,9 +169,15 @@ public void visit(ScopeNode scope) {
public void visit(AnnotationNode annotation) {
buffer.append(AT);
annotation.type().accept(this);
if (annotation.descriptionExpr() != null) {
if (annotation.descriptionExprs() != null) {
leftParen();
annotation.descriptionExpr().accept(this);
for (int i = 0; i < annotation.descriptionExprs().size(); i++) {
annotation.descriptionExprs().get(i).accept(this);
if (i < annotation.descriptionExprs().size() - 1) {
buffer.append(COMMA);
buffer.append(SPACE);
}
}
rightParen();
}
newline();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2022 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.engine.ast;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;

import java.util.List;
import org.junit.Test;

public class ClazzValueTest {

@Test
public void createClazzValue_basic() {
TypeNode clazz = TypeNode.withReference(ConcreteReference.withClazz(List.class));

assertValidValue(ConcreteReference.withClazz(List.class), "List.class");
assertValidValue(
VaporReference.builder()
.setName("ConditionalOnProperty")
.setPakkage("org.springframework.boot.autoconfigure.condition")
.build(),
"ConditionalOnProperty.class");
}

private static void assertValidValue(Reference reference, String value) {
TypeNode type = TypeNode.withReference(reference);
ClazzValue clazzValue = ClazzValue.builder().setType(type).build();
assertEquals(value, clazzValue.value());
assertThat(clazzValue.type()).isEqualTo(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.api.generator.engine.ast.BreakStatement;
import com.google.api.generator.engine.ast.CastExpr;
import com.google.api.generator.engine.ast.ClassDefinition;
import com.google.api.generator.engine.ast.ClazzValue;
import com.google.api.generator.engine.ast.CommentStatement;
import com.google.api.generator.engine.ast.ConcreteReference;
import com.google.api.generator.engine.ast.EmptyLineStatement;
Expand Down Expand Up @@ -152,12 +153,50 @@ public void writeAnnotation_simple() {
}

@Test
public void writeAnnotation_withDescription() {
public void writeAnnotation_withStringDescription() {
AnnotationNode annotation = AnnotationNode.withSuppressWarnings("all");
annotation.accept(writerVisitor);
assertEquals("@SuppressWarnings(\"all\")\n", writerVisitor.write());
}

@Test
public void writeAnnotation_withNamedDescriptions() {
TypeNode conditionalOnPropertyType =
TypeNode.withReference(
VaporReference.builder()
.setName("ConditionalOnProperty")
.setPakkage("org.springframework.boot.autoconfigure.condition")
.build());

AssignmentExpr matchIfMissing =
AssignmentExpr.builder()
.setVariableExpr(
VariableExpr.withVariable(
Variable.builder().setName("matchIfMissing").setType(TypeNode.BOOLEAN).build()))
.setValueExpr(
ValueExpr.withValue(
PrimitiveValue.builder().setValue("false").setType(TypeNode.BOOLEAN).build()))
.build();
AssignmentExpr valueString =
AssignmentExpr.builder()
.setVariableExpr(
VariableExpr.withVariable(
Variable.builder().setName("value").setType(TypeNode.STRING).build()))
.setValueExpr(
ValueExpr.withValue(
StringObjectValue.withValue("spring.cloud.gcp.language.enabled")))
.build();
AnnotationNode annotation =
AnnotationNode.builder()
.setType(conditionalOnPropertyType)
.setDescriptions(Arrays.asList(valueString, matchIfMissing))
.build();
annotation.accept(writerVisitor);
assertEquals(
"@ConditionalOnProperty(value = \"spring.cloud.gcp.language.enabled\", matchIfMissing = false)\n",
writerVisitor.write());
}

@Test
public void writeNewObjectExpr_basic() {
// isGeneric() is true, but generics() is empty.
Expand Down Expand Up @@ -737,6 +776,22 @@ public void writeAssignmentExpr_stringObjectValue() {
assertThat(writerVisitor.write()).isEqualTo("String x = \"Hi! World. \\n\"");
}

@Test
public void writeAssignmentExpr_clazzValue() {
TypeNode clazz = TypeNode.withReference(ConcreteReference.withClazz(List.class));

VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setName("value").setType(clazz).build());

ValueExpr valueExpr = ValueExpr.withValue(ClazzValue.builder().setType(clazz).build());

AssignmentExpr assignExpr =
AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();

assignExpr.accept(writerVisitor);
assertEquals("value = List.class", writerVisitor.write());
}

@Test
public void writeMethodInvocationExpr_basic() {
MethodInvocationExpr methodExpr =
Expand Down