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 java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22 import javax.annotation.Nullable;
23 
24 @AutoValue
25 public abstract class MethodInvocationExpr implements Expr {
methodIdentifier()26   public abstract IdentifierNode methodIdentifier();
27 
returnType()28   public abstract TypeNode returnType();
29 
30   @Nullable
exprReferenceExpr()31   public abstract Expr exprReferenceExpr();
32 
33   @Nullable
staticReferenceType()34   public abstract TypeNode staticReferenceType();
35 
arguments()36   public abstract List<Expr> arguments();
37 
generics()38   public abstract List<Reference> generics();
39 
40   // Private.
methodName()41   abstract String methodName();
42 
isGeneric()43   public boolean isGeneric() {
44     return !generics().isEmpty();
45   }
46 
47   @Override
type()48   public TypeNode type() {
49     return returnType();
50   }
51 
52   @Override
accept(AstNodeVisitor visitor)53   public void accept(AstNodeVisitor visitor) {
54     visitor.visit(this);
55   }
56 
toBuilder()57   public abstract Builder toBuilder();
58 
builder()59   public static Builder builder() {
60     return new AutoValue_MethodInvocationExpr.Builder()
61         .setReturnType(TypeNode.VOID)
62         .setGenerics(Collections.emptyList())
63         .setArguments(Collections.emptyList());
64   }
65 
66   @AutoValue.Builder
67   public abstract static class Builder {
68     // Required.
setMethodName(String methodName)69     public abstract Builder setMethodName(String methodName);
70 
methodName()71     abstract String methodName();
72 
73     // Required only if this is used in an AssignmentExpr (for type-checking).
setReturnType(TypeNode type)74     public abstract Builder setReturnType(TypeNode type);
75 
76     // Optional, but cannot co-exist with a static reference.
setExprReferenceExpr(Expr exprReference)77     public abstract Builder setExprReferenceExpr(Expr exprReference);
78 
79     // Optional, but cannot co-exist with an expression reference.
setStaticReferenceType(TypeNode type)80     public abstract Builder setStaticReferenceType(TypeNode type);
81 
82     // Optional.
setArguments(Expr... arguments)83     public Builder setArguments(Expr... arguments) {
84       return setArguments(Arrays.asList(arguments));
85     }
86 
setArguments(List<Expr> arguments)87     public abstract Builder setArguments(List<Expr> arguments);
88 
89     // Optional.
setGenerics(List<Reference> generics)90     public abstract Builder setGenerics(List<Reference> generics);
91 
92     // Private setter.
setMethodIdentifier(IdentifierNode methodIdentifier)93     abstract Builder setMethodIdentifier(IdentifierNode methodIdentifier);
94 
autoBuild()95     abstract MethodInvocationExpr autoBuild();
96 
build()97     public MethodInvocationExpr build() {
98       // IdentifierNode will validate the name.
99       IdentifierNode identifier = IdentifierNode.builder().setName(methodName()).build();
100       setMethodIdentifier(identifier);
101 
102       MethodInvocationExpr methodInvocationExpr = autoBuild();
103 
104       if (methodInvocationExpr.staticReferenceType() != null) {
105         Preconditions.checkState(
106             TypeNode.isReferenceType(methodInvocationExpr.staticReferenceType()),
107             "Static references can only be made on object types");
108       }
109 
110       Preconditions.checkState(
111           !methodInvocationExpr.type().equals(TypeNode.NULL),
112           "Methods cannot be invoked on null types.");
113 
114       Preconditions.checkState(
115           methodInvocationExpr.exprReferenceExpr() == null
116               || methodInvocationExpr.staticReferenceType() == null,
117           "Only the expression reference or the static reference can be set, not both");
118 
119       NodeValidator.checkNoNullElements(
120           methodInvocationExpr.arguments(),
121           "arguments",
122           String.format("method invocation of %s", methodInvocationExpr.methodIdentifier().name()));
123 
124       NodeValidator.checkNoNullElements(
125           methodInvocationExpr.generics(),
126           "generics",
127           String.format("method invocation of %s", methodInvocationExpr.methodIdentifier().name()));
128 
129       // TODO(v2): If type-checking is ever added for arguments, beware of lambdas and their type
130       // workarounds.
131 
132       return methodInvocationExpr;
133     }
134   }
135 }
136