First steps toward new parameter parsing
diff --git a/src/main/java/com/beust/jcommander/JCommander.java b/src/main/java/com/beust/jcommander/JCommander.java
new file mode 100644
index 0000000..954bfc0
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/JCommander.java
@@ -0,0 +1,150 @@
+package com.beust.jcommander;
+
+import org.testng.collections.Lists;
+import org.testng.collections.Maps;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+
+public class JCommander {
+ private Map<String, ParameterDescription> m_descriptions;
+ private Object m_object;
+
+ public JCommander(Object object) {
+ m_object = object;
+ }
+
+ public JCommander(Object object, String[] args) {
+ m_object = object;
+ parse(args);
+ }
+
+ public void parse(String[] args) {
+ createDescriptions();
+ parseValues(expandArgs(args));
+ }
+
+ /**
+ * Expand the command line parameters to take @ parameters into account.
+ * When @ is encountered, the content of the file that follows is inserted
+ * in the command line
+ * @param originalArgv the original command line parameters
+ * @return the new and enriched command line parameters
+ */
+ private static String[] expandArgs(String[] originalArgv) {
+ List<String> vResult = Lists.newArrayList();
+
+ for (String arg : originalArgv) {
+
+ if (arg.startsWith("@")) {
+ String fileName = arg.substring(1);
+ vResult.addAll(readFile(fileName));
+ }
+ else {
+ vResult.add(arg);
+ }
+ }
+
+ return vResult.toArray(new String[vResult.size()]);
+ }
+
+ /**
+ * Reads the file specified by filename and returns the file content as a string.
+ * End of lines are replaced by a space
+ *
+ * @param fileName the command line filename
+ * @return the file content as a string.
+ */
+ public static List<String> readFile(String fileName) {
+ List<String> result = Lists.newArrayList();
+
+ try {
+ BufferedReader bufRead = new BufferedReader(new FileReader(fileName));
+
+ String line;
+
+ // Read through file one line at time. Print line # and line
+ while ((line = bufRead.readLine()) != null) {
+ result.add(line);
+ }
+
+ bufRead.close();
+ }
+ catch (IOException e) {
+ throw new ParameterException("Could not read file " + fileName + ": " + e);
+ }
+
+ return result;
+ }
+
+ /**
+ * @param string
+ * @return
+ */
+ private static String trim(String string) {
+ String result = string.trim();
+ if (result.startsWith("\"")) {
+ if (result.endsWith("\"")) {
+ return result.substring(1, result.length() - 1);
+ } else {
+ return result.substring(1);
+ }
+ } else {
+ return result;
+ }
+ }
+
+ private void createDescriptions() {
+ m_descriptions = Maps.newHashMap();
+ Class<?> cls = m_object.getClass();
+ for (Field f : cls.getDeclaredFields()) {
+ p("Field:" + f.getName());
+ f.setAccessible(true);
+ Annotation annotation = f.getAnnotation(Parameter.class);
+ if (annotation != null) {
+ Parameter p = (Parameter) annotation;
+ p("Adding description for " + p.name());
+ ParameterDescription pd = new ParameterDescription(m_object, p, f);
+ m_descriptions.put(p.name(), pd);
+ }
+ }
+ }
+
+ private void p(String string) {
+ System.out.println("[JCommander] " + string);
+ }
+
+ private void parseValues(String[] args) {
+ for (int i = 0; i < args.length; i++) {
+ String a = trim(args[i]);
+ if (a.startsWith("-")) {
+ ParameterDescription pd = m_descriptions.get(a);
+ if (pd != null) {
+ Class<?> fieldType = pd.getField().getType();
+ if (fieldType == boolean.class || fieldType == Boolean.class) {
+ pd.addValue(Boolean.TRUE);
+ } else if (i + 1 < args.length) {
+ pd.addValue(args[i + 1]);
+ } else {
+ throw new ParameterException("Parameter expected after " + args[i]);
+ }
+ i++;
+ } else {
+ throw new ParameterException("Unknown option: " + a);
+ }
+ }
+ }
+ }
+
+ public void usage() {
+ System.out.println("Usage:");
+ for (ParameterDescription pd : m_descriptions.values()) {
+ System.out.println("\t" + pd.getName() + "\t" + pd.getDescription());
+ }
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/Parameter.java b/src/main/java/com/beust/jcommander/Parameter.java
new file mode 100644
index 0000000..a30d3b0
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/Parameter.java
@@ -0,0 +1,15 @@
+package com.beust.jcommander;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@Target({ FIELD })
+public @interface Parameter {
+
+ String name() default "";
+
+ String description() default "";
+}
diff --git a/src/main/java/com/beust/jcommander/ParameterDescription.java b/src/main/java/com/beust/jcommander/ParameterDescription.java
new file mode 100644
index 0000000..cdc7579
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ParameterDescription.java
@@ -0,0 +1,69 @@
+package com.beust.jcommander;
+
+import org.testng.collections.Lists;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Set;
+
+public class ParameterDescription {
+ private Object m_object;
+ private Parameter m_parameterAnnotation;
+ private Field m_field;
+ /** Keep track of whether a value was added to flag an error */
+ private boolean m_added = false;
+
+ public ParameterDescription(Object object, Parameter annotation, Field field) {
+ m_object = object;
+ m_parameterAnnotation = annotation;
+ m_field = field;
+ }
+
+ public String getName() {
+ return m_parameterAnnotation.name();
+ }
+
+ public String getDescription() {
+ return m_parameterAnnotation.description();
+ }
+
+ public Field getField() {
+ return m_field;
+ }
+
+ private boolean isMultiOption() {
+ Class<?> fieldType = m_field.getType();
+ return fieldType.equals(List.class) || fieldType.equals(Set.class);
+ }
+
+ public void addValue(Object value) {
+ if (m_added && ! isMultiOption()) {
+ throw new ParameterException("Can only specify option " + getName() + " once.");
+ }
+
+ m_added = true;
+ try {
+ Class<?> fieldType = m_field.getType();
+ if (fieldType.equals(String.class)) {
+ m_field.set(m_object, value);
+ } else if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
+ m_field.set(m_object, Integer.parseInt(value.toString()));
+ } else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) {
+ m_field.set(m_object, Long.parseLong(value.toString()));
+ } else if (fieldType.equals(float.class) || fieldType.equals(Float.class)) {
+ m_field.set(m_object, Float.parseFloat(value.toString()));
+ } else if (isMultiOption()) {
+ List l = (List) m_field.get(m_object);
+ if (l == null) {
+ l = Lists.newArrayList();
+ m_field.set(m_object, l);
+ }
+ l.add(value);
+ }
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/beust/jcommander/ParameterException.java b/src/main/java/com/beust/jcommander/ParameterException.java
new file mode 100644
index 0000000..5004cd0
--- /dev/null
+++ b/src/main/java/com/beust/jcommander/ParameterException.java
@@ -0,0 +1,9 @@
+package com.beust.jcommander;
+
+public class ParameterException extends RuntimeException {
+
+ public ParameterException(String string) {
+ super(string);
+ }
+
+}
diff --git a/src/main/java/org/testng/CommandLineArgs.java b/src/main/java/org/testng/CommandLineArgs.java
new file mode 100644
index 0000000..9a4f826
--- /dev/null
+++ b/src/main/java/org/testng/CommandLineArgs.java
@@ -0,0 +1,87 @@
+package org.testng;
+
+import com.beust.jcommander.Parameter;
+
+public class CommandLineArgs {
+
+ @Parameter(name = "-port", description = "The port")
+ private Integer m_port;
+
+ @Parameter(name = "-host", description = "The host")
+ private String m_host;
+
+ @Parameter(name = "-master", description ="Master mode")
+ private Boolean m_master;
+
+ @Parameter(name = "-slave", description ="Slave mode")
+ private Boolean m_slave;
+
+ @Parameter(name = "-groups", description = "Comma-separated list of group names to be run")
+ private String m_groups;
+
+ @Parameter(name = "-excludedgroups", description ="Comma-separated list of group names to be run")
+ private String m_excludedGroups;
+
+ @Parameter(name = "-d", description ="Output directory")
+ private String m_outputDirectory;
+
+ @Parameter(name = "-junit", description ="JUnit mode")
+ private Boolean m_junit;
+
+ @Parameter(name = "-listener", description = "List of .class files or list of class names" +
+ " implementing ITestListener or ISuiteListener")
+ private String m_listener;
+
+ @Parameter(name = "-methodselectors", description = "List of .class files or list of class " +
+ "names implementing IMethodSelector")
+ private String m_methodSelectors;
+
+ @Parameter(name = "-objectfactory", description = "List of .class files or list of class names " +
+ "implementing ITestRunnerFactory")
+ private String m_objectFactory;
+
+ @Parameter(name = "-parallel", description = "Parallel mode (methods, tests or classes)")
+ private String m_parallelMode;
+
+ @Parameter(name = "-threadcount", description = "Number of threads to use when running tests " +
+ "in parallel")
+ private Integer m_threadCount;
+
+ @Parameter(name = "-dataproviderthreadcount", description = "Number of threads to use when " +
+ "running tests in parallel")
+ private Integer m_dataProviderThreadCount;
+
+ @Parameter(name = "-suitename", description = "Default name of test suite, if not specified " +
+ "in suite definition file or source code")
+ private String m_suiteName;
+
+ @Parameter(name = "-testname", description = "Default name of test, if not specified in suite" +
+ "definition file or source code")
+ private String m_testName;
+
+ @Parameter(name = "-reporter", description = "Extended configuration for custom report listener")
+ private String m_reporter;
+
+ /**
+ * Used as map key for the complete list of report listeners provided with the above argument
+ */
+ @Parameter(name = "-reporterslist")
+ private String m_reportersList;
+
+ @Parameter(name = "-skipfailedinvocationcounts")
+ private Boolean m_skipFailedInvocationCounts;
+
+ @Parameter(name = "-testclass", description = "The list of test classes")
+ private String m_testClass;
+
+ @Parameter(name = "-testnames", description = "The list of test names to run")
+ private String m_testNames;
+
+ @Parameter(name = "-testjar", description = "")
+ private String m_testJar;
+
+ @Parameter(name = "-testRunFactory", description = "")
+ private String m_testRunFactory;
+
+ public static final String DATA_PROVIDER_THREAD_COUNT = "-dataproviderthreadcount";
+}