Merge branch '7-include-annotated-methods-from-superclass' into 'main'

Resolve "Include annotated methods from superclass"

Closes #7

See merge request nairah1/jackfruit!7
This commit is contained in:
Hari Nair
2022-10-01 21:28:20 +00:00
6 changed files with 84 additions and 13 deletions

View File

@@ -1,8 +1,8 @@
# jackfruit
# Jackfruit
## Quick start
Jackfruit processes annotations on Java interfaces to generate code that can read and write Apache Configuration files. The `demo` module includes sample code. In the top level directory, run `mvn clean package`, which will build the annotation library, run the annotation processor on the file `demo/src/main/java/jackfruit/demo/DemoInterface.java`, and generate the class `demo/target/generated-sources/annotations/jackfruit/demo/DemoInterfaceFactory.java`. The file `demo/src/main/java/jackfruit/demo/JackfruitDemo.java` shows some simple examples of use.
Jackfruit processes annotations on Java interfaces and abstract classes to generate code that can read and write Apache Configuration files. The `demo` module includes sample code. In the top level directory, run `mvn clean package`, which will build the annotation library, run the annotation processor on the file `demo/src/main/java/jackfruit/demo/DemoInterface.java`, and generate the class `demo/target/generated-sources/annotations/jackfruit/demo/DemoInterfaceFactory.java`. The file `demo/src/main/java/jackfruit/demo/JackfruitDemo.java` shows some simple examples of use.
## Introduction
@@ -132,6 +132,10 @@ The annotation processor generates a class called DemoInterfaceFactory. An exam
System.out.println("random.toUpperCase() = " + random.toUpperCase());
```
## Inheritance
Methods can be inherited by abstract classes. The `@Jackfruit` annotation must be present on the parent class as well as the inherited class. The annotation processor will build factory classes for both parent and child classes.
## Supported Annotations
The `@Jackfruit` annotation goes on the abstract type. The remaining annotations are for use on methods.

View File

@@ -8,11 +8,12 @@ import jackfruit.annotations.Key;
import jackfruit.annotations.ParserClass;
@Jackfruit(prefix = "prefix")
public abstract class DemoClass {
public abstract class DemoClass extends DemoSuperClass {
@Key("key")
@Comment("One line comment")
@Comment("This method overrides one defined in DemoSuperClass")
@DefaultValue("1")
@Override
public abstract int intMethod();
@Comment("This is a very long comment line that really should be wrapped into more than one line but that's really up to you.")

View File

@@ -0,0 +1,20 @@
package jackfruit.demo;
import jackfruit.annotations.Comment;
import jackfruit.annotations.DefaultValue;
import jackfruit.annotations.Jackfruit;
// prefix can be superseded by inherited classes
@Jackfruit
public abstract class DemoSuperClass extends DemoSuperSuperClass {
@Comment("from DemoSuperClass")
@DefaultValue("-1")
@Override
public abstract int inherited();
@Comment("from DemoSuperClass")
@DefaultValue("2")
@Override
public abstract int intMethod();
}

View File

@@ -0,0 +1,21 @@
package jackfruit.demo;
import jackfruit.annotations.Comment;
import jackfruit.annotations.DefaultValue;
import jackfruit.annotations.Jackfruit;
@Jackfruit
public abstract class DemoSuperSuperClass {
@Comment("from DemoSuperSuperClass")
@DefaultValue("-3")
public abstract int inherited2();
@Comment("from DemoSuperSuperClass")
@DefaultValue("-2")
public abstract int inherited();
@Comment("from DemoSuperSuperClass")
@DefaultValue("3")
public abstract int intMethod();
}

View File

@@ -13,18 +13,18 @@ public class JackfruitDemo {
public static void main(String[] args) {
/*-
// this factory is built by the annotation processor after reading
// DemoClass
DemoClassFactory factory = new DemoClassFactory();
// get an example config object
DemoClass template = factory.getTemplate();
*/
/*-
// or if you prefer, use an interface
DemoInterfaceFactory factory = new DemoInterfaceFactory();
DemoInterface template = factory.getTemplate();
*/
// generate an Apache PropertiesConfiguration object. This can be written out to
// a file

View File

@@ -5,6 +5,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -106,17 +107,41 @@ public class ConfigProcessor extends AbstractProcessor {
classBuilder.addField(loggerField);
*/
// this contains a hierarchy of parent classes
List<DeclaredType> superClasses = new ArrayList<>();
{
TypeElement thisElement = annotatedType;
TypeMirror superClass = thisElement.getSuperclass();
while (superClass.getKind() == TypeKind.DECLARED) {
DeclaredType superType = (DeclaredType) superClass;
// have to use asElement() here
if (superType.asElement().getAnnotation(Jackfruit.class) == null) {
break;
}
superClasses.add(superType);
thisElement = (TypeElement) superType.asElement();
superClass = thisElement.getSuperclass();
}
}
Collections.reverse(superClasses);
superClasses.add((DeclaredType) annotatedType.asType());
// create a list of methods annotated with DefaultValue - ignore everything else
List<ExecutableElement> enclosedMethods = new ArrayList<>();
for (Element e : annotatedType.getEnclosedElements()) {
if (e.getKind() == ElementKind.METHOD && e.getAnnotation(DefaultValue.class) != null
&& e instanceof ExecutableElement)
enclosedMethods.add((ExecutableElement) e);
Map<String, ExecutableElement> enclosedMethods = new LinkedHashMap<>();
for (DeclaredType superClass : superClasses) {
for (Element e : superClass.asElement().getEnclosedElements()) {
if (e.getKind() == ElementKind.METHOD && e.getAnnotation(DefaultValue.class) != null
&& e instanceof ExecutableElement) {
enclosedMethods.put(e.getSimpleName().toString(), (ExecutableElement) e);
}
}
}
// holds the annotation information on each method
Map<ExecutableElement, AnnotationBundle> annotationsMap = new LinkedHashMap<>();
for (ExecutableElement e : enclosedMethods) {
for (ExecutableElement e : enclosedMethods.values()) {
ImmutableAnnotationBundle.Builder builder = ImmutableAnnotationBundle.builder();
builder.key(e.getSimpleName().toString());