【发布时间】:2019-08-08 20:01:14
【问题描述】:
在为我正在制作的应用程序创建语言文件时,我遇到了很多样板代码。我目前有一个包含所有语言字符串的类,然后我使用反射将这些字符串写入文件。
我经常遇到的是,我的字符串中有某些占位符要替换,例如,我可能有这样的字符串:
public static String USER_INFO = "Username: %name% money: %balance%";
我想要实现的是生成一些基于注释的方法,比如我可以使用 lombok 生成 getter/setter 和其他方法。基于上面的字符串,我会有一个名为 Arguments(正确地应该被命名为 Replacers 或更有意义的东西)的注释,如下所示:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface Arguments {
String[] value();
}
我想做的是像这样添加注释:
@Arguments(
value = {"%balance%", "%name%"}
)
public static String USER_INFO = "Username: %name% - money: %balance%";
并自动生成以下替换方法:
public static String USER_INFONameReplacement(String name) {
return USER_INFO.replace("%name%", name);
}
public static String USER_INFOAllReplacement(String name, String balance) {
return USER_INFO.replace("%name%", name).replace("%balance%", balance);
}
public static String USER_INFOBalanceReplacement(String balance) {
return USER_INFO.replace("%balance%", balance);
}
经过一番搜索后,我最终尝试在这样的类中实现 AbstractProcessor:
@SupportedAnnotationTypes(
{"io.github.freakyville.configHelper.annotations.Arguments"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class SuggestProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env) {
}
@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) {
for (TypeElement annoation : annoations) {
Set<? extends Element> annotatedElements = env.getElementsAnnotatedWith(annoation);
Map<Boolean, List<Element>> annotatedFields = annotatedElements.stream().collect(
Collectors.partitioningBy(element ->
((ArrayType) element.asType()).getComponentType().getClass().equals(PrimitiveType.class)));
List<Element> setters = annotatedFields.get(true);
if (setters.isEmpty()) {
continue;
}
String className = ((TypeElement) setters.get(0)
.getEnclosingElement()).getQualifiedName().toString();
Map<String, List<String>> setterMap = setters.stream().collect(Collectors.toMap(
setter -> setter.getSimpleName().toString(),
setter -> Arrays.asList(setter.getAnnotation(Arguments.class).value()))
);
try {
writeBuilderFile(className, setterMap);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private void writeBuilderFile(
String className, Map<String, List<String>> setterMap)
throws IOException {
String packageName = null;
int lastDot = className.lastIndexOf('.');
if (lastDot > 0) {
packageName = className.substring(0, lastDot);
}
String builderSimpleClassName = className
.substring(lastDot + 1);
JavaFileObject builderFile = processingEnv.getFiler()
.createSourceFile(className);
try (PrintWriter out = new PrintWriter(builderFile.openWriter())) {
if (packageName != null) {
out.print("package ");
out.print(packageName);
out.println(";");
out.println();
}
out.print("public class ");
out.print(builderSimpleClassName);
out.println(" {");
out.println();
setterMap.forEach((key, orgArgNames) -> {
for (int i = 0; i < orgArgNames.size(); i++) {
List<String> subList = orgArgNames.subList(0, i + 1);
List<String> argNames = subList.stream().map(v -> v.replace("%", "") + "Replacement").collect(Collectors.toList());
List<String> argsWithTypes = argNames.stream().map(v -> "String " + v).collect(Collectors.toList());
String argumentList = "(" + String.join("", argsWithTypes).substring(0, argsWithTypes.size() - 3) + ")";
String methodName;
if (orgArgNames.size() <= 1) {
methodName = key + "Replace" + subList.stream().map(v -> v.replace("%", "")).collect(Collectors.joining(""));
} else {
methodName = key + "Replace" + subList.stream().map(v -> v.replace("%", "").substring(0, 1).toUpperCase() + v.substring(1)).collect(Collectors.joining(""));
}
out.print(" public static ");
out.print(methodName);
out.print(argumentList);
out.println("{");
StringBuilder replaceSB = new StringBuilder();
replaceSB.append(key);
for (int i1 = 0; i1 < subList.size(); i1++) {
replaceSB
.append(".replace(")
.append("\"")
.append(subList.get(i))
.append("\"")
.append(",")
.append(argNames.get(i))
.append(")");
}
String replace = replaceSB.toString();
out.println("return " + replace + ";");
out.println("}");
out.println("");
}
});
out.println("}");
}
}
}
但我似乎无法让它注册?
所以我的第一个问题是,如果我想实现这一目标,AbstractProcessor 是否可行?如果不是那怎么办?如果是,那为什么不注册?我正在使用 IntelliJ 并进入设置 -> 构建 -> 编译器并将注释处理器更改为启用并将处理器路径设置为我的 SuggestProcessor
【问题讨论】:
标签: java annotations code-generation