Commit 5811fad8 authored by Daniel Bluhm's avatar Daniel Bluhm
Browse files

Correctly handle single and many DataField Annotations


Signed-off-by: Daniel Bluhm's avatarDaniel Bluhm <bluhmdj@ornl.gov>
parent 2830c7ee
......@@ -46,7 +46,12 @@
<version>1.0-rc6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
......@@ -41,7 +41,7 @@ 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.
......@@ -51,99 +51,18 @@ import com.google.auto.service.AutoService;
@AutoService(Processor.class)
public class DataElementProcessor extends AbstractProcessor {
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.
* Visitor that accumulates DataField information from AnnotationValues. This
* Visitor is only intended for use on the AnnotationValues of DataFields
* AnnotationMirrors.
*/
private static class Fields {
/**
* Container for name and class name attributes of DataField in string
* representation.
*/
public class Field {
String name;
String className;
public String getClassName() {
return className;
}
public String getName() {
return name;
}
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 + ")";
}
}
protected List<Field> fields;
protected Field building;
public Fields() {
this.fields = new ArrayList<>();
this.building = null;
}
public boolean isBuilding() {
return this.building != null;
}
public void begin() {
this.building = new Field();
}
public void finish() {
this.fields.add(this.building);
this.building = null;
}
public List<Field> getFields() {
return fields;
}
public void setClassName(final String className) {
this.building.setClassName(className);
}
public void setName(final String name) {
this.building.setName(name);
}
@Override
public String toString() {
return fields.toString();
}
}
/**
* Visitor that accumulates DataField information from AnnotationValues.
* This Visitor is only intended for use on the AnnotationValues of
* DataFields and DataField AnnotationMirrors.
*/
private class DataFieldsVisitor extends SimpleAnnotationValueVisitor8<Optional<Exception>, Fields> {
private class DataFieldsVisitor extends SimpleAnnotationValueVisitor8<Optional<UnexpectedValueError>, Fields> {
/**
* Return error as default action for unhandled annotation values.
*/
@Override
protected Optional<Exception> defaultAction(final Object o, final Fields f) {
protected Optional<UnexpectedValueError> defaultAction(final Object o, final Fields f) {
return Optional.of(
new UnexpectedValueError(
"An unexpected annotation value was encountered: " + o.getClass().getCanonicalName()
......@@ -155,51 +74,70 @@ public class DataElementProcessor extends AbstractProcessor {
* Visit DataField AnnotationMirror.
*/
@Override
public Optional<Exception> visitAnnotation(final AnnotationMirror a, final Fields f) {
public Optional<UnexpectedValueError> 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"
));
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);
for (final AnnotationValue value : getAnnotationValuesForMirror(a)) {
final Optional<UnexpectedValueError> result = value.accept(fieldVisitor, f);
if (result.isPresent()) {
return result;
}
}
f.finish();
return Optional.empty();
}
/**
* Visit DataFields.value array of DataField.
* Visit DataFields value (array of DataField AnnotationMirrors).
*/
@Override
public Optional<Exception> visitArray(final List<? extends AnnotationValue> vals, final Fields f) {
public Optional<UnexpectedValueError> visitArray(final List<? extends AnnotationValue> vals, final Fields f) {
for (final AnnotationValue val : vals) {
Optional<Exception> result = val.accept(this, f);
final Optional<UnexpectedValueError> result = val.accept(this, f);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}
}
/**
* Visitor that accumulates DataField information from AnnotationValues. This
* Visitor is only intended for use on the AnnotationValues of DataField
* AnnotationMirrors.
*/
private class DataFieldVisitor extends SimpleAnnotationValueVisitor8<Optional<UnexpectedValueError>, Fields> {
/**
* Return error as default action for unhandled annotation values.
*/
@Override
protected Optional<UnexpectedValueError> defaultAction(final Object o, final Fields f) {
return Optional.of(
new UnexpectedValueError(
"An unexpected annotation value was encountered: " + o.getClass().getCanonicalName()
)
);
}
/**
* Visit DataField.fieldName.
*/
@Override
public Optional<Exception> visitString(final String s, final Fields f) {
public Optional<UnexpectedValueError> visitString(final String s, final Fields f) {
if (!f.isBuilding()) {
return Optional.of(
new UnexpectedValueError(
"Found String while still expecting DataField AnnotationMirror"
)
);
f.begin();
}
f.setName(s);
if (f.isComplete()) {
f.finish();
}
return Optional.empty();
}
......@@ -207,33 +145,157 @@ public class DataElementProcessor extends AbstractProcessor {
* Visit DataField.fieldType.
*/
@Override
public Optional<Exception> visitType(final TypeMirror t, final Fields f) {
public Optional<UnexpectedValueError> visitType(final TypeMirror t, final Fields f) {
if (!f.isBuilding()) {
return Optional.of(
new UnexpectedValueError(
"Found type while still expecting DataField Annotation Mirror"
)
);
f.begin();
}
f.setClassName(t.toString());
if (f.isComplete()) {
f.finish();
}
return Optional.empty();
}
}
public static class Field {
String name;
String className;
public String getClassName() {
return className;
}
public String getName() {
return name;
}
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 + ")";
}
}
/**
* 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.
*/
protected List<Field> fields;
protected Field building;
public Fields() {
this.fields = new ArrayList<>();
this.building = null;
}
public void begin() {
this.building = new Field();
}
public void finish() {
this.fields.add(this.building);
this.building = null;
}
public List<Field> getFields() {
return fields;
}
public boolean isBuilding() {
return this.building != null;
}
public boolean isComplete() {
return (this.building.getClassName() != null) && (this.building.getName() != null);
}
public void setClassName(final String className) {
this.building.setClassName(className);
}
public void setName(final String name) {
this.building.setName(name);
}
@Override
public String toString() {
return fields.toString();
}
}
public static class UnexpectedValueError extends Exception {
private static final long serialVersionUID = -8486833574190525020L;
public UnexpectedValueError(final String message) {
super(message);
}
}
/**
* Location of DataElement template for use with velocity.
*/
private static final String template = "templates/DataElement.vm";
/**
* Return stack trace as string.
* @param e subject exception
* @return stack trace as string
*/
private static String stackTracetoString(final Throwable e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
/**
* Unwrap an optional exception.
* @param <T> Exception type
* @param e Optional exception to throw if present
* @throws T
*/
private static <T extends Throwable> void unwrap(final Optional<T> e) throws T {
if (e.isPresent()) {
throw e.get();
}
}
protected Messager messager;
protected Elements elementUtils;
protected DataFieldsVisitor fieldVisitor;
protected DataFieldsVisitor fieldsVisitor;
protected DataFieldVisitor fieldVisitor;
/**
* 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());
}
@Override
public void init(final ProcessingEnvironment env) {
messager = env.getMessager();
elementUtils = env.getElementUtils();
fieldVisitor = new DataFieldsVisitor();
fieldsVisitor = new DataFieldsVisitor();
fieldVisitor = new DataFieldVisitor();
super.init(env);
}
......@@ -248,25 +310,32 @@ public class DataElementProcessor extends AbstractProcessor {
return false;
}
final Fields fields = new Fields();
final List<? extends AnnotationValue> fieldAnnotationValues = elem.getAnnotationMirrors().stream()
.filter(
mirror -> mirror.getAnnotationType().toString().equals(
DataFields.class.getCanonicalName()
)
)
.map(mirror -> getAnnotationValuesForMirror(mirror))
.flatMap(List::stream)
.collect(Collectors.toList());
for (AnnotationValue value : fieldAnnotationValues) {
Optional<Exception> result = value.accept(fieldVisitor, fields);
if (result.isPresent()) {
messager.printMessage(Diagnostic.Kind.ERROR, stackTracetoString(result.get()));
return false;
}
}
final List<? extends AnnotationMirror> mirrors = elem.getAnnotationMirrors();
try {
for (
final AnnotationValue value : mirrors.stream()
.filter(mirror -> mirror.getAnnotationType().toString().equals(
DataFields.class.getCanonicalName()
))
.map(mirror -> getAnnotationValuesForMirror(mirror))
.flatMap(List::stream)
.collect(Collectors.toList())
) {
unwrap(value.accept(fieldsVisitor, fields));
}
for (
final AnnotationValue value : mirrors.stream()
.filter( mirror -> mirror.getAnnotationType().toString().equals(
DataField.class.getCanonicalName()
))
.map(mirror -> getAnnotationValuesForMirror(mirror))
.flatMap(List::stream)
.collect(Collectors.toList())
) {
unwrap(value.accept(fieldVisitor, fields));
}
this.writeClass(((TypeElement) elem).getQualifiedName().toString(), fields);
} catch (final IOException e) {
} catch (final IOException | UnexpectedValueError e) {
messager.printMessage(Diagnostic.Kind.ERROR, stackTracetoString(e));
return false;
}
......@@ -274,31 +343,6 @@ public class DataElementProcessor extends AbstractProcessor {
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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment