Resolve "add comments to Apache file"

This commit is contained in:
Hari Nair
2022-09-19 17:30:44 +00:00
parent 72c3a36a1a
commit e073bb5c0f
16 changed files with 223 additions and 106 deletions

View File

@@ -1,2 +1,82 @@
# jackfruit
## Introduction
Jackfruit processes annotations on Java interfaces to generate code that can read and write Apache Configuration files. Include Jackfruit in your project with the following POM:
```
POM GOES HERE
```
An example of an annotated interface is
```
@Jackfruit(prefix = "prefix")
public interface DemoConfig {
// default key is field name
@Key("key")
@Comment("field comment")
@DefaultValue("0")
public int intMethod();
@DefaultValue("0.")
public Double doubleMethod();
@DefaultValue("Default String")
public String StringMethod();
@Comment("This string is serialized into an object")
@DefaultValue("serialized string")
@ParserClass(SomeRandomClassParser.class)
public SomeRandomClass randomClass();
}
```
This corresponds to this Apache Configuration file:
```
# field comment
prefix.key = 0
prefix.doubleMethod = 0.0
prefix.StringMethod = Default String
# This string is serialized into an object
prefix.randomClass = serialized string
```
The annotation processor generates a class called DemoConfigFactory. An example of use is
```
// this factory is built by the annotation processor after reading the DemoConfig interface
DemoConfigFactory factory = new DemoConfigFactory();
// get an example config object
DemoConfig template = factory.getTemplate();
// generate an Apache PropertiesConfiguration object. This can be written out to a file
PropertiesConfiguration config =
factory.toConfig(template, new PropertiesConfigurationLayout());
try {
// this is the template, with comments
config.write(new PrintWriter(System.out));
// write to file
File tmpFile = File.createTempFile("tmp", ".config");
tmpFile.deleteOnExit();
// System.out.println("wrote "+tmpFile.getAbsolutePath());
config.write(new PrintWriter(tmpFile));
// read from file
config = new Configurations().properties(tmpFile);
// print to screen
config.write(new PrintWriter(System.out));
} catch (ConfigurationException | IOException e) {
e.printStackTrace();
}
// create a config object from an Apache PropertiesConfiguration
template = factory.fromConfig(config);
// get one of the config object's properties
System.out.printf("config.StringMethod() = %s\n", template.StringMethod());
```

View File

@@ -7,6 +7,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConfigParams {
public @interface Jackfruit {
public String prefix() default "";
}

View File

@@ -2,6 +2,7 @@ package jackfruit.processor;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.PropertiesConfigurationLayout;
public interface ConfigFactory<T> {
@@ -9,6 +10,6 @@ public interface ConfigFactory<T> {
public T fromConfig(Configuration config);
public PropertiesConfiguration toConfig(T t);
public PropertiesConfiguration toConfig(T t, PropertiesConfigurationLayout layout);
}

View File

@@ -41,7 +41,7 @@ import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import jackfruit.annotations.Comment;
import jackfruit.annotations.ConfigParams;
import jackfruit.annotations.Jackfruit;
import jackfruit.annotations.DefaultValue;
import jackfruit.annotations.Key;
import jackfruit.annotations.ParserClass;
@@ -53,7 +53,7 @@ import jackfruit.annotations.ParserClass;
*
*/
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes("jackfruit.annotations.ConfigParams")
@SupportedAnnotationTypes("jackfruit.annotations.Jackfruit")
@AutoService(Processor.class)
public class ConfigProcessor extends AbstractProcessor {
@@ -68,8 +68,8 @@ public class ConfigProcessor extends AbstractProcessor {
Messager messager = processingEnv.getMessager();
List<Element> annotatedInterfaces = roundEnv.getElementsAnnotatedWith(ConfigParams.class)
.stream().filter(e -> e.getKind() == ElementKind.INTERFACE).collect(Collectors.toList());
List<Element> annotatedInterfaces = roundEnv.getElementsAnnotatedWith(Jackfruit.class).stream()
.filter(e -> e.getKind() == ElementKind.INTERFACE).collect(Collectors.toList());
for (Element element : annotatedInterfaces) {
@@ -77,8 +77,7 @@ public class ConfigProcessor extends AbstractProcessor {
if (element instanceof TypeElement) {
TypeElement annotatedType = (TypeElement) element;
ConfigParams configParams =
(ConfigParams) annotatedType.getAnnotation(ConfigParams.class);
Jackfruit configParams = (Jackfruit) annotatedType.getAnnotation(Jackfruit.class);
String prefix = configParams.prefix();
if (prefix.length() > 0)
prefix += ".";
@@ -87,8 +86,8 @@ public class ConfigProcessor extends AbstractProcessor {
TypeVariableName tvn = TypeVariableName.get(annotatedType.getSimpleName().toString());
// This is the generic class; e.g. "ConfigFactory<TestConfig>"
ParameterizedTypeName ptn =
ParameterizedTypeName.get(ClassName.get(jackfruit.processor.ConfigFactory.class), tvn);
ParameterizedTypeName ptn = ParameterizedTypeName
.get(ClassName.get(jackfruit.processor.ConfigFactory.class), tvn);
String factoryName = String.format("%sFactory", annotatedType.getSimpleName());
@@ -209,28 +208,44 @@ public class ConfigProcessor extends AbstractProcessor {
private MethodSpec buildToConfig(TypeVariableName tvn, Method m,
Map<ExecutableElement, AnnotationBundle> annotationsMap, String prefix) {
ParameterSpec ps = ParameterSpec.builder(tvn, "t").build();
ParameterSpec layout = ParameterSpec.builder(TypeVariableName.get(
org.apache.commons.configuration2.PropertiesConfigurationLayout.class.getCanonicalName()),
"layout").build();
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder(m.getName()).addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC).returns(m.getGenericReturnType()).addParameter(ps);
.addModifiers(Modifier.PUBLIC).returns(m.getGenericReturnType());
methodBuilder.addParameter(ps);
methodBuilder.addParameter(layout);
methodBuilder.addStatement("$T config = new $T()",
org.apache.commons.configuration2.PropertiesConfiguration.class,
org.apache.commons.configuration2.PropertiesConfiguration.class);
methodBuilder.addStatement("config.setLayout($N)", layout);
boolean needBlank = true;
for (ExecutableElement method : annotationsMap.keySet()) {
AnnotationBundle ab = annotationsMap.get(method);
String key = prefix + ab.key();
if (needBlank) {
methodBuilder.addStatement(String.format("$N.setBlancLinesBefore(\"%s\", 1)", key), layout);
needBlank = false;
}
if (ab.parserClass().isPresent()) {
TypeMirror parser = ab.parserClass().get();
String parserName = method.getSimpleName() + "parser";
methodBuilder.addStatement("$T " + parserName + " = new $T()", parser, parser);
methodBuilder.addStatement(String.format("config.setProperty(\"%s\", %s.toString($N.%s()))",
prefix + ab.key(), parserName, method.getSimpleName()), ps);
key, parserName, method.getSimpleName()), ps);
} else {
methodBuilder.addStatement(String.format("config.setProperty(\"%s\", t.%s())",
prefix + ab.key(), method.getSimpleName()));
methodBuilder.addStatement(
String.format("config.setProperty(\"%s\", t.%s())", key, method.getSimpleName()));
}
if (ab.comment().length() > 0)
methodBuilder.addStatement(
String.format("$N.setComment(\"%s\", \"%s\")", key, ab.comment()), layout);
}
methodBuilder.addCode("return config;");
return methodBuilder.build();

View File

@@ -1,90 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jackfruit</artifactId>
<groupId>edu.jhuapl.ses.srn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jackfruit</artifactId>
<groupId>edu.jhuapl.ses.srn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>demo</artifactId>
<artifactId>demo</artifactId>
<name>demo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<name>demo</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>edu.jhuapl.ses.saa</groupId>
<artifactId>crucible-all</artifactId>
<version>1.0.0-b74-7d477359</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>edu.jhuapl.ses.srn</groupId>
<artifactId>annotations</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>edu.jhuapl.ses.saa</groupId>
<artifactId>crucible-all</artifactId>
<version>1.0.0-b74-7d477359</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>edu.jhuapl.ses.srn</groupId>
<artifactId>annotations</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven
defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven
defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -1,7 +1,7 @@
package jackfruit.demo;
import jackfruit.annotations.Comment;
import jackfruit.annotations.ConfigParams;
import jackfruit.annotations.Jackfruit;
import jackfruit.annotations.DefaultValue;
import jackfruit.annotations.Key;
import jackfruit.annotations.ParserClass;
@@ -45,7 +45,7 @@ import jackfruit.annotations.ParserClass;
*
*/
@ConfigParams(prefix = "prefix")
@Jackfruit(prefix = "prefix")
public interface DemoConfig {
// default key is field name
@@ -60,7 +60,7 @@ public interface DemoConfig {
@DefaultValue("Default String")
public String StringMethod();
// parser creates an object from a string
@Comment("This string is serialized into an object")
@DefaultValue("serialized string")
@ParserClass(SomeRandomClassParser.class)
public SomeRandomClass randomClass();

View File

@@ -1,8 +1,11 @@
package jackfruit.demo;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.PropertiesConfigurationLayout;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.ex.ConfigurationException;
public class JackfruitDemo {
@@ -15,22 +18,35 @@ public class JackfruitDemo {
// get an example config object
DemoConfig template = factory.getTemplate();
// generate an Apache PropertiesConfiguration object. This can be written out to a file
PropertiesConfiguration config = factory.toConfig(template);
// generate an Apache PropertiesConfiguration object. This can be written out to a file
PropertiesConfiguration config =
factory.toConfig(template, new PropertiesConfigurationLayout());
try {
// this is the template, with comments
config.write(new PrintWriter(System.out));
// write to file
File tmpFile = File.createTempFile("tmp", ".config");
tmpFile.deleteOnExit();
// System.out.println("wrote "+tmpFile.getAbsolutePath());
config.write(new PrintWriter(tmpFile));
// read from file
config = new Configurations().properties(tmpFile);
// print to screen - this still has comments?
config.write(new PrintWriter(System.out));
} catch (ConfigurationException | IOException e) {
e.printStackTrace();
}
// create a config object from an Apache PropertiesConfiguration
template = factory.fromConfig(config);
System.out.printf("config.StringMethod() = %s\n", template.StringMethod());
// get one of the config object's properties
System.out.printf("config.StringMethod() = %s\n", template.StringMethod());
}
}

View File

@@ -36,7 +36,7 @@ public class TestProcessor {
System.out.println(System.getProperty("java.class.path"));
*/
boolean ignore = false;
boolean ignore = true;
if (!ignore) {
Log4j2Configurator lc = Log4j2Configurator.getInstance();
lc.setPattern("%highlight{%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%c{1}:%L] %msg%n%throwable}");