From 5b426a0538b9c237d624ec9486403b0e8b2923c3 Mon Sep 17 00:00:00 2001 From: Hari Nair Date: Thu, 22 Sep 2022 21:33:58 +0000 Subject: [PATCH] Resolve "Allow abstract classes" --- README.md | 3 -- .../main/java/jackfruit/demo/DemoClass.java | 42 +++++++++++++++++++ .../{DemoConfig.java => DemoInterface.java} | 8 +--- .../java/jackfruit/demo/JackfruitDemo.java | 14 +++++-- .../java/jackfruit/annotations/Comment.java | 9 +++- .../jackfruit/annotations/DefaultValue.java | 13 +++++- .../java/jackfruit/annotations/Jackfruit.java | 5 ++- .../main/java/jackfruit/annotations/Key.java | 9 +++- .../java/jackfruit/annotations/Parameter.java | 14 ------- .../jackfruit/annotations/ParserClass.java | 5 ++- .../jackfruit/processor/ConfigProcessor.java | 21 ++++++---- 11 files changed, 99 insertions(+), 44 deletions(-) create mode 100644 demo/src/main/java/jackfruit/demo/DemoClass.java rename demo/src/main/java/jackfruit/demo/{DemoConfig.java => DemoInterface.java} (88%) delete mode 100644 jackfruit/src/main/java/jackfruit/annotations/Parameter.java diff --git a/README.md b/README.md index d0967ac..0e0f448 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,3 @@ public interface Parser { public String toString(T t); } ``` - - - diff --git a/demo/src/main/java/jackfruit/demo/DemoClass.java b/demo/src/main/java/jackfruit/demo/DemoClass.java new file mode 100644 index 0000000..53d4407 --- /dev/null +++ b/demo/src/main/java/jackfruit/demo/DemoClass.java @@ -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 doubles(); + + @Comment("List of RandomClass") + @DefaultValue("obj1 obj2") + @ParserClass(SomeRandomClassParser.class) + public abstract List randoms(); + + public void noAnnotationsOnThisMethod() { + System.out.println("This method was not processed"); + } + +} diff --git a/demo/src/main/java/jackfruit/demo/DemoConfig.java b/demo/src/main/java/jackfruit/demo/DemoInterface.java similarity index 88% rename from demo/src/main/java/jackfruit/demo/DemoConfig.java rename to demo/src/main/java/jackfruit/demo/DemoInterface.java index 24876c0..bc3b941 100644 --- a/demo/src/main/java/jackfruit/demo/DemoConfig.java +++ b/demo/src/main/java/jackfruit/demo/DemoInterface.java @@ -33,12 +33,6 @@ import jackfruit.annotations.ParserClass; *
    *
  • Optional, name of class to create object from String using its fromString() method
  • *
- *
  • - * - *

    - * How to handle blocks like List? - *

    - * Use JavaPoet to build a factory? *

    * Inspired by owner. * @@ -47,7 +41,7 @@ import jackfruit.annotations.ParserClass; */ @Jackfruit(prefix = "prefix") -public interface DemoConfig { +public interface DemoInterface { // default key is field name @Key("key") diff --git a/demo/src/main/java/jackfruit/demo/JackfruitDemo.java b/demo/src/main/java/jackfruit/demo/JackfruitDemo.java index 1baecff..8bc9aa6 100644 --- a/demo/src/main/java/jackfruit/demo/JackfruitDemo.java +++ b/demo/src/main/java/jackfruit/demo/JackfruitDemo.java @@ -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 = diff --git a/jackfruit/src/main/java/jackfruit/annotations/Comment.java b/jackfruit/src/main/java/jackfruit/annotations/Comment.java index 33219b5..191f254 100644 --- a/jackfruit/src/main/java/jackfruit/annotations/Comment.java +++ b/jackfruit/src/main/java/jackfruit/annotations/Comment.java @@ -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 ""; } diff --git a/jackfruit/src/main/java/jackfruit/annotations/DefaultValue.java b/jackfruit/src/main/java/jackfruit/annotations/DefaultValue.java index 3d05aff..b7bfc6d 100644 --- a/jackfruit/src/main/java/jackfruit/annotations/DefaultValue.java +++ b/jackfruit/src/main/java/jackfruit/annotations/DefaultValue.java @@ -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. + *

    + * 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 ""; } diff --git a/jackfruit/src/main/java/jackfruit/annotations/Jackfruit.java b/jackfruit/src/main/java/jackfruit/annotations/Jackfruit.java index afa7305..fc20294 100644 --- a/jackfruit/src/main/java/jackfruit/annotations/Jackfruit.java +++ b/jackfruit/src/main/java/jackfruit/annotations/Jackfruit.java @@ -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. *

    - * For example: - * + * For example:
    * @Jackfruit(prefix = "myPrefix") + *

    + * Inspired by owner. * * @author nairah1 * diff --git a/jackfruit/src/main/java/jackfruit/annotations/Key.java b/jackfruit/src/main/java/jackfruit/annotations/Key.java index 60cabd2..83ebb13 100644 --- a/jackfruit/src/main/java/jackfruit/annotations/Key.java +++ b/jackfruit/src/main/java/jackfruit/annotations/Key.java @@ -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 ""; } diff --git a/jackfruit/src/main/java/jackfruit/annotations/Parameter.java b/jackfruit/src/main/java/jackfruit/annotations/Parameter.java deleted file mode 100644 index 6ab6b12..0000000 --- a/jackfruit/src/main/java/jackfruit/annotations/Parameter.java +++ /dev/null @@ -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 ""; -} diff --git a/jackfruit/src/main/java/jackfruit/annotations/ParserClass.java b/jackfruit/src/main/java/jackfruit/annotations/ParserClass.java index 723e615..b079ab2 100644 --- a/jackfruit/src/main/java/jackfruit/annotations/ParserClass.java +++ b/jackfruit/src/main/java/jackfruit/annotations/ParserClass.java @@ -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(); } diff --git a/jackfruit/src/main/java/jackfruit/processor/ConfigProcessor.java b/jackfruit/src/main/java/jackfruit/processor/ConfigProcessor.java index 6089ae8..c1bbcf8 100644 --- a/jackfruit/src/main/java/jackfruit/processor/ConfigProcessor.java +++ b/jackfruit/src/main/java/jackfruit/processor/ConfigProcessor.java @@ -68,10 +68,11 @@ public class ConfigProcessor extends AbstractProcessor { Messager messager = processingEnv.getMessager(); - List annotatedInterfaces = roundEnv.getElementsAnnotatedWith(Jackfruit.class).stream() - .filter(e -> e.getKind() == ElementKind.INTERFACE).collect(Collectors.toList()); + List 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 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 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 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(),