CommandLineArgument.java 12 KB

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