Extended reporters configuration
diff --git a/src/main/org/testng/TestNG.java b/src/main/org/testng/TestNG.java
index a1c534c..f9efe46 100644
--- a/src/main/org/testng/TestNG.java
+++ b/src/main/org/testng/TestNG.java
@@ -24,12 +24,7 @@
 
 import javax.xml.parsers.ParserConfigurationException;
 
-import org.testng.internal.AnnotationTypeEnum;
-import org.testng.internal.ClassHelper;
-import org.testng.internal.HostFile;
-import org.testng.internal.IResultListener;
-import org.testng.internal.Invoker;
-import org.testng.internal.Utils;
+import org.testng.internal.*;
 import org.testng.internal.annotations.DefaultAnnotationTransformer;
 import org.testng.internal.annotations.IAnnotationFinder;
 import org.testng.internal.annotations.IAnnotationTransformer;
@@ -683,7 +678,7 @@
     if(m_useDefaultListeners) {
       m_reporters.add(new SuiteHTMLReporter());
       m_reporters.add(new FailedReporter());
-      m_reporters.add(new XMLReporter());
+//      m_reporters.add(new XMLReporter());
       m_reporters.add(new EmailableReporter());
     }
   }
@@ -1112,6 +1107,23 @@
     if(null != objectFactory) {
       setObjectFactory(objectFactory);
     }
+
+    List<ReporterConfig> reporterConfigs =
+            (List<ReporterConfig>) cmdLineArgs.get(TestNGCommandLineArgs.REPORTERS_LIST);
+    if (reporterConfigs != null) {
+      for (ReporterConfig reporterConfig : reporterConfigs) {
+        addReporter(reporterConfig);
+      }
+    }
+  }
+
+  private void addReporter(ReporterConfig reporterConfig) {
+    Object instance = reporterConfig.newReporterInstance();
+    if (instance != null) {
+      addListener(instance);
+    } else {
+      LOGGER.warn("Could not find reporte class : " + reporterConfig.getClassname());
+    }
   }
 
   private void setClientPort(int clientPort) {
diff --git a/src/main/org/testng/TestNGAntTask.java b/src/main/org/testng/TestNGAntTask.java
index 8845c51..fa8a755 100644
--- a/src/main/org/testng/TestNGAntTask.java
+++ b/src/main/org/testng/TestNGAntTask.java
@@ -135,6 +135,11 @@
   private String m_suiteName="Ant suite";

   private String m_testName="Ant test";

 

+  /**

+   * The list of report listeners added via &lt;reporter&gt; sub-element of the Ant task

+   */

+  private List<ReporterConfig> reporterConfigs = new ArrayList<ReporterConfig>();

+

   public void setParallel(String parallel) {

     m_parallelMode= parallel;

   }

@@ -566,6 +571,13 @@
     	argv.add(m_testName);

     }

 

+    if (!reporterConfigs.isEmpty()) {

+      for (ReporterConfig reporterConfig : reporterConfigs) {

+        argv.add(TestNGCommandLineArgs.REPORTER);

+        argv.add(reporterConfig.serialize());

+      }

+    }

+

     if(m_xmlFilesets.size() > 0) {

       for(String file : fileset(m_xmlFilesets)) {

         argv.add(file);

@@ -995,4 +1007,8 @@
       ppp(line);

     }

   }

+

+  public void addConfiguredReporter(ReporterConfig reporterConfig) {

+    reporterConfigs.add(reporterConfig);

+  }

 }

diff --git a/src/main/org/testng/TestNGCommandLineArgs.java b/src/main/org/testng/TestNGCommandLineArgs.java
index b6d52d4..1a614bc 100644
--- a/src/main/org/testng/TestNGCommandLineArgs.java
+++ b/src/main/org/testng/TestNGCommandLineArgs.java
@@ -69,6 +69,14 @@
   public static final String PARALLEL_MODE = "-parallel";
   public static final String SUITE_NAME_OPT = "-suitename";
   public static final String TEST_NAME_OPT = "-testname";
+  /**
+   * Used to pass a reporter configuration in the form <code>-reporter <reporter_name_or_class>:option=value[,option=value]</code>
+   */
+  public static final String REPORTER = "-reporter";
+  /**
+   * Used as map key for the complete list of report listeners provided with the above argument
+   */
+  public static final String REPORTERS_LIST = "-reporterslist";
 
   /** 
    * When given a file name to form a class name, the file name is parsed and divided 
@@ -331,6 +339,16 @@
           i++;
         }
       }
+      else if (REPORTER.equalsIgnoreCase(argv[i])) {
+        if ((i + 1) < argv.length) {
+          ReporterConfig reporterConfig = ReporterConfig.deserialize(trim(argv[i + 1]));
+          if (arguments.get(REPORTERS_LIST) == null) {
+            arguments.put(REPORTERS_LIST, new ArrayList<ReporterConfig>());
+          }
+          ((List<ReporterConfig>)arguments.get(REPORTERS_LIST)).add(reporterConfig);
+          i++;
+        }
+      }
       //
       // Unknown option
       //
diff --git a/src/main/org/testng/internal/Parameters.java b/src/main/org/testng/internal/Parameters.java
index 5d1f5b9..e44c071 100644
--- a/src/main/org/testng/internal/Parameters.java
+++ b/src/main/org/testng/internal/Parameters.java
@@ -138,8 +138,9 @@
       }
     }
   }
-  
-  private static Object convertType(Class type, String value, String paramName) {
+
+  //TODO: Cosmin - making this public is not the best solution
+  public static Object convertType(Class type, String value, String paramName) {
     Object result = null;
     
     if(NULL_VALUE.equals(value.toLowerCase())) {
diff --git a/src/main/org/testng/reporters/XMLReporter.java b/src/main/org/testng/reporters/XMLReporter.java
index e2a2349..6335a48 100644
--- a/src/main/org/testng/reporters/XMLReporter.java
+++ b/src/main/org/testng/reporters/XMLReporter.java
@@ -5,9 +5,7 @@
 import org.testng.xml.XmlSuite;
 
 import java.io.File;
-import java.io.UnsupportedEncodingException;
 import java.util.*;
-import java.net.URLEncoder;
 
 /**
  * The main entry for the XML generation operation
@@ -16,11 +14,13 @@
  */
 public class XMLReporter implements IReporter {
 
-  private XMLReporterConfig config;
+  private XMLReporterConfig config = new XMLReporterConfig();
   private XMLStringBuffer rootBuffer;
 
   public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
-    config = new XMLReporterConfig(outputDirectory);
+    if (Utils.isStringEmpty(config.getOutputDirectory())) {
+      config.setOutputDirectory(outputDirectory);
+    }
 
     rootBuffer = new XMLStringBuffer("");
     rootBuffer.push(XMLReporterConfig.TAG_TESTNG_RESULTS);
@@ -29,7 +29,7 @@
       writeSuite(xmlSuites.get(i), suites.get(i));
     }
     rootBuffer.pop();
-    Utils.writeFile(config.getOutput(), "testng-results.xml", rootBuffer.toXML());
+    Utils.writeFile(config.getOutputDirectory(), "testng-results.xml", rootBuffer.toXML());
   }
 
   private void writeReporterOutput(XMLStringBuffer xmlBuffer) {
@@ -43,11 +43,6 @@
   }
 
   private void writeSuite(XmlSuite xmlSuite, ISuite suite) {
-    try {
-      Utils.writeFile(config.getOutput(),  URLEncoder.encode(xmlSuite.getName(), "utf-8") + "-testng.xml", xmlSuite.toXml());
-    } catch(UnsupportedEncodingException e) {
-      throw new RuntimeException("no utf-8 in your JVM, awful awful things are happening", e);      
-    }
     switch (config.getFileFragmentationLevel()) {
       case XMLReporterConfig.FF_LEVEL_NONE:
         writeSuiteToBuffer(rootBuffer, suite);
@@ -69,13 +64,8 @@
   }
 
   private File referenceSuite(XMLStringBuffer xmlBuffer, ISuite suite) {
-    String relativePath;
-    try {
-      relativePath = URLEncoder.encode(suite.getName(), "utf-8") + File.separatorChar + "testng-results.xml";
-    } catch(UnsupportedEncodingException e) {
-      throw new RuntimeException("no utf-8 in your JVM, awful awful things are happening", e);
-    }
-    File suiteFile = new File(config.getOutput(), relativePath);
+    String relativePath = suite.getName() + File.separatorChar + "testng-results.xml";
+    File suiteFile = new File(config.getOutputDirectory(), relativePath);
     Properties attrs = new Properties();
     attrs.setProperty(XMLReporterConfig.ATTR_URL, relativePath);
     xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_SUITE, attrs);
@@ -129,4 +119,53 @@
     }
     return result;
   }
+
+  //TODO: This is not the smartest way to implement the config
+  public int getFileFragmentationLevel() {
+    return config.getFileFragmentationLevel();
+  }
+
+  public void setFileFragmentationLevel(int fileFragmentationLevel) {
+    config.setFileFragmentationLevel(fileFragmentationLevel);
+  }
+
+  public int getStackTraceOutputMethod() {
+    return config.getStackTraceOutputMethod();
+  }
+
+  public void setStackTraceOutputMethod(int stackTraceOutputMethod) {
+    config.setStackTraceOutputMethod(stackTraceOutputMethod);
+  }
+
+  public String getOutputDirectory() {
+    return config.getOutputDirectory();
+  }
+
+  public void setOutputDirectory(String outputDirectory) {
+    config.setOutputDirectory(outputDirectory);
+  }
+
+  public boolean isGenerateGroupsAttribute() {
+    return config.isGenerateGroupsAttribute();
+  }
+
+  public void setGenerateGroupsAttribute(boolean generateGroupsAttribute) {
+    config.setGenerateGroupsAttribute(generateGroupsAttribute);
+  }
+
+  public boolean isSplitClassAndPackageNames() {
+    return config.isSplitClassAndPackageNames();
+  }
+
+  public void setSplitClassAndPackageNames(boolean splitClassAndPackageNames) {
+    config.setSplitClassAndPackageNames(splitClassAndPackageNames);
+  }
+
+  public String getTimestampFormat() {
+    return config.getTimestampFormat();
+  }
+
+  public void setTimestampFormat(String timestampFormat) {
+    config.setTimestampFormat(timestampFormat);
+  }
 }
diff --git a/src/main/org/testng/reporters/XMLReporterConfig.java b/src/main/org/testng/reporters/XMLReporterConfig.java
index 72d1c6f..d87b052 100644
--- a/src/main/org/testng/reporters/XMLReporterConfig.java
+++ b/src/main/org/testng/reporters/XMLReporterConfig.java
@@ -1,13 +1,9 @@
 package org.testng.reporters;
 
-import java.io.File;
-import java.io.Serializable;
-
 /**
  * @author Hani Suleiman Date: Mar 27, 2007 Time: 9:16:28 AM
  */
-public class XMLReporterConfig implements Serializable
-{
+public class XMLReporterConfig {
   /**
    * Indicates that no file fragmentation should be performed. This value indicates the XML generator to write all the
    * results in one big file. Not recommended for large test suites.
@@ -86,7 +82,7 @@
    * Indicates the way that the file fragmentation should be performed. Set this property to one of the FF_LEVEL_*
    * values for the desired output structure
    */
-  private int fileFragmentationLevel = FF_LEVEL_SUITE;
+  private int fileFragmentationLevel = FF_LEVEL_NONE;
 
   /**
    * Stack trace output method for the failed tests using one of the STACKTRACE_* constants.
@@ -118,19 +114,12 @@
    */
   private String timestampFormat = FMT_DEFAULT;
 
-  private String subDirectory = "xml";
-  private static final long serialVersionUID = 5375520916207107244L;
-
-  public XMLReporterConfig(String outputDirectory) {
-    this.outputDirectory = outputDirectory;
+  public int getFileFragmentationLevel() {
+    return fileFragmentationLevel;
   }
 
-  public String getSubDirectory() {
-    return subDirectory;
-  }
-
-  public void setSubDirectory(String subDirectory) {
-    this.subDirectory = subDirectory;
+  public void setFileFragmentationLevel(int fileFragmentationLevel) {
+    this.fileFragmentationLevel = fileFragmentationLevel;
   }
 
   public int getStackTraceOutputMethod() {
@@ -141,24 +130,14 @@
     this.stackTraceOutputMethod = stackTraceOutputMethod;
   }
 
-  public int getFileFragmentationLevel() {
-    return fileFragmentationLevel;
-  }
-
-  public void setFileFragmentationLevel(int fileFragmentationLevel) {
-    this.fileFragmentationLevel = fileFragmentationLevel;
-  }
-
   public String getOutputDirectory() {
     return outputDirectory;
   }
 
-  public File getOutput() {
-    if(subDirectory != null && subDirectory.length() > 0)
-      return new File(outputDirectory, subDirectory);
-    return new File(outputDirectory);
+  public void setOutputDirectory(String outputDirectory) {
+    this.outputDirectory = outputDirectory;
   }
-  
+
   public boolean isGenerateGroupsAttribute() {
     return generateGroupsAttribute;
   }
@@ -167,14 +146,6 @@
     this.generateGroupsAttribute = generateGroupsAttribute;
   }
 
-  public String getTimestampFormat() {
-    return timestampFormat;
-  }
-
-  public void setTimestampFormat(String timestampFormat) {
-    this.timestampFormat = timestampFormat;
-  }
-
   public boolean isSplitClassAndPackageNames() {
     return splitClassAndPackageNames;
   }
@@ -182,4 +153,12 @@
   public void setSplitClassAndPackageNames(boolean splitClassAndPackageNames) {
     this.splitClassAndPackageNames = splitClassAndPackageNames;
   }
+
+  public String getTimestampFormat() {
+    return timestampFormat;
+  }
+
+  public void setTimestampFormat(String timestampFormat) {
+    this.timestampFormat = timestampFormat;
+  }
 }
diff --git a/src/main/org/testng/reporters/XMLSuiteResultWriter.java b/src/main/org/testng/reporters/XMLSuiteResultWriter.java
index a94bb78..a22aec0 100644
--- a/src/main/org/testng/reporters/XMLSuiteResultWriter.java
+++ b/src/main/org/testng/reporters/XMLSuiteResultWriter.java
@@ -38,7 +38,7 @@
       writeAllToBuffer(xmlBuffer, suiteResult);
     } else {
       String parentDir =
-              config.getOutput().getAbsolutePath() + File.separatorChar + suiteResult.getTestContext().getSuite().getName();
+              config.getOutputDirectory() + File.separatorChar + suiteResult.getTestContext().getSuite().getName();
       File file = referenceSuiteResult(xmlBuffer, parentDir, suiteResult);
       XMLStringBuffer suiteXmlBuffer = new XMLStringBuffer("");
       writeAllToBuffer(suiteXmlBuffer, suiteResult);
@@ -48,7 +48,7 @@
 
   private void writeAllToBuffer(XMLStringBuffer xmlBuffer, ISuiteResult suiteResult) {
     xmlBuffer.push(XMLReporterConfig.TAG_TEST, getSuiteResultAttributes(suiteResult));
-    Set<ITestResult> testResults = new HashSet<ITestResult>();
+    Set<ITestResult> testResults = new HashSet();
     addAllTestResults(testResults, suiteResult.getTestContext().getPassedTests());
     addAllTestResults(testResults, suiteResult.getTestContext().getFailedTests());
     addAllTestResults(testResults, suiteResult.getTestContext().getSkippedTests());