Skip to content

Commit

Permalink
*Generate doc for options that are collections of enums. (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmnbroad authored Aug 25, 2020
1 parent 38cd6af commit 3142c77
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.broadinstitute.barclay.argparser.*;
import org.broadinstitute.barclay.utils.Utils;

import java.lang.reflect.*;
import java.util.*;
Expand Down Expand Up @@ -471,7 +472,7 @@ protected void processPositionalArguments(
argBindings.put("synonyms", "NA");
argBindings.put("exclusiveOf", "NA");
argBindings.put("type", argumentTypeString(positionalArgDef.getUnderlyingField().getGenericType()));
argBindings.put("options", Collections.EMPTY_LIST);
argBindings.put("options", getPossibleValues(positionalArgDef, "positional"));
argBindings.put("attributes", "NA");
argBindings.put("required", "yes");
argBindings.put("minRecValue", "NA");
Expand All @@ -481,7 +482,7 @@ protected void processPositionalArguments(
argBindings.put("defaultValue", "NA");
argBindings.put("minElements", positionalArgDef.getPositionalArgumentsAnnotation().minElements());
argBindings.put("maxElements", positionalArgDef.getPositionalArgumentsAnnotation().maxElements());

argBindings.put("collection", positionalArgDef.isCollection());
args.get("positional").add(argBindings);
args.get("all").add(argBindings);
}
Expand Down Expand Up @@ -758,56 +759,100 @@ protected String processNamedArgument(
String.join(", ", argDef.getMutexTargetList()) :
"NA");

// enum options
argBindings.put("options",
argDef.getUnderlyingField().getType().isEnum() ?
docForEnumArgument(argDef.getUnderlyingField().getType()) :
Collections.EMPTY_LIST);
// possible values
argBindings.put("options", getPossibleValues(argDef, argDef.getLongName()));

List<String> attributes = new ArrayList<>();
final List<String> attributes = new ArrayList<>();
if (!argDef.isOptional()) {
attributes.add("required");
}
if (argDef.isDeprecated()) {
attributes.add("deprecated");
}
argBindings.put("attributes", attributes.size() > 0 ? String.join(", ", attributes) : "NA");

argBindings.put("collection", argDef.isCollection());
return kind;
}

/**
* Return a (possibly empty) list of possible values that can be specified for this argument. Each
* value in the list is a map with "name" and "summary" keys.
* @param argDef {ArgumentDefinition}
* @return list of possible options for {@code argDef}. May be empty. May may not be null.
*/
private List<Map<String, String>> getPossibleValues(final ArgumentDefinition argDef, final String displayName) {
Utils.nonNull(argDef);

final Field underlyingField = argDef.getUnderlyingField();
Class<?> targetClass = underlyingField.getType();

// enum options
if (argDef.isCollection() && underlyingField.getGenericType() instanceof ParameterizedType) {
// if this argument is a Collection (including an EnumSet), use the type of the generic type
// parameter as the target class, instead of the collection class itself
final Type typeParamType = underlyingField.getGenericType();
final ParameterizedType pType = (ParameterizedType) typeParamType;
final Type genericTypes[] = pType.getActualTypeArguments();
if (genericTypes.length != 1 || genericTypes[0] instanceof ParameterizedType) {
return Collections.emptyList();
}
try {
targetClass = Class.forName(genericTypes[0].getTypeName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(String.format("No class found for type parameter (%s) used for argument (%s)",
genericTypes[0].getTypeName(), displayName), e);
}
}

return targetClass.isEnum() ?
docForEnumArgument(targetClass) :
Collections.emptyList();
}

/**
* Helper routine that provides a FreeMarker map for an enumClass, grabbing the
* values of the enum and their associated javadoc documentation.
* values of the enum and their associated javadoc or {@link CommandLineArgumentParser.ClpEnum} documentation.
*
* @param enumClass
* @return
* @param enumClass an enum Class that may optionally implement {@link CommandLineArgumentParser.ClpEnum}
* @return a List of maps with keys for "name" and "summary" for each of the class's possible enum constants
*/
private List<Map<String, Object>> docForEnumArgument(final Class<?> enumClass) {
@SuppressWarnings("unchecked")
private <T extends Enum<T>> List<Map<String, String>> docForEnumArgument(final Class<?> enumClass) {
final ClassDoc doc = this.getDoclet().getClassDocForClass(enumClass);
if ( doc == null ) {
throw new RuntimeException("Tried to get docs for enum " + enumClass + " but got null instead");
throw new RuntimeException(String.format("Unable to get ClassDoc for enum %s", enumClass));
}

final Set<String> enumConstantFieldNames = enumConstantsNames(enumClass);
final List<Map<String, Object>> bindings = new ArrayList<Map<String, Object>>();
for (final FieldDoc fieldDoc : doc.fields(false)) {
if (enumConstantFieldNames.contains(fieldDoc.name()) ) {
bindings.add(
new HashMap<String, Object>() {
static final long serialVersionUID = 0L;
{
put("name", fieldDoc.name());
put("summary", fieldDoc.commentText());
}
}
);
final List<Map<String, String>> bindings = new ArrayList<>();
if (CommandLineArgumentParser.ClpEnum.class.isAssignableFrom(enumClass)) {
final T[] enumConstants = (T[]) enumClass.getEnumConstants();
for ( final T enumConst : enumConstants ) {
bindings.add(createPossibleValuesMap(
enumConst.name(),
((CommandLineArgumentParser.ClpEnum) enumConst).getHelpDoc()));
}
} else {
final Set<String> enumConstantFieldNames = enumConstantsNames(enumClass);
for (final FieldDoc fieldDoc : doc.fields(false)) {
if (enumConstantFieldNames.contains(fieldDoc.name())) {
bindings.add(createPossibleValuesMap(fieldDoc.name(), fieldDoc.commentText()));
}
}
}

return bindings;
}

private HashMap<String, String> createPossibleValuesMap(final String name, final String summary) {
return new HashMap<String, String>() {
static final long serialVersionUID = 0L;
{
put("name", name);
put("summary", summary);
}
};
}

/**
* @return a non-null set of fields that are enum constants
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@
</#if>
<#if arg.options?has_content>
<p>
The ${arg.name} argument is an enumerated type (${arg.type}), which can have one of the following values:
<#if arg.collection>
The ${arg.name} argument is an enumerated type (${arg.type}), which can accept the following values:
<#else>
The ${arg.name} argument is a list of an enumerated type (${arg.type}), which can accept one or more of the following values:
</#if>
<dl class="enum">
<#list arg.options as option>
<dt class="enum">${option.name}</dt>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ public String getHelpDoc() {
optional = false)
TestEnum requiredClpEnum;

@Argument(fullName="enumCollection")
List<TestEnum> enumCollection = new ArrayList<>();

/**
* Mutually exclusive args
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,13 @@ _bashTabCompletionDocletTestLaunch_masterCompletionFunction()
NUM_POSITIONAL_ARGUMENTS=2
POSITIONAL_ARGUMENT_TYPE=("List[File]")
DEPENDENT_ARGUMENTS=()
NORMAL_COMPLETION_ARGUMENTS=(--requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
NORMAL_COMPLETION_ARGUMENTS=(--enumCollection --requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
MUTUALLY_EXCLUSIVE_ARGS=("--mutexSourceField;mutexTargetField1,mutexTargetField2" "--mutexTargetField1;mutexSourceField" "--mutexTargetField2;mutexSourceField" )
SYNONYMOUS_ARGS=("--requiredClpEnum;-requiredClpEnum" "--requiredFileList;-reqFilList" "--requiredInputFilesFromArgCollection;-rRequiredInputFilesFromArgCollection" "--requiredStringInputFromArgCollection;-requiredStringInputFromArgCollection" "--requiredStringList;-reqStrList" "--enumSetLong;-ES" "--fullAnonymousArgName;-anonymousClassArg" "--mutexSourceField;-mutexSourceField" "--mutexTargetField1;-mutexTargetField1" "--mutexTargetField2;-mutexTargetField2" "--optionalClpEnum;-optionalClpEnum" "--optionalDouble;-optDouble" "--optionalDoubleList;-optDoubleList" "--optionalFileList;-optFilList" "--optionalFlag;-optFlag" "--optionalInputFilesFromArgCollection;-optionalInputFilesFromArgCollection" "--optionalStringInputFromArgCollection;-optionalStringInputFromArgCollection" "--optionalStringList;-optStrList" "--advancedOptionalInt;-advancedOptInt" "--deprecatedString;-depStr" )
MIN_OCCURRENCES=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 )
MAX_OCCURRENCES=(2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 )
ALL_LEGAL_ARGUMENTS=(--requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
ALL_ARGUMENT_VALUE_TYPES=("TestEnum" "List[File]" "List[File]" "String" "List[String]" "String" "EnumSet[TestEnum]" "List[File]" "List[File]" "List[File]" "List[File]" "TestEnum" "double" "List[Double]" "List[File]" "boolean" "List[File]" "String" "List[String]" "List[String]" "int" "int" )
MIN_OCCURRENCES=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 )
MAX_OCCURRENCES=(2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 )
ALL_LEGAL_ARGUMENTS=(--enumCollection --requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
ALL_ARGUMENT_VALUE_TYPES=("List[TestEnum]" "TestEnum" "List[File]" "List[File]" "String" "List[String]" "String" "EnumSet[TestEnum]" "List[File]" "List[File]" "List[File]" "List[File]" "TestEnum" "double" "List[Double]" "List[File]" "boolean" "List[File]" "String" "List[String]" "List[String]" "int" "int" )

# Complete the arguments for this tool:
_bashTabCompletionDocletTestLaunch_handleArgs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,13 @@ _bashTabCompletionDocletTestLaunchWithDefaults_masterCompletionFunction()
NUM_POSITIONAL_ARGUMENTS=2
POSITIONAL_ARGUMENT_TYPE=("List[File]")
DEPENDENT_ARGUMENTS=()
NORMAL_COMPLETION_ARGUMENTS=(--requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
NORMAL_COMPLETION_ARGUMENTS=(--enumCollection --requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
MUTUALLY_EXCLUSIVE_ARGS=("--mutexSourceField;mutexTargetField1,mutexTargetField2" "--mutexTargetField1;mutexSourceField" "--mutexTargetField2;mutexSourceField" )
SYNONYMOUS_ARGS=("--requiredClpEnum;-requiredClpEnum" "--requiredFileList;-reqFilList" "--requiredInputFilesFromArgCollection;-rRequiredInputFilesFromArgCollection" "--requiredStringInputFromArgCollection;-requiredStringInputFromArgCollection" "--requiredStringList;-reqStrList" "--enumSetLong;-ES" "--fullAnonymousArgName;-anonymousClassArg" "--mutexSourceField;-mutexSourceField" "--mutexTargetField1;-mutexTargetField1" "--mutexTargetField2;-mutexTargetField2" "--optionalClpEnum;-optionalClpEnum" "--optionalDouble;-optDouble" "--optionalDoubleList;-optDoubleList" "--optionalFileList;-optFilList" "--optionalFlag;-optFlag" "--optionalInputFilesFromArgCollection;-optionalInputFilesFromArgCollection" "--optionalStringInputFromArgCollection;-optionalStringInputFromArgCollection" "--optionalStringList;-optStrList" "--advancedOptionalInt;-advancedOptInt" "--deprecatedString;-depStr" )
MIN_OCCURRENCES=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 )
MAX_OCCURRENCES=(2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 )
ALL_LEGAL_ARGUMENTS=(--requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
ALL_ARGUMENT_VALUE_TYPES=("TestEnum" "List[File]" "List[File]" "String" "List[String]" "String" "EnumSet[TestEnum]" "List[File]" "List[File]" "List[File]" "List[File]" "TestEnum" "double" "List[Double]" "List[File]" "boolean" "List[File]" "String" "List[String]" "List[String]" "int" "int" )
MIN_OCCURRENCES=(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 )
MAX_OCCURRENCES=(2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 2147483647 )
ALL_LEGAL_ARGUMENTS=(--enumCollection --requiredClpEnum --requiredFileList --requiredInputFilesFromArgCollection --requiredStringInputFromArgCollection --requiredStringList --usesFieldNameForArgName --enumSetLong --fullAnonymousArgName --mutexSourceField --mutexTargetField1 --mutexTargetField2 --optionalClpEnum --optionalDouble --optionalDoubleList --optionalFileList --optionalFlag --optionalInputFilesFromArgCollection --optionalStringInputFromArgCollection --optionalStringList --testPlugin --advancedOptionalInt --deprecatedString )
ALL_ARGUMENT_VALUE_TYPES=("List[TestEnum]" "TestEnum" "List[File]" "List[File]" "String" "List[String]" "String" "EnumSet[TestEnum]" "List[File]" "List[File]" "List[File]" "List[File]" "TestEnum" "double" "List[Double]" "List[File]" "boolean" "List[File]" "String" "List[String]" "List[String]" "int" "int" )

# Complete the arguments for this tool:
_bashTabCompletionDocletTestLaunchWithDefaults_handleArgs
Expand Down
Loading

0 comments on commit 3142c77

Please sign in to comment.