/*************************************************************************
 Copyright © 2000-2003 Novell, Inc. All Rights Reserved.

 THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
 TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE
 AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS
 THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS
 TO DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S
 SAMPLE CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION
 RIGHTS TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT
 OF DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR
 DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.
 ************************************************************************/

package com.novell.nds.dirxml.driver.vrtest;


import java.io.*;
import java.util.*;

import com.novell.nds.dirxml.vrtest.*;
import com.novell.nds.dirxml.driver.*;
import com.novell.nds.dirxml.driver.xds.*;
import com.novell.nds.dirxml.driver.xds.util.*;


/**
 *	The VRTest driver.
 */
public class VRTestDriverShim
        extends CommonImpl
        implements DriverShim
{

    //this constant identifies the channel/module being traced
    //  (e.g., VRTest Driver\Driver)
    static private final String TRACE_SUFFIX = "Driver";

    //the number of driver parameters:
    static private final int NO_OF_PARAMS = 2;

    //parameter tag names:
    static private final String TAG_PORT = "port";
    static private final String TAG_HOST = "host";

    //parameter default values:
    static private final String DEFAULT_HOST = "localhost";

    //the name of the application
    static private final String APPLICATION_NAME = "VRTest";


    /**
     *  Object implementing
     *  <code>com.novell.nds.dirxml.driver.SubscriptionShim</code>,
     *  which is supplied to the DirXML engine via the
     *  <code>getSubscriptionShim()</code> method.
     *  <p>
     *  @see #getSubscriptionShim()
     */
    private VRTestSubscriptionShim subscriptionShim;

    /**
     *  Object implementing
     *  <code>com.novell.nds.dirxml.driver.PublicationShim</code>,
     *  which is supplied to the DirXML engine via the
     *  <code>getPublicationShim()</code> method.
     *  <p>
     *  @see #getPublicationShim()
     */
    private VRTestPublicationShim publicationShim;


    /**
     *  A Java driver shim <code>must</code> have a constructor which takes
     *  no parameters.  The DirXML engine uses this to construct the driver
     *  object.
     */
    public VRTestDriverShim()
    {
        //create temporary trace object until we know what the driver's
        //  RDN is
        setDriverRDN(TRACE_ID);
        setTrace(null); //no channel
        setDriverParams();

        this.subscriptionShim = null;
        this.publicationShim = null;
    }//VRTestDriverShim()


    /**
     *  A non-interface method that describes the parameters
     *  this DriverShim is expecting.
     *  <p>
     *  @see VRTestDriverShim#init(XmlDocument)
     */
    private void setDriverParams()
    {
        //The XDS.jar library automatically checks parameter data
        //types for you.  When a RequiredConstraint is added to a parameter,
        //the library will check init documents to ensure the parameter is
        //present and has a value.  When you add RangeConstraints or
        //EnumConstraints to a parameter, the library will check parameter
        //values to see if they satisfy these constraints.

        Parameter param;

        driverParams = new HashMap(NO_OF_PARAMS);

        //non-authentication params
        param = new Parameter(TAG_PORT, //tag name
                              null, //default
                              DataType.INT); //data type
        param.add(RequiredConstraint.REQUIRED);
        param.add(new RangeConstraint(PORT_MIN, PORT_MAX));
        driverParams.put(param.tagName(), param);

        param = new Parameter(TAG_HOST,
                              DEFAULT_HOST,
                              DataType.STRING);
        driverParams.put(param.tagName(), param);

        //state parameters; must be optional since they are not available when
        //  driver is first started
        param = new Parameter(TAG_RUN_COUNT, //tag name
                              DEFAULT_COUNT, //default
                              DataType.LONG); //data type
        param.add(RangeConstraint.POSITIVE);
        driverParams.put(param.tagName(), param);
    }//setDriverParams():void


    /**
     *  Initializes this driver.
     *  <p>
     *  @param initXML XML document that contains the driver's
     *      initialization parameters
     *  @return an XML document containing status messages for this
     *      operation
     */
    public XmlDocument init(XmlDocument initXML)
    {
        trace.trace("init", 1);

        XDSResultDocument result;
        StatusAttributes attrs;

        //create result document for reporting status to DirXML engine
        //can't add source info yet since we don't have the driver's RDN
        result = new XDSResultDocument();

        try
        {
            XDSInitDocument init;
            XDSStatusElement status;
            XDSDriverStateElement driverState;
            Parameter param;

            //parse initialization document; validation isn't required
            //  in this context since the incoming document didn't pass
            //  through any style sheets
            init = new XDSInitDocument(initXML);

            setDriverRDN(init.rdn());
            //create new trace object that uses driver's instance name
            setTrace(TRACE_SUFFIX);
            //append a <source> element now that we know the driver's instance name
            appendSourceInfo(result);

            //get driver parameters from the initialization doc
            init.parameters(driverParams);

            setAPI(new VRTestAPIWrapper((this.driverParams.get(TAG_HOST)).toString(),
                                        ((Parameter) this.driverParams.get(TAG_PORT)).toInteger().intValue()));
            param = (Parameter) this.driverParams.get(TAG_RUN_COUNT);
            setRunCount(param.toLong().longValue());
            param.overrideValue(String.valueOf(getRunCount()));

            //create a publicationShim and subscriptionShim instance
            subscriptionShim = new VRTestSubscriptionShim(this);
            publicationShim = new VRTestPublicationShim(this);

            driverState = result.appendInitParamsElement().appendDriverStateElement();
            appendStateInfo(driverState);

            //append a successful <status> element to the result doc
            attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
                                             StatusType.DRIVER_STATUS,
                                             null); //event-id
            status = XDSUtil.appendStatus(result, //doc to append to
                                          attrs, //status attribute values
                                          null); //description
            //append the parameter values the driver is actually using
            status.parametersAppend(driverParams);
        }//try
        catch (Exception e) //don't want to catch Error class with Throwable
        {
            //e instance of XDSException:
            //
            //  init document is malformed or invalid -- or --
            //  it is missing required parameters or contains
            //  illegal parameter values

            //e instance of RuntimeException:

            //  e.g., NullPointerException

            attrs = StatusAttributes.factory(StatusLevel.FATAL,
                                             StatusType.DRIVER_STATUS,
                                             null); //event-id
            XDSUtil.appendStatus(result, //doc to append to
                                 attrs, //status attribute values
                                 null, //description
                                 e, //exception
                                 XDSUtil.appendStackTrace(e), //append stack trace?
                                 initXML); //xml to append
        }//catch

        //return result doc w/ status to DirXML engine
        return result.toXML();
    }//init(XmlDocument):XmlDocument


    /**
     *  Shuts down this driver.
     *  <p>
     *  @param reasonXML unused
     *  @return an XML document containing status messages for this
     *      operation
     */
    public XmlDocument shutdown(XmlDocument reasonXML)
    {
        trace.trace("shutdown", 1);

        XDSResultDocument result;
        StatusAttributes attrs;

        result = newResultDoc();

        try
        {
            if (publicationShim != null)
            {
                publicationShim.shutdown();
            }

            if (subscriptionShim != null)
            {
                subscriptionShim.shutdown();
            }

            //append a successful <status> element to the result doc
            attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
                                             StatusType.DRIVER_STATUS,
                                             null); //event-id
            XDSUtil.appendStatus(result, //doc to append to
                                 attrs, //status attribute values
                                 null); //description
        }//try
        catch (Exception e) //don't want to catch Error class with Throwable
        {
            //something bad happened...
            attrs = StatusAttributes.factory(StatusLevel.FATAL,
                                             StatusType.DRIVER_STATUS,
                                             null); //event-id
            XDSUtil.appendStatus(result, //do to append to
                                 attrs, //status attribute values
                                 null, //description
                                 e, //exception
                                 true, //append stack trace?
                                 null); //xml to append
        }//catch

        //return the result doc w/ status to DirXML engine
        return result.toXML();
    }//shutdown(XmlDocument):XmlDocument


    /**
     *  Get's this driver's subscriber.
     *  <p>
     *  @see VRTestSubscriptionShim#init(XmlDocument)
     *  @return an instance of VRTestSubscriptionShim; will not return
     *      <code>null</code>
     */
    public SubscriptionShim getSubscriptionShim()
    {
        trace.trace("getSubscriptionShim", 1);

        return subscriptionShim;
    }//getSubscriptionShim():SubscriptionShim


    /**
     *  Gets this driver's publisher.
     *  <p>
     *  @see VRTestPublicationShim#init(XmlDocument)
     *  @return an instance of VRTestPublicationShim; will not return
     *      <code>null</code>
     */
    public PublicationShim getPublicationShim()
    {
        trace.trace("getPublicationShim", 1);

        return publicationShim;
    }//getPublicationShim():PublicationShim


    /**
     *	Gets the VRTest server's schema.
     *  <p>
     *  This will be called only when the driver is not running. In other
     *  words, if this method is called init()/shutdown() will not be called
     *  for the current instance of the DriverShim.
     *  <p>
     *  @param initXML XML document containing the driver shim
     *      initialization parameters as well as the subscription shim and
     *      publication shim initialization parameters.
     *  @return XML document containing the application schema or an
     *      error status.
     */
    public XmlDocument getSchema(XmlDocument initXML)
    {
        trace.trace("getSchema", 1);

        XDSSchemaResultDocument resultDoc;
        StatusAttributes attrs;

        resultDoc = new XDSSchemaResultDocument();

        try
        {
            XDSInitDocument initDoc;
            VRTestSchema schema;
            XDSDriverOptionsElement options;

            //parse initialization document
            initDoc = new XDSInitDocument(initXML);

            //append a <source> element to the result document
            setDriverRDN(initDoc.rdn());
            setTrace(TRACE_SUFFIX);
            appendSourceInfo(resultDoc);

            //get driver parameters from the initialization doc; because
            //  the run-count tag is used by the driver, subscriber, and
            //  publisher, we have to limit the scope of this operation
            //  to just the <driver-options> element or an XDSParameterException
            //  will be thrown

            options = initDoc.extractInitParamsElement().extractDriverOptionsElement();
            options.parameters(driverParams);

            setAPI(new VRTestAPIWrapper((driverParams.get(TAG_HOST)).toString(),
                                        ((Parameter) driverParams.get(TAG_PORT)).toInteger().intValue()));
            connect();
            schema = api.getSchema();
            disconnect();

            appendSchemaDefElement(resultDoc, schema);

            attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
                                             StatusType.DRIVER_GENERAL,
                                             null); //event-id
            XDSUtil.appendStatus(resultDoc, //doc to append to
                                 attrs, //status attribute values
                                 null); //description
        }//try
        catch (Exception e) //don't want to catch Error class with Throwable
        {
            //e instance of XDSException:
            //
            //  init document is malformed or invalid -- or --
            //  it is missing required parameters or contains
            //  illegal parameter values

            //e instance of RuntimeException:

            //  e.g., NullPointerException

            resultDoc.empty();
            attrs = StatusAttributes.factory(StatusLevel.ERROR,
                                             StatusType.DRIVER_STATUS,
                                             null); //event-id
            XDSUtil.appendStatus(resultDoc, //doc to append to
                                 attrs, //status attribute values
                                 null, //description
                                 e, //exception
                                 XDSUtil.appendStackTrace(e), //append stack trace?
                                 initXML); //xml to append
        }//catch
        finally
        {
            //ensure we close our connection to the VRTest server
            disconnect();
        }//finally

        //return result doc w/ schema and status to DirXML engine
        return resultDoc.toXML();
    }//getSchema(XmlDocument):XmlDocument


    /**
     *	Appends a <code>&lt;schema-def&gt;</code> element to
     *  <code>resultDoc</code>.
     *  <p>
     *	@param resultDoc the document to append to; must not be <code>null</code>
     *  @param schema the VRTest server's schema; must not be <code>null</code>
     */
    static private void appendSchemaDefElement(XDSSchemaResultDocument resultDoc,
                                               VRTestSchema schema
                                               )
    {
        //  <!-- the element we're constructing -->
        //
        //	<schema-def hierarchcial="true">
        // 		<class-def class-name="name" container="false">
        //			<attr-def
        //				attr-name="name"
        //				case-sensitive="false"
        //				multi-valued="true"
        //				naming="false"
        //				read-only="false"
        //				required="false"
        //				type="string"
        //			/> <!-- * -->
        //		</class-def> <!-- * -->
        //	</schema-def>
        //
        //	<!-- * == zero or more -->
        //	<!-- hierarchical is optional; default is true -->

        XDSSchemaDefElement schemaDef;

        schemaDef = resultDoc.appendSchemaDefElement();
        schemaDef.setApplicationName(APPLICATION_NAME);
        schemaDef.setHierarchical(schema.isHierarchical());

        //append a <class-def> element to the <schema-def> element
        //	for each class in the schema
        appendClassDefElements(schemaDef, schema);
    }//appendSchemaDefElement(XDSSchemaResultDocument, VRTestSchema):void


    /**
     *	Appends one <code>&ltclass-def&gt</code> element to
     *  <code>schemaDef</code>.
     *  <p>
     *	@param schemaDef the element to append to; must not be <code>null</code>
     *  @param schema the VRTest server's schema; must not be <code>null</code>
     */
    static private void appendClassDefElements(XDSSchemaDefElement schemaDef,
                                               VRTestSchema schema
                                               )
    {
        //  <!-- the element we're constructing -->
        //
        // 	<class-def class-name="name" container="false">
        //	</class-def> <!-- * -->
        //
        //	<!-- * == zero or more-->
        //	<!-- class-name is required -->
        //	<!-- container is optional; default is false -->

        XDSClassDefElement classDef;
        ListIterator cs;
        VRTestClassSchema classSchema;

        cs = schema.getClassSchemas().listIterator();
        while (cs.hasNext())
        {
            classSchema = (VRTestClassSchema) cs.next();
            classDef = schemaDef.appendClassDefElement();
            classDef.setClassName(classSchema.getName());
            classDef.setContainer(classSchema.isContainer());

            //append an <attr-def> element to the current <class-def> element
            //	for each attribute schema defined in the test server's schema
            //	definition file
            appendAttrDefElements(classDef, classSchema);
        }
    }//appendClassDefElements(XDSSchemaDefElement, VRTestSchema):void


    /**
     *	Appends zero or more <code>&lt;attr-def&gt;</code> element(s) to
     *  <code>classDef</code>.
     *  <p>
     *	@param classDef the element to append to; must not be <code>null</code>
     *  @param classSchema the VRTest server's schema; must not be <code>null</code>
     */
    static private void appendAttrDefElements(XDSClassDefElement classDef,
                                              VRTestClassSchema classSchema
                                              )
    {
        //  <!-- the element we're constructing -->
        //
        //	<attr-def
        //		attr-name="name"
        //		case-sensitive="false"
        //		multi-valued="true"
        //		naming="false"
        //		read-only="false"
        //		required="false"
        //		type="string"
        //	/> <!-- * -->
        //
        //	<!-- * == zero or more -->
        //	<!-- attr-name is reqired --->
        //	<!-- case-sensitive is optional; default is false -->
        //	<!-- multi-valued is optional; default is true -->
        //	<!-- naming is optional; default is false -->
        //	<!-- read-only is optional; default is false -->
        //  <!-- required is optionall default is false -->
        //	<!-- type is optional; default is "string" -->

        VRTestAttributeSchema namingAttrSchema;
        VRTestAttributeSchema attrSchema;
        Enumeration attrSchemas;
        boolean isNamingAttr;
        XDSAttrDefElement attrDef;

        namingAttrSchema = classSchema.getNamingAttributeSchema();
        attrSchemas = classSchema.enumAttributeSchemas();
        while (attrSchemas.hasMoreElements())
        {
            attrSchema = (VRTestAttributeSchema) attrSchemas.nextElement();
            isNamingAttr = (attrSchema == namingAttrSchema);

            attrDef = classDef.appendAttrDefElement();
            attrDef.setAttrName(attrSchema.getName());
            attrDef.setCaseSensitive(attrSchema.isCaseSensitive());
            attrDef.setMultiValued(attrSchema.isMultiValued());
            attrDef.setNaming(isNamingAttr);
            attrDef.setReadOnly(attrSchema.isReadOnly());
            attrDef.setRequired(attrSchema.isRequired());
            attrDef.setType(ValueType.STRING);
        }
    }//appendAttrDefElements(XDSClassDefElement, VRTestClassSchema):void

}//class VRTestDriverShim


