diff --git a/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java b/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java new file mode 100644 index 0000000000..e72f291936 --- /dev/null +++ b/src/main/java/com/google/api/generator/engine/ast/ThisObjectValue.java @@ -0,0 +1,54 @@ +// 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.engine.ast; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; + +@AutoValue +public abstract class ThisObjectValue implements ObjectValue { + private static final String THIS_VALUE = "this"; + + @Override + public abstract TypeNode type(); + + @Override + public String value() { + return THIS_VALUE; + } + + public static ThisObjectValue withType(TypeNode type) { + return builder().setType(type).build(); + } + + private static Builder builder() { + return new AutoValue_ThisObjectValue.Builder(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract Builder setType(TypeNode type); + + abstract ThisObjectValue autoBuild(); + + private ThisObjectValue build() { + ThisObjectValue thisObjectValue = autoBuild(); + Preconditions.checkState( + TypeNode.isReferenceType(thisObjectValue.type()), + "The \"this\" object can only refer to object types"); + return thisObjectValue; + } + } +} diff --git a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel index 70e53dd433..88ed8a8fdd 100644 --- a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel +++ b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel @@ -25,6 +25,7 @@ TESTS = [ "VariableTest", "VaporReferenceTest", "MethodInvocationExprTest", + "ThisObjectValueTest", ] filegroup( diff --git a/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java b/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java new file mode 100644 index 0000000000..83e7c75ab8 --- /dev/null +++ b/src/test/java/com/google/api/generator/engine/ast/ThisObjectValueTest.java @@ -0,0 +1,51 @@ +// 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.engine.ast; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class ThisObjectValueTest { + @Test + public void validThisObjectValue_basic() { + VaporReference ref = + VaporReference.builder() + .setName("Student") + .setPakkage("com.google.example.examples.v1") + .build(); + TypeNode typeNode = TypeNode.withReference(ref); + ThisObjectValue.withType(typeNode); + // No exception thrown, we're good. + } + + @Test + public void invalidThisObjectValue_nonReferenceType() { + assertThrows( + IllegalStateException.class, + () -> { + ThisObjectValue.withType(TypeNode.DOUBLE); + }); + } + + @Test + public void invalidThisObjectValue_nullType() { + assertThrows( + IllegalStateException.class, + () -> { + ThisObjectValue.withType(TypeNode.NULL); + }); + } +} 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 0188c74797..d3f9fb4412 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 @@ -44,6 +44,7 @@ import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.StringObjectValue; import com.google.api.generator.engine.ast.TernaryExpr; +import com.google.api.generator.engine.ast.ThisObjectValue; import com.google.api.generator.engine.ast.ThrowExpr; import com.google.api.generator.engine.ast.TryCatchStatement; import com.google.api.generator.engine.ast.TypeNode; @@ -1654,6 +1655,54 @@ public void writeClassDefinition_statementsAndMethods() { "}\n")); } + @Test + public void writeThisObjectValue_methodReturn() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + MethodDefinition methodDefinition = + MethodDefinition.builder() + .setName("apply") + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.withReference(ref)) + .setReturnExpr( + ValueExpr.builder().setValue(ThisObjectValue.withType(classType)).build()) + .build(); + methodDefinition.accept(writerVisitor); + assertEquals( + writerVisitor.write(), + String.format("public Student apply() {\n" + "return this;\n" + "}\n")); + } + + @Test + public void writeThisObjectValue_accessFieldAndInvokeMethod() { + VaporReference ref = + VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build(); + TypeNode classType = TypeNode.withReference(ref); + ThisObjectValue thisObjectValue = ThisObjectValue.withType(classType); + ValueExpr thisValueExpr = ValueExpr.withValue(thisObjectValue); + VariableExpr varExpr = + VariableExpr.builder() + .setVariable(Variable.builder().setName("id").setType(TypeNode.STRING).build()) + .build(); + Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build(); + VariableExpr thisVariableExpr = + VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(thisValueExpr).build(); + + MethodInvocationExpr methodExpr = + MethodInvocationExpr.builder() + .setMethodName("getName") + .setExprReferenceExpr(ValueExpr.withValue(thisObjectValue)) + .setArguments(Arrays.asList(varExpr)) + .setReturnType(TypeNode.STRING) + .build(); + AssignmentExpr assignmentExpr = + AssignmentExpr.builder().setVariableExpr(thisVariableExpr).setValueExpr(methodExpr).build(); + + assignmentExpr.accept(writerVisitor); + assertThat(writerVisitor.write()).isEqualTo("this.name = this.getName(id)"); + } + private static String createLines(int numLines) { return new String(new char[numLines]).replace("\0", "%s"); }