mirror of
https://github.com/JHUAPL/Jackfruit.git
synced 2026-01-08 20:08:02 -05:00
Merge branch '10-factory-constructor-with-prefix' into 'main'
Resolve "Factory constructor with prefix" Closes #10 See merge request nairah1/jackfruit!10
This commit is contained in:
22
README.md
22
README.md
@@ -134,14 +134,32 @@ The annotation processor generates a class called DemoInterfaceFactory. An exam
|
||||
|
||||
## 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.
|
||||
Jackfruit annotations can be inherited by derived 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.
|
||||
|
||||
### Jackfruit
|
||||
This annotation goes on the abstract type to signify that it should be run through the annotation processor. There is an optional "prefix" argument that can be used to add a prefix to all of the configuration keys created by the processor.
|
||||
This annotation goes on the abstract type to signify that it should be run through the annotation processor. There is an optional "prefix" argument that can be used to add a prefix to all of the configuration keys created by the processor. The Jackfruit annotation is not inherited by derived classes.
|
||||
|
||||
Every Factory class has a default constructor with this prefix (or an empty string if no prefix is specified) and a constructor where a prefix may be supplied. This is useful in case the user wants to create many configurations with the same parameters but different prefixes. The DemoInterface example above generates the following Factory code:
|
||||
```
|
||||
private final String prefix;
|
||||
|
||||
public DemoInterfaceFactory() {
|
||||
this.prefix = "prefix.";
|
||||
}
|
||||
|
||||
public DemoInterfaceFactory(String prefix) {
|
||||
if (prefix.length() > 0) prefix += ".";
|
||||
this.prefix = prefix;
|
||||
}
|
||||
```
|
||||
You can use the same set of parameters with a different prefix:
|
||||
```
|
||||
DemoInterfaceFactory factory = new DemoInterfaceFactory("anotherPrefix");
|
||||
```
|
||||
|
||||
### Comment
|
||||
|
||||
|
||||
@@ -61,6 +61,15 @@ public class JackfruitDemo {
|
||||
List<SomeRandomClass> randoms = template.randoms();
|
||||
for (SomeRandomClass random : randoms)
|
||||
System.out.println("random.toUpperCase() = " + random.toUpperCase());
|
||||
|
||||
// create a new factory with a different prefix, but same parameters
|
||||
factory = new DemoClassFactory("anotherPrefix");
|
||||
template = factory.fromConfig(config);
|
||||
|
||||
// this will not find anything
|
||||
randoms = template.randoms();
|
||||
for (SomeRandomClass random : randoms)
|
||||
System.out.println("random.toUpperCase() = " + random.toUpperCase());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -163,6 +163,21 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
annotationsMap.put(e, buildAnnotationBundle(e, defaultValues));
|
||||
}
|
||||
|
||||
// default constructor; initialize prefix
|
||||
String prefixMemberName = "prefix";
|
||||
classBuilder.addField(String.class, prefixMemberName, Modifier.PRIVATE, Modifier.FINAL);
|
||||
MethodSpec constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
|
||||
.addStatement("this.$N = $S", prefixMemberName, prefix).build();
|
||||
classBuilder.addMethod(constructor);
|
||||
|
||||
// add a constructor where caller can set prefix
|
||||
constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(String.class, prefixMemberName)
|
||||
.addStatement("if ($N.length() > 0) $N += $S", prefixMemberName, prefixMemberName,
|
||||
".")
|
||||
.addStatement("this.$N = $N", prefixMemberName, prefixMemberName).build();
|
||||
classBuilder.addMethod(constructor);
|
||||
|
||||
// generate the methods from the interface
|
||||
List<MethodSpec> methods = new ArrayList<>();
|
||||
for (Method m : ConfigFactory.class.getMethods()) {
|
||||
@@ -171,17 +186,17 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
continue;
|
||||
|
||||
if (m.getName().equals("toConfig")) {
|
||||
MethodSpec toConfig = buildToConfig(tvn, m, annotationsMap, prefix);
|
||||
MethodSpec toConfig = buildToConfig(tvn, m, annotationsMap, prefixMemberName);
|
||||
methods.add(toConfig);
|
||||
}
|
||||
|
||||
if (m.getName().equals("getTemplate")) {
|
||||
MethodSpec getTemplate = buildGetTemplate(tvn, m, annotationsMap, prefix);
|
||||
MethodSpec getTemplate = buildGetTemplate(tvn, m, annotationsMap);
|
||||
methods.add(getTemplate);
|
||||
}
|
||||
|
||||
if (m.getName().equals("fromConfig")) {
|
||||
MethodSpec fromConfig = buildFromConfig(tvn, m, annotationsMap, prefix);
|
||||
MethodSpec fromConfig = buildFromConfig(tvn, m, annotationsMap, prefixMemberName);
|
||||
methods.add(fromConfig);
|
||||
}
|
||||
|
||||
@@ -300,7 +315,7 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
* @return
|
||||
*/
|
||||
private MethodSpec buildToConfig(TypeVariableName tvn, Method m,
|
||||
Map<ExecutableElement, AnnotationBundle> annotationsMap, String prefix) {
|
||||
Map<ExecutableElement, AnnotationBundle> annotationsMap, String prefixMemberName) {
|
||||
ParameterSpec ps = ParameterSpec.builder(tvn, "t").build();
|
||||
ParameterSpec layout = ParameterSpec.builder(TypeVariableName.get(
|
||||
org.apache.commons.configuration2.PropertiesConfigurationLayout.class.getCanonicalName()),
|
||||
@@ -319,9 +334,10 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
boolean needBlank = true;
|
||||
for (ExecutableElement method : annotationsMap.keySet()) {
|
||||
AnnotationBundle ab = annotationsMap.get(method);
|
||||
String key = prefix + ab.key();
|
||||
String key = ab.key();
|
||||
if (needBlank) {
|
||||
methodBuilder.addStatement("$N.setBlancLinesBefore($S, 1)", layout, key);
|
||||
methodBuilder.addStatement("$N.setBlancLinesBefore($N + $S, 1)", layout, prefixMemberName,
|
||||
key);
|
||||
needBlank = false;
|
||||
}
|
||||
|
||||
@@ -372,14 +388,16 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
methodBuilder.addStatement("$L.add(element)", listName);
|
||||
}
|
||||
methodBuilder.endControlFlow();
|
||||
methodBuilder.addStatement("config.setProperty($S, $L)", key, listName);
|
||||
methodBuilder.addStatement("config.setProperty($N + $S, $L)", prefixMemberName, key,
|
||||
listName);
|
||||
} else {
|
||||
if (ab.parserClass().isPresent()) {
|
||||
// store the serialized string as the property
|
||||
methodBuilder.addStatement("config.setProperty($S, $L.toString($N.$L()))", key,
|
||||
parserName, ps, method.getSimpleName());
|
||||
methodBuilder.addStatement("config.setProperty($N + $S, $L.toString($N.$L()))",
|
||||
prefixMemberName, key, parserName, ps, method.getSimpleName());
|
||||
} else {
|
||||
methodBuilder.addStatement("config.setProperty($S, t.$L())", key, method.getSimpleName());
|
||||
methodBuilder.addStatement("config.setProperty($N + $S, t.$L())", prefixMemberName, key,
|
||||
method.getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +405,8 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
if (ab.comment().length() > 0) {
|
||||
String commentName = String.format("%sComment", method.getSimpleName());
|
||||
methodBuilder.addStatement("$T $L = $S", String.class, commentName, ab.comment());
|
||||
methodBuilder.addStatement("$N.setComment($S, $L)", layout, key, commentName);
|
||||
methodBuilder.addStatement("$N.setComment($N + $S, $L)", layout, prefixMemberName, key,
|
||||
commentName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,7 +416,7 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private MethodSpec buildGetTemplate(TypeVariableName tvn, Method m,
|
||||
Map<ExecutableElement, AnnotationBundle> annotationsMap, String prefix) {
|
||||
Map<ExecutableElement, AnnotationBundle> annotationsMap) {
|
||||
|
||||
// this builds the getTemplate() method
|
||||
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(m.getName())
|
||||
@@ -511,7 +530,8 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
ParameterizedTypeName.get(ClassName.get(java.util.ArrayList.class), argType);
|
||||
String listName = method.getSimpleName() + "List";
|
||||
builder.addStatement("$T " + listName + " = new $T()", listType, arrayListType);
|
||||
builder.addStatement("String [] parts = config.getStringArray($S)", prefix + bundle.key());
|
||||
builder.addStatement("String [] parts = config.getStringArray($N + $S)", prefix,
|
||||
bundle.key());
|
||||
builder.beginControlFlow("for (String part : parts)");
|
||||
builder.beginControlFlow("if (part.trim().length() > 0)");
|
||||
if (bundle.parserClass().isPresent()) {
|
||||
@@ -540,25 +560,25 @@ public class ConfigProcessor extends AbstractProcessor {
|
||||
builder.addStatement("return $L", listName);
|
||||
} else {
|
||||
if (bundle.parserClass().isPresent()) {
|
||||
builder.addStatement("return $L.fromString(config.getString($S))", parserName,
|
||||
prefix + bundle.key());
|
||||
builder.addStatement("return $L.fromString(config.getString($N + $S))", parserName,
|
||||
prefix, bundle.key());
|
||||
} else {
|
||||
if (ConfigProcessorUtils.isBoolean(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getBoolean($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getBoolean($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isByte(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getByte($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getByte($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isDouble(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getDouble($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getDouble($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isFloat(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getFloat($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getFloat($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isInteger(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getInt($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getInt($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isLong(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getLong($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getLong($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isShort(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getShort($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getShort($N + $S)", prefix, bundle.key());
|
||||
} else if (ConfigProcessorUtils.isString(bundle.erasure(), processingEnv)) {
|
||||
builder.addStatement("return config.getString($S)", prefix + bundle.key());
|
||||
builder.addStatement("return config.getString($N + $S)", prefix, bundle.key());
|
||||
} else {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
||||
"Can't handle return type " + m.getReturnType().getCanonicalName());
|
||||
|
||||
Reference in New Issue
Block a user