diff --git a/src/de/steamwar/linkage/LinkageProcessor.java b/src/de/steamwar/linkage/LinkageProcessor.java index 9a650d6..488f9bf 100644 --- a/src/de/steamwar/linkage/LinkageProcessor.java +++ b/src/de/steamwar/linkage/LinkageProcessor.java @@ -23,12 +23,16 @@ import de.steamwar.linkage.plan.BuildPlan; import de.steamwar.linkage.plan.FieldBuilder; import de.steamwar.linkage.plan.MethodBuilder; import de.steamwar.linkage.types.Plain_GENERIC; +import lombok.Cleanup; import lombok.Getter; import lombok.SneakyThrows; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; -import javax.lang.model.element.*; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; @@ -39,6 +43,7 @@ import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @SupportedAnnotationTypes("de.steamwar.linkage.Linked") public class LinkageProcessor extends AbstractProcessor { @@ -49,7 +54,6 @@ public class LinkageProcessor extends AbstractProcessor { private static String pluginMain; private String name; - private String packageName; private String className; private Messager messager; @@ -69,7 +73,6 @@ public class LinkageProcessor extends AbstractProcessor { messager = processingEnv.getMessager(); - packageName = "de.steamwar." + name + ".linkage"; className = "LinkageUtils"; mainClass(); } @@ -87,22 +90,15 @@ public class LinkageProcessor extends AbstractProcessor { return; } context = pluginYMLFile.get().getName().equals("bungee.yml") ? Context.BUNGEE : Context.SPIGOT; - Optional mainName = getMainName(pluginYMLFile.get()); - if (!mainName.isPresent()) { + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYMLFile.get()))); + Optional mainName = reader.lines() + .filter(line -> line.startsWith("main:")) + .map(line -> line.substring(line.indexOf(':') + 1).trim()) + .findFirst(); + if (mainName.isPresent()) { + pluginMain = mainName.get(); + } else { messager.printMessage(Diagnostic.Kind.ERROR, "Could not find main class in plugin.yml or bungee.yml"); - return; - } - pluginMain = mainName.get(); - } - - @SneakyThrows - private Optional getMainName(File pluginYML) { - if (!pluginYML.exists()) return Optional.empty(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYML)))) { - return reader.lines() - .filter(line -> line.startsWith("main:")) - .map(line -> line.substring(line.indexOf(':') + 1).trim()) - .findFirst(); } } @@ -113,31 +109,30 @@ public class LinkageProcessor extends AbstractProcessor { processed = true; Writer writer = processingEnv.getFiler().createSourceFile("de.steamwar." + name + ".linkage.LinkageUtils").openWriter(); + BuildPlan buildPlan = new BuildPlan("de.steamwar." + name + ".linkage", className); - BuildPlan buildPlan = new BuildPlan(packageName, className); + Set elements = roundEnv.getElementsAnnotatedWith(Linked.class).stream() + .filter(element -> element.getKind() == ElementKind.CLASS) + .map(TypeElement.class::cast) + .peek(typeElement -> System.out.println("Found element: " + typeElement.getQualifiedName().toString())) + .collect(Collectors.toSet()); + + Map, List> groupedByChecks = elements.stream() + .collect(Collectors.groupingBy(element -> checks(element, buildPlan))); - Set elements = roundEnv.getElementsAnnotatedWith(Linked.class); Map neededFields = new HashMap<>(); Set fieldInjections = new HashSet<>(); - for (Element element : elements) { - if (element.getKind() != ElementKind.CLASS) { - continue; - } - TypeElement typeElement = (TypeElement) element; - Linked linked = element.getAnnotation(Linked.class); - if (linked == null) { - continue; - } + for (TypeElement typeElement : elements) { if (getLinkagesOfType(typeElement).size() > 1) { neededFields.put(typeElement.getQualifiedName().toString(), typeElement); } - List variableElements = typeElement.getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.FIELD).map(VariableElement.class::cast).filter(e -> { - return e.getAnnotation(LinkedInstance.class) != null; - }).collect(Collectors.toList()); - if (variableElements.isEmpty()) { - continue; - } + List variableElements = typeElement.getEnclosedElements().stream() + .filter(e -> e.getKind() == ElementKind.FIELD) + .map(VariableElement.class::cast) + .filter(e -> e.getAnnotation(LinkedInstance.class) != null) + .collect(Collectors.toList()); + if (variableElements.isEmpty()) continue; for (VariableElement variableElement : variableElements) { if (!variableElement.getModifiers().contains(Modifier.PUBLIC)) { @@ -177,30 +172,37 @@ public class LinkageProcessor extends AbstractProcessor { fieldInjections.forEach(Runnable::run); Map methods = new HashMap<>(); - for (Element element : elements) { - if (element.getKind() != ElementKind.CLASS) { - continue; - } - TypeElement typeElement = (TypeElement) element; - - System.out.println("Found element: " + typeElement.getQualifiedName().toString()); - Map> linkages = getLinkagesOfType(typeElement); - if (linkages.isEmpty()) { - continue; + for (Map.Entry, List> entry : groupedByChecks.entrySet()) { + Map>> groupedByMethod = new HashMap<>(); + for (TypeElement typeElement : entry.getValue()) { + for (Map.Entry> linkages : getLinkagesOfType(typeElement).entrySet()) { + groupedByMethod.computeIfAbsent(linkages.getKey(), ignored -> new HashMap<>()) + .put(typeElement, linkages.getValue()); + } } - for (Map.Entry> entry : linkages.entrySet()) { - MethodBuilder method = methods.computeIfAbsent(entry.getKey(), s -> { + for (Map.Entry>> group : groupedByMethod.entrySet()) { + MethodBuilder method = methods.computeIfAbsent(group.getKey(), s -> { MethodBuilder methodBuilder = new MethodBuilder(s, "void"); buildPlan.addMethod(methodBuilder); return methodBuilder; }); - for (LinkageType type : entry.getValue()) { - specialElements(typeElement, buildPlan, method::addLine, () -> { + + if (!entry.getKey().isEmpty()) method.addLine("if (" + String.join(" && ", entry.getKey()) + ") {"); + for (Map.Entry> toGenerate : group.getValue().entrySet()) { + TypeElement typeElement = toGenerate.getKey(); + String instance = getElement(typeElement, neededFields); + if (toGenerate.getValue().size() > 1 && instance.startsWith("new ")) { + method.addLine(typeElement.getSimpleName() + " local" + typeElement.getSimpleName().toString() + " = " + instance + ";"); + instance = "local" + typeElement.getSimpleName().toString(); + } + String finalInstance = instance; + toGenerate.getValue().forEach(linkageType -> { buildPlan.addImport(typeElement.getQualifiedName().toString()); - type.generateCode(buildPlan, method, getElement(typeElement, neededFields), typeElement); + linkageType.generateCode(buildPlan, method, finalInstance, typeElement); }); } + if (!entry.getKey().isEmpty()) method.addLine("}"); } } @@ -211,21 +213,21 @@ public class LinkageProcessor extends AbstractProcessor { } private String getElement(TypeElement typeElement, Map neededFields) { + String s = typeElement.getSimpleName().toString(); if (neededFields.containsKey(typeElement.getQualifiedName().toString())) { - String s = typeElement.getSimpleName().toString(); return s.substring(0, 1).toLowerCase() + s.substring(1); } - return "new " + typeElement.getSimpleName().toString() + "()"; + return "new " + s + "()"; } - private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer stringConsumer, Runnable inner) { + private Set checks(TypeElement typeElement, BuildPlan buildPlan) { + Set checks = new HashSet<>(); MinVersion minVersion = typeElement.getAnnotation(MinVersion.class); MaxVersion maxVersion = typeElement.getAnnotation(MaxVersion.class); EventMode eventMode = typeElement.getAnnotation(EventMode.class); PluginCheck[] pluginChecks = typeElement.getAnnotationsByType(PluginCheck.class); if (context == Context.SPIGOT) { errorOnNonNull(typeElement, eventMode); - List checks = new ArrayList<>(); if (minVersion != null) { buildPlan.addImport("de.steamwar.core.Core"); checks.add("Core.getVersion() >= " + minVersion.value()); @@ -240,12 +242,8 @@ public class LinkageProcessor extends AbstractProcessor { return "Bukkit.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null"; }).forEach(checks::add); } - if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); - inner.run(); - if (!checks.isEmpty()) stringConsumer.accept("}"); } else { errorOnNonNull(typeElement, minVersion, maxVersion); - List checks = new ArrayList<>(); if (eventMode != null) { buildPlan.addImport("de.steamwar.bungeecore.BungeeCore"); checks.add(eventMode.value().getPrefix() + "BungeeCore.EVENT_MODE"); @@ -256,10 +254,15 @@ public class LinkageProcessor extends AbstractProcessor { return "BungeeCord.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null"; }).forEach(checks::add); } - if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); - inner.run(); - if (!checks.isEmpty()) stringConsumer.accept("}"); } + return checks; + } + + private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer stringConsumer, Runnable inner) { + Set checks = checks(typeElement, buildPlan); + if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); + inner.run(); + if (!checks.isEmpty()) stringConsumer.accept("}"); } private void errorOnNonNull(TypeElement typeElement, Annotation... annotations) { @@ -274,16 +277,10 @@ public class LinkageProcessor extends AbstractProcessor { private Map> getLinkagesOfType(TypeElement typeElement) { Map> linkages = new HashMap<>(); - LinkageType superClassType = resolveSingle(typeElement.getSuperclass()); - if (superClassType != null) { - linkages.computeIfAbsent(superClassType.method(), s -> new ArrayList<>()).add(superClassType); - } - for (TypeMirror typeMirror : typeElement.getInterfaces()) { - LinkageType interfaceType = resolveSingle(typeMirror); - if (interfaceType != null) { - linkages.computeIfAbsent(interfaceType.method(), s -> new ArrayList<>()).add(interfaceType); - } - } + Stream.concat(Stream.of(typeElement.getSuperclass()), typeElement.getInterfaces().stream()) + .map(this::resolveSingle) + .filter(Objects::nonNull) + .forEach(linkageType -> linkages.computeIfAbsent(linkageType.method(), s -> new ArrayList<>()).add(linkageType)); if (linkages.size() == 1 && linkages.containsKey("unlink")) { linkages.put(plain_GENERIC.method(), Collections.singletonList(plain_GENERIC)); }