diff --git a/src/main/java/com/google/api/generator/engine/ast/OperatorKind.java b/src/main/java/com/google/api/generator/engine/ast/OperatorKind.java index 5c5a6a4642..a895b7d991 100644 --- a/src/main/java/com/google/api/generator/engine/ast/OperatorKind.java +++ b/src/main/java/com/google/api/generator/engine/ast/OperatorKind.java @@ -22,6 +22,7 @@ public enum OperatorKind { LOGICAL_OR, RELATIONAL_EQUAL_TO, RELATIONAL_NOT_EQUAL_TO, + RELATIONAL_LESS_THAN, UNARY_LOGICAL_NOT, UNARY_POST_INCREMENT; diff --git a/src/main/java/com/google/api/generator/engine/ast/RelationalOperationExpr.java b/src/main/java/com/google/api/generator/engine/ast/RelationalOperationExpr.java index cd8bf06766..80f0e4711b 100644 --- a/src/main/java/com/google/api/generator/engine/ast/RelationalOperationExpr.java +++ b/src/main/java/com/google/api/generator/engine/ast/RelationalOperationExpr.java @@ -53,14 +53,13 @@ public static RelationalOperationExpr notEqualToWithExprs(Expr lhsExpr, Expr rhs .build(); } - // TODO(summerji): Add convenience wrapper lessThanWithExprs - // public static RelationalOperationExpr lessThanWithExprs(Expr lhsExpr, Expr rhsExpr) { - // return builder() - // .setLhsExpr(lhsExpr) - // .setRhsExpr(rhsExpr) - // .setOperatorKind(OperatorKind.RELATIONAL_LESS_THAN) - // .build(); - // } + public static RelationalOperationExpr lessThanWithExprs(Expr lhsExpr, Expr rhsExpr) { + return builder() + .setLhsExpr(lhsExpr) + .setRhsExpr(rhsExpr) + .setOperatorKind(OperatorKind.RELATIONAL_LESS_THAN) + .build(); + } private static Builder builder() { return new AutoValue_RelationalOperationExpr.Builder(); @@ -95,6 +94,10 @@ private RelationalOperationExpr build() { Preconditions.checkState(isValidEqualityType(lhsExprType, rhsExprType), errorMsg); } + if (operator.equals(OperatorKind.RELATIONAL_LESS_THAN)) { + Preconditions.checkState(isValidRelationalType(lhsExprType, rhsExprType), errorMsg); + } + return relationalOperationExpr; } @@ -134,5 +137,14 @@ private boolean isValidEqualityType(TypeNode lhsType, TypeNode rhsType) { return false; } + + // isValidRelationalType checks expressions' types for relational operators (<, >, <=, >=). + // The <, >, <=, and >= can be used with primitive data types that can be represented in + // numbers. + // It will work with char, byte, short, int, long, float, double, but not with boolean. + // These operators are not supported for objects. + private boolean isValidRelationalType(TypeNode lhsType, TypeNode rhsType) { + return TypeNode.isNumericType(lhsType) && TypeNode.isNumericType(rhsType); + } } } diff --git a/src/main/java/com/google/api/generator/engine/ast/TypeNode.java b/src/main/java/com/google/api/generator/engine/ast/TypeNode.java index fbebef1b5d..27396ffb85 100644 --- a/src/main/java/com/google/api/generator/engine/ast/TypeNode.java +++ b/src/main/java/com/google/api/generator/engine/ast/TypeNode.java @@ -145,7 +145,8 @@ public static boolean isNumericType(TypeNode type) { || type.equals(TypeNode.DOUBLE) || type.equals(TypeNode.SHORT) || type.equals(TypeNode.FLOAT) - || type.equals(TypeNode.CHAR); + || type.equals(TypeNode.CHAR) + || type.equals(TypeNode.BYTE); } public static boolean isBoxedType(TypeNode type) { diff --git a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java index 5ffed7bdc2..b770de98bc 100644 --- a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java +++ b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java @@ -110,6 +110,7 @@ public class JavaWriterVisitor implements AstNodeVisitor { private static final String OPERATOR_ADDITION = "+"; private static final String OPERATOR_EQUAL_TO = "=="; private static final String OPERATOR_NOT_EQUAL_TO = "!="; + private static final String OPERATOR_LESS_THAN = "<"; private static final String OPERATOR_INCREMENT = "++"; private static final String OPERATOR_LOGICAL_NOT = "!"; private static final String OPERATOR_LOGICAL_AND = "&&"; @@ -918,6 +919,9 @@ private void operator(OperatorKind kind) { case RELATIONAL_NOT_EQUAL_TO: buffer.append(OPERATOR_NOT_EQUAL_TO); break; + case RELATIONAL_LESS_THAN: + buffer.append(OPERATOR_LESS_THAN); + break; case UNARY_POST_INCREMENT: buffer.append(OPERATOR_INCREMENT); break; diff --git a/src/test/java/com/google/api/generator/engine/ast/RelationalOperationExprTest.java b/src/test/java/com/google/api/generator/engine/ast/RelationalOperationExprTest.java index 251aadf44a..fcb999716e 100644 --- a/src/test/java/com/google/api/generator/engine/ast/RelationalOperationExprTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/RelationalOperationExprTest.java @@ -132,7 +132,7 @@ public void equalToOperationExpr_validBoxedWithMatchedBoxedType() { // No need swap LHS and RHS test case. VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "y"); - RelationalOperationExpr.equalToWithExprs(rhsExpr, rhsExpr); + RelationalOperationExpr.equalToWithExprs(lhsExpr, rhsExpr); // No exception thrown, so we succeeded. } @@ -143,7 +143,7 @@ public void equalToOperationExpr_validBoxedWithMatchedUnBoxedType() { // "notEqualToOperationExpr_validMatchedNumericBoxTYpe". VariableExpr lhsExpr = createVariableExpr(TypeNode.FLOAT_OBJECT, "x"); VariableExpr rhsExpr = createVariableExpr(TypeNode.FLOAT, "y"); - RelationalOperationExpr.equalToWithExprs(rhsExpr, rhsExpr); + RelationalOperationExpr.equalToWithExprs(lhsExpr, rhsExpr); // No exception thrown, so we succeeded. } @@ -771,6 +771,215 @@ public void equalToOperationExpr_invalidObjectTypeWithArray() { () -> RelationalOperationExpr.equalToWithExprs(lhsExpr, rhsExpr)); } + /** ================== Less Than Operators: expr types are numeric types =================== */ + // The expression types on LHS or RHS could be any numeric type or any numeric boxed type. + @Test + public void lessThanOperationExpr_validMatchedNumericType() { + // LHS: Numeric type, RHS: Matched numeric type. + // No need swap LHS and RHS test case. + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validUnmatchedNumericType() { + // LHS: Numeric type, RHS: Unmatched numeric type. + // No need swap LHS and RHS test case. + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.SHORT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validMatchedNumericBoxedType() { + // LHS: Numeric type, RHS: Matched numeric type. + // Swap case in "lessThanOperationExpr_validNumericBoxedTypeWithMatchedNumericType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.FLOAT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.FLOAT_OBJECT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validNumericBoxedTypeWithMatchedNumericType() { + // LHS: Numeric boxed type, RHS: Matched numeric type. + // Swap case in "lessThanOperationExpr_validMatchedNumericBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.CHAR_OBJECT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.CHAR, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validUnmatchedNumericBoxedType() { + // LHS: Numeric type, RHS: Unmatched numeric boxed type. + // Swap case in "lessThanOperationExpr_validNumericBoxedTypeWithUnmatchedUnBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.DOUBLE, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.LONG_OBJECT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validNumericBoxedTypeWithUnmatchedUnBoxedType() { + // LHS: Numeric boxed type, RHS: Unmatched numeric type. + // Swap case in "lessThanOperationExpr_validUnmatchedNumericBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.BYTE, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validNumericBoxedTypeWithMatchedBoxedType() { + // LHS: Numeric boxed type, RHS: Matched numeric boxed type. + // No need swap case. + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + @Test + public void lessThanOperationExpr_validNumericBoxedTypeWithUnmatchedBoxedType() { + // LHS: Numeric boxed type, RHS: Unmatched numeric boxed type. + // No need swap case. + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.LONG_OBJECT, "y"); + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + // No exception thrown, so we succeeded. + } + + /** ================= Less Than Operators: expr types are non-numeric types ================== */ + // Invalid if any of expression type on LHS or RHS is non-numeric type or non numeric boxed type. + @Test + public void lessThanOperationExpr_invalidNumericTypeWithNullType() { + // LHS: Null type, RHS: Numeric type. + // Swap case in "lessThanOperationExpr_invalidNumericWithNullType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT, "x"); + ValueExpr rhsExpr = ValueExpr.withValue(NullObjectValue.create()); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNumericBoxedTypeWithNullType() { + // LHS: Numeric boxed type, RHS: Null type. + // Swap case in "lessThanOperationExpr_invalidNullWithNumericBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + ValueExpr rhsExpr = ValueExpr.withValue(NullObjectValue.create()); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNumericTypeWithObjectType() { + // LHS: Numeric type, RHS: Object Type. + // Swap case in "lessThanOperationExpr_invalidObjectNumericType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT, "x"); + NewObjectExpr rhsExpr = NewObjectExpr.withType(TypeNode.OBJECT); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNumericBoxedTypeWithObjectType() { + // LHS: Numeric boxed type, RHS: Object Type. + // Swap case in "lessThanOperationExpr_invalidObjectNumericBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + NewObjectExpr rhsExpr = NewObjectExpr.withType(TypeNode.OBJECT); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNumericBoxedTypeWithReferenceType() { + // LHS: Numeric boxed type, RHS: Reference Type. + // Swap case in "lessThanOperationExpr_invalidReferenceTypeWithNumericBoxedType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.STRING, "y"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidReferenceTypeWithNumericBoxedType() { + // LHS: Reference type, RHS: Numeric boxed Type. + // Swap case in "lessThanOperationExpr_invalidNumericBoxedTypeWithReferenceType". + VariableExpr lhsExpr = createVariableExpr(TypeNode.STRING, "x"); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "y"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidObjectNumericType() { + // LHS: Object type, RHS: Numeric Type. + // Swap case in "lessThanOperationExpr_invalidNumericTypeWithObjectType". + NewObjectExpr lhsExpr = NewObjectExpr.withType(TypeNode.OBJECT); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT, "x"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidObjectNumericBoxedType() { + // LHS: Object type, RHS: Numeric boxed Type. + // Swap case in "lessThanOperationExpr_invalidNumericBoxedTypeWithObjectType". + NewObjectExpr lhsExpr = NewObjectExpr.withType(TypeNode.OBJECT); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNumericWithNullType() { + // LHS: Null type, RHS: Numeric box type. + + ValueExpr lhsExpr = ValueExpr.withValue(NullObjectValue.create()); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT, "x"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidNullWithNumericBoxedType() { + // LHS: Null type, RHS: Numeric box type. + // Swap case in "lessThanOperationExpr_invalidNumericTypeWithNullType". + ValueExpr lhsExpr = ValueExpr.withValue(NullObjectValue.create()); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + + @Test + public void lessThanOperationExpr_invalidVoidType() { + // LHS: Null type, RHS: Numeric box type. + // No need swap case + MethodInvocationExpr lhsExpr = + MethodInvocationExpr.builder() + .setMethodName("doNothing") + .setReturnType(TypeNode.VOID) + .build(); + VariableExpr rhsExpr = createVariableExpr(TypeNode.INT_OBJECT, "x"); + assertThrows( + IllegalStateException.class, + () -> RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr)); + } + private VariableExpr createVariableExpr(TypeNode type, String name) { Variable variable = Variable.builder().setName(name).setType(type).build(); VariableExpr variableExpr = VariableExpr.withVariable(variable); diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index 408f905bd9..30632ea40c 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -2098,6 +2098,21 @@ public void writeRelationOperationExpr_notEqualTo() { assertThat(writerVisitor.write()).isEqualTo("SomeClass.getName() != null"); } + @Test + public void writeRelationalOperationExpr_lessThan() { + VariableExpr lhsExpr = VariableExpr.withVariable(createVariable("i", TypeNode.INT)); + MethodInvocationExpr rhsExpr = + MethodInvocationExpr.builder() + .setMethodName("getMaxNumber") + .setReturnType(TypeNode.INT) + .build(); + + RelationalOperationExpr lessThanWithExprs = + RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr); + lessThanWithExprs.accept(writerVisitor); + assertThat(writerVisitor.write()).isEqualTo("i < getMaxNumber()"); + } + @Test public void writeLogicalOperationExpr_logicalAnd() { VariableExpr lhsExpr = VariableExpr.withVariable(createVariable("isEmpty", TypeNode.BOOLEAN));