package org.testng;

import java.util.List;
import org.testng.collections.Lists;
import org.testng.internal.ClassHelper;
import org.testng.internal.InstanceCreator;
import org.testng.internal.PropertyUtils;
import org.testng.internal.Utils;

/**
 * Stores the information regarding the configuration of a pluggable report listener. Used also in
 * conjunction with the &lt;reporter&gt; sub-element of the Ant task
 *
 * <p>NOTE: this class needs to be public. It's used by TestNG Ant task
 */
public class ReporterConfig {

  /** The class name of the reporter listener */
  private String m_className;

  /** The properties of the reporter listener */
  private final List<Property> m_properties = Lists.newArrayList();

  public void addProperty(Property property) {
    m_properties.add(property);
  }

  public List<Property> getProperties() {
    return m_properties;
  }

  public String getClassName() {
    return m_className;
  }

  public void setClassName(String className) {
    this.m_className = className;
  }

  public String serialize() {
    StringBuilder sb = new StringBuilder();
    sb.append(m_className);
    if (!m_properties.isEmpty()) {
      sb.append(":");

      for (int i = 0; i < m_properties.size(); i++) {
        ReporterConfig.Property property = m_properties.get(i);
        sb.append(property.name);
        sb.append("=");
        sb.append(property.value);
        if (i < m_properties.size() - 1) {
          sb.append(",");
        }
      }
    }
    return sb.toString();
  }

  public static ReporterConfig deserialize(String inputString) {

    if (Utils.isStringEmpty(inputString)) {
      return null;
    }

    ReporterConfig reporterConfig = new ReporterConfig();

    int clsNameEndIndex = inputString.indexOf(':');
    if (clsNameEndIndex == -1) {
      reporterConfig.setClassName(inputString);
    } else {
      reporterConfig.setClassName(inputString.substring(0, clsNameEndIndex));
      String propString = inputString.substring(clsNameEndIndex + 1, inputString.length());
      String[] props = propString.split(",");
      for (String prop : props) {
        String[] propNameAndVal = prop.split("=");
        if (propNameAndVal.length == 2) {
          reporterConfig.addProperty(new Property(propNameAndVal[0], propNameAndVal[1]));
        }
      }
    }

    return reporterConfig;
  }

  /** Creates a reporter based on the current configuration */
  public IReporter newReporterInstance() {

    Class<?> reporterClass = ClassHelper.forName(m_className);
    if (reporterClass == null) {
      return null;
    }

    Object tmp = InstanceCreator.newInstance(reporterClass);
    if (!(tmp instanceof IReporter)) {
      throw new TestNGException(m_className + " is not a IReporter");
    }

    IReporter result = (IReporter) tmp;
    for (ReporterConfig.Property property : m_properties) {
      PropertyUtils.setProperty(result, property.name, property.value);
    }
    return result;
  }

  public static class Property {
    private final String name;
    private final String value;

    public Property(String name, String value) {
      this.name = name;
      this.value = value;
    }
  }

  @Override
  public String toString() {
    StringBuilder buf = new StringBuilder();
    buf.append("\nClass = ").append(m_className);
    for (Property prop : m_properties) {
      buf.append("\n\t ").append(prop.name).append("=").append(prop.value);
    }
    return buf.toString();
  }
}

@@ -14,16 +14,101 @@
 estng;%0A%0A
+import java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method;%0A
 import j
@@ -2089,30 +2089,8 @@
  + 1
-, inputString.length()
 );%0A 
@@ -2810,24 +2810,43 @@
 r%22);%0A    %7D%0A%0A
+    Object config;%0A
     IReporte
@@ -2873,16 +2873,516 @@
 r) tmp;%0A
+%0A    // support reporters with object-based configurations%0A    // NOTE: By convention, such reporters publish their configurations via a 'getConfig()' method.%0A%0A    try %7B%0A      // get invoker for 'getConfig()' method%0A      Method getConfig = reporterClass.getMethod(%22getConfig%22);%0A      // get reporter configuration%0A      config = getConfig.invoke(result);%0A    %7D catch (NoSuchMethodException %7C IllegalAccessException %7C InvocationTargetException e) %7B%0A      // self-config%0A      config = result;%0A    %7D%0A%0A
     for 
@@ -3465,22 +3465,22 @@
 roperty(
-result
+config
 , proper

Add support for object-based configurations Resolves #2301