// 
// The Waveset SPML General License 
// 
// Version 0.1, April 2003
// Copyright (C) 2003 Waveset Technologies, Inc..
// 6034 West Courtyard Drive, Suite 210, Austin, Texas 78730
// All rights reserved.
// 
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions, and the disclaimers in Sections 6
//    and 7 below.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the disclaimers in Sections 6
//    and 7 below in the documentation and/or other materials provided
//    with the distribution.
// 
// 3. The end-user documentation included with the redistribution, if
//    any, must include the following acknowledgment:
// 
//    "This product includes software developed by 
//     Waveset Technologies, Inc. (www.waveset.com)."
// 
//    Alternately, this acknowledgment may appear in the software itself, if
//    and wherever such third-party acknowledgments normally appear.
//  
// 4. The names "Waveset" and "Waveset Technologies, Inc." must not be
//    used to endorse or promote products derived from this software
//    without the prior written permission of Waveset. For written
//    permission, please contact www.waveset.com.
//  
// 5. Products derived from this software may not be called "Waveset",
//    nor may "Waveset" appear in their name, without the prior written
//    permission of Waveset.
//  
// 6. NO WARRANTY 
// 
//    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED
//    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//    DISCLAIMED.
// 
// 7.  LIMITATION OF LIABILITY
// 
//    IN NO EVENT SHALL THE WAVESET OR ITS LICENSORS BE LIABLE FOR ANY
//    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
//    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
//    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// End of Terms and Conditions
// ====================================================================
//
// Component Name: org.openspml.message.SpmlRequest
//
// Author(s): Jeff Larson
//
// Description:
//
// Base class for all SPML request objects.
//
//

package org.openspml.message;

import java.util.*;
import org.w3c.dom.*;

import org.openspml.util.*;

/**
 * The base class for all SPML request objects. It provides a request ID,
 * request mode, and a collection of vendor specfific operational attributes.
 */
public abstract class SpmlRequest implements Constants {

    //////////////////////////////////////////////////////////////////////
    //
    // Fields
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Internal constants for the execution type.
     */
    public static final String EXEC_SYNCHRONOUS = 
    ENUM_PREFIX + "synchronous";

    public static final String EXEC_ASYNCHRONOUS = 
    ENUM_PREFIX + "asynchronous";

    /**
     * Optional request ID.  Typed in the schema as xsd:ID.
     */
    String _requestId;

    /**
     * True to request asynchronous execution.
     */
    boolean _async;

    /**
     * Optional operational attributes.
     * This includes information that needs to be sent with the request
     * which isn't part of the objectClass being manipulated, but which
     * may be recognized by the PST.
     */
    List _operationalAttributes;

    /**
     * Identifier of the object to manipulate.
     * Relevant for all requests except SearchRequest, SchemaRequest,
     * CancelRequest, and StatusRequest.  
     // Hmm, more than I thought
     // perhaps it does make sense to factor out another class?
     * 
     */
    Identifier _identifier;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    //////////////////////////////////////////////////////////////////////
    
    /**
     * Set the optional request id.
     */
    public void setRequestId(String s) {
        _requestId = s;
    }

    /**
     * Set a flag indicating whether the request is to be performed
     * asynchronously.
     */
    public void setAsynchronous(boolean b) {
        _async = b;
    }
    
    /**
     * Set the list of operational attributes.  The list must
     * contain <code>Attribute</code> objects.
     * @see #Attribute
     */
    public void setOperationalAttributes(List l) {
        _operationalAttributes = l;
    }

    /**
     * Set one operational attribute specified as an Attribute object.
     * @see #Attribute
     */
    public void setOperationalAttribute(Attribute a) {
        if (_operationalAttributes == null)
            _operationalAttributes = new ArrayList();
        // todo, check for duplicates
        _operationalAttributes.add(a);
    }

    /**
     * Set an operational attribute specified as a name/value pair.
     * <p>
     * Note that while the method allows values of any type, in 
     * practice this should be limited to String values until the
     * specification is extended to support complex value types.
     */
    public void setOperationalAttribute(String name, Object value) {
        setOperationalAttribute(new Attribute(name, value));
    }

    /**
     * Sets the target object identifier.
     * This is relevant only for AddRequest, ModifyRequest, and DeleteRequest.
     */
    public void setIdentifier(Identifier id) {
        _identifier = id;
    }

    /**
     * Sets the target object identifier.
     * This will implicitly construct an <code>Identifier</code> object whose
     * type is <code>GenericString</code>.
     */
    public void setIdentifier(String id) {
        if (id == null)
            _identifier = null;
        else 
            _identifier = new Identifier(id);
    }

    /**
     * Build an empty SpmlRequest object that corresponds to this
     * request.  Used in a few error conditions to return the
     * expected response object when one was not actually sent back from
     * the server.
     */
    public abstract SpmlResponse createResponse();

    //////////////////////////////////////////////////////////////////////
    //
    // Accessors
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Return the optional request id.
     */
    public String getRequestId() {
        return _requestId;
    }
    
    /**
     * Return true if asynchronous execution was requested.
     */
    public boolean isAsynchronous() {
        return _async;
    }

    /**
     * Return the list of operational attributes.
     * The list will contain <code>Attribute</code> objects.
     */
    public List getOperationalAttributes() {
        return _operationalAttributes;
    }

    /**
     * Return the target object identifier.
     * @see #Identifier
     */
    public Identifier getIdentifier() {
        return _identifier;
    }

    /**
     * Return the target object identifier as a string.
     */
    public String getIdentifierString() {
        return (_identifier != null) ? _identifier.getId() : null;
    }

    /**
     * Lookup an attribute by name.
     // More common to look for these by name than in other requests?
     // Assuming the list is short, otherwise use a HashMap.
     */
    public Attribute getOperationalAttribute(String name) {
        return Attribute.getAttribute(_operationalAttributes, name);
    }

    public Object getOperationalAttributeValue(String name) {
        return Attribute.getAttributeValue(_operationalAttributes, name);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // XML
    //
    // We have no toXml method, rather the base class calls us 
    // to fill in our parts.
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Return the tag name for the request element.
     */
    public abstract String getElementName();

    /**
     * Add subclass specific start tag attributes.
     */
    void addSubclassAttributes(SpmlBuffer b) {
    }

    /**
     * Add subclass specific elements.
     */
    void addSubclassElements(SpmlBuffer b) {
    }

    /**
     * Render requst XML.  
     */
    public String toXml() {
        SpmlBuffer b = new SpmlBuffer();
        toXml(b);
        return b.toString();
    }

    /**
     * Render requst XML into an existing buffer.
     * According to the schema, operational attributes must
     * come first.
     */
    public void toXml(SpmlBuffer b) {

        String tag = getElementName();
        b.addOpenStartTag(tag);

        if (_requestId != null)
            b.addAttribute("requestID", _requestId);

        // will this default to sync?
        if (_async)
            b.addAttribute("execution", EXEC_ASYNCHRONOUS);

        addSubclassAttributes(b);

        b.closeStartTag();
        b.incIndent();

        Attribute.toXml(b, "operationalAttributes", 
                        _operationalAttributes);

        // Not technically part of the SpmlRequest schema, 
        // but for all requests that include it, it follows 
        // operational attributes.
        // Will this have a reasonable default?
        if (_identifier != null)
            _identifier.toXml(b);

        addSubclassElements(b);

        b.decIndent();
        b.addEndTag(tag);
    }

    /**
     * Expected to be overloaded.
     */
    void parseXml(XmlElement e) {

	_requestId = e.getAttribute("requestID");
	String s = e.getAttribute("execution");
	_async = EXEC_ASYNCHRONOUS.equals(s);
	
	for (XmlElement child = e.getChildElement() ; 
	     child != null ; child = child.next()) {
	    String tag = child.getLocalName();
 
            if (tag.equals(Identifier.ELEMENT))
                _identifier = new Identifier(child);

            else if (tag.equals("operationalAttributes"))
                _operationalAttributes = Attribute.parseList(child);

            else {
                // since these are supposed to be ordered,
                // can stop now
                break;
            }
        }
    }

    /**
     * Parse one of the "root" requests.
     */
    public static SpmlRequest parseRequest(Element domel) {

        // wrap it in one of these to provide a more convenient
        // set of methods for our parsers
        return parseRequest(new XmlElement(domel));
    }

    /**
     * Parse one of the "root" requests.
     */
    public static SpmlRequest parseRequest(XmlElement e) {

	SpmlRequest req = null;

	String name = e.getLocalName();

	if (name.equals(BatchRequest.ELEMENT))
	    req = new BatchRequest(e);

	else if (name.equals(StatusRequest.ELEMENT))
	    req = new StatusRequest(e);

	else if (name.equals(CancelRequest.ELEMENT))
	    req = new CancelRequest(e);

	else if (name.equals(SchemaRequest.ELEMENT))
	    req = new SchemaRequest(e);

	else if (name.equals(AddRequest.ELEMENT))
	    req = new AddRequest(e);

	else if (name.equals(ModifyRequest.ELEMENT))
	    req = new ModifyRequest(e);

	else if (name.equals(DeleteRequest.ELEMENT))
	    req = new DeleteRequest(e);

	else if (name.equals(SearchRequest.ELEMENT))
	    req = new SearchRequest(e);

	else if (name.equals(ExtendedRequest.ELEMENT))
	    req = new ExtendedRequest(e);

	return req;
    }

    /**
     * Handy function to call in the debugger.
     */
    public void dumpFile(String name) {

        Util.writeFileQuietly(name, toXml());
    }

}
