From dcc154eaa5fab78adc472d765d4a20a2b62502b1 Mon Sep 17 00:00:00 2001 From: Tristan Tarrant Date: Wed, 1 Jul 2026 09:37:07 +0200 Subject: [PATCH] [#546] Reduce reflection in generated metadata and runtime code - Add initialValueFactory (Supplier) to ProcessedOption so generated code can restore Collection/Map fields without getDeclaredConstructor().newInstance() - GeneratedProcessedOption.restoreInitialValue() uses factory when available - CodeGenerator emits setInitialValueFactory(ArrayList::new) etc. for @OptionList, @OptionGroup, and @Arguments fields - CommandContext.ContextFrame uses ProcessedOption.getFieldAccessor() for caching and restoring field values instead of raw Field reflection - Move internal command metadata providers into inner classes of AliasCommand, UnAliasCommand, ExportCommand for direct private field access without reflection - Annotation processor warns when private fields require reflective access, suggesting package-private visibility for reflection-free generated code --- .../processor/AeshAnnotationProcessor.java | 11 + .../org/aesh/processor/CodeGenerator.java | 87 ++++++- .../org/aesh/command/alias/AliasCommand.java | 85 +++++++ .../aesh/command/alias/UnAliasCommand.java | 62 +++++ .../aesh/command/export/ExportCommand.java | 60 +++++ .../command/impl/context/CommandContext.java | 66 +++-- .../internal/GeneratedProcessedOption.java | 26 +- .../impl/internal/ProcessedOption.java | 6 + .../InternalCommandMetadataRegistry.java | 238 +----------------- 9 files changed, 363 insertions(+), 278 deletions(-) diff --git a/aesh-processor/src/main/java/org/aesh/processor/AeshAnnotationProcessor.java b/aesh-processor/src/main/java/org/aesh/processor/AeshAnnotationProcessor.java index 18dd8d20..c255b646 100644 --- a/aesh-processor/src/main/java/org/aesh/processor/AeshAnnotationProcessor.java +++ b/aesh-processor/src/main/java/org/aesh/processor/AeshAnnotationProcessor.java @@ -221,6 +221,17 @@ private void validatePositionalIndexes(TypeElement element, List infos) { + for (FieldAccessorInfo info : infos) { + if (info.isPrivate && !info.synthetic) + return true; + } + return false; + } + private static boolean isPrivateField(VariableElement field) { return field.getModifiers().contains(javax.lang.model.element.Modifier.PRIVATE); } @@ -1334,6 +1357,50 @@ private static String[] getAnnotationStringArrayValue(TypeElement element, Strin return new String[0]; } + /** + * Emit a setInitialValueFactory() call that creates an empty collection/map + * of the declared field type, avoiding reflection at runtime. + */ + private static void emitInitialValueFactory(StringBuilder sb, String var, + TypeMirror fieldType, Types typeUtils) { + TypeMirror effectiveType = fieldType; + if (isOptionalType(effectiveType)) + effectiveType = unwrapOptionalTypeMirror(effectiveType); + String erasedName = typeUtils.erasure(effectiveType).toString(); + String factoryExpr = collectionFactoryExpression(erasedName); + if (factoryExpr != null) { + sb.append(" ").append(var) + .append(".setInitialValueFactory(").append(factoryExpr).append(");\n"); + } + } + + private static String collectionFactoryExpression(String erasedTypeName) { + switch (erasedTypeName) { + case "java.util.ArrayList": + case "java.util.List": + case "java.util.Collection": + return "java.util.ArrayList::new"; + case "java.util.LinkedList": + return "java.util.LinkedList::new"; + case "java.util.HashSet": + case "java.util.Set": + return "java.util.HashSet::new"; + case "java.util.LinkedHashSet": + return "java.util.LinkedHashSet::new"; + case "java.util.TreeSet": + return "java.util.TreeSet::new"; + case "java.util.HashMap": + case "java.util.Map": + return "java.util.HashMap::new"; + case "java.util.LinkedHashMap": + return "java.util.LinkedHashMap::new"; + case "java.util.TreeMap": + return "java.util.TreeMap::new"; + default: + return null; + } + } + // --- Type utility methods --- private static boolean isOptionalType(TypeMirror type) { diff --git a/aesh/src/main/java/org/aesh/command/alias/AliasCommand.java b/aesh/src/main/java/org/aesh/command/alias/AliasCommand.java index 1eea236d..36c65639 100644 --- a/aesh/src/main/java/org/aesh/command/alias/AliasCommand.java +++ b/aesh/src/main/java/org/aesh/command/alias/AliasCommand.java @@ -19,6 +19,7 @@ */ package org.aesh.command.alias; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -28,9 +29,17 @@ import org.aesh.command.CommandResult; import org.aesh.command.completer.CompleterInvocation; import org.aesh.command.completer.OptionCompleter; +import org.aesh.command.impl.internal.FieldAccessor; +import org.aesh.command.impl.internal.OptionType; +import org.aesh.command.impl.internal.ProcessedCommand; +import org.aesh.command.impl.internal.ProcessedCommandBuilder; +import org.aesh.command.impl.internal.ProcessedOption; import org.aesh.command.invocation.CommandInvocation; +import org.aesh.command.metadata.CommandMetadataProvider; import org.aesh.command.option.Arguments; import org.aesh.command.option.Option; +import org.aesh.command.parser.CommandLineParserException; +import org.aesh.converter.CLConverterManager; import org.aesh.readline.alias.AliasManager; /** @@ -86,4 +95,80 @@ public void complete(CompleterInvocation completerInvocation) { } } } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final class Metadata implements CommandMetadataProvider { + private static final FieldAccessor PRINT_ACCESSOR = new FieldAccessor() { + public void set(Object inst, Object val) { + ((AliasCommand) inst).print = val != null && (Boolean) val; + } + + public Object get(Object inst) { + return ((AliasCommand) inst).print; + } + }; + private static final FieldAccessor ARGS_ACCESSOR = new FieldAccessor() { + public void set(Object inst, Object val) { + ((AliasCommand) inst).arguments = (List) val; + } + + public Object get(Object inst) { + return ((AliasCommand) inst).arguments; + } + }; + + public Class commandType() { + return AliasCommand.class; + } + + public AliasCommand newInstance() { + throw new UnsupportedOperationException("AliasCommand requires AliasManager"); + } + + public boolean isGroupCommand() { + return false; + } + + public Class[] groupCommandClasses() { + return new Class[0]; + } + + public String commandName() { + return "alias"; + } + + public String[] commandAliases() { + return new String[] { "unalias" }; + } + + public ProcessedCommand buildProcessedCommand(AliasCommand instance) throws CommandLineParserException { + ProcessedOption printOpt = ProcessedOption.createDirect( + "p", "print", "display help information", + Boolean.class, "print", OptionType.BOOLEAN, + CLConverterManager.getInstance().getConverter(Boolean.class), + PRINT_ACCESSOR); + printOpt.setFieldResetter(inst -> ((AliasCommand) inst).print = false); + + ProcessedOption argsOpt = ProcessedOption.createDirect( + null, "arguments", "", + String.class, "arguments", OptionType.ARGUMENTS, + CLConverterManager.getInstance().getConverter(String.class), + ARGS_ACCESSOR); + argsOpt.setFieldResetter(inst -> ((AliasCommand) inst).arguments = null); + argsOpt.setCompleter(new AliasCompletor()); + + ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) + .name("alias") + .aliases(Arrays.asList("unalias")) + .description("") + .command(instance) + .generateHelp(false) + .disableParsing(false) + .create(); + + pc.addOption(printOpt); + pc.setArguments(argsOpt); + return pc; + } + } } diff --git a/aesh/src/main/java/org/aesh/command/alias/UnAliasCommand.java b/aesh/src/main/java/org/aesh/command/alias/UnAliasCommand.java index fdd02758..4f5208ba 100644 --- a/aesh/src/main/java/org/aesh/command/alias/UnAliasCommand.java +++ b/aesh/src/main/java/org/aesh/command/alias/UnAliasCommand.java @@ -27,8 +27,16 @@ import org.aesh.command.CommandResult; import org.aesh.command.completer.CompleterInvocation; import org.aesh.command.completer.OptionCompleter; +import org.aesh.command.impl.internal.FieldAccessor; +import org.aesh.command.impl.internal.OptionType; +import org.aesh.command.impl.internal.ProcessedCommand; +import org.aesh.command.impl.internal.ProcessedCommandBuilder; +import org.aesh.command.impl.internal.ProcessedOption; import org.aesh.command.invocation.CommandInvocation; +import org.aesh.command.metadata.CommandMetadataProvider; import org.aesh.command.option.Arguments; +import org.aesh.command.parser.CommandLineParserException; +import org.aesh.converter.CLConverterManager; import org.aesh.readline.alias.AliasManager; /** @@ -79,4 +87,58 @@ public void complete(CompleterInvocation completerInvocation) { } } } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final class Metadata implements CommandMetadataProvider { + private static final FieldAccessor ARGS_ACCESSOR = new FieldAccessor() { + public void set(Object inst, Object val) { + ((UnAliasCommand) inst).arguments = (List) val; + } + + public Object get(Object inst) { + return ((UnAliasCommand) inst).arguments; + } + }; + + public Class commandType() { + return UnAliasCommand.class; + } + + public UnAliasCommand newInstance() { + throw new UnsupportedOperationException("UnAliasCommand requires AliasManager"); + } + + public boolean isGroupCommand() { + return false; + } + + public Class[] groupCommandClasses() { + return new Class[0]; + } + + public String commandName() { + return "unalias"; + } + + public ProcessedCommand buildProcessedCommand(UnAliasCommand instance) throws CommandLineParserException { + ProcessedOption argsOpt = ProcessedOption.createDirect( + null, "arguments", "", + String.class, "arguments", OptionType.ARGUMENTS, + CLConverterManager.getInstance().getConverter(String.class), + ARGS_ACCESSOR); + argsOpt.setFieldResetter(inst -> ((UnAliasCommand) inst).arguments = null); + argsOpt.setCompleter(new AliasCompletor()); + + ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) + .name("unalias") + .description("remove an alias") + .command(instance) + .generateHelp(false) + .disableParsing(false) + .create(); + + pc.setArguments(argsOpt); + return pc; + } + } } diff --git a/aesh/src/main/java/org/aesh/command/export/ExportCommand.java b/aesh/src/main/java/org/aesh/command/export/ExportCommand.java index b1cc316c..0f8a8451 100644 --- a/aesh/src/main/java/org/aesh/command/export/ExportCommand.java +++ b/aesh/src/main/java/org/aesh/command/export/ExportCommand.java @@ -25,8 +25,16 @@ import org.aesh.command.CommandDefinition; import org.aesh.command.CommandException; import org.aesh.command.CommandResult; +import org.aesh.command.impl.internal.FieldAccessor; +import org.aesh.command.impl.internal.OptionType; +import org.aesh.command.impl.internal.ProcessedCommand; +import org.aesh.command.impl.internal.ProcessedCommandBuilder; +import org.aesh.command.impl.internal.ProcessedOption; import org.aesh.command.impl.invocation.AeshCommandInvocation; +import org.aesh.command.metadata.CommandMetadataProvider; import org.aesh.command.option.Arguments; +import org.aesh.command.parser.CommandLineParserException; +import org.aesh.converter.CLConverterManager; /** * @author Aesh team @@ -57,4 +65,56 @@ public CommandResult execute(AeshCommandInvocation commandInvocation) throws Com return CommandResult.SUCCESS; } + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static final class Metadata implements CommandMetadataProvider { + private static final FieldAccessor ARGS_ACCESSOR = new FieldAccessor() { + public void set(Object inst, Object val) { + ((ExportCommand) inst).arguments = (List) val; + } + + public Object get(Object inst) { + return ((ExportCommand) inst).arguments; + } + }; + + public Class commandType() { + return ExportCommand.class; + } + + public ExportCommand newInstance() { + throw new UnsupportedOperationException("ExportCommand requires ExportManager"); + } + + public boolean isGroupCommand() { + return false; + } + + public Class[] groupCommandClasses() { + return new Class[0]; + } + + public String commandName() { + return "export"; + } + + public ProcessedCommand buildProcessedCommand(ExportCommand instance) throws CommandLineParserException { + ProcessedOption argsOpt = ProcessedOption.createDirect( + null, "arguments", "", + String.class, "arguments", OptionType.ARGUMENTS, + CLConverterManager.getInstance().getConverter(String.class), + ARGS_ACCESSOR); + argsOpt.setFieldResetter(inst -> ((ExportCommand) inst).arguments = null); + + ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) + .name("export") + .description("") + .command(instance) + .generateHelp(false) + .disableParsing(false) + .create(); + + pc.setArguments(argsOpt); + return pc; + } + } } diff --git a/aesh/src/main/java/org/aesh/command/impl/context/CommandContext.java b/aesh/src/main/java/org/aesh/command/impl/context/CommandContext.java index 382f3124..8443ff48 100644 --- a/aesh/src/main/java/org/aesh/command/impl/context/CommandContext.java +++ b/aesh/src/main/java/org/aesh/command/impl/context/CommandContext.java @@ -451,18 +451,15 @@ public ContextFrame(CommandLineParser parser, Command command) { } private void cacheFieldValues() { - // Cache all option and argument values for quick access ProcessedCommand pc = parser.getProcessedCommand(); for (ProcessedOption opt : pc.getOptions()) { - Object value = getFieldValueByReflection(opt.getFieldName()); + Object value = getOptionValue(opt); if (value != null) { cachedValues.put(opt.getFieldName(), value); - // Also cache by option name for convenience if (opt.name() != null && !opt.name().isEmpty()) { cachedValues.put(opt.name(), value); } - // Track inherited options separately if (opt.isInherited()) { inheritedValues.put(opt.getFieldName(), value); inheritedOptions.put(opt.getFieldName(), opt); @@ -473,13 +470,11 @@ private void cacheFieldValues() { } } - // Cache singular arguments for (ProcessedOption arg : pc.getArgumentOptions()) { - Object value = getFieldValueByReflection(arg.getFieldName()); + Object value = getOptionValue(arg); if (value != null) { cachedValues.put(arg.getFieldName(), value); cachedValues.put("_argument", value); - // Track inherited argument if (arg.isInherited()) { inheritedValues.put(arg.getFieldName(), value); inheritedOptions.put(arg.getFieldName(), arg); @@ -487,9 +482,8 @@ private void cacheFieldValues() { } } - // Cache arguments (list) if (pc.getArguments() != null) { - Object value = getFieldValueByReflection(pc.getArguments().getFieldName()); + Object value = getOptionValue(pc.getArguments()); if (value != null) { cachedValues.put(pc.getArguments().getFieldName(), value); cachedValues.put("_arguments", value); @@ -497,15 +491,18 @@ private void cacheFieldValues() { } } - private Object getFieldValueByReflection(String fieldName) { + private Object getOptionValue(ProcessedOption opt) { + if (opt.getFieldAccessor() != null) { + return opt.getFieldAccessor().get(command); + } try { - Field field = findField(command.getClass(), fieldName); + Field field = findField(command.getClass(), opt.getFieldName()); if (field != null) { field.setAccessible(true); return field.get(command); } } catch (IllegalAccessException e) { - // Ignore, return null + // Ignore } return null; } @@ -541,24 +538,43 @@ public Command getCommand() { return command; } - /** - * Restore cached values back to the command instance. - * This is needed because parsing may reset the command's fields. - */ private void restoreCachedValues() { - for (Map.Entry entry : cachedValues.entrySet()) { - try { - Field field = findField(command.getClass(), entry.getKey()); - if (field != null) { - field.setAccessible(true); - field.set(command, entry.getValue()); - } - } catch (IllegalAccessException e) { - // Ignore, continue with other fields + ProcessedCommand pc = parser.getProcessedCommand(); + restoreOption(pc.getOptions()); + restoreOption(pc.getArgumentOptions()); + if (pc.getArguments() != null) { + Object value = cachedValues.get(pc.getArguments().getFieldName()); + if (value != null) { + setOptionValue(pc.getArguments(), value); } } } + private void restoreOption(List options) { + for (ProcessedOption opt : options) { + Object value = cachedValues.get(opt.getFieldName()); + if (value != null) { + setOptionValue(opt, value); + } + } + } + + private void setOptionValue(ProcessedOption opt, Object value) { + if (opt.getFieldAccessor() != null) { + opt.getFieldAccessor().set(command, value); + return; + } + try { + Field field = findField(command.getClass(), opt.getFieldName()); + if (field != null) { + field.setAccessible(true); + field.set(command, value); + } + } catch (IllegalAccessException e) { + // Ignore + } + } + /** * Get the parser. * diff --git a/aesh/src/main/java/org/aesh/command/impl/internal/GeneratedProcessedOption.java b/aesh/src/main/java/org/aesh/command/impl/internal/GeneratedProcessedOption.java index 74eb1142..4a65b22f 100644 --- a/aesh/src/main/java/org/aesh/command/impl/internal/GeneratedProcessedOption.java +++ b/aesh/src/main/java/org/aesh/command/impl/internal/GeneratedProcessedOption.java @@ -75,17 +75,21 @@ public void resetField(Object instance) { @Override protected void restoreInitialValue(Object instance) { Object val = initialValue; - if (initialValue instanceof Collection) { - try { - val = initialValue.getClass().getDeclaredConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - val = new ArrayList<>(); - } - } else if (initialValue instanceof Map) { - try { - val = initialValue.getClass().getDeclaredConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - val = new HashMap<>(); + if (initialValue instanceof Collection || initialValue instanceof Map) { + if (initialValueFactory != null) { + val = initialValueFactory.get(); + } else if (initialValue instanceof Collection) { + try { + val = initialValue.getClass().getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException | LinkageError e) { + val = new ArrayList<>(); + } + } else { + try { + val = initialValue.getClass().getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException | LinkageError e) { + val = new HashMap<>(); + } } } fieldAccessor.set(instance, val); diff --git a/aesh/src/main/java/org/aesh/command/impl/internal/ProcessedOption.java b/aesh/src/main/java/org/aesh/command/impl/internal/ProcessedOption.java index 63505532..d586b4aa 100644 --- a/aesh/src/main/java/org/aesh/command/impl/internal/ProcessedOption.java +++ b/aesh/src/main/java/org/aesh/command/impl/internal/ProcessedOption.java @@ -35,6 +35,7 @@ import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -128,6 +129,7 @@ public class ProcessedOption { protected FieldAccessor fieldAccessor; protected Object initialValue; protected boolean initialValueCaptured; + protected Supplier initialValueFactory; private String mixinFieldName; private Field cachedField; private Class cachedFieldClass; @@ -301,6 +303,10 @@ public void setOptionalValue(boolean optionalValue) { this.optionalValue = optionalValue; } + public void setInitialValueFactory(Supplier factory) { + this.initialValueFactory = factory; + } + /** * Set whether this option's field is wrapped in {@link java.util.Optional}. * When true, injected values are wrapped with Optional.of() and reset diff --git a/aesh/src/main/java/org/aesh/command/internal/InternalCommandMetadataRegistry.java b/aesh/src/main/java/org/aesh/command/internal/InternalCommandMetadataRegistry.java index efe212e9..cf3b8638 100644 --- a/aesh/src/main/java/org/aesh/command/internal/InternalCommandMetadataRegistry.java +++ b/aesh/src/main/java/org/aesh/command/internal/InternalCommandMetadataRegistry.java @@ -17,30 +17,19 @@ */ package org.aesh.command.internal; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.function.Consumer; - -import org.aesh.command.Command; import org.aesh.command.alias.AliasCommand; import org.aesh.command.alias.UnAliasCommand; import org.aesh.command.export.ExportCommand; -import org.aesh.command.impl.internal.FieldAccessor; -import org.aesh.command.impl.internal.OptionType; -import org.aesh.command.impl.internal.ProcessedCommand; -import org.aesh.command.impl.internal.ProcessedCommandBuilder; -import org.aesh.command.impl.internal.ProcessedOption; import org.aesh.command.metadata.CommandMetadataProvider; import org.aesh.command.metadata.MetadataRegistry; -import org.aesh.command.parser.CommandLineParserException; -import org.aesh.converter.CLConverterManager; /** * Hand-written metadata registry for aesh's internal commands. *

* These commands cannot use the annotation processor because the aesh module - * cannot depend on aesh-processor (circular dependency). The metadata is - * equivalent to what the processor would generate. + * cannot depend on aesh-processor (circular dependency). The metadata providers + * are inner classes of each command, giving them direct access to private fields + * without reflection. * * @author Aesh team * @since 3.16 @@ -51,228 +40,13 @@ public final class InternalCommandMetadataRegistry implements MetadataRegistry { public CommandMetadataProvider get(String commandClassName) { switch (commandClassName) { case "org.aesh.command.alias.AliasCommand": - return new AliasCommandMetadata(); + return new AliasCommand.Metadata(); case "org.aesh.command.alias.UnAliasCommand": - return new UnAliasCommandMetadata(); + return new UnAliasCommand.Metadata(); case "org.aesh.command.export.ExportCommand": - return new ExportCommandMetadata(); + return new ExportCommand.Metadata(); default: return null; } } - - /** Create a FieldAccessor backed by a reflected Field. */ - private static FieldAccessor fieldAccessor(Field f) { - return new FieldAccessor() { - @Override - public void set(Object instance, Object value) { - try { - f.set(instance, value); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public Object get(Object instance) { - try { - return f.get(instance); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - } - - /** Create a resetter that sets a field to null. */ - private static Consumer nullResetter(Field f) { - return instance -> { - try { - f.set(instance, null); - } catch (Exception e) { - throw new RuntimeException(e); - } - }; - } - - /** Create a resetter that sets a boolean field to false. */ - private static Consumer falseResetter(Field f) { - return instance -> { - try { - f.set(instance, false); - } catch (Exception e) { - throw new RuntimeException(e); - } - }; - } - - private static Field field(Class cls, String name) { - try { - Field f = cls.getDeclaredField(name); - f.setAccessible(true); - return f; - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - - // ---- AliasCommand ---- - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static final class AliasCommandMetadata implements CommandMetadataProvider { - private static final Field F_PRINT = field(AliasCommand.class, "print"); - private static final Field F_ARGUMENTS = field(AliasCommand.class, "arguments"); - - public Class commandType() { - return AliasCommand.class; - } - - public AliasCommand newInstance() { - throw new UnsupportedOperationException("AliasCommand requires AliasManager"); - } - - public boolean isGroupCommand() { - return false; - } - - public Class[] groupCommandClasses() { - return new Class[0]; - } - - public String commandName() { - return "alias"; - } - - public String[] commandAliases() { - return new String[] { "unalias" }; - } - - public ProcessedCommand buildProcessedCommand(AliasCommand instance) - throws CommandLineParserException { - ProcessedOption printOpt = ProcessedOption.createDirect( - "p", "print", "display help information", - Boolean.class, "print", OptionType.BOOLEAN, - CLConverterManager.getInstance().getConverter(Boolean.class), - fieldAccessor(F_PRINT)); - printOpt.setFieldResetter(falseResetter(F_PRINT)); - - ProcessedOption argsOpt = ProcessedOption.createDirect( - null, "arguments", "", - String.class, "arguments", OptionType.ARGUMENTS, - CLConverterManager.getInstance().getConverter(String.class), - fieldAccessor(F_ARGUMENTS)); - argsOpt.setFieldResetter(nullResetter(F_ARGUMENTS)); - argsOpt.setCompleter(new AliasCommand.AliasCompletor()); - - ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) - .name("alias") - .aliases(Arrays.asList("unalias")) - .description("") - .command(instance) - .generateHelp(false) - .disableParsing(false) - .create(); - - pc.addOption(printOpt); - pc.setArguments(argsOpt); - return pc; - } - } - - // ---- UnAliasCommand ---- - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static final class UnAliasCommandMetadata implements CommandMetadataProvider { - private static final Field F_ARGUMENTS = field(UnAliasCommand.class, "arguments"); - - public Class commandType() { - return UnAliasCommand.class; - } - - public UnAliasCommand newInstance() { - throw new UnsupportedOperationException("UnAliasCommand requires AliasManager"); - } - - public boolean isGroupCommand() { - return false; - } - - public Class[] groupCommandClasses() { - return new Class[0]; - } - - public String commandName() { - return "unalias"; - } - - public ProcessedCommand buildProcessedCommand(UnAliasCommand instance) - throws CommandLineParserException { - ProcessedOption argsOpt = ProcessedOption.createDirect( - null, "arguments", "", - String.class, "arguments", OptionType.ARGUMENTS, - CLConverterManager.getInstance().getConverter(String.class), - fieldAccessor(F_ARGUMENTS)); - argsOpt.setFieldResetter(nullResetter(F_ARGUMENTS)); - argsOpt.setCompleter(new UnAliasCommand.AliasCompletor()); - - ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) - .name("unalias") - .description("remove an alias") - .command(instance) - .generateHelp(false) - .disableParsing(false) - .create(); - - pc.setArguments(argsOpt); - return pc; - } - } - - // ---- ExportCommand ---- - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static final class ExportCommandMetadata implements CommandMetadataProvider { - private static final Field F_ARGUMENTS = field(ExportCommand.class, "arguments"); - - public Class commandType() { - return ExportCommand.class; - } - - public ExportCommand newInstance() { - throw new UnsupportedOperationException("ExportCommand requires ExportManager"); - } - - public boolean isGroupCommand() { - return false; - } - - public Class[] groupCommandClasses() { - return new Class[0]; - } - - public String commandName() { - return "export"; - } - - public ProcessedCommand buildProcessedCommand(ExportCommand instance) - throws CommandLineParserException { - ProcessedOption argsOpt = ProcessedOption.createDirect( - null, "arguments", "", - String.class, "arguments", OptionType.ARGUMENTS, - CLConverterManager.getInstance().getConverter(String.class), - fieldAccessor(F_ARGUMENTS)); - argsOpt.setFieldResetter(nullResetter(F_ARGUMENTS)); - - ProcessedCommand pc = ((ProcessedCommandBuilder) ProcessedCommandBuilder.builder()) - .name("export") - .description("") - .command(instance) - .generateHelp(false) - .disableParsing(false) - .create(); - - pc.setArguments(argsOpt); - return pc; - } - } }