From da63bd4c8282c6661c1db8a5abd1a6063490b968 Mon Sep 17 00:00:00 2001 From: Remko Popma Date: Fri, 1 Nov 2019 23:16:06 +0900 Subject: [PATCH] [#850] Add MixinInfo class to keep track of the `@Mixin`-annotated element --- .../AbstractCommandSpecProcessor.java | 38 ++++---- .../annotation/processing/MixinInfo.java | 89 +++++++++++++++++++ 2 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 picocli-codegen/src/main/java/picocli/codegen/annotation/processing/MixinInfo.java diff --git a/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/AbstractCommandSpecProcessor.java b/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/AbstractCommandSpecProcessor.java index e257896c9..1c868424b 100644 --- a/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/AbstractCommandSpecProcessor.java +++ b/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/AbstractCommandSpecProcessor.java @@ -1,6 +1,5 @@ package picocli.codegen.annotation.processing; -import picocli.CommandLine; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; import picocli.CommandLine.IFactory; @@ -438,19 +437,16 @@ private void buildMixin(Element element, RoundEnvironment roundEnv, Context cont logger.fine("Built mixin: " + mixin + " from " + element); if (EnumSet.of(ElementKind.FIELD, ElementKind.PARAMETER).contains(element.getKind())) { VariableElement variableElement = (VariableElement) element; - String name = element.getAnnotation(Mixin.class).name(); - if (name.length() == 0) { - name = variableElement.getSimpleName().toString(); - } - Element targetType = element.getEnclosingElement(); - CommandSpec mixee = buildCommand(targetType, context, roundEnv); - Map mixinInfo = context.mixinInfoMap.get(mixee); - if (mixinInfo == null) { - mixinInfo = new IdentityHashMap(2); - context.mixinInfoMap.put(mixee, mixinInfo); + MixinInfo mixinInfo = new MixinInfo(variableElement, mixin); + + CommandSpec mixee = buildCommand(mixinInfo.enclosingElement(), context, roundEnv); + Set mixinInfos = context.mixinInfoMap.get(mixee); + if (mixinInfos == null) { + mixinInfos = new HashSet(2); + context.mixinInfoMap.put(mixee, mixinInfos); } - mixinInfo.put(mixin, name); - logger.fine("Mixin name=" + name + ", target command=" + mixee.userObject()); + mixinInfos.add(mixinInfo); + logger.fine("Mixin name=" + mixinInfo.mixinName() + ", target command=" + mixee.userObject()); } } @@ -791,7 +787,7 @@ static class Context { Map options = new LinkedHashMap(); Map parameters = new LinkedHashMap(); Map argGroupElements = new LinkedHashMap(); - Map> mixinInfoMap = new IdentityHashMap>(); + Map> mixinInfoMap = new IdentityHashMap>(); Map parentCommandElements = new LinkedHashMap(); Map specElements = new LinkedHashMap(); Map unmatchedElements = new LinkedHashMap(); @@ -804,10 +800,10 @@ private void connectModel(AbstractCommandSpecProcessor proc) { logger.fine(String.format("%s has CommandSpec[name=%s]", cmd.getKey(), cmd.getValue().name())); } logger.fine("Known mixins..."); - for (Map.Entry> mixinEntry : mixinInfoMap.entrySet()) { + for (Map.Entry> mixinEntry : mixinInfoMap.entrySet()) { logger.fine(String.format("mixins for %s:", mixinEntry.getKey().userObject())); - for (Map.Entry mixinInfo : mixinEntry.getValue().entrySet()) { - logger.fine(String.format("mixin name=%s userObj=%s:", mixinInfo.getValue(), mixinInfo.getKey().userObject())); + for (MixinInfo mixinInfo : mixinEntry.getValue()) { + logger.fine(String.format("mixin name=%s userObj=%s:", mixinInfo.mixinName(), mixinInfo.mixin().userObject())); } } @@ -873,11 +869,11 @@ private void connectModel(AbstractCommandSpecProcessor proc) { proc.error(entry.getKey(), "@Unmatched must be enclosed in a @Command, but was %s: %s", entry.getKey().getEnclosingElement(), entry.getKey().getEnclosingElement().getSimpleName()); } } - for (Map.Entry> mixinEntry : mixinInfoMap.entrySet()) { + for (Map.Entry> mixinEntry : mixinInfoMap.entrySet()) { CommandSpec mixee = mixinEntry.getKey(); - for (Map.Entry mixinInfo : mixinEntry.getValue().entrySet()) { - logger.fine(String.format("Adding mixin name=%s to %s", mixinInfo.getValue(), mixee.name())); - mixee.addMixin(mixinInfo.getValue(), mixinInfo.getKey()); + for (MixinInfo mixinInfo : mixinEntry.getValue()) { + logger.fine(String.format("Adding mixin name=%s to %s", mixinInfo.mixinName(), mixee.name())); + mixee.addMixin(mixinInfo.mixinName(), mixinInfo.mixin()/*, mixinInfo.annotatedElement()*/); } } diff --git a/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/MixinInfo.java b/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/MixinInfo.java new file mode 100644 index 000000000..76b469c61 --- /dev/null +++ b/picocli-codegen/src/main/java/picocli/codegen/annotation/processing/MixinInfo.java @@ -0,0 +1,89 @@ +package picocli.codegen.annotation.processing; + +import picocli.CommandLine; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Model.IAnnotatedElement; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.VariableElement; +import java.util.EnumSet; +import java.util.List; + +/** + * Holds information on the `@Mixin`-annotated program element. + */ +class MixinInfo { + private final String mixinName; + private final IAnnotatedElement annotatedElement; + private final VariableElement element; + private final CommandSpec mixin; + + public MixinInfo(VariableElement element, CommandSpec mixin) { + this.element = element; + this.mixin = mixin; + + String name = element.getAnnotation(CommandLine.Mixin.class).name(); + if (name.length() == 0) { + name = element.getSimpleName().toString(); + } + this.mixinName = name; + Element targetType = element.getEnclosingElement(); + int position = -1; + if (EnumSet.of(ElementKind.METHOD, ElementKind.CONSTRUCTOR).contains(targetType.getKind())) { + List parameters = ((ExecutableElement) targetType).getParameters(); + for (int i = 0; i < parameters.size(); i++) { + if (parameters.get(i).getSimpleName().contentEquals(element.getSimpleName())) { + position = i; + break; + } + } + } + annotatedElement = new TypedMember(element, position); + } + + public Element enclosingElement() { + return element.getEnclosingElement(); + } + + public String mixinName() { + return mixinName; + } + + public CommandSpec mixin() { + return mixin; + } + + public IAnnotatedElement annotatedElement() { + return annotatedElement; + } + + public int hashCode() { + int result = 17; + result += result * 37 + mixinName.hashCode(); + result += result * 37 + element.getSimpleName().hashCode(); + result += result * 37 + mixin.userObject().toString().hashCode(); + return result; + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof MixinInfo)) { + return false; + } + MixinInfo other = (MixinInfo) object; + if (!mixinName.equals(other.mixinName)) { + return false; + } + if (!element.getSimpleName().equals(other.element.getSimpleName())) { + return false; + } + if (!mixin.userObject().toString().equals(other.mixin.userObject().toString())) { + return false; + } + return true; + } +}