Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Bluhm <bluhmdj@ornl.gov>
  • Loading branch information
dbluhm committed May 8, 2020
1 parent 6e925dc commit a91e0e6
Showing 1 changed file with 167 additions and 90 deletions.
@@ -1,12 +1,16 @@
package org.eclipse.ice.dev.annotations.processors;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
Expand All @@ -29,34 +33,59 @@

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.eclipse.ice.dev.annotations.DataElement;
import org.eclipse.ice.dev.annotations.DataField;

import com.google.auto.service.AutoService;

/**
* Processor for DataElement Annotations.
*
* This will generate an implementation for an interface annotated with
* DataElement, populating the implementation with metadata and fields
* specified with the DataField annotation.
*/
@SupportedAnnotationTypes("org.eclipse.ice.dev.annotations.DataElement")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class DataElementProcessor extends AbstractProcessor {

private static final String template = "templates/DataElement.vm";
protected Messager messager;
public class UnexpectedValueError extends Exception {
private static final long serialVersionUID = -8486833574190525020L;

public UnexpectedValueError(String message) {
super(message);
}
}

/**
* A simple container for fields discovered on a DataElement.
*/
private static class Fields {
/**
* Container for name and class name attributes of DataField in string
* representation.
*/
public class Field {
String name;
String className;
public void setName(String name) {
this.name = name;
}
public void setClassName(String className) {
this.className = className;

public String getClassName() {
return className;
}

public String getName() {
return name;
}
public String getClassName() {
return className;

public void setClassName(final String className) {
this.className = className;
}

public void setName(final String name) {
this.name = name;
}

@Override
public String toString() {
return "Field (name=" + name + ", className=" + className + ")";
Expand All @@ -67,150 +96,198 @@ public String toString() {
protected Field building;

public Fields() {
this.fields = new ArrayList<Field>();
this.fields = new ArrayList<>();
this.building = null;
}

public boolean isBuilding() {
return this.building != null;
}

public void begin() {
this.building = new Field();
}

public void setName(String name) {
this.building.setName(name);
public void finish() {
this.fields.add(this.building);
this.building = null;
}

public void setClassName(String className) {
public List<Field> getFields() {
return fields;
}

public void setClassName(final String className) {
this.building.setClassName(className);
}

public void finish() {
this.fields.add(this.building);
this.building = null;
public void setName(final String name) {
this.building.setName(name);
}

@Override
public String toString() {
return fields.toString();
}

public List<Field> getFields() {
return fields;
}
}

private class FieldVisitor extends SimpleAnnotationValueVisitor8<Void, Fields> {

protected final Elements elementUtils;

FieldVisitor(Elements elementUtils) {
this.elementUtils = elementUtils;
}
/**
* Visitor that accumulates DataField information from AnnotationValues.
*/
private class FieldVisitor extends SimpleAnnotationValueVisitor8<Optional<Exception>, Fields> {

@Override
protected Void defaultAction(Object o, Fields f) {
return null;
protected Optional<Exception> defaultAction(final Object o, final Fields f) {
return Optional.of(new UnexpectedValueError("An unexpected annotation value was encountered"));
}

@Override
public Void visitAnnotation(AnnotationMirror a, Fields f) {
if (a.getAnnotationType().toString().equals(DataField.class.getCanonicalName())) {
final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues =
elementUtils.getElementValuesWithDefaults(a);
f.begin();
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry
: elementValues.entrySet()) {
entry.getValue().accept(this, f);
public Optional<Exception> visitAnnotation(final AnnotationMirror a, final Fields f) {
if (!a.getAnnotationType().toString().equals(DataField.class.getCanonicalName())) {
return Optional.of(new UnexpectedValueError("Found AnnotationMirror not of type DataField"));
}

f.begin();
for (AnnotationValue value : getAnnotationValuesForMirror(a)) {
Optional<Exception> result = value.accept(this, f);
if (result.isPresent()) {
return result;
}
f.finish();
}
return null;
f.finish();
return Optional.empty();
}

@Override
public Void visitArray(List<? extends AnnotationValue> vals, Fields f) {
for (AnnotationValue val : vals) {
val.accept(this, f);
public Optional<Exception> visitArray(final List<? extends AnnotationValue> vals, final Fields f) {
for (final AnnotationValue val : vals) {
Optional<Exception> result = val.accept(this, f);
if (result.isPresent()) {
return result;
}
}
return null;
return Optional.empty();
}

@Override
public Void visitString(String s, Fields f) {
public Optional<Exception> visitString(final String s, final Fields f) {
if (!f.isBuilding()) {
return Optional
.of(new UnexpectedValueError("Found String while still expecting DataField AnnotationMirror"));
}
f.setName(s);
return null;
return Optional.empty();
}

@Override
public Void visitType(TypeMirror t, Fields f) {
public Optional<Exception> visitType(final TypeMirror t, final Fields f) {
if (!f.isBuilding()) {
return Optional
.of(new UnexpectedValueError("Found type while still expecting DataField Annotation Mirror"));
}
f.setClassName(t.toString());
return null;
return Optional.empty();
}
}

/**
* Location of DataElement template for use with velocity.
*/
private static final String template = "templates/DataElement.vm";

protected Messager messager;
protected Elements elementUtils;

@Override
public void init(ProcessingEnvironment env) {
public void init(final ProcessingEnvironment env) {
messager = env.getMessager();
elementUtils = env.getElementUtils();
super.init(env);
}

private void writeClass(String className, Fields fields) throws IOException {
@Override
public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
final FieldVisitor fieldVisitor = new FieldVisitor();

for (final Element elem : roundEnv.getElementsAnnotatedWith(DataElement.class)) {
if (!elem.getKind().isInterface()) {
messager.printMessage(Diagnostic.Kind.ERROR, "DataElement annotation is only for interfaces");
return false;
}
final Fields fields = new Fields();
final List<? extends AnnotationValue> values = elem.getAnnotationMirrors().stream()
.map(mirror -> getAnnotationValuesForMirror(mirror)).flatMap(List::stream).collect(Collectors.toList());
for (AnnotationValue value : values) {
Optional<Exception> result = value.accept(fieldVisitor, fields);
if (result.isPresent()) {
messager.printMessage(Diagnostic.Kind.ERROR, stackTracetoString(result.get()));
return false;
}
}
try {
this.writeClass(((TypeElement) elem).getQualifiedName().toString(), fields);
} catch (final IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, stackTracetoString(e));
return false;
}
}
return true;
}

/**
* Return stack trace as string.
* @param e subject exception
* @return stack trace as string
*/
private String stackTracetoString(Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}

/**
* Get a list of annotation values from an annotation mirror.
* @param mirror the mirror from which to grab values.
* @return list of AnnotationValue
*/
private List<AnnotationValue> getAnnotationValuesForMirror(final AnnotationMirror mirror) {
final Map<? extends ExecutableElement, ? extends AnnotationValue> values = elementUtils
.getElementValuesWithDefaults(mirror);
return values.entrySet().stream().map(entry -> entry.getValue()).collect(Collectors.toList());
}

/**
* Write the implementation of DataElement annotated class to file.
* @param className the annotated class name
* @param fields the fields extracted from DataField annotations on class
* @throws IOException
*/
private void writeClass(final String className, final Fields fields) throws IOException {
String packageName = null;
int lastDot = className.lastIndexOf('.');
final int lastDot = className.lastIndexOf('.');
if (lastDot > 0) {
packageName = className.substring(0, lastDot);
}

String interfaceName = className.substring(lastDot + 1);
String generatedClassName = className + "Implementation";
String generatedSimpleClassName = generatedClassName.substring(lastDot + 1);
final String interfaceName = className.substring(lastDot + 1);
final String generatedClassName = className + "Implementation";
final String generatedSimpleClassName = generatedClassName.substring(lastDot + 1);

Properties p = new Properties();
final Properties p = new Properties();
p.setProperty("resource.loader", "class");
p.setProperty(
"class.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"
"class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"
);
Velocity.init(p);
VelocityContext context = new VelocityContext();
final VelocityContext context = new VelocityContext();
context.put("package", packageName);
context.put("interface", interfaceName);
context.put("class", generatedSimpleClassName);
context.put("fields", fields.getFields());
JavaFileObject generatedClassFile = processingEnv.getFiler().createSourceFile(generatedClassName);
final JavaFileObject generatedClassFile = processingEnv.getFiler().createSourceFile(generatedClassName);
try (Writer writer = generatedClassFile.openWriter()) {
Velocity.mergeTemplate(template, "UTF-8", context, writer);
}
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
final Elements elementUtils = this.processingEnv.getElementUtils();
FieldVisitor fieldVisitor = new FieldVisitor(elementUtils);

for (TypeElement annotation : annotations) {
for (Element elem : roundEnv.getElementsAnnotatedWith(annotation)) {
if (!elem.getKind().isInterface()) {
messager.printMessage(Diagnostic.Kind.ERROR, "DataElement annotation is only for interfaces");
return false;
}
Fields fields = new Fields();
List<? extends AnnotationMirror> annotationMirrors = elem.getAnnotationMirrors();
for (AnnotationMirror annotationMirror : annotationMirrors) {
final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues =
elementUtils.getElementValuesWithDefaults(annotationMirror);
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry
: elementValues.entrySet()) {
entry.getValue().accept(fieldVisitor, fields);
}
}
try {
this.writeClass(((TypeElement) elem).getQualifiedName().toString(), fields);
} catch (IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, e.getStackTrace().toString());
return false;
}
}
}
return true;
}
}

0 comments on commit a91e0e6

Please sign in to comment.