in core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java [188:411]
private static ClassDeclaration classDecl(
JavaTypeFactoryImpl.SyntheticRecordType type) {
ClassDeclaration classDeclaration =
Expressions.classDecl(
Modifier.PUBLIC | Modifier.STATIC,
type.getName(),
null,
ImmutableList.of(Serializable.class),
new ArrayList<>());
// For each field:
// public T0 f0;
// ...
for (Types.RecordField field : type.getRecordFields()) {
classDeclaration.memberDeclarations.add(
Expressions.fieldDecl(
field.getModifiers(),
Expressions.parameter(
field.getType(), field.getName()),
null));
}
// Constructor:
// Foo(T0 f0, ...) { this.f0 = f0; ... }
final BlockBuilder blockBuilder = new BlockBuilder();
final List<ParameterExpression> parameters = new ArrayList<>();
final ParameterExpression thisParameter =
Expressions.parameter(type, "this");
// Here a constructor without parameter is used because the generated
// code could cause error if number of fields is too large.
classDeclaration.memberDeclarations.add(
Expressions.constructorDecl(
Modifier.PUBLIC,
type,
parameters,
blockBuilder.toBlock()));
// equals method():
// public boolean equals(Object o) {
// if (this == o) return true;
// if (!(o instanceof MyClass)) return false;
// final MyClass that = (MyClass) o;
// return this.f0 == that.f0
// && equal(this.f1, that.f1)
// ...
// }
final BlockBuilder blockBuilder2 = new BlockBuilder();
final ParameterExpression thatParameter =
Expressions.parameter(type, "that");
final ParameterExpression oParameter =
Expressions.parameter(Object.class, "o");
blockBuilder2.add(
Expressions.ifThen(
Expressions.equal(thisParameter, oParameter),
Expressions.return_(null, Expressions.constant(true))));
blockBuilder2.add(
Expressions.ifThen(
Expressions.not(
Expressions.typeIs(oParameter, type)),
Expressions.return_(null, Expressions.constant(false))));
blockBuilder2.add(
Expressions.declare(
Modifier.FINAL,
thatParameter,
Expressions.convert_(oParameter, type)));
final List<Expression> conditions = new ArrayList<>();
for (Types.RecordField field : type.getRecordFields()) {
conditions.add(
Primitive.is(field.getType())
? Expressions.equal(
Expressions.field(thisParameter, field.getName()),
Expressions.field(thatParameter, field.getName()))
: Expressions.call(BuiltInMethod.OBJECTS_EQUAL.method,
Expressions.field(thisParameter, field.getName()),
Expressions.field(thatParameter, field.getName())));
}
blockBuilder2.add(
Expressions.return_(null, Expressions.foldAnd(conditions)));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
boolean.class,
"equals",
Collections.singletonList(oParameter),
blockBuilder2.toBlock()));
// hashCode method:
// public int hashCode() {
// int h = 0;
// h = hash(h, f0);
// ...
// return h;
// }
final BlockBuilder blockBuilder3 = new BlockBuilder();
final ParameterExpression hParameter =
Expressions.parameter(int.class, "h");
final ConstantExpression constantZero =
Expressions.constant(0);
blockBuilder3.add(
Expressions.declare(0, hParameter, constantZero));
for (Types.RecordField field : type.getRecordFields()) {
final Method method = BuiltInMethod.HASH.method;
blockBuilder3.add(
Expressions.statement(
Expressions.assign(
hParameter,
Expressions.call(
method.getDeclaringClass(),
method.getName(),
ImmutableList.of(
hParameter,
Expressions.field(thisParameter, field))))));
}
blockBuilder3.add(
Expressions.return_(null, hParameter));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
int.class,
"hashCode",
Collections.emptyList(),
blockBuilder3.toBlock()));
// compareTo method:
// public int compareTo(MyClass that) {
// int c;
// c = compare(this.f0, that.f0);
// if (c != 0) return c;
// ...
// return 0;
// }
final BlockBuilder blockBuilder4 = new BlockBuilder();
final ParameterExpression cParameter =
Expressions.parameter(int.class, "c");
final int mod = type.getRecordFields().size() == 1 ? Modifier.FINAL : 0;
blockBuilder4.add(
Expressions.declare(mod, cParameter, null));
final ConditionalStatement conditionalStatement =
Expressions.ifThen(
Expressions.notEqual(cParameter, constantZero),
Expressions.return_(null, cParameter));
for (Types.RecordField field : type.getRecordFields()) {
MethodCallExpression compareCall;
try {
final Method method = (field.nullable()
? BuiltInMethod.COMPARE_NULLS_LAST
: BuiltInMethod.COMPARE).method;
compareCall =
Expressions.call(method.getDeclaringClass(), method.getName(),
Expressions.field(thisParameter, field),
Expressions.field(thatParameter, field));
} catch (RuntimeException e) {
if (e.getCause() instanceof NoSuchMethodException) {
// Just ignore the field in compareTo
// "create synthetic record class" blindly creates compareTo for
// all the fields, however not all the records will actually be used
// as sorting keys (e.g. temporary state for aggregate calculation).
// In those cases it is fine if we skip the problematic fields.
continue;
}
throw e;
}
blockBuilder4.add(
Expressions.statement(
Expressions.assign(
cParameter,
compareCall)));
blockBuilder4.add(conditionalStatement);
}
blockBuilder4.add(
Expressions.return_(null, constantZero));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
int.class,
"compareTo",
Collections.singletonList(thatParameter),
blockBuilder4.toBlock()));
// toString method:
// public String toString() {
// return "{f0=" + f0
// + ", f1=" + f1
// ...
// + "}";
// }
final BlockBuilder blockBuilder5 = new BlockBuilder();
Expression expression5 = null;
for (Types.RecordField field : type.getRecordFields()) {
if (expression5 == null) {
expression5 =
Expressions.constant("{" + field.getName() + "=");
} else {
expression5 =
Expressions.add(
expression5,
Expressions.constant(", " + field.getName() + "="));
}
expression5 =
Expressions.add(
expression5,
Expressions.field(thisParameter, field.getName()));
}
expression5 =
expression5 == null
? Expressions.constant("{}")
: Expressions.add(
expression5,
Expressions.constant("}"));
blockBuilder5.add(
Expressions.return_(
null,
expression5));
classDeclaration.memberDeclarations.add(
Expressions.methodDecl(
Modifier.PUBLIC,
String.class,
"toString",
Collections.emptyList(),
blockBuilder5.toBlock()));
return classDeclaration;
}