Merge branch '4-allow-abstract-classes' into 'main'

Resolve "Allow abstract classes"

Closes #4

See merge request nairah1/jackfruit!4
This commit is contained in:
Hari Nair
2022-09-22 21:33:58 +00:00
11 changed files with 99 additions and 44 deletions

View File

@@ -137,6 +137,3 @@ public interface Parser<T> {
public String toString(T t);
}
```

View File

@@ -0,0 +1,42 @@
package jackfruit.demo;
import java.util.List;
import jackfruit.annotations.Comment;
import jackfruit.annotations.DefaultValue;
import jackfruit.annotations.Jackfruit;
import jackfruit.annotations.Key;
import jackfruit.annotations.ParserClass;
@Jackfruit(prefix = "prefix")
public abstract class DemoClass {
@Key("key")
@Comment("field comment")
@DefaultValue("0")
public abstract int intMethod();
@DefaultValue("0.")
public abstract Double doubleMethod();
@DefaultValue("Default String")
public abstract String StringMethod();
@Comment("This string is serialized into an object")
@DefaultValue("serialized string")
@ParserClass(SomeRandomClassParser.class)
public abstract SomeRandomClass randomClass();
@Comment("List of Doubles")
@DefaultValue("0. 5.34 17")
public abstract List<Double> doubles();
@Comment("List of RandomClass")
@DefaultValue("obj1 obj2")
@ParserClass(SomeRandomClassParser.class)
public abstract List<SomeRandomClass> randoms();
public void noAnnotationsOnThisMethod() {
System.out.println("This method was not processed");
}
}

View File

@@ -33,12 +33,6 @@ import jackfruit.annotations.ParserClass;
* <ul>
* <li>Optional, name of class to create object from String using its fromString() method</li>
* </ul>
* <li>
* </ul>
* <p>
* How to handle blocks like List<MISEObservingMode>?
* <p>
* Use <a href="https://github.com/square/javapoet">JavaPoet</a> to build a factory?
* <p>
* Inspired by <a href="http://owner.aeonbits.org/">owner</a>.
*
@@ -47,7 +41,7 @@ import jackfruit.annotations.ParserClass;
*/
@Jackfruit(prefix = "prefix")
public interface DemoConfig {
public interface DemoInterface {
// default key is field name
@Key("key")

View File

@@ -13,13 +13,19 @@ public class JackfruitDemo {
public static void main(String[] args) {
// this factory is built by the annotation processor after reading the
// DemoConfig interface
DemoConfigFactory factory = new DemoConfigFactory();
// this factory is built by the annotation processor after reading
// DemoClass
DemoClassFactory factory = new DemoClassFactory();
// get an example config object
DemoConfig template = factory.getTemplate();
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
PropertiesConfiguration config =

View File

@@ -5,8 +5,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The Comment annotation specifies the comment that appears in the configuration file above the
* parameter itself.
*
* @author nairah1
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Target(ElementType.METHOD)
public @interface Comment {
public String value() default "";
}

View File

@@ -5,8 +5,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DefaultValue annotation is a String used to initialize the parameter. This is a required
* annotation. Methods without this annotation are ignored.
* <p>
* Strings and primitives (and their corresponding wrapper types) are read natively. Other objects
* will need to use the {@link ParserClass} annotation to specify a class which implements the
* {@link Parser} interface to convert the object to and from a String.
*
* @author nairah1
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Target(ElementType.METHOD)
public @interface DefaultValue {
public String value() default "";
}

View File

@@ -10,9 +10,10 @@ import java.lang.annotation.Target;
* an optional "prefix" argument that can be used to add a prefix to all of the configuration keys
* created by the processor.
* <p>
* For example:
*
* For example: <br>
* &#x0040;Jackfruit(prefix = "myPrefix")
* <p>
* Inspired by <a href="http://owner.aeonbits.org/">owner</a>.
*
* @author nairah1
*

View File

@@ -5,8 +5,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The Key annotation can be used to specify the name for the configuration key. The default is to
* use the name of the method.
*
* @author nairah1
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Target(ElementType.METHOD)
public @interface Key {
public String value() default "";
}

View File

@@ -1,14 +0,0 @@
package jackfruit.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Parameter {
public String key() default "";
public String comment() default "";
}

View File

@@ -6,13 +6,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This class will convert a String to/from an Object
* The ParserClass annotation specifies a class which implements the {@link Parser} interface to
* convert an object to and from a String.
*
* @author nairah1
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Target(ElementType.METHOD)
public @interface ParserClass {
public Class<?> value();
}

View File

@@ -68,10 +68,11 @@ public class ConfigProcessor extends AbstractProcessor {
Messager messager = processingEnv.getMessager();
List<Element> annotatedInterfaces = roundEnv.getElementsAnnotatedWith(Jackfruit.class).stream()
.filter(e -> e.getKind() == ElementKind.INTERFACE).collect(Collectors.toList());
List<Element> annotatedElements = roundEnv.getElementsAnnotatedWith(Jackfruit.class).stream()
.filter(e -> e.getKind() == ElementKind.INTERFACE || e.getKind() == ElementKind.CLASS)
.collect(Collectors.toList());
for (Element element : annotatedInterfaces) {
for (Element element : annotatedElements) {
try {
if (element instanceof TypeElement) {
@@ -95,17 +96,19 @@ public class ConfigProcessor extends AbstractProcessor {
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(factoryName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL).addSuperinterface(ptn);
/*-
/*-
// logger for the generated class
FieldSpec loggerField = FieldSpec.builder(org.apache.logging.log4j.Logger.class, "logger")
.initializer("$T.getLogger()", org.apache.logging.log4j.LogManager.class)
.addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC).build();
classBuilder.addField(loggerField);
*/
// create a list of annotated methods
*/
// 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 instanceof ExecutableElement)
if (e.getKind() == ElementKind.METHOD && e.getAnnotation(DefaultValue.class) != null
&& e instanceof ExecutableElement)
enclosedMethods.add((ExecutableElement) e);
}
@@ -124,11 +127,11 @@ public class ConfigProcessor extends AbstractProcessor {
List<TypeMirror> typeArgs = new ArrayList<>();
if (erasure.getKind() == TypeKind.DECLARED) {
// these are the parameter types for a List
// these are the parameter types for a generic class
List<? extends TypeMirror> args = ((DeclaredType) returnType).getTypeArguments();
typeArgs.addAll(args);
} else if (erasure.getKind().isPrimitive()) {
// no type arguments here
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
String.format("Unsupported kind %s for type %s!", erasure.getKind().toString(),