com.redhat.ceylon.compiler.java.codegen.AnnotationInvocation Maven / Gradle / Ivy
package com.redhat.ceylon.compiler.java.codegen;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.redhat.ceylon.common.NonNull;
import com.redhat.ceylon.common.Nullable;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCAnnotation;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.util.ListBuffer;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Type;
/**
* The invocation of an annotation constructor or annotation class
*/
public class AnnotationInvocation {
private Function constructorDeclaration;
private final List constructorParameters = new ArrayList();
private Declaration primary;
private List annotationArguments = new ArrayList();
private boolean interop;
public AnnotationInvocation() {
}
/**
* The annotation constructor, if this is the invocation of an annotation constructor
*/
public Function getConstructorDeclaration() {
return constructorDeclaration;
}
public void setConstructorDeclaration(Function constructorDeclaration) {
this.constructorDeclaration = constructorDeclaration;
}
/**
* The parameters of the annotation constructor
*/
@NonNull
public List getConstructorParameters() {
return constructorParameters;
}
public void addConstructorParameter(AnnotationConstructorParameter p) {
constructorParameters.add(p);
}
public void addConstructorParameter(int index, AnnotationConstructorParameter p) {
constructorParameters.add(index, p);
}
public int indexOfConstructorParameter(Parameter parameter) {
int index = 0;
for (AnnotationConstructorParameter acp : getConstructorParameters()) {
if (acp.getParameter().equals(parameter)) {
return index;
}
index++;
}
return -1;
}
@Nullable
public AnnotationConstructorParameter findConstructorParameter(Parameter parameter) {
for (AnnotationConstructorParameter p : constructorParameters) {
if (p.getParameter().equals(parameter)) {
return p;
}
}
return null;
}
/**
* The primary of the invocation:
* Either an annotation constructor ({@code Function})
* or an annotation class ({@code Class}).
*/
public Declaration getPrimary() {
return primary;
}
public void setPrimary(Class primary) {
this.primary = primary;
}
public void setPrimary(Function primary) {
this.primary = primary;
}
/**
* Is this an annotation class instantiation (i.e. is the primary a Class)?
*/
public boolean isInstantiation() {
return getPrimary() instanceof Class;
}
/**
* The type of the annotation class ultimately being instantiated
*/
public Type getAnnotationClassType() {
if (isInstantiation()) {
return ((Class)getPrimary()).getType();
} else {
// TODO Function may not be declared to return this!
return ((Function)getPrimary()).getType();
}
}
/**
* The parameters of the primary
*/
public List getParameters() {
return ((Functional)primary).getFirstParameterList().getParameters();
}
/**
* The parameters of the class ultimately being instantiated
*/
public List getClassParameters() {
return ((Class)getAnnotationClassType().getDeclaration()).getParameterList().getParameters();
}
/**
* The arguments of the invocation
*/
public List getAnnotationArguments() {
return annotationArguments;
}
/**
* True if this is an interop annotation constructor
* (i.e. one invented by the model loader for the purposes of being able
* to use a Java annotation).
*/
public boolean isInterop() {
return interop;
}
public void setInterop(boolean interop) {
this.interop = interop;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (getConstructorDeclaration() != null) {
sb.append(getConstructorDeclaration().getName()).append("(");
List ctorParams = getConstructorParameters();
for (AnnotationConstructorParameter param : ctorParams) {
sb.append(param.getFieldName()).append(", ");
}
if (!ctorParams.isEmpty()) {
sb.setLength(sb.length()-2);
}
sb.append(")\n\t=> ");
}
sb.append(primary != null ? primary.getName() : "NULL").append("{");
for (AnnotationArgument argument : annotationArguments) {
sb.append("\n\t\t").append(argument).append(";");
}
return sb.append("\n\t};").toString();
}
/**
* Make a type expression for the underlying annotation class
*/
public JCExpression makeAnnotationType(ExpressionTransformer exprGen) {
Type type = getAnnotationClassType();
if (isInterop()) {
return exprGen.makeJavaType(type.getSatisfiedTypes().get(0));
} else {
return exprGen.makeJavaType(type, ExpressionTransformer.JT_ANNOTATION);
}
}
public JCExpression makeAnnotation(
ExpressionTransformer exprGen,
AnnotationInvocation ai,
com.redhat.ceylon.langtools.tools.javac.util.List parameterPath) {
ListBuffer args = new ListBuffer();
for (AnnotationArgument aa : getAnnotationArguments()) {
Parameter name = aa.getParameter();
if (!isInstantiation()) {
AnnotationInvocation annotationInvocation = (AnnotationInvocation)getConstructorDeclaration().getAnnotationConstructor();
for (AnnotationArgument a2 : annotationInvocation.getAnnotationArguments()) {
if (a2.getTerm() instanceof ParameterAnnotationTerm) {
if (((ParameterAnnotationTerm)a2.getTerm()).getSourceParameter().equals(aa.getParameter())) {
name = a2.getParameter();
break;
}
}
}
}
args.append(makeAnnotationArgument(exprGen, ai,
name,
parameterPath.append(aa), aa.getTerm()));
}
return exprGen.make().Annotation(makeAnnotationType(exprGen),
args.toList());
}
/**
* Make an annotation argument (@code member = value) for the given term
*/
public JCExpression makeAnnotationArgument(ExpressionTransformer exprGen, AnnotationInvocation ai,
Parameter bind,
com.redhat.ceylon.langtools.tools.javac.util.List fieldPath, AnnotationTerm term) {
return exprGen.make().Assign(
exprGen.naming.makeName(bind.getModel(),
Naming.NA_ANNOTATION_MEMBER | Naming.NA_MEMBER),
term.makeAnnotationArgumentValue(exprGen, ai,
fieldPath));
}
/**
* Encode this invocation into a {@code @AnnotationInstantiation}
* (if the annotation constructors just calls the annotation class)
* or {@code @AnnotationInstantiationTree}
* (if the annotation constructor calls another annotation constructor)
*/
public JCAnnotation encode(AbstractTransformer gen, ListBuffer instantiations) {
ListBuffer arguments = new ListBuffer();
for (AnnotationArgument argument : getAnnotationArguments()) {
arguments.append(gen.make().Literal(
argument.getTerm().encode(gen, instantiations)));
}
JCExpression primary;
if (isInstantiation()) {
primary = gen.makeJavaType(getAnnotationClassType());
} else {
primary = gen.naming.makeName((Function)getPrimary(), Naming.NA_FQ | Naming.NA_WRAPPER);
}
JCAnnotation atInstantiation = gen.make().Annotation(
gen.make().Type(gen.syms().ceylonAtAnnotationInstantiationType),
com.redhat.ceylon.langtools.tools.javac.util.List.of(
gen.make().Assign(
gen.naming.makeUnquotedIdent("arguments"),
gen.make().NewArray(null, null, arguments.toList())),
gen.make().Assign(
gen.naming.makeUnquotedIdent("primary"),
gen.naming.makeQualIdent(primary, "class"))
));
if (instantiations.isEmpty()) {
return atInstantiation;
} else {
return gen.make().Annotation(
gen.make().Type(gen.syms().ceylonAtAnnotationInstantiationTreeType),
com.redhat.ceylon.langtools.tools.javac.util.List.of(
gen.make().NewArray(null, null, instantiations.prepend(atInstantiation).toList())));
}
}
public Iterable findAnnotationArgumentForClassParameter(Node node, Set s, Parameter classParameter) {
List result = new ArrayList(1);
if (isInstantiation()) {
for (AnnotationArgument aa : getAnnotationArguments()) {
if (aa.getParameter().equals(classParameter)) {
result.add(aa);
}
}
} else {
// we're invoking another constructor
AnnotationInvocation ctor = (AnnotationInvocation)((Function)getPrimary()).getAnnotationConstructor();
if (!s.add(ctor.constructorDeclaration)) {
throw new BugException(node, "recursive annotation constructor: "+ctor.constructorDeclaration.getQualifiedNameString()+" invokes itself");
}
// find it's arguments
for (AnnotationArgument otherArgument : ctor.findAnnotationArgumentForClassParameter(node, s, classParameter)) {
if (otherArgument.getTerm() instanceof ParameterAnnotationTerm) {
Parameter sourceParameter = ((ParameterAnnotationTerm)otherArgument.getTerm()).getSourceParameter();
for (AnnotationArgument aa : getAnnotationArguments()) {
if (aa.getParameter().equals(sourceParameter)) {
result.add(aa);
}
}
}
}
}
return result;
}
public com.redhat.ceylon.langtools.tools.javac.util.List makeExprAnnotations(ExpressionTransformer exprGen,
AnnotationInvocation toplevel,
com.redhat.ceylon.langtools.tools.javac.util.List fieldPath) {
// Collect into groups according to their type
Map, List> groups = new LinkedHashMap, List>();
for (AnnotationArgument aa : getAnnotationArguments()) {
AnnotationTerm term = aa.getTerm();
List group = groups.get(term.getClass());
if (group == null) {
group = new ArrayList(1);
groups.put(term.getClass(), group);
}
group.add(aa);
}
// Make a @*Exprs annotation for each type
ListBuffer exprsAnnos = new ListBuffer();
for (List group : groups.values()) {
AnnotationTerm factory = null;
ListBuffer valueAnnos = new ListBuffer();
for (AnnotationArgument aa : group) {
AnnotationTerm term = aa.getTerm();
com.redhat.ceylon.langtools.tools.javac.util.List annos = term.makeExprAnnotations(exprGen, this, fieldPath.append(aa));
if (annos != null) {
factory = group.get(0).getTerm();
valueAnnos.appendList(annos);
}
}
if (!valueAnnos.isEmpty()) {
com.redhat.ceylon.langtools.tools.javac.util.List exprs = factory.makeExprs(exprGen, valueAnnos.toList());
if (exprs != null) {
exprsAnnos.appendList(exprs);
} else {
exprs = factory.makeExprs(exprGen, valueAnnos.toList());
}
}
}
return exprsAnnos.toList();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy