1 // Copyright 2020 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.engine.ast;
16 
17 import com.google.auto.value.AutoValue;
18 import com.google.common.base.Preconditions;
19 import com.google.common.collect.ImmutableList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 
24 @AutoValue
25 public abstract class AnonymousClassExpr implements Expr {
26   @Override
type()27   public abstract TypeNode type();
28 
methods()29   public abstract ImmutableList<MethodDefinition> methods();
30 
statements()31   public abstract ImmutableList<Statement> statements();
32 
33   // TODO(unsupported): nested class is not supported.
34   @Override
accept(AstNodeVisitor visitor)35   public void accept(AstNodeVisitor visitor) {
36     visitor.visit(this);
37   }
38 
builder()39   public static Builder builder() {
40     return new AutoValue_AnonymousClassExpr.Builder()
41         .setMethods(Collections.emptyList())
42         .setStatements(Collections.emptyList());
43   }
44 
45   @AutoValue.Builder
46   public abstract static class Builder {
setType(TypeNode type)47     public abstract Builder setType(TypeNode type);
48 
setMethods(MethodDefinition... methods)49     public Builder setMethods(MethodDefinition... methods) {
50       return setMethods(Arrays.asList(methods));
51     }
52 
setMethods(List<MethodDefinition> methods)53     public abstract Builder setMethods(List<MethodDefinition> methods);
54 
setStatements(List<Statement> statements)55     public abstract Builder setStatements(List<Statement> statements);
56 
autoBuild()57     public abstract AnonymousClassExpr autoBuild();
58 
build()59     public AnonymousClassExpr build() {
60       AnonymousClassExpr anonymousClassExpr = autoBuild();
61 
62       // 1. The anonymous class expression should be reference types.
63       Preconditions.checkState(
64           TypeNode.isReferenceType(anonymousClassExpr.type()),
65           "Anonymous class expression must be reference types.");
66 
67       // 2. Check that there are no null methods or statements.
68       String contextInfo =
69           String.format("anonymous class of type %s", anonymousClassExpr.type().reference().name());
70       NodeValidator.checkNoNullElements(anonymousClassExpr.methods(), "methods", contextInfo);
71       NodeValidator.checkNoNullElements(anonymousClassExpr.statements(), "statements", contextInfo);
72 
73       for (MethodDefinition method : anonymousClassExpr.methods()) {
74         // 3. Static methods are not allowed in anonymous class.
75         Preconditions.checkState(!method.isStatic(), "Anonymous class cannot have static methods.");
76         // 4. Anonymous class cannot have explicit constructors.
77         Preconditions.checkState(
78             !method.isConstructor(), "Anonymous class cannot have explicit constructors.");
79       }
80       // 5. Static variable expression is not allowed unless it is final.
81       for (Statement statement : anonymousClassExpr.statements()) {
82         if (statement instanceof ExprStatement) {
83           Expr expr = ((ExprStatement) statement).expression();
84           if (expr instanceof VariableExpr) {
85             Preconditions.checkState(
86                 validVariableExpr((VariableExpr) expr),
87                 "Anonymous class cannot have static variable expression unless it is final.");
88           } else if (expr instanceof AssignmentExpr) {
89             Preconditions.checkState(
90                 validVariableExpr(((AssignmentExpr) expr).variableExpr()),
91                 "Anonymous class cannot have static variable expression in assignment expression"
92                     + " unless it is final.");
93           }
94         }
95       }
96       return anonymousClassExpr;
97     }
98 
validVariableExpr(VariableExpr expr)99     private boolean validVariableExpr(VariableExpr expr) {
100       return !expr.isStatic() || expr.isFinal();
101     }
102   }
103 }
104