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.api.generator.engine.lexicon.Keyword; 18 import com.google.auto.value.AutoValue; 19 import com.google.common.base.Preconditions; 20 import com.google.common.collect.ImmutableList; 21 import java.util.Collections; 22 import java.util.LinkedHashSet; 23 import java.util.List; 24 import java.util.stream.Collectors; 25 import javax.annotation.Nullable; 26 27 @AutoValue 28 public abstract class VariableExpr implements Expr { variable()29 public abstract Variable variable(); 30 31 @Nullable exprReferenceExpr()32 public abstract Expr exprReferenceExpr(); 33 34 @Nullable staticReferenceType()35 public abstract TypeNode staticReferenceType(); 36 37 /** Variable declaration fields. */ isDecl()38 public abstract boolean isDecl(); 39 scope()40 public abstract ScopeNode scope(); 41 isStatic()42 public abstract boolean isStatic(); 43 isFinal()44 public abstract boolean isFinal(); 45 isVolatile()46 public abstract boolean isVolatile(); 47 48 // Optional annotations()49 public abstract ImmutableList<AnnotationNode> annotations(); 50 51 // Please use this only in conjunction with methods. 52 // Supports only parameterized types like Map<K, V>. 53 // TODO(unsupported): Fully generic arguments, e.g. foobar(K key, V value). 54 // This list can contain only IdentifierNode or TypeNode. templateNodes()55 public abstract ImmutableList<AstNode> templateNodes(); 56 57 // Private. 58 // Can either contain String or TypeNode objects. templateObjects()59 abstract ImmutableList<Object> templateObjects(); 60 61 @Override type()62 public TypeNode type() { 63 if (isDecl()) { 64 return TypeNode.VOID; 65 } 66 return variable().type(); 67 } 68 69 @Override accept(AstNodeVisitor visitor)70 public void accept(AstNodeVisitor visitor) { 71 visitor.visit(this); 72 } 73 withVariable(Variable variable)74 public static VariableExpr withVariable(Variable variable) { 75 return builder().setVariable(variable).build(); 76 } 77 builder()78 public static Builder builder() { 79 return new AutoValue_VariableExpr.Builder() 80 .setIsDecl(false) 81 .setIsFinal(false) 82 .setIsStatic(false) 83 .setIsVolatile(false) 84 .setScope(ScopeNode.LOCAL) 85 .setTemplateObjects(ImmutableList.of()) 86 .setAnnotations(Collections.emptyList()); 87 } 88 toBuilder()89 public abstract Builder toBuilder(); 90 91 @AutoValue.Builder 92 public abstract static class Builder { setVariable(Variable variable)93 public abstract Builder setVariable(Variable variable); 94 95 // Optional, but cannot co-exist with a variable declaration. setExprReferenceExpr(Expr exprReference)96 public abstract Builder setExprReferenceExpr(Expr exprReference); 97 98 // Optional, but cannot co-exist with an expression reference. setStaticReferenceType(TypeNode type)99 public abstract Builder setStaticReferenceType(TypeNode type); 100 setIsDecl(boolean isDecl)101 public abstract Builder setIsDecl(boolean isDecl); 102 setScope(ScopeNode scope)103 public abstract Builder setScope(ScopeNode scope); 104 setIsStatic(boolean isStatic)105 public abstract Builder setIsStatic(boolean isStatic); 106 setIsFinal(boolean isFinal)107 public abstract Builder setIsFinal(boolean isFinal); 108 setIsVolatile(boolean isVolatile)109 public abstract Builder setIsVolatile(boolean isVolatile); 110 setAnnotations(List<AnnotationNode> annotations)111 public abstract Builder setAnnotations(List<AnnotationNode> annotations); 112 annotations()113 abstract ImmutableList<AnnotationNode> annotations(); 114 115 // This should be used only for method arguments. setTemplateObjects(List<Object> objects)116 public abstract Builder setTemplateObjects(List<Object> objects); 117 118 // Private. setTemplateNodes(List<AstNode> nodes)119 abstract Builder setTemplateNodes(List<AstNode> nodes); 120 variable()121 abstract Variable variable(); 122 templateObjects()123 abstract ImmutableList<Object> templateObjects(); 124 autoBuild()125 abstract VariableExpr autoBuild(); 126 build()127 public VariableExpr build() { 128 NodeValidator.checkNoNullElements( 129 templateObjects(), 130 "template objects", 131 String.format("variable expr %s", variable().identifier().name())); 132 setTemplateNodes( 133 templateObjects().stream() 134 .map( 135 o -> { 136 Preconditions.checkState( 137 o instanceof String || o instanceof TypeNode, 138 "Template objects can only be Strings or Typenodes"); 139 if (o instanceof String) { 140 return (AstNode) IdentifierNode.withName((String) o); 141 } 142 return (AstNode) o; 143 }) 144 .collect(Collectors.toList())); 145 146 // Remove duplicates while maintaining insertion order. 147 ImmutableList<AnnotationNode> processedAnnotations = annotations(); 148 setAnnotations( 149 new LinkedHashSet<>(processedAnnotations).stream().collect(Collectors.toList())); 150 151 VariableExpr variableExpr = autoBuild(); 152 153 // TODO: should match on AnnotationNode @Target of ElementType.FIELD 154 if (!variableExpr.isDecl()) { 155 Preconditions.checkState( 156 variableExpr.annotations().isEmpty(), 157 "Annotation can only be added to variable declaration."); 158 } 159 160 if (variableExpr.isDecl() || variableExpr.exprReferenceExpr() != null) { 161 Preconditions.checkState( 162 variableExpr.isDecl() ^ (variableExpr.exprReferenceExpr() != null), 163 "Variable references cannot be declared"); 164 } 165 166 Preconditions.checkState( 167 variableExpr.exprReferenceExpr() == null || variableExpr.staticReferenceType() == null, 168 "Only the expression reference or the static reference can be set, not both"); 169 170 if (variableExpr.exprReferenceExpr() != null) { 171 Preconditions.checkState( 172 TypeNode.isReferenceType(variableExpr.exprReferenceExpr().type()), 173 "Variables can only be referenced from object types"); 174 } 175 if (variableExpr.staticReferenceType() != null) { 176 Preconditions.checkState( 177 TypeNode.isReferenceType(variableExpr.staticReferenceType()), 178 String.format( 179 "Static field references can only be done on static types, but instead found %s", 180 variableExpr.staticReferenceType())); 181 } 182 183 // A variable name of "class" is valid only when it's a static reference. 184 String varName = variableExpr.variable().identifier().name(); 185 if (Keyword.isKeyword(varName)) { 186 Preconditions.checkState( 187 variableExpr.staticReferenceType() != null 188 || (variableExpr.exprReferenceExpr() != null 189 && TypeNode.isReferenceType(variableExpr.exprReferenceExpr().type())), 190 String.format( 191 "Variable field name %s is invalid on non-static or non-reference types", varName)); 192 } 193 194 return variableExpr; 195 } 196 } 197 } 198