/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.xmlrpc;

import java.util.*;
import org.apache.commons.lang.CharSetUtils;
import com.silverwrist.util.*;
import com.silverwrist.dynamo.iface.*;

/**
 * Implements the functions called by the UserLand Software XML-RPC Validator, which act as a test
 * for the XML-RPC protocol stack.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
public class Validator1Suite implements XmlRpcDispatcher, XmlRpcTypeNames
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static final int DISP_ARRAYOFSTRUCTS = 0;
  private static final int DISP_COUNTTHEENTITIES = 1;
  private static final int DISP_EASYSTRUCT = 2;
  private static final int DISP_ECHOSTRUCT = 3;
  private static final int DISP_MANYTYPES = 4;
  private static final int DISP_MODERATESIZEARRAY = 5;
  private static final int DISP_NESTEDSTRUCT = 6;
  private static final int DISP_SIMPLESTRUCTRETURN = 7;

  private static final Map DISPATCH_MAP;
  private static final List[] SIGNATURES;
  private static final String[] HELP_RESOURCES = {
    "help.v1.arrayOf", "help.v1.countEnt", "help.v1.ezStruct", "help.v1.echoStr",
    "help.v1.manyType", "help.v1.mSAC", "help.v1.nestStr", "help.v1.simple"
  };

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public Validator1Suite()
  { // do nothing
  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  /**
   * <CODE>validator1.arrayOfStructsTest(array) returns integer</CODE><P>
   * The parameter is an array of structs containing at least three elements, named
   * "moe", "larry", and "curly", all of which are integers.  Function must add up all
   * the "curly" elements and return the sum.
   *
   * @param r The {@link com.silverwrist.dynamo.iface.Request} object containing the
   *          parameters.
   * @return The return value from the method call.
   * @exception com.silverwrist.dynamo.xmlrpc.FaultCode If there is an error in the parameters.
   */
  private Integer arrayOfStructsTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.arrayOfStructsTest: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof List))
      throw new XmlRpcParameterError("validator1.arrayOfStructsTest: array expected");

    int accum = 0;
    Iterator it = ((List)foo).iterator();
    while (it.hasNext())
    { // cycle through the list of structs
      foo = it.next();
      if (!(foo instanceof Map))
	throw new XmlRpcParameterError("validator1.arrayOfStructsTest: struct expected");
      Map struc = (Map)foo;
      foo = struc.get("curly");
      if ((foo!=null) && (foo instanceof Integer))
	accum += ((Integer)foo).intValue();

    } // end while

    return new Integer(accum);

  } // end arrayOfStructsTest

  /**
   * <CODE>validator1.countTheEntities(string) returns struct</CODE><P>
   */
  private Map countTheEntities(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.countTheEntities: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof String))
      throw new XmlRpcParameterError("validator1.countTheEntities: string expected");
    String s = (String)foo;

    // Create the output value.
    HashMap rc = new HashMap();
    rc.put("ctLeftAngleBrackets",new Integer(CharSetUtils.count(s,"<")));
    rc.put("ctRightAngleBrackets",new Integer(CharSetUtils.count(s,">")));
    rc.put("ctAmpersands",new Integer(CharSetUtils.count(s,"&")));
    rc.put("ctApostrophes",new Integer(CharSetUtils.count(s,"'")));
    rc.put("ctQuotes",new Integer(CharSetUtils.count(s,"\"")));
    return rc;

  } // end countTheEntities

  private Integer easyStructTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.easyStructTest: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.easyStructTest: struct expected");

    Map struc = (Map)foo;
    int sum = 0;
    foo = struc.get("moe");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    foo = struc.get("larry");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    foo = struc.get("curly");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    return new Integer(sum);

  } // end easyStructTest

  private Map echoStructTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.echoStructTest: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.echoStructTest: struct expected");
    return (Map)foo;

  } // end echoStructTest

  private List manyTypesTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=6)
      throw new XmlRpcParameterError("validator1.manyTypesTest: wrong parameter count");
    if (!(pm.get("0") instanceof Integer))
      throw new XmlRpcParameterError("validator1.manyTypesTest: integer expected");
    if (!(pm.get("1") instanceof Boolean))
      throw new XmlRpcParameterError("validator1.manyTypesTest: boolean expected");
    if (!(pm.get("2") instanceof String))
      throw new XmlRpcParameterError("validator1.manyTypesTest: string expected");
    if (!(pm.get("3") instanceof Double))
      throw new XmlRpcParameterError("validator1.manyTypesTest: double expected");
    if (!(pm.get("4") instanceof Date))
      throw new XmlRpcParameterError("validator1.manyTypesTest: date expected");
    if (!(pm.get("5") instanceof byte[]))
      throw new XmlRpcParameterError("validator1.manyTypesTest: base64 expected");

    ArrayList rc = new ArrayList(6);
    for (int i=0; i<6; i++)
      rc.add(pm.get(String.valueOf(i)));
    return rc;

  } // end manyTypesTest

  private String moderateSizeArrayCheck(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.moderateSizeArrayCheck: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof List))
      throw new XmlRpcParameterError("validator1.moderateSizeArrayCheck: array expected");

    List arr = (List)foo;
    StringBuffer rc = new StringBuffer(arr.get(0).toString());
    rc.append(arr.get(arr.size()-1));
    return rc.toString();

  } // end moderateSizeArrayCheck

  private Integer nestedStructTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.nestedStructTest: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.nestedStructTest: struct expected");
    Map current = (Map)foo;

    // Navigate down through the structure until we find the right map.
    foo = current.get("2000");
    if ((foo==null) || !(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.nestedStructTest: struct expected");
    current = (Map)foo;
    foo = current.get("04");
    if ((foo==null) || !(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.nestedStructTest: struct expected");
    current = (Map)foo;
    foo = current.get("01");
    if ((foo==null) || !(foo instanceof Map))
      throw new XmlRpcParameterError("validator1.nestedStructTest: struct expected");
    current = (Map)foo;

    // Get the three integer fields, add them and return the result.
    int sum = 0;
    foo = current.get("moe");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    foo = current.get("larry");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    foo = current.get("curly");
    if ((foo!=null) && (foo instanceof Integer))
      sum += ((Integer)foo).intValue();
    return new Integer(sum);

  } // end nestedStructTest

  private Map simpleStructReturnTest(Request r) throws FaultCode
  {
    Map pm = r.getParameters();
    if (pm.size()!=1)
      throw new XmlRpcParameterError("validator1.simpleStructReturnTest: wrong parameter count");
    Object foo = pm.get("0");
    if (!(foo instanceof Integer))
      throw new XmlRpcParameterError("validator1.simpleStructReturnTest: integer expected");
    int base_val = ((Integer)foo).intValue();

    // create the return structure
    HashMap rc = new HashMap();
    rc.put("times10",new Integer(base_val * 10));
    rc.put("times100",new Integer(base_val * 100));
    rc.put("times1000",new Integer(base_val * 1000));
    return rc;

  } // end simpleStructReturnTest

  /*--------------------------------------------------------------------------------
   * Implementations from interface XmlRpcDispatcher
   *--------------------------------------------------------------------------------
   */

  public String getSessionIDParamValue(Request r, Application app)
  {
    return null;

  } // end getSessionIDParamValue

  public Object dispatchXmlRpcCall(Request r, Application app) throws FaultCode
  {
    Integer disp = (Integer)(DISPATCH_MAP.get(r.getQueryString()));
    if (disp==null)
      throw new XmlRpcMethodNotFound(r.getQueryString());

    switch (disp.intValue())
    {
      case DISP_ARRAYOFSTRUCTS:
	return arrayOfStructsTest(r);

      case DISP_COUNTTHEENTITIES:
	return countTheEntities(r);

      case DISP_EASYSTRUCT:
	return easyStructTest(r);

      case DISP_ECHOSTRUCT:
	return echoStructTest(r);

      case DISP_MANYTYPES:
	return manyTypesTest(r);

      case DISP_MODERATESIZEARRAY:
	return moderateSizeArrayCheck(r);

      case DISP_NESTEDSTRUCT:
	return nestedStructTest(r);

      case DISP_SIMPLESTRUCTRETURN:
	return simpleStructReturnTest(r);

      default:
	throw new XmlRpcMethodNotFound(r.getQueryString());

    } // end switch

  } // end dispatchXmlRpcCall

  public Collection getSupportedMethods(Request r)
  {
    return DISPATCH_MAP.keySet();

  } // end getSupportedMethods

  public List getMethodSignature(Request r, String method) throws FaultCode
  {
    Integer disp = (Integer)(DISPATCH_MAP.get(method));
    if (disp==null)
      throw new XmlRpcMethodNotFound(method);
    return SIGNATURES[disp.intValue()];

  } // end getMethodSignature

  public String getMethodHelp(Request r, String method) throws FaultCode
  {
    Integer disp = (Integer)(DISPATCH_MAP.get(method));
    if (disp==null)
      throw new XmlRpcMethodNotFound(method);
    ResourceBundle b = ResourceBundle.getBundle("com.silverwrist.dynamo.xmlrpc.XmlRpcMessages",
						Locale.getDefault());
    return b.getString(HELP_RESOURCES[disp.intValue()]);

  } // end getMethodHelp

  /*--------------------------------------------------------------------------------
   * Static initializer
   *--------------------------------------------------------------------------------
   */

  static
  { // Create the dispatch map.
    HashMap mtmp = new HashMap();
    mtmp.put("validator1.arrayOfStructsTest",new Integer(DISP_ARRAYOFSTRUCTS));
    mtmp.put("validator1.countTheEntities",new Integer(DISP_COUNTTHEENTITIES));
    mtmp.put("validator1.easyStructTest",new Integer(DISP_EASYSTRUCT));
    mtmp.put("validator1.echoStructTest",new Integer(DISP_ECHOSTRUCT));
    mtmp.put("validator1.manyTypesTest",new Integer(DISP_MANYTYPES));
    mtmp.put("validator1.moderateSizeArrayCheck",new Integer(DISP_MODERATESIZEARRAY));
    mtmp.put("validator1.nestedStructTest",new Integer(DISP_NESTEDSTRUCT));
    mtmp.put("validator1.simpleStructReturnTest",new Integer(DISP_SIMPLESTRUCTRETURN));
    DISPATCH_MAP = Collections.unmodifiableMap(mtmp);

    // Create the signatures.
    List[] arr = new List[8];
    ArrayList tmp = new ArrayList(2);
    tmp.add(TYPE_INT);
    tmp.add(TYPE_ARRAY);
    List l = Collections.unmodifiableList(tmp);
    arr[DISP_ARRAYOFSTRUCTS] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_STRUCT);
    tmp.add(TYPE_STRING);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_COUNTTHEENTITIES] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_INT);
    tmp.add(TYPE_STRUCT);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_EASYSTRUCT] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_STRUCT);
    tmp.add(TYPE_STRUCT);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_ECHOSTRUCT] = Collections.singletonList(l);
    tmp = new ArrayList(7);
    tmp.add(TYPE_ARRAY);
    tmp.add(TYPE_INT);
    tmp.add(TYPE_BOOLEAN);
    tmp.add(TYPE_STRING);
    tmp.add(TYPE_DOUBLE);
    tmp.add(TYPE_DATETIME);
    tmp.add(TYPE_BASE64);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_MANYTYPES] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_STRING);
    tmp.add(TYPE_ARRAY);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_MODERATESIZEARRAY] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_INT);
    tmp.add(TYPE_STRUCT);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_NESTEDSTRUCT] = Collections.singletonList(l);
    tmp = new ArrayList(2);
    tmp.add(TYPE_STRUCT);
    tmp.add(TYPE_INT);
    l = Collections.unmodifiableList(tmp);
    arr[DISP_SIMPLESTRUCTRETURN] = Collections.singletonList(l);
    SIGNATURES = arr;

  } // end static

} // end class Validator1Suite
