Commit 691d7f7e authored by Daniel Bluhm's avatar Daniel Bluhm

Add AbstractAnnotationExtractor

And move common methods for AnnotationExtractors to it.
Signed-off-by: Daniel Bluhm's avatarDaniel Bluhm <bluhmdj@ornl.gov>
parent 3e875eba
package org.eclipse.ice.dev.annotations.processors;
import java.lang.annotation.Annotation;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract base class for AnnotationExtractors providing common methods to
* all extractors.
*
* @author Daniel Bluhm
*
* @param <T> Type of data extracted from element.
*/
public abstract class AbstractAnnotationExtractor<T> implements AnnotationExtractor<T> {
/**
* Logger.
*/
protected final Logger logger;
/**
* Elements used to retrieve defaults for annotation values.
*/
protected Elements elementUtils;
public AbstractAnnotationExtractor(Elements elementUtils) {
this.logger = LoggerFactory.getLogger(getClass());
this.elementUtils = elementUtils;
}
@Override
public Optional<T> extractIfApplies(Element element) {
Optional<T> value = Optional.empty();
try {
value = Optional.of(extract(element));
} catch (InvalidElementException e) {
logger.debug(
"Failed to extract metadata from annotation, returning empty:",
e
);
}
return value;
}
/**
* Determine if an annotation of a given type decorates this element.
*
* @param cls class of annotation to check
* @return whether annotation is present or not
*/
public boolean hasAnnotation(Element element, Class<? extends Annotation> cls) {
return element.getAnnotation(cls) != null;
}
/**
* Get the AnnotationMirror of a given type if present on the element.
*
* @param <T> Type of annotation to retrieve
* @param cls class of annotation to retrieve
* @return AnnotationMirror or null if not found
*/
public <R extends Annotation> Optional<R> getAnnotation(
Element element, Class<R> cls
) {
R value = element.getAnnotation(cls);
if (value == null) {
return Optional.empty();
}
return Optional.of(value);
}
/**
* Find and return annotation mirror of a given type if present.
*
* When an annotation value is a Class, Annotation Mirrors must be used to
* retrieve a TypeMirror to the class (using {@link
* Elements#getElementValuesWithDefaults(AnnotationMirror)}).
*
* If the annotation in question has no Class values, it is recommended to
* use the Annotation directly with {@link #getAnnotation(Element, Class)}.
*
* @param annotationClass Class of annotation mirror to retrieve
* @return {@link Optional} of annotation mirror
*/
public Optional<AnnotationMirror> getAnnotationMirror(
Element element, Class<?> annotationClass
) {
return element.getAnnotationMirrors().stream()
.filter(m -> m.getAnnotationType()
.toString().equals(annotationClass.getCanonicalName())
).findAny()
.map(m -> Optional.of((AnnotationMirror) m))
.orElse(Optional.empty());
}
}
\ No newline at end of file
/*******************************************************************************
* Copyright (c) 2020- UT-Battelle, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Daniel Bluhm - Initial implementation
*******************************************************************************/
package org.eclipse.ice.dev.annotations.processors;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
/**
* Helper for accessing and working with Annotated Classes.
*
* @author Daniel Bluhm
*/
public class AnnotatedElement {
/**
* List of all annotation mirrors on this element.
*/
private List<? extends AnnotationMirror> mirrors;
/**
* Elements used to retrieve defaults for annotation values.
*/
protected Elements elementUtils;
/**
* The element representing an interface annotated with
* {@code @DataElement}.
*/
protected Element element;
/**
* Construct an AnnotatedElement from an Element.
* @param element The annotated element
* @param elementUtils Elements helper from processing environment
*/
public AnnotatedElement(Element element, Elements elementUtils) {
this.element = element;
this.elementUtils = elementUtils;
}
/**
* Determine if an annotation of a given type decorates this element.
*
* @param cls class of annotation to check
* @return whether annotation is present or not
*/
public boolean hasAnnotation(Class<? extends Annotation> cls) {
return this.element.getAnnotation(cls) != null;
}
/**
* Get the AnnotationMirror of a given type if present on the element.
*
* @param <T> Type of annotation to retrieve
* @param cls class of annotation to retrieve
* @return AnnotationMirror or null if not found
*/
public <T extends Annotation> Optional<T> getAnnotation(Class<T> cls) {
T value = this.element.getAnnotation(cls);
if (value == null) {
return Optional.empty();
}
return Optional.of(value);
}
/**
* Get a map of annotation value names to the value identified by that name.
*
* This is useful when dealing with a complicated Annotation potentially
* containing a value that is a Class object. Otherwise, it is recommended to
* directly retrieve the value from an Annotation instance.
* @param annotationClass the class of the annotation from which values
* will be retrieved.
* @return Map of String to unwrapped AnnotationValue (Object)
*/
public Map<String, Object> getAnnotationValueMap(Class<?> annotationClass) {
return this.getAnnotationMirror(annotationClass)
.map(mirror -> elementUtils.getElementValuesWithDefaults(mirror))
.map(map -> map.entrySet().stream()
.collect(Collectors.toMap(
entry -> entry.getKey().getSimpleName().toString(),
entry -> entry.getValue().getValue()
))
).orElse(Collections.emptyMap());
}
/**
* Get a list of annotation values from an annotation mirror of a given type.
*
* This is useful when dealing with a complicated Annotation potentially
* containing a value that is a Class object. Otherwise, it is recommended to
* directly retrieve the value from an Annotation instance.
*
* @param annotationClass the class of the annotation from which values will be
* retrieved.
* @return list of AnnotationValue
*/
public List<AnnotationValue> getAnnotationValues(Class<?> annotationClass) {
return this.getAnnotationMirror(annotationClass)
.map(mirror -> elementUtils.getElementValuesWithDefaults(mirror))
.map(map -> map.entrySet().stream()
.map(entry -> (AnnotationValue) entry.getValue())
.collect(Collectors.toList())
).orElse(Collections.emptyList());
}
/**
* Find and return annotation of a given on this element.
*
* @param annotationClass Class of annotation mirror to retrieve
* @return {@link Optional} of annotation mirror
*/
private Optional<AnnotationMirror> getAnnotationMirror(Class<?> annotationClass) {
if (this.mirrors == null) {
this.mirrors = this.element.getAnnotationMirrors();
}
return this.mirrors.stream()
.filter(m -> m.getAnnotationType()
.toString().equals(annotationClass.getCanonicalName())
).findAny()
.map(m -> Optional.of((AnnotationMirror) m))
.orElse(Optional.empty());
}
}
......@@ -15,8 +15,6 @@ import java.util.Optional;
import javax.lang.model.element.Element;
import org.slf4j.Logger;
/**
* Interface for classes acting as extractors of annotation info.
* @author Daniel Bluhm
......@@ -37,28 +35,11 @@ public interface AnnotationExtractor<T> {
*/
public T extract(Element element) throws InvalidElementException;
/**
* Get handle to logger.
* @return logger.
*/
public Logger log();
/**
* Extract information from element and annotations found on or within
* element if possible, return empty otherwise.
* @param element from which information will be extracted.
* @return extracted information wrapped in optional or empty.
*/
public default Optional<T> extractIfApplies(Element element) {
Optional<T> value = Optional.empty();
try {
value = Optional.of(extract(element));
} catch (InvalidElementException e) {
log().debug(
"Failed to extract metadata from annotation, returning empty:",
e
);
}
return value;
}
public Optional<T> extractIfApplies(Element element);
}
\ No newline at end of file
......@@ -20,8 +20,6 @@ import javax.lang.model.util.Elements;
import org.eclipse.ice.dev.annotations.DataElement;
import org.eclipse.ice.dev.annotations.DataField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.Builder;
......@@ -32,16 +30,7 @@ import lombok.Builder;
* @author Daniel Bluhm
*/
public class DataElementExtractor
implements AnnotationExtractor<DataElementMetadata> {
/**
* Logger.
*/
private static final Logger logger = LoggerFactory.getLogger(DataElementExtractor.class);
/**
* Element utilities from annotation processing environment.
*/
private Elements elementUtils;
extends AbstractAnnotationExtractor<DataElementMetadata> {
/**
* Extractor for data fields found on data element.
......@@ -59,15 +48,10 @@ public class DataElementExtractor
Elements elementUtils,
DataFieldExtractor dataFieldExtractor
) {
this.elementUtils = elementUtils;
super(elementUtils);
this.dataFieldExtractor = dataFieldExtractor;
}
@Override
public Logger log() {
return logger;
}
@Override
public DataElementMetadata extract(Element element) throws InvalidElementException {
if (element.getKind() != ElementKind.CLASS) {
......@@ -75,8 +59,7 @@ public class DataElementExtractor
"Element must be class, found " + element.toString()
);
}
AnnotatedElement helper = new AnnotatedElement(element, elementUtils);
if (!helper.hasAnnotation(DataElement.class)) {
if (!hasAnnotation(element, DataElement.class)) {
throw new InvalidElementException(
"Element is not annotated with DataElement"
);
......@@ -86,7 +69,7 @@ public class DataElementExtractor
fields.collect(extractFields(element));
DataElementMetadata data = DataElementMetadata.builder()
.name(extractName(helper))
.name(extractName(element))
.packageName(extractPackageName(element))
.fields(fields)
.build();
......@@ -98,8 +81,8 @@ public class DataElementExtractor
* @param element from which name will be extracted.
* @return String or null if DataElement annotation is missing.
*/
private String extractName(AnnotatedElement element) {
return element.getAnnotation(DataElement.class)
private String extractName(Element element) {
return getAnnotation(element, DataElement.class)
.map(DataElement::name)
.orElse(null);
}
......@@ -132,7 +115,10 @@ public class DataElementExtractor
try {
return dataFieldExtractor.extract(dataField);
} catch (InvalidElementException e) {
logger.warn("Invalid element encountered while extracting DataField", e);
logger.warn(
"Invalid element encountered while extracting DataField",
e
);
return null;
}
})
......
......@@ -97,7 +97,7 @@ public class DataElementProcessor extends AbstractProcessor {
.elementUtils(elementUtils)
.dataFieldExtractor(new DataFieldExtractor(elementUtils))
.build();
PersistenceExtractor persistenceExtractor = new PersistenceExtractor();
PersistenceExtractor persistenceExtractor = new PersistenceExtractor(elementUtils);
WriterGeneratorFactory generatorFactory = new WriterGeneratorFactory(
Set.of(
DataElementWriterGenerator.class,
......
......@@ -23,27 +23,15 @@ import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import org.eclipse.ice.dev.annotations.DataField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extractor for DataField annotated elements.
* @author Daniel Bluhm
*/
public class DataFieldExtractor implements AnnotationExtractor<Field> {
/**
* Logger.
*/
private static final Logger logger = LoggerFactory.getLogger(DataFieldExtractor.class);
/**
* Element helper from annotation processing environment.
*/
private Elements elementUtils;
public class DataFieldExtractor extends AbstractAnnotationExtractor<Field> {
public DataFieldExtractor(Elements elementUtils) {
this.elementUtils = elementUtils;
super(elementUtils);
}
/**
......@@ -56,11 +44,6 @@ public class DataFieldExtractor implements AnnotationExtractor<Field> {
.map(Class::getCanonicalName)
.collect(Collectors.toSet());
@Override
public Logger log() {
return logger;
}
@Override
public Field extract(Element element) throws InvalidElementException {
DataField fieldInfo = element.getAnnotation(DataField.class);
......
......@@ -12,20 +12,20 @@
package org.eclipse.ice.dev.annotations.processors;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import org.eclipse.ice.dev.annotations.Persisted;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extractor for persistence metadata on Persisted annotated elements.
* @author Daniel Bluhm
*/
public class PersistenceExtractor implements AnnotationExtractor<PersistenceMetadata> {
/**
* Logger.
*/
private static final Logger logger = LoggerFactory.getLogger(PersistenceExtractor.class);
public class PersistenceExtractor
extends AbstractAnnotationExtractor<PersistenceMetadata> {
public PersistenceExtractor(Elements elementUtils) {
super(elementUtils);
}
@Override
public PersistenceMetadata extract(Element element) throws InvalidElementException {
......@@ -39,10 +39,4 @@ public class PersistenceExtractor implements AnnotationExtractor<PersistenceMeta
.collection(persisted.collection())
.build();
}
@Override
public Logger log() {
return logger;
}
}
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