org.neo4j.codegen.ExpressionTemplate Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-codegen Show documentation
Show all versions of neo4j-codegen Show documentation
Simple library for generating code.
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.codegen;
import static org.neo4j.codegen.TypeReference.OBJECT;
import static org.neo4j.codegen.TypeReference.VOID;
import static org.neo4j.codegen.TypeReference.typeReference;
import java.lang.reflect.Modifier;
public abstract class ExpressionTemplate {
protected final TypeReference type;
protected ExpressionTemplate(TypeReference type) {
this.type = type;
}
public static ExpressionTemplate self(TypeReference type) {
return new ExpressionTemplate(type) {
@Override
void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.load(new LocalVariable(method.clazz.handle(), "this", 0));
}
};
}
/** invoke a static method or constructor */
public static ExpressionTemplate invoke(final MethodReference method, final ExpressionTemplate... arguments) {
// Try to materialize this expression early
Expression[] materialized = tryMaterialize(arguments);
if (materialized != null) {
return Expression.invoke(method, materialized);
}
// some part needs reference to the method context, so this expression will transitively need it
return new ExpressionTemplate(method.returns()) {
@Override
protected void templateAccept(CodeBlock generator, ExpressionVisitor visitor) {
visitor.invoke(method, materialize(generator, arguments));
}
};
}
/** invoke an instance method */
public static ExpressionTemplate invoke(
final ExpressionTemplate target, final MethodReference method, final ExpressionTemplate... arguments) {
if (target instanceof Expression) {
Expression[] materialized = tryMaterialize(arguments);
if (materialized != null) {
return Expression.invoke((Expression) target, method, materialized);
}
}
return new ExpressionTemplate(method.returns()) {
@Override
protected void templateAccept(CodeBlock generator, ExpressionVisitor visitor) {
visitor.invoke(target.materialize(generator), method, materialize(generator, arguments));
}
};
}
/** load a local variable */
public static ExpressionTemplate load(final String name, final TypeReference type) {
return new ExpressionTemplate(type) {
@Override
protected void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.load(method.local(name));
}
};
}
public static ExpressionTemplate get(ExpressionTemplate target, Class> fieldType, String fieldName) {
return get(target, typeReference(fieldType), fieldName);
}
/** instance field */
public static ExpressionTemplate get(ExpressionTemplate target, TypeReference fieldType, String fieldName) {
return get(target, Lookup.field(fieldType, fieldName), fieldType);
}
/** instance field */
public static ExpressionTemplate get(final ExpressionTemplate target, final FieldReference field) {
if (target instanceof Expression) {
return Expression.get((Expression) target, field);
}
return new ExpressionTemplate(field.type()) {
@Override
void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.getField(target.materialize(method), field);
}
};
}
/** static field from the class that will host this expression */
public static ExpressionTemplate get(TypeReference fieldType, String fieldName) {
return get(Lookup.field(fieldType, fieldName), fieldType);
}
/** instance field */
public static ExpressionTemplate get(
final ExpressionTemplate target, final Lookup field, TypeReference type) {
return new ExpressionTemplate(type) {
@Override
void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.getField(target.materialize(method), field.lookup(method));
}
};
}
/** static field */
public static ExpressionTemplate get(final Lookup field, TypeReference type) {
return new ExpressionTemplate(type) {
@Override
void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.getStatic(field.lookup(method));
}
};
}
Expression materialize(final CodeBlock method) {
return new Expression(type) {
@Override
public void accept(ExpressionVisitor visitor) {
ExpressionTemplate.this.templateAccept(method, visitor);
}
};
}
public static ExpressionTemplate cast(Class> clazz, ExpressionTemplate expression) {
return cast(typeReference(clazz), expression);
}
public static ExpressionTemplate cast(TypeReference type, ExpressionTemplate expression) {
return new ExpressionTemplate(type) {
@Override
protected void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.cast(type, expression.materialize(method));
}
};
}
public static ExpressionTemplate add(ExpressionTemplate lhs, ExpressionTemplate rhs, TypeReference type) {
return new ExpressionTemplate(type) {
@Override
protected void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.add(lhs.materialize(method), rhs.materialize(method));
}
};
}
public static ExpressionTemplate subtract(ExpressionTemplate lhs, ExpressionTemplate rhs, TypeReference type) {
return new ExpressionTemplate(type) {
@Override
protected void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.subtract(lhs.materialize(method), rhs.materialize(method));
}
};
}
abstract void templateAccept(CodeBlock method, ExpressionVisitor visitor);
private static Expression[] tryMaterialize(ExpressionTemplate[] templates) {
if (templates instanceof Expression[]) {
return (Expression[]) templates;
}
Expression[] materialized = new Expression[templates.length];
for (int i = 0; i < materialized.length; i++) {
if (templates[i] instanceof Expression) {
materialized[i] = (Expression) templates[i];
} else {
return null;
}
}
return materialized;
}
static Expression[] materialize(CodeBlock method, ExpressionTemplate[] templates) {
Expression[] expressions = new Expression[templates.length];
for (int i = 0; i < expressions.length; i++) {
expressions[i] = templates[i].materialize(method);
}
return expressions;
}
// TODO I am not crazy about the way type parameters are sent here
public static ExpressionTemplate invokeSuperConstructor(
final ExpressionTemplate[] parameters, final TypeReference[] parameterTypes) {
assert parameters.length == parameterTypes.length;
return new ExpressionTemplate(OBJECT) {
@Override
void templateAccept(CodeBlock method, ExpressionVisitor visitor) {
visitor.invoke(
Expression.SUPER,
new MethodReference(
method.clazz.handle().parent(), "", VOID, Modifier.PUBLIC, parameterTypes),
materialize(method, parameters));
}
};
}
public TypeReference type() {
return type;
}
}