aboutsummaryrefslogtreecommitdiffstats
path: root/pf4j
diff options
context:
space:
mode:
authorAndreas Rudolph <andy@openindex.de>2019-01-03 19:42:30 +0100
committerDecebal Suiu <decebal.suiu@gmail.com>2019-01-03 20:42:30 +0200
commit7d9f6a012bee888333e11c1284e41ea472e1838a (patch)
treec24e38548300b37464a90e9a004199e4794469b5 /pf4j
parent7e8c7fe04bddf360e86708cfd8120773703223e3 (diff)
downloadpf4j-7d9f6a012bee888333e11c1284e41ea472e1838a.tar.gz
pf4j-7d9f6a012bee888333e11c1284e41ea472e1838a.zip
explicitly configure extension points for an extension (#265)
Diffstat (limited to 'pf4j')
-rw-r--r--pf4j/src/main/java/org/pf4j/Extension.java13
-rw-r--r--pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java42
-rw-r--r--pf4j/src/main/java/org/pf4j/util/ClassUtils.java59
3 files changed, 101 insertions, 13 deletions
diff --git a/pf4j/src/main/java/org/pf4j/Extension.java b/pf4j/src/main/java/org/pf4j/Extension.java
index dceecbd..a5131f3 100644
--- a/pf4j/src/main/java/org/pf4j/Extension.java
+++ b/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 {};
+
}
diff --git a/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java b/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java
index 2e89df3..e0f3513 100644
--- a/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java
+++ b/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;
}
-
}
diff --git a/pf4j/src/main/java/org/pf4j/util/ClassUtils.java b/pf4j/src/main/java/org/pf4j/util/ClassUtils.java
index b85d54a..77e46e3 100644
--- a/pf4j/src/main/java/org/pf4j/util/ClassUtils.java
+++ b/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
@@ -73,6 +78,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}.
*
* @param classes