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 RelationalOperationExpr implements OperationExpr { lhsExpr()22 public abstract Expr lhsExpr(); 23 rhsExpr()24 public abstract Expr rhsExpr(); 25 26 @Override operatorKind()27 public abstract OperatorKind operatorKind(); 28 29 @Override type()30 public TypeNode type() { 31 return TypeNode.BOOLEAN; 32 } 33 34 @Override accept(AstNodeVisitor visitor)35 public void accept(AstNodeVisitor visitor) { 36 visitor.visit(this); 37 } 38 39 // Convenience wrapper. equalToWithExprs(Expr lhsExpr, Expr rhsExpr)40 public static RelationalOperationExpr equalToWithExprs(Expr lhsExpr, Expr rhsExpr) { 41 return builder() 42 .setLhsExpr(lhsExpr) 43 .setRhsExpr(rhsExpr) 44 .setOperatorKind(OperatorKind.RELATIONAL_EQUAL_TO) 45 .build(); 46 } 47 48 // Convenience wrapper. notEqualToWithExprs(Expr lhsExpr, Expr rhsExpr)49 public static RelationalOperationExpr notEqualToWithExprs(Expr lhsExpr, Expr rhsExpr) { 50 return builder() 51 .setLhsExpr(lhsExpr) 52 .setRhsExpr(rhsExpr) 53 .setOperatorKind(OperatorKind.RELATIONAL_NOT_EQUAL_TO) 54 .build(); 55 } 56 lessThanWithExprs(Expr lhsExpr, Expr rhsExpr)57 public static RelationalOperationExpr lessThanWithExprs(Expr lhsExpr, Expr rhsExpr) { 58 return builder() 59 .setLhsExpr(lhsExpr) 60 .setRhsExpr(rhsExpr) 61 .setOperatorKind(OperatorKind.RELATIONAL_LESS_THAN) 62 .build(); 63 } 64 builder()65 private static Builder builder() { 66 return new AutoValue_RelationalOperationExpr.Builder(); 67 } 68 69 @AutoValue.Builder 70 abstract static class Builder { 71 72 // Private setter. setLhsExpr(Expr expr)73 abstract Builder setLhsExpr(Expr expr); 74 75 // Private setter. setRhsExpr(Expr expr)76 abstract Builder setRhsExpr(Expr expr); 77 78 // Private setter. setOperatorKind(OperatorKind operator)79 abstract Builder setOperatorKind(OperatorKind operator); 80 autoBuild()81 abstract RelationalOperationExpr autoBuild(); 82 build()83 private RelationalOperationExpr build() { 84 RelationalOperationExpr relationalOperationExpr = autoBuild(); 85 TypeNode lhsExprType = relationalOperationExpr.lhsExpr().type(); 86 TypeNode rhsExprType = relationalOperationExpr.rhsExpr().type(); 87 OperatorKind operator = relationalOperationExpr.operatorKind(); 88 final String errorMsg = 89 String.format( 90 "Relational operator %s can not be applied to %s, %s.", 91 operator, lhsExprType.toString(), rhsExprType.toString()); 92 93 if (operator.equals(OperatorKind.RELATIONAL_EQUAL_TO) 94 || operator.equals(OperatorKind.RELATIONAL_NOT_EQUAL_TO)) { 95 Preconditions.checkState(isValidEqualityType(lhsExprType, rhsExprType), errorMsg); 96 } 97 98 if (operator.equals(OperatorKind.RELATIONAL_LESS_THAN)) { 99 Preconditions.checkState(isValidRelationalType(lhsExprType, rhsExprType), errorMsg); 100 } 101 102 return relationalOperationExpr; 103 } 104 105 // isValidEqualityType checks expressions' type for equality operator (==) and non-equality 106 // operator (!=). isValidEqualityType(TypeNode lhsType, TypeNode rhsType)107 private boolean isValidEqualityType(TypeNode lhsType, TypeNode rhsType) { 108 // If the expression's types are matched, return true 109 if (lhsType.equals(rhsType)) { 110 return true; 111 } 112 113 // If the expressions' type are array, the types should be array and matched, or either is 114 // null type; 115 if (lhsType.isArray() || rhsType.isArray()) { 116 return lhsType.equals(TypeNode.NULL) || rhsType.equals(TypeNode.NULL); 117 } 118 119 // If lhs expression type is numeric type (char, byte, short, int, long, double), the rhs 120 // expression type should be any numeric type or any numeric boxed type 121 if (TypeNode.isNumericType(lhsType) && TypeNode.isNumericType(rhsType)) { 122 return true; 123 } 124 125 // If lhs expression type is new Object or null, the rhs type should be a reference type or 126 // null or boxed type; 127 if (lhsType.equals(TypeNode.OBJECT) || lhsType.equals(TypeNode.NULL)) { 128 return TypeNode.isReferenceType(rhsType) 129 || rhsType.equals(TypeNode.OBJECT) 130 || rhsType.equals(TypeNode.NULL); 131 } 132 133 // If lhs expression type is Boxed type or a referenced type, rhs should be null or object, 134 // other cases have been covered in previous conditions. 135 if (TypeNode.isBoxedType(lhsType) || TypeNode.isReferenceType(lhsType)) { 136 return rhsType.equals(TypeNode.NULL) || rhsType.equals(TypeNode.OBJECT); 137 } 138 139 return false; 140 } 141 142 // isValidRelationalType checks expressions' types for relational operators (<, >, <=, >=). 143 // The <, >, <=, and >= can be used with primitive data types that can be represented in 144 // numbers. 145 // It will work with char, byte, short, int, long, float, double, but not with boolean. 146 // These operators are not supported for objects. isValidRelationalType(TypeNode lhsType, TypeNode rhsType)147 private boolean isValidRelationalType(TypeNode lhsType, TypeNode rhsType) { 148 return TypeNode.isNumericType(lhsType) && TypeNode.isNumericType(rhsType); 149 } 150 } 151 } 152