CommandLineArgument.java 12 KB

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