// 
// 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.ObjectClassDefinition
//
// Author(s): Jeff Larson
//
// Description:
//
// Memory representation for an <objectClassDefinition> element.
//
//

package org.openspml.message;

import java.util.*;
import org.openspml.util.*;

/**
 * A class representing an <code>objectClassDefinition</code> element.
 */
public class ObjectClassDefinition {

    //////////////////////////////////////////////////////////////////////
    //
    // Fields
    //
    // In theory, have to allow arbitrary attributes and elements.
    //
    //////////////////////////////////////////////////////////////////////

    static final String ELEMENT = "objectClassDefinition";

    /**
     * Our parent schema, used to resolve attribute definitions.
     */
    Schema _schema;

    /**
     * This is typed as a dsml:AttributeDescriptionValue, 
     * what does that mean?
     */
    String _name;

    /**
     * Optional description.
     */
    String _description;

    /**
     * Arbitrary properties.  
     * <p>
     * The spec has these as dsml:DsmlAttr which are essentially
     * just name/value pairs, so implementing as a Map.
     * Could do the same for other attributes and get rid
     * of the Attribute class, but that provides some handy
     * XML handling methods.
     */
    Map _properties;

    /**
     * List of AttributeDefinitionReference objects.
     * <p>
     * Spec defines this as AttributeDefinitionReferences
     * which like ObjectClassDefinitionReferences is a 	
     * sequence of AttributeDefinitionReference and "any" 
     * elements.  
     */
    List _memberAttributes;

    /**
     * List of ObjectClassDefinitionReference objects
     * representing the superior classes.
     * <p>
     * Spec defines this as ObjectClassDefinitionReferences
     * which in turn is a sequence of unbounded
     * ObjectClassDefinitionReference followed by unbounded
     * "any" elements.  I suspect the intent was to allow 
     * a mixture of ObjectClassDefinitionReference's and other
     * elements, but not necessarily sequencing them.
     */
    List _superiorClasses;

    /**
     * Transient cache of resolved attribute definitions.
     */
    List _attributeDefinitions;

    // NOTE: Spec has:
    // <xsd:anyAttribute namespace="##other" processContents="lax" />
    // potentially need to maintain an arbitrary attribute map
    //
    // Spec has:
    // <xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
    // potentially need to maintain list of DOM elements?

    //////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    //////////////////////////////////////////////////////////////////////

    public ObjectClassDefinition() {
    }

    ObjectClassDefinition(XmlElement e) {
	parseXml(e);
    }

    public void setSchema(Schema s) {
        _schema = s;
        // references want to know too
        if (_superiorClasses != null) {
            for (int i = 0 ; i < _superiorClasses.size() ; i++) {
                ObjectClassDefinitionReference ref = 
                    (ObjectClassDefinitionReference)_superiorClasses.get(i);
                ref.setSchema(s);
            }
        }
        if (_memberAttributes != null) {
            for (int i = 0 ; i < _memberAttributes.size() ; i++) {
                AttributeDefinitionReference ref = 
                    (AttributeDefinitionReference)_memberAttributes.get(i);
                ref.setSchema(s);
            }
        }
    }

    public void setName(String s) {
	_name = s;
    }

    public void setDescription(String s) {
	_description = s;
    }

    public void setProperties(Map props) {
	_properties = props;
    }
    
    public void setProperty(String name, Object value) {
	if (_properties == null)
	    _properties = new HashMap();
	_properties.put(name, value);
    }
    
    public void setMemeberAttributes(List l) {
	_memberAttributes = l;
    }

    public void addMemberAttribute(AttributeDefinitionReference ref) {
	if (_memberAttributes == null)
	    _memberAttributes = new ArrayList();
	_memberAttributes.add(ref);
    }

    public void setSuperiorClasses(List l) {
	_superiorClasses = l;
    }

    public void addSuperiorClass(ObjectClassDefinitionReference ref) {
	if (_superiorClasses == null)
	    _superiorClasses = new ArrayList();
	_superiorClasses.add(ref);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Accessors
    //
    //////////////////////////////////////////////////////////////////////
    
    public String getName() {
	return _name;
    }

    public String getDescription() {
	return _description;
    }

    public Map getProperties() {
	return _properties;
    }
    
    public Object getProperty(String name) {
	return (_properties != null) ? _properties.get(name) : null;
    }

    public List getMemberAttributes() {
	return _memberAttributes;
    }

    public List getSuperiorClasses() {
	return _superiorClasses;	
    }

    /**
     * Convenience accessor to return a list of resolved
     * AttributeDefinition objects.
     // Hmm, what we really need is a combination of the definition
     // and the reference so we can combine the required flag with the
     // multi flag etc.
     */
    public List getAttributeDefinitions() {

        if (_attributeDefinitions == null) {
            if (_schema != null)
                _attributeDefinitions = 
                    _schema.getAttributeDefinitions(_memberAttributes);
        }
        return _attributeDefinitions;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // XML
    //
    //////////////////////////////////////////////////////////////////////

    String getElementName() {
        return ELEMENT;
    }

    void toXml(SpmlBuffer b) {

        b.addOpenStartTag(ELEMENT);
	b.addAttribute("name", _name);
	b.addAttribute("description", _description);
        b.closeStartTag();
        b.incIndent();

	// spec uses <xsd:sequence> so they must be
	// in this order

	Attribute.toXml(b, "properties", _properties);

	if (_memberAttributes != null) {
	    b.addStartTag("memberAttributes");
	    b.incIndent();
            Iterator it = _memberAttributes.iterator();
            while (it.hasNext()) {
                AttributeDefinitionReference ref = 
		    (AttributeDefinitionReference)it.next();
                ref.toXml(b);
            }
	    b.decIndent();
	    b.addEndTag("memberAttributes");
	}

	if (_superiorClasses != null) {
	    b.addStartTag("memberAttributes");
	    b.incIndent();
            Iterator it = _superiorClasses.iterator();
            while (it.hasNext()) {
                ObjectClassDefinitionReference ref = 
		    (ObjectClassDefinitionReference)it.next();
                ref.toXml(b);
            }
	    b.decIndent();
	    b.addEndTag("memberAttributes");
	}

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

    void parseXml(XmlElement e) {

	// in theory, need to recognize and maintain 
	// other arbitrary elements

	_name = e.getAttribute("name");
	_description = e.getAttribute("description");

	for (XmlElement child = e.getChildElement() ; 
	     child != null ; child = child.next()) {

	    String tag = child.getLocalName();

	    if (tag.equals("properties"))
                _properties = Attribute.parseMap(child);

	    else if (tag.equals("memberAttributes")) {
		for (XmlElement att = child.getChildElement();
		     att != null ; att = att.next()) {
		    addMemberAttribute(new AttributeDefinitionReference(att));
		}
	    }

	    else if (tag.equals("superiorClasses")) {
		for (XmlElement cls = child.getChildElement();
		     cls != null ; cls = cls.next()) {
		    addSuperiorClass(new ObjectClassDefinitionReference(cls));
		}
	    }

	    // in theory, need to recognize and maintain 
	    // other arbitrary elements

        }
    }

}
