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