AnnotationUtils.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import autumn.lang.compiler.ast.nodes.Annotation;
import autumn.lang.compiler.ast.nodes.AnnotationList;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.lang.compiler.compilers.ModuleCompiler;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomAnnotation;
import com.mackenziehigh.autumn.lang.compiler.typesystem.ICustomAnnotatable;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotation;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotationType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IType;
import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.tree.AnnotationNode;
/**
* An instance of this class provides generalized methods for processing annotations.
*
* @author Mackenzie High
*/
public final class AnnotationUtils
{
/**
* Essentially, this is the enclosing module that is bing compiled.
*/
private final ModuleCompiler module;
/**
* Sole Constructor.
*
* @param module is essentially the module that is being compiled.
*/
public AnnotationUtils(final ModuleCompiler module)
{
Preconditions.checkNotNull(module);
this.module = module;
}
/**
* This method compiles a single annotation to its bytecode representation.
*
* <p>
* Note: This method does not perform any type-checking.
* </p>
*
* @param annotation is the annotation to compile.
* @return the bytecode representation of the given annotation.
*/
public AnnotationNode compileAnnotation(final IAnnotation annotation)
{
Preconditions.checkNotNull(annotation);
final String descriptor = annotation.getAnnotationType().getDescriptor();
final AnnotationNode node = new AnnotationNode(descriptor);
final List<String> values = annotation.getAnnotationValues();
if (values == null)
{
node.values = null;
}
else
{
node.values = Lists.newArrayList("value", values);
}
return node;
}
/**
* This method compiles a list of annotations to their bytecode representations.
*
* <p>
* Note: This method does not perform any type-checking.
* </p>
*
* @param annotations are the annotations to compile.
* @return an immutable list containing the compiled list of annotations.
*/
public List<AnnotationNode> compileAnnotationList(final Iterable<IAnnotation> annotations)
{
Preconditions.checkNotNull(annotations);
final List<AnnotationNode> result = Lists.newLinkedList();
for (IAnnotation anno : annotations)
{
result.add(this.compileAnnotation(anno));
}
return ImmutableList.copyOf(result);
}
/**
* This method gets the type representations of the annotations in an annotation-list.
*
* <p>
* This method performs type-checking on the annotation-list.
* </p>
*
* @param annotations is the annotation-list itself.
* @return an immutable list containing the types of the annotations in the annotation-list.
*/
public List<IAnnotation> typesOf(final AnnotationList annotations)
{
Preconditions.checkNotNull(annotations);
final List<IAnnotation> result = Lists.newLinkedList();
for (Annotation anno : annotations.getAnnotations())
{
final IAnnotation type = this.typeOf(anno);
result.add(type);
}
return ImmutableList.copyOf(result);
}
/**
* This method creates the type-system representation of an annotation from its AST node.
*
* <p>
* The method will issue an error-message, if the annotation-type does not exist.
* </p>
*
* @param annotation is the AST node that represents the annotation.
* @return the annotation's type-system representation,
* or null, if no such representation can be created.
*/
public IAnnotation typeOf(final Annotation annotation)
{
Preconditions.checkNotNull(annotation);
final IType type = module.imports.resolveReturnType(annotation.getType());
if (type == null || type instanceof IAnnotationType == false)
{
return null;
}
final List<String> values = annotation.getValues();
final IAnnotation result = new CustomAnnotation(null, values, (IAnnotationType) type);
return result;
}
/**
* This method creates the type-system representation of an annotation a Class object.
*
* @param type is the class-object that represents the annotation-type.
* @return the annotation's type-system representation,
* or null, if no such representation can be created.
*/
public IAnnotation typeOf(final Class type)
{
Preconditions.checkNotNull(type);
final IAnnotationType annotation_type = (IAnnotationType) module.program.typesystem
.typefactory()
.fromClass(type);
final IAnnotation result = new CustomAnnotation(null, null, annotation_type);
return result;
}
/**
* This method adds a marker annotation to an annotatable custom entity.
*
* @param annotated is the annotatable custom entity.
* @param type is the type of the annotation.
*/
public void add(final ICustomAnnotatable annotated,
final Class type)
{
Preconditions.checkNotNull(annotated);
Preconditions.checkNotNull(type);
/**
* Get the type-system representation of the marker annotation.
*/
final IAnnotation annotation = typeOf(type);
/**
* Get the annotations that are already applied to the entity.
*/
final List<IAnnotation> immutable = annotated.getAnnotations();
/**
* We need an immutable list in order to add the new annotation.
*/
final List<IAnnotation> mutable = new LinkedList<IAnnotation>(immutable);
/**
* Add the new marker annotation to the entity.
*/
mutable.add(annotation);
annotated.setAnnotations(mutable);
}
}