io.deephaven.engine.table.impl.lang.QueryLanguageFunctionGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of deephaven-engine-table Show documentation
Show all versions of deephaven-engine-table Show documentation
Engine Table: Implementation and closely-coupled utilities
/**
* Copyright (c) 2016-2022 Deephaven Data Labs and Patent Pending
*/
package io.deephaven.engine.table.impl.lang;
import io.deephaven.util.type.TypeUtils;
import com.github.javaparser.ast.expr.BinaryExpr;
import java.io.*;
import java.text.*;
import java.time.LocalDate;
public class QueryLanguageFunctionGenerator {
public static double[] plus(double a[], int b) {
double[] ret = new double[a.length];
for (int i = 0; i < a.length; i++) {
ret[i] = a[i] + b;
}
return ret;
}
public static void main(String args[]) {
final long start = System.currentTimeMillis();
// 0 - operation name
// 1 - type of param 1
// 2 - type of param 2
// 3 - promoted type
// 4 - null of param 1
// 5 - null of param 2
// 6 - null of promoted type
// 7 - operation symbol
// 8 - optional cast (for non-fp division)
// 9 - optional compareTo thing
// 10 - optional operation description for exception message (only for arrayArrayFormatter)
// 11 - optional nonzero literal value of param type 1 (for testing)
// 12 - optional nonzero literal value of param type 2 (for testing)
MessageFormat varVarFormatter = new MessageFormat("" +
" public static {3} {0}({1} a, {2} b)'{'\n" +
" return a==QueryConstants.NULL_{4} || b==QueryConstants.NULL_{5} ? QueryConstants.NULL_{6} : a{7}{8}b;\n"
+
" '}'");
MessageFormat varVarTestFormatter = new MessageFormat("" +
" public static void test_{0}_{1}_{2}() '{'\n" +
" final {1} value1 = {11};\n" +
" final {2} value2 = {12};\n" +
" final {1} zero1 = 0;\n" +
" final {2} zero2 = 0;\n" +
"\n" +
" {3} actualResult = -1, expectedResult = -1;\n" +
" int compareResult;\n" +
" String description;\n" +
"\n" +
" try '{'\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(value1, value2);\n" +
" expectedResult = value1{7}{8}value2;\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(value1, value2), value1{7}{8}value2)\";\n"
+
" TestCase.assertEquals(description, 0, compareResult);\n" +
/*
* ---------- This one runs into ArithmeticExceptions doing stuff like 0 % 0 ---------- "\n" +
* " actualResult = QueryLanguageFunctionUtils.{0}(value1, zero2);\n" +
* " expectedResult = value1{7}{8}zero2;\n" +
* " compareResult = {13}.compare(actualResult, expectedResult);\n" +
* " description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(value1, zero2), value1{7}{8}zero2)\";\n"
* + " TestCase.assertEquals(description, 0, compareResult);\n" +
*/
"\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(value1, QueryConstants.NULL_{5});\n" +
" expectedResult = QueryConstants.NULL_{6};\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(value1, QueryConstants.NULL_{5}), QueryConstants.NULL_{6})\";\n"
+
" TestCase.assertEquals(description, 0, compareResult);\n" +
"\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(zero1, value2);\n" +
" expectedResult = zero1{7}{8}value2;\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(zero1, value2), zero1{7}{8}value2)\";\n"
+
" TestCase.assertEquals(description, 0, compareResult);\n" +
"\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(QueryConstants.NULL_{4}, value2);\n" +
" expectedResult = QueryConstants.NULL_{6};\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(QueryConstants.NULL_{4}, value2), QueryConstants.NULL_{6})\";\n"
+
" TestCase.assertEquals(description, 0, compareResult);\n" +
"\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(QueryConstants.NULL_{4}, QueryConstants.NULL_{5});\n"
+
" expectedResult = QueryConstants.NULL_{6};\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(QueryConstants.NULL_{4}, QueryConstants.NULL_{5}), QueryConstants.NULL_{6})\";\n"
+
" TestCase.assertEquals(description, 0, compareResult);\n" +
/*---------- Same issue as above ----------
"\n" +
" actualResult = QueryLanguageFunctionUtils.{0}(zero1, zero2);\n" +
" expectedResult = zero1{7}{8}zero2;\n" +
" compareResult = {13}.compare(actualResult, expectedResult);\n" +
" description = \"{13}.compare(QueryLanguageFunctionUtils.{0}(zero1, zero2), zero1{7}{8}zero2)\";\n" +
" TestCase.assertEquals(description, 0, compareResult);\n" +*/
" '}' catch (Exception ex) '{'\n" +
" throw new RuntimeException(\"Comparison failure: actualResult=\" + actualResult + \", expectedResult=\" + expectedResult, ex);\n"
+
" '}'\n" +
"\n" +
" '}'");
// requires that value2 > value1
MessageFormat varVarCompareTestFormatter = new MessageFormat("" +
" public static void test_compare_{1}_{2}_compare() '{'\n" +
" final {1} value1 = {11};\n" +
" final {2} value2 = {12};\n" +
" final {1} zero1 = 0;\n" +
" final {2} zero2 = 0;\n\n" +
" TestCase.assertEquals(-1, QueryLanguageFunctionUtils.compareTo(value1, Float.NaN));\n" +
" TestCase.assertEquals(1, QueryLanguageFunctionUtils.compareTo(Float.NaN, value1));\n" +
" TestCase.assertEquals(-1, QueryLanguageFunctionUtils.compareTo(value1, Double.NaN));\n" +
" TestCase.assertEquals(1, QueryLanguageFunctionUtils.compareTo(Double.NaN, value1));\n" +
" TestCase.assertEquals( 0, QueryLanguageFunctionUtils.compareTo(zero1, zero2));\n" +
" TestCase.assertEquals( 0, QueryLanguageFunctionUtils.compareTo(zero2, zero1));\n" +
" TestCase.assertEquals( 0, QueryLanguageFunctionUtils.compareTo(QueryConstants.NULL_{4}, QueryConstants.NULL_{5}));\n"
+
" TestCase.assertEquals( 0, QueryLanguageFunctionUtils.compareTo(value1, value1));\n" +
" TestCase.assertEquals( 0, QueryLanguageFunctionUtils.compareTo(value2, value2));\n" +
" TestCase.assertEquals(-1, QueryLanguageFunctionUtils.compareTo(value1, value2));\n" +
" TestCase.assertEquals( 1, QueryLanguageFunctionUtils.compareTo(value2, value1));\n" +
" TestCase.assertEquals(-1, QueryLanguageFunctionUtils.compareTo(-value1, value2));\n" +
" TestCase.assertEquals(-1, QueryLanguageFunctionUtils.compareTo(-value2, value1));\n" +
" TestCase.assertEquals( 1, QueryLanguageFunctionUtils.compareTo(-value1, -value2));\n" +
" '}'");
/*
* Special varVar formatter for boolean operations. If one expression in a ternary if is a boxed type and the
* other is a primitive, Java's inclination is to unbox the one that's boxed.
*
* Since the engine uses {@code Boolean} to store booleans while supporting {@code null}, we must manually box
* the result of boolean operations if we wish to support nulls.
*
* See JLS Chapter 15 section 25 -- https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25"
*
* Note that we do not provide null-safe handling for the "conditional-and" and "conditional-or" operators ("&&"
* and "||"). To do so while maintaining their short-circuit behavior would require different parser changes.
*/
MessageFormat varVarBooleanFormatter = new MessageFormat("" +
" public static {3} {0}({1} a, {2} b)'{'\n" +
" return a==QueryConstants.NULL_{4} || b==QueryConstants.NULL_{5} ? QueryConstants.NULL_{6} : Boolean.valueOf(a{7}{8}b);\n"
+
" '}'");
MessageFormat varVarCompareToFormatter = new MessageFormat("" +
" public static int compareTo({1} a, {2} b) '{'\n" +
" if (a==QueryConstants.NULL_{4})'{'\n" +
" return (b==QueryConstants.NULL_{5}) ? 0 : -1;\n" +
" '}'\n" +
" \n" +
" if (b==QueryConstants.NULL_{5})'{'\n" +
" return 1;\n" +
" '}'\n" +
"\n" +
" return a Long.MAX_VALUE) '{'\n" +
" return -1;\n" +
" '}' else if(b < Long.MIN_VALUE) '{'\n" +
" return 1;\n" +
" '}' else '{'\n" +
" final long longValue = (long) b;\n" +
" if (longValue > a) '{'\n" +
" return -1;\n" +
" '}' else if (longValue == a) '{'\n" +
" if (b - longValue == 0d) '{'\n" +
" return 0;\n" +
" '}' else if (b - longValue > 0d) '{'\n" +
" return -1;\n" +
" '}'\n" +
" '}'\n" +
" return 1;\n" +
" '}'\n" +
" '}'");
MessageFormat inverseCompareToFormatter = new MessageFormat("" +
" public static int compareTo({1} a, {2} b) '{'\n" +
" return -compareTo(b, a);\n" +
" '}'");
MessageFormat varVarCompareToUserFormatter = new MessageFormat("" +
" public static boolean {0}({1} a, {2} b)'{'\n" +
" return compareTo(a,b){9};\n" +
" '}'");
MessageFormat varVarEqualsFormatter = new MessageFormat("" +
" public static boolean eq({1} a, {2} b) '{'\n" +
" if (a==QueryConstants.NULL_{4})'{'\n" +
" return (b==QueryConstants.NULL_{5});\n" +
" '}'\n" +
" \n" +
" if (b==QueryConstants.NULL_{5})'{'\n" +
" return false;\n" +
" '}'\n" +
"\n" +
" return a==b;\n" +
" '}'");
MessageFormat arrayArrayFormatter = new MessageFormat("" +
" public static {3}[] {0}Array({1} a[], {2} b[])'{'\n" +
" if (a.length != b.length) throw new IllegalArgumentException(\"Attempt to {10} two arrays ({1}, {2}) of different length\" +\n"
+
" \" (a.length=\" + a.length + \", b.length=\" + b.length + '')'');\n" +
" \n" +
" {3}[] ret = new {3}[a.length];\n" +
" for (int i = 0; i < a.length; i++) '{'\n" +
" ret[i] = {0}(a[i],b[i]);\n" +
" '}'\n" +
" \n" +
" return ret;\n" +
" '}'");
MessageFormat arrayVarFormatter = new MessageFormat("" +
" public static {3}[] {0}Array({1} a[], {2} b)'{'\n" +
" {3}[] ret = new {3}[a.length];\n" +
" for (int i = 0; i < a.length; i++) '{'\n" +
" ret[i] = {0}(a[i],b);\n" +
" '}'\n" +
"\n" +
" return ret;\n" +
" '}'");
MessageFormat varArrayFormatter = new MessageFormat("" +
" public static {3}[] {0}Array({1} a, {2} b[])'{'\n" +
" {3}[] ret = new {3}[b.length];\n" +
" for (int i = 0; i < b.length; i++) '{'\n" +
" ret[i] = {0}(a,b[i]);\n" +
" '}'\n" +
"\n" +
" return ret;\n" +
" '}'");
MessageFormat castFormatter = new MessageFormat("" +
" public static {2} {2}Cast({1} a)'{'\n" +
" return a==QueryConstants.NULL_{4} ? QueryConstants.NULL_{5} : ({2})a;\n" +
" '}'");
/*
* Note that this will only work when unboxing -- e.g. doubleCast(a) when 'a' is a Double. Casting from an
* Integer to a double requires: doubleCast(intCast(theInteger))
*
* See the language specification, or comments in the parser, for more details.
*/
MessageFormat castFromObjFormatter = new MessageFormat("" +
" public static {1} {1}Cast(Object a)'{'\n" +
" return a==null ? QueryConstants.NULL_{4} : ({1})a;\n" +
" '}'");
MessageFormat negateFormatter = new MessageFormat("" +
" public static {3} negate({1} a)'{'\n" +
" return a==QueryConstants.NULL_{4} ? QueryConstants.NULL_{6} : -a;\n" +
" '}'");
final int sbCapacity = (int) Math.pow(2, 20);
StringBuilder buf = new StringBuilder(sbCapacity);
StringBuilder testBuf = new StringBuilder(sbCapacity);
buf.append("/*\n" +
" * Copyright (c) 2016-").append(LocalDate.now().getYear())
.append(" Deephaven Data Labs and Patent Pending\n" +
" * GENERATED CODE - DO NOT MODIFY DIRECTLY\n" +
" * This class generated by " + QueryLanguageFunctionGenerator.class.getCanonicalName() + "\n" +
" */\n" +
"\n");
buf.append("package io.deephaven.engine.table.impl.lang;\n\n");
buf.append("import io.deephaven.util.QueryConstants;\n");
buf.append("import org.jpy.PyObject;\n\n");
buf.append("@SuppressWarnings({\"unused\", \"WeakerAccess\", \"SimplifiableIfStatement\"})\n");
buf.append("public final class QueryLanguageFunctionUtils {\n\n");
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
buf.append("" +
" public static boolean eq(Object obj1, Object obj2){\n" +
" //noinspection SimplifiableBooleanExpression\n" +
" return obj1==obj2 || (!(obj1==null ^ obj2==null) && obj1.equals(obj2));\n" +
" }\n" +
" \n" +
" @SuppressWarnings({\"unchecked\"})\n" +
" public static int compareTo(Comparable obj1, Comparable obj2) {\n" +
" if (obj1==null){\n" +
" return (obj2==null) ? 0 : -1;\n" +
" }\n" +
" \n" +
" if (obj2==null){\n" +
" return 1;\n" +
" }\n" +
"\n" +
" return obj1.compareTo(obj2);\n" +
" }\n" +
"\n" +
" public static boolean not(boolean a){\n" +
" return !a;\n" +
" }\n" +
"\n" +
" public static Boolean not(Boolean a){\n" +
" return a==QueryConstants.NULL_BOOLEAN ? QueryConstants.NULL_BOOLEAN : Boolean.valueOf(!a);\n" +
" }\n\n");
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
/* Now start the test class: */
testBuf.append("/*\n")
.append(" * Copyright (c) 2016-").append(LocalDate.now().getYear())
.append(" * Deephaven Data Labs and Patent Pending\n")
.append(" * GENERATED CODE - DO NOT MODIFY DIRECTLY\n")
.append(" * This class generated by ").append(QueryLanguageFunctionGenerator.class.getCanonicalName())
.append("\n")
.append(" */\n")
.append("\n");
testBuf.append("package io.deephaven.engine.table.impl.lang;\n\n");
testBuf.append("import io.deephaven.util.QueryConstants;\n\n");
testBuf.append("import junit.framework.TestCase;\n\n");
testBuf.append("@SuppressWarnings({\"unused\", \"WeakerAccess\", \"NumericOverflow\"})\n");
testBuf.append("public final class TestLanguageFunctionUtil extends TestCase {\n\n");
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class>[] classes =
new Class[] {int.class, double.class, long.class, float.class, char.class, byte.class, short.class};
BinaryExpr.Operator[] operators = new BinaryExpr.Operator[] {
BinaryExpr.Operator.PLUS,
BinaryExpr.Operator.MINUS,
BinaryExpr.Operator.MULTIPLY,
BinaryExpr.Operator.DIVIDE,
BinaryExpr.Operator.REMAINDER,
};
// Verbs corresponding to each operator, used in exception messages: "Attempt to _____ two arrays of different
// length"
String[] operatorDescriptions =
new String[] {"add", "subtract", "multiply", "divide", "calculate remainder of"};
for (int i = 0; i < operators.length; i++) {
BinaryExpr.Operator operator = operators[i];
String opDescription = operatorDescriptions[i];
for (Class> classA : classes) {
for (Class> classB : classes) {
append(buf, varVarFormatter, operator, classA, classB);
appendTest(testBuf, varVarTestFormatter, operator, classA, classB);
append(buf, arrayArrayFormatter, operator, opDescription, classA, classB);
append(buf, arrayVarFormatter, operator, classA, classB);
append(buf, varArrayFormatter, operator, classA, classB);
}
}
}
// compare tests
for (Class> classA : classes) {
for (Class> classB : classes) {
appendTest(testBuf, varVarCompareTestFormatter, classA, classB, getSmallLiteral(classA),
getBiggerLiteral(classB));
}
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
classes = new Class[] {int.class, long.class, char.class, byte.class, short.class};
operators = new BinaryExpr.Operator[] {
BinaryExpr.Operator.BINARY_OR,
BinaryExpr.Operator.XOR,
BinaryExpr.Operator.BINARY_AND
};
for (BinaryExpr.Operator operator : operators) {
for (Class> clazz : classes) {
append(buf, varVarFormatter, operator, clazz, clazz);
append(buf, arrayArrayFormatter, operator, operator.name(), clazz, clazz);
append(buf, arrayVarFormatter, operator, clazz, clazz);
append(buf, varArrayFormatter, operator, clazz, clazz);
}
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
classes = new Class[] {int.class, double.class, long.class, float.class, char.class, byte.class, short.class};
for (Class> classA : classes) {
for (Class> classB : classes) {
// handle special cases with float/double arguments (need to handle NaN/precision differently)
if (classA.equals(long.class) && classB.equals(double.class)) {
// the plus is just to avoid a npe
append(buf, longDoubleCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else if (classA.equals(double.class) && classB.equals(long.class)) {
// the plus is just to avoid a npe
append(buf, inverseCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else if (classA.equals(long.class) && classB.equals(float.class)) {
// the plus is just to avoid a npe
append(buf, longDoubleCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else if (classA.equals(float.class) && classB.equals(long.class)) {
// the plus is just to avoid a npe
append(buf, inverseCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else if (classA.equals(double.class) || classB.equals(double.class)) {
// if either arg is a double, we promote to double
// the plus is just to avoid a npe
append(buf, varDoubleCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else if (classA.equals(float.class) || classB.equals(float.class)) {
// if both args can contain a float, use float comparator, otherwise promote to double
if (classA.isAssignableFrom(float.class) && classB.isAssignableFrom(float.class)) {
// the plus is just to avoid a npe
append(buf, varFloatCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
} else {
// the plus is just to avoid a npe
append(buf, varDoubleCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
}
} else {
// the plus is just to avoid a npe
append(buf, varVarCompareToFormatter, BinaryExpr.Operator.PLUS, classA, classB);
}
// the plus is just to avoid a npe
append(buf, varVarEqualsFormatter, BinaryExpr.Operator.PLUS, classA, classB);
append(buf, arrayArrayFormatter, BinaryExpr.Operator.EQUALS, "check equality of", classA, classB);
append(buf, arrayVarFormatter, BinaryExpr.Operator.EQUALS, classA, classB);
append(buf, varArrayFormatter, BinaryExpr.Operator.EQUALS, classA, classB);
}
}
operators = new BinaryExpr.Operator[] {
BinaryExpr.Operator.LESS,
BinaryExpr.Operator.GREATER,
BinaryExpr.Operator.LESS_EQUALS,
BinaryExpr.Operator.GREATER_EQUALS
};
for (BinaryExpr.Operator operator : operators) {
for (Class> classA : classes) {
for (Class> classB : classes) {
append(buf, varVarCompareToUserFormatter, operator, classA, classB);
append(buf, arrayArrayFormatter, operator, "compare", classA, classB);
append(buf, arrayVarFormatter, operator, classA, classB);
append(buf, varArrayFormatter, operator, classA, classB);
}
}
}
for (BinaryExpr.Operator operator : operators) {
append(buf, varVarCompareToUserFormatter, operator, Comparable.class, Comparable.class);
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
// no or and and because we like shortcircuit
operators = new BinaryExpr.Operator[] {
BinaryExpr.Operator.BINARY_OR,
BinaryExpr.Operator.XOR,
BinaryExpr.Operator.BINARY_AND,
};
for (BinaryExpr.Operator operator : operators) {
append(buf, varVarBooleanFormatter, operator, Boolean.class, Boolean.class);
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
append(buf, arrayArrayFormatter, BinaryExpr.Operator.EQUALS, "check equality of", Boolean.class, boolean.class);
append(buf, arrayArrayFormatter, BinaryExpr.Operator.EQUALS, "check equality of", boolean.class, Boolean.class);
append(buf, arrayArrayFormatter, BinaryExpr.Operator.EQUALS, "check equality of", boolean.class, boolean.class);
append(buf, arrayArrayFormatter, BinaryExpr.Operator.EQUALS, "check equality of", Object.class, Object.class);
append(buf, arrayVarFormatter, BinaryExpr.Operator.EQUALS, boolean.class, Boolean.class);
append(buf, arrayVarFormatter, BinaryExpr.Operator.EQUALS, Object.class, Object.class);
append(buf, varArrayFormatter, BinaryExpr.Operator.EQUALS, Boolean.class, boolean.class);
append(buf, varArrayFormatter, BinaryExpr.Operator.EQUALS, Object.class, Object.class);
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
operators = new BinaryExpr.Operator[] {
BinaryExpr.Operator.LESS,
BinaryExpr.Operator.GREATER,
BinaryExpr.Operator.LESS_EQUALS,
BinaryExpr.Operator.GREATER_EQUALS
};
for (BinaryExpr.Operator operator : operators) {
append(buf, arrayArrayFormatter, operator, "compare", Comparable.class, Comparable.class);
append(buf, arrayVarFormatter, operator, Comparable.class, Comparable.class);
append(buf, varArrayFormatter, operator, Comparable.class, Comparable.class);
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
classes = new Class[] {int.class, double.class, long.class, float.class, char.class, byte.class, short.class};
// Functions for null-safe casts between primitive types
for (Class> classA : classes) {
for (Class> classB : classes) {
// don't create functions for redundant casts (e.g. intCast(int))
if (classA != classB) {
// the plus is just so we don't get a npe
append(buf, castFormatter, BinaryExpr.Operator.PLUS, classA, classB);
}
}
}
// Functions for null-safe casts from Object to primitive types
for (Class> c : classes) {
// the plus and Object are just so we don't get a npe
append(buf, castFromObjFormatter, BinaryExpr.Operator.PLUS, c, Object.class);
}
// Special casts for PyObject to primitive
buf.append(" public static int intPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_INT;\n");
buf.append(" }\n");
buf.append(" return o.getIntValue();\n");
buf.append(" }\n\n");
buf.append(" public static double doublePyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_DOUBLE;\n");
buf.append(" }\n");
buf.append(" return o.getDoubleValue();\n");
buf.append(" }\n\n");
buf.append(" public static long longPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_LONG;\n");
buf.append(" }\n");
buf.append(" return o.getLongValue();\n");
buf.append(" }\n\n");
buf.append(" public static float floatPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_FLOAT;\n");
buf.append(" }\n");
buf.append(" return (float) o.getDoubleValue();\n");
buf.append(" }\n\n");
buf.append(" public static char charPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_CHAR;\n");
buf.append(" }\n");
buf.append(" return (char) o.getIntValue();\n");
buf.append(" }\n\n");
buf.append(" public static byte bytePyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_BYTE;\n");
buf.append(" }\n");
buf.append(" return (byte) o.getIntValue();\n");
buf.append(" }\n\n");
buf.append(" public static short shortPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return QueryConstants.NULL_SHORT;\n");
buf.append(" }\n");
buf.append(" return (short) o.getIntValue();\n");
buf.append(" }\n\n");
buf.append(" public static String doStringPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return null;\n");
buf.append(" }\n");
buf.append(" return o.getStringValue();\n");
buf.append(" }\n\n");
buf.append(" public static boolean booleanPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" throw new NullPointerException(\"Provided value is unexpectedly null;");
buf.append(" cannot cast to boolean\");\n");
buf.append(" }\n");
buf.append(" return o.getBooleanValue();\n");
buf.append(" }\n\n");
buf.append(" public static Boolean doBooleanPyCast(Object a) {\n");
buf.append(" if (a != null && !(a instanceof PyObject)) {\n");
buf.append(" throw new IllegalArgumentException(\"Provided value is not a PyObject\");\n");
buf.append(" }\n");
buf.append(" PyObject o = (PyObject) a;\n");
buf.append(" if (o == null || o.isNone()) {\n");
buf.append(" return null;\n");
buf.append(" }\n");
buf.append(" return o.getBooleanValue();\n");
buf.append(" }\n\n");
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
classes = new Class[] {int.class, double.class, long.class, float.class, char.class, byte.class, short.class};
for (Class> clazz : classes) {
// the plus is just so we don't get a npe
append(buf, negateFormatter, BinaryExpr.Operator.PLUS, clazz, clazz);
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------
buf.append("}\n");
testBuf.append("}\n");
String fileName =
"./engine/table/src/main/java/io/deephaven/engine/tables/lang/QueryLanguageFunctionUtils.java";
String testFileName =
"./engine/table/src/test/java/io/deephaven/engine/tables/lang/TestLanguageFunctionUtil.java";
try {
try (BufferedWriter out = new BufferedWriter(new FileWriter(fileName))) {
out.write(buf.toString());
}
try (BufferedWriter testOut = new BufferedWriter(new FileWriter(testFileName))) {
testOut.write(testBuf.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finished generating QueryLanguageFunctionUtils in "
+ new DecimalFormat().format(System.currentTimeMillis() - start) + " millis");
System.out.println("Wrote QueryLanguageFunctionUtils to: " + fileName);
System.out.println("Wrote TestLanguageFunctionUtil to: " + testFileName);
}
private static void append(StringBuilder buf, MessageFormat messageFormat, BinaryExpr.Operator op,
Class> type1, Class> type2) {
append(buf, messageFormat, op, null, type1, type2);
}
private static void append(StringBuilder buf, MessageFormat messageFormat, BinaryExpr.Operator op,
String opDescription, Class> type1, Class> type2) {
append(buf, messageFormat, op, opDescription, type1, type2, null, null);
}
private static void appendTest(StringBuilder buf, MessageFormat messageFormat, BinaryExpr.Operator op,
Class> type1, Class> type2) {
append(buf, messageFormat, op, null, type1, type2, getLiteral(type1), getLiteral(type2));
}
private static void appendTest(StringBuilder buf, MessageFormat messageFormat,
Class> type1, Class> type2,
String literal1, String literal2) {
append(buf, messageFormat, null, null, type1, type2, literal1, literal2);
}
private static void append(StringBuilder buf, MessageFormat messageFormat, BinaryExpr.Operator op,
String opDescription, Class> type1, Class> type2, String literal1, String literal2) {
Class> promotedType;
if (op == BinaryExpr.Operator.EQUALS || op == BinaryExpr.Operator.LESS || op == BinaryExpr.Operator.GREATER
|| op == BinaryExpr.Operator.LESS_EQUALS || op == BinaryExpr.Operator.GREATER_EQUALS) {
promotedType = boolean.class;
} else if (io.deephaven.util.type.TypeUtils.getBoxedType(type1) == Boolean.class
|| io.deephaven.util.type.TypeUtils.getBoxedType(type2) == Boolean.class) {
promotedType = Boolean.class;
} else {
promotedType = QueryLanguageParser.binaryNumericPromotionType(type1, type2);
}
String cast = "";
if (op == BinaryExpr.Operator.DIVIDE && isNonFPNumber(type2)) {
cast = "(double)";
promotedType = double.class;
}
String compareTo = "";
if (op == BinaryExpr.Operator.LESS) {
compareTo = "<0";
} else if (op == BinaryExpr.Operator.GREATER) {
compareTo = ">0";
} else if (op == BinaryExpr.Operator.LESS_EQUALS) {
compareTo = "<=0";
} else if (op == BinaryExpr.Operator.GREATER_EQUALS) {
compareTo = ">=0";
}
Class> type1Unboxed = io.deephaven.util.type.TypeUtils.getUnboxedType(type1);
Class> type2Unboxed = io.deephaven.util.type.TypeUtils.getUnboxedType(type2);
Class> promotedTypeUnboxed = io.deephaven.util.type.TypeUtils.getUnboxedType(promotedType);
final String operatorName = op == null ? null : QueryLanguageParser.getOperatorName(op);
final String operatorSymbol = op == null ? null : QueryLanguageParser.getOperatorSymbol(op);
buf.append(messageFormat.format(new Object[] {
operatorName,
type1.getSimpleName(),
type2.getSimpleName(),
promotedType.getSimpleName(),
type1Unboxed == null ? "" : type1Unboxed.getSimpleName().toUpperCase(),
type2Unboxed == null ? "" : type2Unboxed.getSimpleName().toUpperCase(),
promotedTypeUnboxed == null ? "" : promotedTypeUnboxed.getSimpleName().toUpperCase(),
operatorSymbol,
cast,
compareTo,
opDescription,
literal1,
literal2,
TypeUtils.getBoxedType(promotedType).getSimpleName()
})).append("\n\n");
}
/**
* Returns a String of an example literal value of {@code type}. Used for generating tests.
*/
private static String getLiteral(Class> type) {
if (type.equals(boolean.class)) {
return "false";
} else if (type.equals(char.class)) {
return "'0'";
} else if (type.equals(byte.class)) {
return "(byte)42";
} else if (type.equals(short.class)) {
return "(short)42";
} else if (type.equals(int.class)) {
return "42";
} else if (type.equals(long.class)) {
return "42L";
} else if (type.equals(float.class)) {
return "42f";
} else if (type.equals(double.class)) {
return "42d";
} else {
throw new IllegalArgumentException("Unsupported type " + type);
}
}
/**
* Returns a String of an example small literal value of {@code type}. Used for generating comparison tests.
*/
private static String getSmallLiteral(Class> type) {
if (type.equals(boolean.class)) {
return "false";
} else if (type.equals(char.class)) {
return "(char)1";
} else if (type.equals(byte.class)) {
return "(byte)1";
} else if (type.equals(short.class)) {
return "(short)1";
} else if (type.equals(int.class)) {
return "1";
} else if (type.equals(long.class)) {
return "1L";
} else if (type.equals(float.class)) {
return "0.01f";
} else if (type.equals(double.class)) {
return "0.01d";
} else {
throw new IllegalArgumentException("Unsupported type " + type);
}
}
/**
* Returns a String of an example bigger literal value of {@code type}. Must be larger than the value produced by
* getSmallLiteral (across all types). Used for generating comparison tests.
*/
private static String getBiggerLiteral(Class> type) {
if (type.equals(boolean.class)) {
return "true";
} else if (type.equals(char.class)) {
return "'1'";
} else if (type.equals(byte.class)) {
return "(byte)42";
} else if (type.equals(short.class)) {
return "(short)42";
} else if (type.equals(int.class)) {
return "42";
} else if (type.equals(long.class)) {
return "42L";
} else if (type.equals(float.class)) {
return "42.0f";
} else if (type.equals(double.class)) {
return "42.0d";
} else {
throw new IllegalArgumentException("Unsupported type " + type);
}
}
private static boolean isNonFPNumber(Class> type) {
type = TypeUtils.getUnboxedType(type);
// noinspection SimplifiableIfStatement
if (type == null) {
return false;
}
return type == int.class || type == long.class || type == byte.class || type == short.class
|| type == char.class;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy