Browse Source

explicitly configure extension points for an extension (#265)

pull/276/head
Andreas Rudolph 6 years ago committed by Decebal Suiu
parent
commit
7d9f6a012b
  1. 13
      pf4j/src/main/java/org/pf4j/Extension.java
  2. 42
      pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java
  3. 59
      pf4j/src/main/java/org/pf4j/util/ClassUtils.java

13
pf4j/src/main/java/org/pf4j/Extension.java

@ -34,4 +34,17 @@ public @interface Extension {
int ordinal() default 0;
/**
* An array of extension points, that are implemented by this extension.
* This explicit configuration overrides the automatic detection of extension points in the
* {@link org.pf4j.processor.ExtensionAnnotationProcessor}.
* <p>
* In case your extension is directly derived from an extension point this attribute is NOT required.
* But under certain <a href="https://github.com/pf4j/pf4j/issues/264">more complex scenarios</a> it
* might be useful to explicitly set the extension points for an extension.
*
* @return classes of extension points, that are implemented by this extension
*/
Class<? extends ExtensionPoint>[] points() default {};
}

42
pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java

@ -17,11 +17,13 @@ package org.pf4j.processor;
import org.pf4j.Extension;
import org.pf4j.ExtensionPoint;
import org.pf4j.util.ClassUtils;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
@ -168,22 +170,37 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
private List<TypeElement> findExtensionPoints(TypeElement extensionElement) {
List<TypeElement> extensionPointElements = new ArrayList<>();
// search in interfaces
for (TypeMirror item : extensionElement.getInterfaces()) {
boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(item, getExtensionPointType());
if (isExtensionPoint) {
TypeElement extensionPointElement = (TypeElement) ((DeclaredType) item).asElement();
// use extension points, that were explicitly set in the extension annotation
AnnotationValue annotatedExtensionPoints = ClassUtils.getAnnotationValue(extensionElement, Extension.class, "points");
List<? extends AnnotationValue> extensionPointClasses = (annotatedExtensionPoints != null) ?
(List<? extends AnnotationValue>) annotatedExtensionPoints.getValue() :
null;
if (extensionPointClasses != null && !extensionPointClasses.isEmpty()) {
for (AnnotationValue extensionPointClass : extensionPointClasses) {
String extensionPointClassName = extensionPointClass.getValue().toString();
TypeElement extensionPointElement = processingEnv.getElementUtils().getTypeElement(extensionPointClassName);
extensionPointElements.add(extensionPointElement);
}
}
// detect extension points automatically, if they are not explicitly configured (default behaviour)
else {
// search in interfaces
for (TypeMirror item : extensionElement.getInterfaces()) {
boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(item, getExtensionPointType());
if (isExtensionPoint) {
TypeElement extensionPointElement = (TypeElement) ((DeclaredType) item).asElement();
extensionPointElements.add(extensionPointElement);
}
}
// search in superclass
TypeMirror superclass = extensionElement.getSuperclass();
if (superclass.getKind() != TypeKind.NONE) {
boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(superclass, getExtensionPointType());
if (isExtensionPoint) {
TypeElement extensionPointElement = (TypeElement) ((DeclaredType) superclass).asElement();
extensionPointElements.add(extensionPointElement);
// search in superclass
TypeMirror superclass = extensionElement.getSuperclass();
if (superclass.getKind() != TypeKind.NONE) {
boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(superclass, getExtensionPointType());
if (isExtensionPoint) {
TypeElement extensionPointElement = (TypeElement) ((DeclaredType) superclass).asElement();
extensionPointElements.add(extensionPointElement);
}
}
}
@ -228,5 +245,4 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
return storage;
}
}

59
pf4j/src/main/java/org/pf4j/util/ClassUtils.java

@ -15,9 +15,14 @@
*/
package org.pf4j.util;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Decebal Suiu
@ -72,6 +77,60 @@ public class ClassUtils {
}
*/
/**
* Get a certain annotation of a {@link TypeElement}.
* See <a href="https://stackoverflow.com/a/10167558">stackoverflow.com</a> for more information.
*
* @param typeElement the type element, that contains the requested annotation
* @param annotationClass the class of the requested annotation
* @return the requested annotation or null, if no annotation of the provided class was found
* @throws NullPointerException if <code>typeElement</code> or <code>annotationClass</code> is null
*/
public static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> annotationClass) {
String annotationClassName = annotationClass.getName();
for (AnnotationMirror m : typeElement.getAnnotationMirrors()) {
if (m.getAnnotationType().toString().equals(annotationClassName)) {
return m;
}
}
return null;
}
/**
* Get a certain parameter of an {@link AnnotationMirror}.
* See <a href="https://stackoverflow.com/a/10167558">stackoverflow.com</a> for more information.
*
* @param annotationMirror the annotation, that contains the requested parameter
* @param annotationParameter the name of the requested annotation parameter
* @return the requested parameter or null, if no parameter of the provided name was found
* @throws NullPointerException if <code>annotationMirror</code> is null
*/
public static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String annotationParameter) {
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
if (entry.getKey().getSimpleName().toString().equals(annotationParameter)) {
return entry.getValue();
}
}
return null;
}
/**
* Get a certain annotation parameter of a {@link TypeElement}.
* See <a href="https://stackoverflow.com/a/10167558">stackoverflow.com</a> for more information.
*
* @param typeElement the type element, that contains the requested annotation
* @param annotationClass the class of the requested annotation
* @param annotationParameter the name of the requested annotation parameter
* @return the requested parameter or null, if no annotation for the provided class was found or no annotation parameter was found
* @throws NullPointerException if <code>typeElement</code> or <code>annotationClass</code> is null
*/
public static AnnotationValue getAnnotationValue(TypeElement typeElement, Class<?> annotationClass, String annotationParameter) {
AnnotationMirror annotationMirror = getAnnotationMirror(typeElement, annotationClass);
return (annotationMirror != null) ?
getAnnotationValue(annotationMirror, annotationParameter) :
null;
}
/**
* Uses {@link Class#getSimpleName()} to convert from {@link Class} to {@link String}.
*

Loading…
Cancel
Save