CommandLineArgument.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package de.jplag;
  2. import static de.jplag.CLI.CLUSTERING_GROUP_NAME;
  3. import static de.jplag.CLI.CLUSTERING_PREPROCESSING_GROUP_NAME;
  4. import static de.jplag.options.JPlagOptions.*;
  5. import static net.sourceforge.argparse4j.impl.Arguments.append;
  6. import static net.sourceforge.argparse4j.impl.Arguments.storeTrue;
  7. import java.util.Collection;
  8. import java.util.List;
  9. import java.util.Optional;
  10. import net.sourceforge.argparse4j.impl.Arguments;
  11. import net.sourceforge.argparse4j.inf.*;
  12. import de.jplag.clustering.ClusteringAlgorithm;
  13. import de.jplag.clustering.ClusteringOptions;
  14. import de.jplag.clustering.algorithm.InterClusterSimilarity;
  15. import de.jplag.options.SimilarityMetric;
  16. import de.jplag.strategy.ComparisonMode;
  17. /**
  18. * Command line arguments for the JPlag CLI. Each argument is defined through an enumeral.
  19. * @author Timur Saglam
  20. */
  21. public enum CommandLineArgument {
  22. ROOT_DIRECTORY(new Builder("rootDir", String.class).nargs(NumberOfArgumentValues.ZERO_OR_MORE_VALUES)),
  23. NEW_DIRECTORY(new Builder("-new", String.class).nargs(NumberOfArgumentValues.ONE_OR_MORE_VALUES)),
  24. OLD_DIRECTORY(new Builder("-old", String.class).nargs(NumberOfArgumentValues.ONE_OR_MORE_VALUES)),
  25. LANGUAGE(new Builder("-l", String.class).defaultsTo(de.jplag.java.Language.SHORT_NAME).choices(LanguageLoader.getAllLanguageNames())),
  26. BASE_CODE("-bc", String.class),
  27. VERBOSITY(new Builder("-v", String.class).defaultsTo("quiet").choices(List.of("quiet", "long"))), // TODO SH: Replace verbosity when integrating a
  28. // real logging library
  29. DEBUG("-d", Boolean.class),
  30. SUBDIRECTORY("-S", String.class),
  31. SUFFIXES("-p", String.class),
  32. EXCLUDE_FILE("-x", String.class),
  33. MIN_TOKEN_MATCH("-t", Integer.class),
  34. SIMILARITY_THRESHOLD(new Builder("-m", Float.class).defaultsTo(DEFAULT_SIMILARITY_THRESHOLD)),
  35. SHOWN_COMPARISONS(new Builder("-n", Integer.class).defaultsTo(DEFAULT_SHOWN_COMPARISONS)),
  36. RESULT_FOLDER(new Builder("-r", String.class).defaultsTo("result")),
  37. COMPARISON_MODE(new Builder("-c", String.class).defaultsTo(DEFAULT_COMPARISON_MODE.getName()).choices(ComparisonMode.allNames())),
  38. CLUSTER_ENABLE(new Builder("--cluster-skip", Boolean.class).argumentGroup(CLUSTERING_GROUP_NAME).action(Arguments.storeTrue())),
  39. CLUSTER_ALGORITHM(
  40. new Builder("--cluster-alg", ClusteringAlgorithm.class).argumentGroup(CLUSTERING_GROUP_NAME)
  41. .defaultsTo(ClusteringOptions.DEFAULTS.getAlgorithm())),
  42. CLUSTER_METRIC(
  43. new Builder("--cluster-metric", SimilarityMetric.class).argumentGroup(CLUSTERING_GROUP_NAME)
  44. .defaultsTo(ClusteringOptions.DEFAULTS.getSimilarityMetric())),
  45. CLUSTER_SPECTRAL_BANDWIDTH(
  46. new Builder("--cluster-spectral-bandwidth", Float.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("bandwidth")
  47. .defaultsTo(ClusteringOptions.DEFAULTS.getSpectralKernelBandwidth())),
  48. CLUSTER_SPECTRAL_NOISE(
  49. new Builder("--cluster-spectral-noise", Float.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("noise")
  50. .defaultsTo(ClusteringOptions.DEFAULTS.getSpectralGaussianProcessVariance())),
  51. CLUSTER_SPECTRAL_MIN_RUNS(
  52. new Builder("--cluster-spectral-min-runs", Integer.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("min")
  53. .defaultsTo(ClusteringOptions.DEFAULTS.getSpectralMinRuns())),
  54. CLUSTER_SPECTRAL_MAX_RUNS(
  55. new Builder("--cluster-spectral-max-runs", Integer.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("max")
  56. .defaultsTo(ClusteringOptions.DEFAULTS.getSpectralMaxRuns())),
  57. CLUSTER_SPECTRAL_KMEANS_ITERATIONS(
  58. new Builder("--cluster-spectral-kmeans-interations", Integer.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("iterations")
  59. .defaultsTo(ClusteringOptions.DEFAULTS.getSpectralMaxKMeansIterationPerRun())),
  60. CLUSTER_AGGLOMERATIVE_THRESHOLD(
  61. new Builder("--cluster-agglomerative-threshold", Float.class).argumentGroup(CLUSTERING_GROUP_NAME).metaVar("threshold")
  62. .defaultsTo(ClusteringOptions.DEFAULTS.getAgglomerativeThreshold())),
  63. CLUSTER_AGGLOMERATIVE_INTER_CLUSTER_SIMILARITY(
  64. new Builder("--cluster-agglomerative-inter-cluster-similarity", InterClusterSimilarity.class).argumentGroup(CLUSTERING_GROUP_NAME)
  65. .defaultsTo(ClusteringOptions.DEFAULTS.getAgglomerativeInterClusterSimilarity())),
  66. CLUSTER_PREPROCESSING_NONE(
  67. new Builder("--cluster-pp-none", Boolean.class).mutuallyExclusiveGroup(CLUSTERING_PREPROCESSING_GROUP_NAME)
  68. .action(Arguments.storeTrue())),
  69. CLUSTER_PREPROCESSING_CDF(
  70. new Builder("--cluster-pp-cdf", Boolean.class).mutuallyExclusiveGroup(CLUSTERING_PREPROCESSING_GROUP_NAME).action(Arguments.storeTrue())),
  71. CLUSTER_PREPROCESSING_PERCENTILE(
  72. new Builder("--cluster-pp-percentile", Float.class).mutuallyExclusiveGroup(CLUSTERING_PREPROCESSING_GROUP_NAME).metaVar("percentile")),
  73. CLUSTER_PREPROCESSING_THRESHOLD(
  74. new Builder("--cluster-pp-threshold", Float.class).mutuallyExclusiveGroup(CLUSTERING_PREPROCESSING_GROUP_NAME).metaVar("threshold"));
  75. private final String flag;
  76. private final NumberOfArgumentValues numberOfValues;
  77. private final String description;
  78. private final Optional<Object> defaultValue;
  79. private final Optional<Collection<String>> choices;
  80. private final Optional<String> argumentGroup;
  81. private final Optional<String> mutuallyExclusiveGroup;
  82. private final Optional<ArgumentAction> action;
  83. private final Optional<String> metaVar;
  84. private final Class<?> type;
  85. private CommandLineArgument(String flag, Class<?> type) {
  86. this(new Builder(flag, type));
  87. }
  88. private CommandLineArgument(Builder builder) {
  89. this.flag = builder.flag;
  90. this.type = builder.type;
  91. this.defaultValue = builder.defaultValue;
  92. this.choices = builder.choices;
  93. this.argumentGroup = builder.argumentGroup;
  94. this.mutuallyExclusiveGroup = builder.mutuallyExclusiveGroup;
  95. this.action = builder.action;
  96. this.metaVar = builder.metaVar;
  97. this.numberOfValues = builder.nargs.orElse(NumberOfArgumentValues.SINGLE_VALUE);
  98. this.description = retrieveDescriptionFromMessages();
  99. }
  100. /**
  101. * @return the flag name of the command line argument.
  102. */
  103. public String flag() {
  104. return flag;
  105. }
  106. /**
  107. * @return the flag name of the command line argument without leading dashes and inner dashes replaced with underscores.
  108. */
  109. public String flagWithoutDash() {
  110. return flag.replaceAll("^-+", "").replaceAll("-", "_");
  111. }
  112. /**
  113. * Returns the value of this argument for arguments with a single value. Convenience method for
  114. * {@link Namespace#get(String)} and {@link CommandLineArgument#flagWithoutDash()}.
  115. * @param <T> is the argument type.
  116. * @param namespace stores a value for the argument.
  117. * @return the argument value.
  118. */
  119. public <T> T getFrom(Namespace namespace) {
  120. return namespace.get(flagWithoutDash());
  121. }
  122. /**
  123. * Returns the value of this argument for arguments that allow more than a single value. Convenience method for
  124. * {@link Namespace#getList(String)} and {@link CommandLineArgument#flagWithoutDash()}.
  125. * <p>
  126. * Depending on the action of the option, result types may change.
  127. * </p>
  128. * @param <T> is the argument type.
  129. * @param namespace stores a value for the argument.
  130. * @return the argument value.
  131. */
  132. public <T> List<T> getListFrom(Namespace namespace) {
  133. return namespace.getList(flagWithoutDash());
  134. }
  135. /**
  136. * Parses the command line argument with a specific parser.
  137. * @param parser is that parser.
  138. */
  139. public void parseWith(ArgumentParser parser, CliGroupHelper groupHelper) {
  140. ArgumentContainer argContainer = mutuallyExclusiveGroup.map(groupHelper::getMutuallyExclusiveGroup)
  141. .or(() -> argumentGroup.map(groupHelper::getArgumentGroup)).orElse(parser);
  142. Argument argument = argContainer.addArgument(flag).help(description);
  143. choices.ifPresent(it -> argument.choices(it));
  144. defaultValue.ifPresent(it -> argument.setDefault(it));
  145. action.ifPresent(argument::action);
  146. metaVar.ifPresent(argument::metavar);
  147. argument.type(type);
  148. if (type == Boolean.class) {
  149. argument.action(storeTrue());
  150. }
  151. if (!numberOfValues.toString().isEmpty()) {
  152. // For multi-value arguments keep all invocations.
  153. // This causes the argument value to change its type to 'List<List<String>>'.
  154. // Also, when the retrieved value after parsing the CLI is 'null', the argument is not used.
  155. argument.nargs(numberOfValues.toString());
  156. argument.action(append());
  157. }
  158. }
  159. /**
  160. * Dynamically loads the description from the message file. For an option named <code>NEW_OPTION</code> the messages key
  161. * should be <code>CommandLineArgument.NewOption</code>.
  162. */
  163. private String retrieveDescriptionFromMessages() {
  164. StringBuilder builder = new StringBuilder();
  165. for (String substring : toString().toLowerCase().split("_")) {
  166. builder.append(substring.substring(0, 1).toUpperCase());
  167. builder.append(substring.substring(1));
  168. }
  169. return Messages.getString(getClass().getSimpleName() + "." + builder.toString());
  170. }
  171. private static class Builder {
  172. private final String flag;
  173. private final Class<?> type;
  174. private Optional<Object> defaultValue = Optional.empty();
  175. private Optional<Collection<String>> choices = Optional.empty();
  176. private Optional<String> argumentGroup = Optional.empty();
  177. private Optional<String> mutuallyExclusiveGroup = Optional.empty();
  178. private Optional<ArgumentAction> action = Optional.empty();
  179. private Optional<String> metaVar = Optional.empty();
  180. private Optional<NumberOfArgumentValues> nargs = Optional.empty();
  181. public Builder(String flag, Class<?> type) {
  182. this.flag = flag;
  183. this.type = type;
  184. }
  185. public Builder defaultsTo(Object defaultValue) {
  186. this.defaultValue = Optional.of(defaultValue);
  187. return this;
  188. }
  189. public Builder choices(Collection<String> choices) {
  190. this.choices = Optional.of(choices);
  191. return this;
  192. }
  193. public Builder argumentGroup(String argumentGroup) {
  194. this.argumentGroup = Optional.of(argumentGroup);
  195. return this;
  196. }
  197. public Builder mutuallyExclusiveGroup(String mutuallyExclusiveGroup) {
  198. this.mutuallyExclusiveGroup = Optional.of(mutuallyExclusiveGroup);
  199. return this;
  200. }
  201. public Builder action(ArgumentAction action) {
  202. this.action = Optional.of(action);
  203. return this;
  204. }
  205. public Builder metaVar(String metaVar) {
  206. this.metaVar = Optional.of(metaVar);
  207. return this;
  208. }
  209. public Builder nargs(NumberOfArgumentValues nargs) {
  210. this.nargs = Optional.of(nargs);
  211. return this;
  212. }
  213. }
  214. }