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 
20 @AutoValue
21 public abstract class UnaryOperationExpr implements OperationExpr {
22 
expr()23   public abstract Expr expr();
24 
25   @Override
operatorKind()26   public abstract OperatorKind operatorKind();
27 
28   @Override
type()29   public abstract TypeNode type();
30 
31   @Override
accept(AstNodeVisitor visitor)32   public void accept(AstNodeVisitor visitor) {
33     visitor.visit(this);
34   }
35 
isPostfixIncrement()36   public boolean isPostfixIncrement() {
37     return operatorKind().equals(OperatorKind.UNARY_POST_INCREMENT);
38   }
39 
postfixIncrementWithExpr(Expr expr)40   public static UnaryOperationExpr postfixIncrementWithExpr(Expr expr) {
41     return builder()
42         .setExpr(expr)
43         .setOperatorKind(OperatorKind.UNARY_POST_INCREMENT)
44         .setType(expr.type())
45         .build();
46   }
47 
logicalNotWithExpr(Expr expr)48   public static UnaryOperationExpr logicalNotWithExpr(Expr expr) {
49     return builder()
50         .setOperatorKind(OperatorKind.UNARY_LOGICAL_NOT)
51         .setExpr(expr)
52         .setType(TypeNode.BOOLEAN)
53         .build();
54   }
55 
builder()56   private static Builder builder() {
57     return new AutoValue_UnaryOperationExpr.Builder();
58   }
59 
60   @AutoValue.Builder
61   abstract static class Builder {
62 
63     // Private setter.
setExpr(Expr expr)64     abstract Builder setExpr(Expr expr);
65 
66     // Private setter.
setOperatorKind(OperatorKind operator)67     abstract Builder setOperatorKind(OperatorKind operator);
68 
69     // Private setter.
setType(TypeNode type)70     abstract Builder setType(TypeNode type);
71 
autoBuild()72     abstract UnaryOperationExpr autoBuild();
73 
build()74     private UnaryOperationExpr build() {
75       UnaryOperationExpr unaryOperationExpr = autoBuild();
76       TypeNode exprType = unaryOperationExpr.expr().type();
77       OperatorKind operator = unaryOperationExpr.operatorKind();
78 
79       // Add final keyword checking for post/prefix ++, -- when needed.
80       if (operator.equals(OperatorKind.UNARY_POST_INCREMENT)
81           && unaryOperationExpr.expr() instanceof VariableExpr) {
82         VariableExpr varExpr = (VariableExpr) unaryOperationExpr.expr();
83         Preconditions.checkState(
84             !varExpr.isFinal(),
85             String.format("Cannot increment the final variable '%s'.", varExpr.variable().name()));
86 
87         Preconditions.checkState(
88             !varExpr.isDecl(),
89             String.format(
90                 "Cannot increment the declaration of variable %s", varExpr.variable().name()));
91       }
92 
93       final String errorMsg =
94           String.format(
95               "Unary operator %s can not be applied to %s. ", operator, exprType.toString());
96 
97       Preconditions.checkState(
98           !exprType.equals(TypeNode.VOID) && !exprType.equals(TypeNode.NULL), errorMsg);
99 
100       if (operator.equals(OperatorKind.UNARY_LOGICAL_NOT)) {
101         Preconditions.checkState(isValidLogicalNotType(exprType), errorMsg);
102       }
103 
104       if (operator.equals(OperatorKind.UNARY_POST_INCREMENT)) {
105         Preconditions.checkState(isValidIncrementType(exprType), errorMsg);
106       }
107 
108       return unaryOperationExpr;
109     }
110   }
111 
isValidLogicalNotType(TypeNode exprType)112   private static boolean isValidLogicalNotType(TypeNode exprType) {
113     // Logical not (!) can only be applied on boolean/Boolean type
114     return exprType.equals(TypeNode.BOOLEAN);
115   }
116 
isValidIncrementType(TypeNode exprType)117   private static boolean isValidIncrementType(TypeNode exprType) {
118     // Increment (++) can be applied on numeric types(int, double, float, long, short, char)
119     // and its boxed type (exclude Boolean)
120     return TypeNode.isNumericType(exprType);
121   }
122 }
123