Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,17 @@ private void validatePositionalIndexes(TypeElement element, List<VariableElement
}

private void validateField(VariableElement field) {
if (field.getModifiers().contains(Modifier.PRIVATE)
&& (hasAeshAnnotation(field)
|| field.getAnnotation(org.aesh.command.option.Mixin.class) != null
|| field.getAnnotation(org.aesh.command.option.ParentCommand.class) != null)) {
messager.printMessage(Diagnostic.Kind.WARNING,
"Private field '" + field.getSimpleName()
+ "' requires reflective access at runtime. "
+ "Consider using package-private visibility for reflection-free "
+ "access from the generated metadata class.",
field);
}
if (field.getAnnotation(org.aesh.command.option.OptionList.class) != null) {
TypeMirror collectionType = elementUtils
.getTypeElement(Collection.class.getCanonicalName()).asType();
Expand Down
87 changes: 77 additions & 10 deletions aesh-processor/src/main/java/org/aesh/processor/CodeGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ private static void generateOptionList(StringBuilder sb, String simpleName, Vari
sb.append(", new Accessor(").append(accIdx).append("));\n");
sb.append(" ").append(var).append(".setValueSeparator(").append(charLiteral(ol.valueSeparator()))
.append(");\n");
emitInitialValueFactory(sb, var, field.asType(), typeUtils);
if (isOptionalWrapped)
sb.append(" ").append(var).append(".setOptionalWrapped(true);\n");
if (ol.required())
Expand Down Expand Up @@ -647,6 +648,7 @@ private static void generateOptionGroup(StringBuilder sb, String simpleName, Var
emitConverterExpression(sb, field, "converter", elementUtils, valueType);
sb.append(", new Accessor(").append(accIdx).append("));\n");
sb.append(" ").append(var).append(".setValueSeparator(',');\n");
emitInitialValueFactory(sb, var, field.asType(), typeUtils);
if (isOptionalWrapped)
sb.append(" ").append(var).append(".setOptionalWrapped(true);\n");
if (og.required())
Expand Down Expand Up @@ -694,6 +696,7 @@ private static void generateArguments(StringBuilder sb, String simpleName, Varia
sb.append(" ");
emitConverterExpression(sb, field, "converter", elementUtils, elementType);
sb.append(", new Accessor(").append(accIdx).append("));\n");
emitInitialValueFactory(sb, var, field.asType(), typeUtils);
if (isOptionalWrapped)
sb.append(" ").append(var).append(".setOptionalWrapped(true);\n");
if (a.required())
Expand Down Expand Up @@ -809,6 +812,10 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple

// set method
sb.append(" public void set(Object inst, Object val) {\n");
boolean setNeedsTry = hasPrivateFields(infos) || (parentInfo != null && parentInfo.isPrivate);
if (setNeedsTry) {
sb.append(" try {\n");
}
sb.append(" switch (idx) {\n");
for (FieldAccessorInfo info : infos) {
sb.append(" case ").append(info.index).append(": ");
Expand All @@ -817,10 +824,10 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
.append(" = val != null && (Boolean) val;");
} else if (info.mixinFieldName != null) {
if (info.isPrivate) {
sb.append("try { Object m = ").append(fieldConstantName(info.mixinFieldName))
sb.append("{ Object m = ").append(fieldConstantName(info.mixinFieldName))
.append(".get(inst); if (m != null) ")
.append(fieldConstantName(info.mixinFieldName, info.fieldName))
.append(".set(m, val); } catch (IllegalAccessException e) { throw new RuntimeException(e); }");
.append(".set(m, val); }");
} else {
sb.append("if (((").append(info.commandSimpleName).append(") inst).")
.append(info.mixinFieldName).append(" != null) ((")
Expand All @@ -830,8 +837,7 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
}
} else {
if (info.isPrivate) {
sb.append("try { ").append(fieldConstantName(info.fieldName))
.append(".set(inst, val); } catch (IllegalAccessException e) { throw new RuntimeException(e); }");
sb.append(fieldConstantName(info.fieldName)).append(".set(inst, val);");
} else {
sb.append("((").append(info.commandSimpleName).append(") inst).")
.append(info.fieldName).append(" = (").append(info.fieldType).append(") val;");
Expand All @@ -840,21 +846,28 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
sb.append(" break;\n");
}
sb.append(" }\n");
if (setNeedsTry) {
sb.append(" } catch (IllegalAccessException e) { throw new RuntimeException(e); }\n");
}
sb.append(" }\n\n");

// get method
sb.append(" public Object get(Object inst) {\n");
boolean getNeedsTry = hasPrivateFields(infos);
if (getNeedsTry) {
sb.append(" try {\n");
}
sb.append(" switch (idx) {\n");
for (FieldAccessorInfo info : infos) {
sb.append(" case ").append(info.index).append(": ");
if (info.synthetic) {
sb.append("return this.").append(info.fieldName).append(";");
} else if (info.mixinFieldName != null) {
if (info.isPrivate) {
sb.append("try { Object m = ").append(fieldConstantName(info.mixinFieldName))
sb.append("{ Object m = ").append(fieldConstantName(info.mixinFieldName))
.append(".get(inst); return m != null ? ")
.append(fieldConstantName(info.mixinFieldName, info.fieldName))
.append(".get(m) : null; } catch (IllegalAccessException e) { throw new RuntimeException(e); }");
.append(".get(m) : null; }");
} else {
sb.append("return ((").append(info.commandSimpleName).append(") inst).")
.append(info.mixinFieldName).append(" != null ? ((")
Expand All @@ -863,8 +876,7 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
}
} else {
if (info.isPrivate) {
sb.append("try { return ").append(fieldConstantName(info.fieldName))
.append(".get(inst); } catch (IllegalAccessException e) { throw new RuntimeException(e); }");
sb.append("return ").append(fieldConstantName(info.fieldName)).append(".get(inst);");
} else {
sb.append("return ((").append(info.commandSimpleName).append(") inst).")
.append(info.fieldName).append(";");
Expand All @@ -874,6 +886,9 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
}
sb.append(" default: return null;\n");
sb.append(" }\n");
if (getNeedsTry) {
sb.append(" } catch (IllegalAccessException e) { throw new RuntimeException(e); }\n");
}
sb.append(" }\n\n");

// accept method (BiConsumer for parent command injection)
Expand All @@ -882,8 +897,8 @@ private static void generateAccessorClass(StringBuilder sb, String commandSimple
if (parentInfo != null) {
if (parentInfo.isPrivate) {
sb.append(" try { ").append(fieldConstantName(parentInfo.fieldName))
.append(".set(cmd, parent); }\n");
sb.append(" catch (IllegalAccessException e) { throw new RuntimeException(e); }\n");
.append(".set(cmd, parent);")
.append(" } catch (IllegalAccessException e) { throw new RuntimeException(e); }\n");
} else {
sb.append(" ((").append(commandSimpleName).append(") cmd).").append(parentInfo.fieldName)
.append(" = (").append(parentInfo.fieldType).append(") parent;\n");
Expand Down Expand Up @@ -1022,6 +1037,14 @@ private static void generateParentCommandInjector(StringBuilder sb, String simpl
sb.append(" processedCommand.setParentCommandInjector(Accessor.PARENT_INJECTOR);\n\n");
}

private static boolean hasPrivateFields(List<FieldAccessorInfo> 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);
}
Expand Down Expand Up @@ -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) {
Expand Down
85 changes: 85 additions & 0 deletions aesh/src/main/java/org/aesh/command/alias/AliasCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.aesh.command.alias;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

Expand All @@ -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;

/**
Expand Down Expand Up @@ -86,4 +95,80 @@ public void complete(CompleterInvocation completerInvocation) {
}
}
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public static final class Metadata implements CommandMetadataProvider<AliasCommand> {
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<String>) val;
}

public Object get(Object inst) {
return ((AliasCommand) inst).arguments;
}
};

public Class<AliasCommand> commandType() {
return AliasCommand.class;
}

public AliasCommand newInstance() {
throw new UnsupportedOperationException("AliasCommand requires AliasManager");
}

public boolean isGroupCommand() {
return false;
}

public Class<? extends Command>[] 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;
}
}
}
62 changes: 62 additions & 0 deletions aesh/src/main/java/org/aesh/command/alias/UnAliasCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -79,4 +87,58 @@ public void complete(CompleterInvocation completerInvocation) {
}
}
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public static final class Metadata implements CommandMetadataProvider<UnAliasCommand> {
private static final FieldAccessor ARGS_ACCESSOR = new FieldAccessor() {
public void set(Object inst, Object val) {
((UnAliasCommand) inst).arguments = (List<String>) val;
}

public Object get(Object inst) {
return ((UnAliasCommand) inst).arguments;
}
};

public Class<UnAliasCommand> commandType() {
return UnAliasCommand.class;
}

public UnAliasCommand newInstance() {
throw new UnsupportedOperationException("UnAliasCommand requires AliasManager");
}

public boolean isGroupCommand() {
return false;
}

public Class<? extends Command>[] 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;
}
}
}
Loading
Loading