//
// CIMClass.java
//
// The contents of this file are subject to the SNIA Public License Version 1.0
// (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.snia.org/resources/openSource.html
//
// 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 CIMClass.java
// 
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s): Peter Rivera
//
// This source uses the Solaris WBEM APIs version 2.2. 
// Synchronization will be maintained with the Solaris WBEM APIS,
// which are being standardized through the Java Community Process.
//
//
// $Id: CIMClass.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
// $Log: CIMClass.java,v $
// Revision 1.1  2003/07/17 18:32:55  kumpf
// HP-BAPU: PEP-64 CIM Client and CIM Listener Java Interface
//
// Revision 1.1.1.1  2001/10/31 18:14:12  karl
// import of SNIA 0_7
//
// Revision 1.11  2001/08/05 12:01:42  davidsi
// - Fixed getProperties() to only return the properties defined by the
//   class.
//
// Revision 1.10  2001/01/02 19:27:08  davidsi
// - Fixed index off-by-one error when extracting origin/classname info.
//
// Revision 1.9  2000/11/28 18:58:59  davidsi
// - toString() now prints complete class information rather than a summary.
//
// Revision 1.8  2000/09/15 16:01:55  davidsi
// - BUG FIX. From Naveen Sharma. No longer leave methods as a null reference
//   that gets set when a method is added since it requires users of the
//   class to be aware that a null may be returned.
//
// Revision 1.7  2000/08/16 05:59:52  shawn
// merge P_TOMCAT31 branch
//
// Revision 1.6.8.1  2000/08/10 00:03:27  shawn
// head merge
//
// Revision 1.6  2000/05/11 10:12:19  davidsi
// - Fixed tabs.
//
// Revision 1.5  2000/05/07 13:32:26  davidsi
// - Removed the reference to the parent's CIMClass instance. Origin class
//   (etc) extractions are now all based on string syntax rather than class
//   traversal.
// - Made the creation of the Method vector lazy as many classes don't
//   require methods.
//
// Revision 1.4  2000/05/06 17:04:21  davidsi
// - Modifications to bring classes inline with the SUN WBEM APIs 2.2.
//
// Revision 1.3  2000/04/09 13:19:15  davidsi
// - Switched to StringBuffer (from String) for XML generation.
//
// Revision 1.2  2000/02/13 15:09:06  davidsi
// - Added text to cover re-use of Solaris WBEM APIs.
//
// Revision 1.1.1.1  2000/02/06 16:41:03  davidsi
// Initial version.
//
//
//
package org.snia.wbem.cim;

import java.net.MalformedURLException;
import java.util.Vector;
import java.io.Serializable;
import org.snia.wbemcmd.xml.*;

/** 
    Creates and instantiates a CIM class.  In CIM, a class object may
    be a class or an associator.  A CIM class must contain a name and 
    may contain methods, properties, and qualifiers.  It is a template
    for creating a CIM instance.  A CIM class represents a collection
    of CIM instances, all of which support a common type (for example, 
    a set of properties, methods, and associations).
 */

public class CIMClass  extends CIMElement implements Serializable {
    private static Vector emptyMethods=new Vector();	// people should not modify the
							// returned vectors so we can use
							// a static entry for when no methods
							// are set -- this saves memory because
							// many classes don't have methods!
    private String parentClassName=null;	// Name of parent class
    private Vector qualifiers=new Vector();	// CIMQualifier
    private Vector properties=new Vector();	// CIMProperty
    private Vector methods=new Vector();	// CIMMethod
    private boolean associationFlag;		// TRUE if an association
    private boolean isKeyed=false;		// TRUE when class has keys
	
    /** 
        Constructor - Creates an uninitiated a new CIM object
        reprenting a CIM class. The class object created by this
        constructor can only be used in an operation such as the
        copy constructor.  It cannot be used to create a class by
        appending names, properties, etc. since it is unitiated.

        Use one of the other constructors to create an initiated new CIM class
        object.
        @exception "unitialized handle" if this
        unitialized handle is used
    */
    public CIMClass() {
    }

    /**
        Constructor - Creates a new CIM object with the specified name
        reprenting a CIM class. 

        @param  nname - Name of the CIM class. 

        @exception "unitialized handle" if this unitialized handle 
                   is used
    */
    public CIMClass(String nname) {
	setName(nname); 
    }

    
    /** 
        Constructor - Creates a new CIM object using the specified 
        XML definition
    
        @param node The node containing an XML definition of CIMClass
     */ 
    public CIMClass(org.w3c.dom.Node node) {
	XMLNode result=XMLNode.getXMLNode(node);
	String superClassName;
	String inClassName;
	
	try {
	    result=result.mustFindElement(XMLNode.XML_ELEMENT_CLASS);
	    inClassName=result.mustGetAttribute(XMLParameters.paramName);
		    
	    superClassName=result.getAttribute(XMLParameters.paramSuperName);
	
	    setName(inClassName);
	    if (superClassName!=null)
		setSuperClass(superClassName);
	
	    // 
	    // Find qualifier information
	    //
	    for(result=result.getChild();
		    result!=null && result.getToken()==XMLNode.XML_ELEMENT_QUALIFIER;
		    result=result.getNext())
		qualifiers.addElement(new CIMQualifier(result));

	    // 
	    // Load properties
	    //
	    for(;result!=null;result=result.getNext()) {
		int token=result.getToken();
		if (token==XMLNode.XML_ELEMENT_PROPERTY ||
		    token==XMLNode.XML_ELEMENT_PROPERTY_ARRAY ||
		    token==XMLNode.XML_ELEMENT_PROPERTY_REF ) {
		    CIMProperty cp=new CIMProperty(result);
		    if (!isKeyed) {
			// If class not yet key'd then see if a key is being 
			// added
			isKeyed=(cp.getQualifier("key")!=null);
		    }		
		    properties.addElement(cp);
		} else
		    break;
	    }
	
	    // 
	    // Load methods
	    //
	    for(;result!=null && result.getToken()==XMLNode.XML_ELEMENT_METHOD;
		    result=result.getNext()) {
		if (methods==null)
		    methods=new Vector();
		methods.addElement(new CIMMethod(result));
	    }

	} catch (CIMException ce) {
	    System.err.println("Failed to create class:"+ce.toString());
	    throw new java.lang.InstantiationError(ce.toString());
	}
    }
    
    
    /**
       Returns a String representation of the CIMClass. 

       @return String empty or cimclass string
    */

    public String toString() 
    {
	int i;
	
	String ret=new String();
	
	if (qualifiers.size()!=0) {
	     ret=ret.concat("[\n");
	     if (associationFlag)
		ret=ret.concat("ASSOCIATION,");
	     for(i=0;i<qualifiers.size();i++) {
		if (i>0)
		    ret=ret.concat(",");
		ret=ret.concat(((CIMQualifier)(qualifiers.elementAt(i))).toMOF());
	     }
	     ret=ret.concat("]\n");
	}
	ret=ret.concat("class ");
	ret=ret.concat(getName());

	if (parentClassName!=null) {
	    ret=ret.concat(":");
	    ret=ret.concat(parentClassName);
	}
	ret=ret.concat("\n{\n");
	for(i=0;i<properties.size();i++)
	    ret=ret.concat(((CIMProperty)(properties.elementAt(i))).toMOF());

	if (methods!=null) {
	    for(i=0;i<methods.size();i++)
		ret=ret.concat(((CIMMethod)(methods.elementAt(i))).toMOF());
	}
	ret=ret.concat("};\n");
	return(ret);
    }

    /**
       toMOF
       Returns a MOF representation of the CIM class. 
    
       @return String MOF representation of this class.
    */
    public String toMOF() 
    {
	int i;
	
	String ret=new String();
	
	if (qualifiers.size()!=0) {
	     ret=ret.concat("[\n");
	     if (associationFlag)
		ret=ret.concat("ASSOCIATION,");
	     for(i=0;i<qualifiers.size();i++) {
		if (i>0)
		    ret=ret.concat(",");
		ret=ret.concat(((CIMQualifier)(qualifiers.elementAt(i))).toMOF());
	     }
	     ret=ret.concat("]\n");
	}
	ret=ret.concat("class ");
	ret=ret.concat(getName());

	if (parentClassName!=null) {
	    ret=ret.concat(":");
	    ret=ret.concat(parentClassName);
	}
	ret=ret.concat("\n{\n");
	for(i=0;i<properties.size();i++)
	    ret=ret.concat(((CIMProperty)(properties.elementAt(i))).toMOF());

	if (methods!=null) {
	    for(i=0;i<methods.size();i++)
		ret=ret.concat(((CIMMethod)(methods.elementAt(i))).toMOF());
	}
	ret=ret.concat("};\n");
	return(ret);
    }
 
    
    /**
      getSuperClass - returns the name of the parent for this class

      @return String with parent class name.
    */
    public String getSuperClass() 
    {
	if (parentClassName==null)
	    return(null);
	return(parentClassName);
    }
 
    /** 
        setSuperClassName - Sets the name of the parent class from
        the input parameter. \REF{CLASSNAME}.
        @param String defining parent name.
        @return Throws IllegalName if superClassName argument not legal CIM
        identifier
        @exception throws IllegalName if the name is not correct. See
             \URL[ClassNames]{DefinitionofTerms.html#CLASSNAME}
    */
    public void setSuperClass(String sClass) {
	parentClassName=sClass;
    }

    /**
         isKeyed -  Returns true if class has keyed properties. 

         @return  boolean Returns true or false
     */
    public boolean isKeyed() {
	return(isKeyed);
    }
    
    /**
        setIsKeyed - Sets whether this class has keys 

        @param boolean isKeyedValue value
     */
    public void setIsKeyed(boolean isKeyedValue) {
	isKeyed=isKeyedValue;
    } 
    
    /**
         getKeys - Returns the properties that are keys for this class

         @return  Vector containing the list of key properties
     */
    public Vector getKeys() {
	Vector v=new Vector();
	for(int i=0;i<properties.size();i++) {
	    CIMProperty p=((CIMProperty)(properties.elementAt(i)));
	    if (p.isKey())
		v.addElement(p);
	}
	return(v);
    }
 
    /**
       getQualifier - get the specified CIM Qualifier in this class
  
       @param String name - The string name of the CIM qualifier. 

       @return CIMQualifier Returns the CIM Qualifier in this class else null
     */
    public CIMQualifier getQualifier(String name) {
	for(int i=0;i<qualifiers.size();i++) {
	    CIMQualifier q=((CIMQualifier)(qualifiers.elementAt(i)));
	    if (q.getName().equalsIgnoreCase(name))
		return(q);
	}
	return(null);
    }


    
 
    /**
       getProperty - get the CIMProperty for the specified class

       @param String name - name of the property to get.

       @return CIMProperty return the CIM property else if exist else Null 
			 if the property does not exist 
    */
    public CIMProperty getProperty(String propName) 
    {
	String oClass;
	
	//
	// See if property name is in the form originClass.propName
	// and if it is, work to find real origin class
	//
	oClass=splitName1(propName);
	if (oClass!=null) {
	    propName=splitName2(propName);
	    for(int i=0;i<properties.size();i++) {
		CIMProperty cp=(CIMProperty)(properties.elementAt(i));
		if (!cp.getOriginClass().equalsIgnoreCase(oClass)
		    && (cp.getName().equalsIgnoreCase(propName)))
		    return(cp);
	    }
	    return(null);
	} 

	for(int i=0;i<properties.size();i++) {
	    CIMProperty cp=(CIMProperty)(properties.elementAt(i));
	    
	    if (cp.getName().equalsIgnoreCase(propName))
		return(cp);
	}
	return(null);
    }

 
    /**
       getProperty - get the CIMProperty for the specified class and its origin class.

       @param String name - name of the property to get.
       @param String originClass - name of the class in which the property was defined. 

       @return CIMProperty Returns the specified CIMProperty from the specified
                         originating class.
    */
    public CIMProperty getProperty(String name,String originClass) 
    {
	return(getProperty(name+"."+name));
    } 

    /**
       getMethod  - // Returns the specified method 
     */
    public CIMMethod getMethod(String methodName) 
    {
	String oClass;
	
	//
	// See if method name is in the form originClass.propName
	// and if it is, work to find real origin class
	//
	oClass=splitName1(methodName);
	if (oClass!=null) {
	    if (methods!=null) {
		int tsize=methods.size();
		for(int i=0;i<tsize;i++) {
		    CIMMethod q=((CIMMethod)(methods.elementAt(i)));
		    if (!q.getOriginClass().equalsIgnoreCase(oClass) 
			&& (q.getName().equalsIgnoreCase(methodName)))
			return(q);
		}
	    }
	    return(null);
	} 
	if (methods!=null) {
	    int tsize=methods.size();
	    for(int i=0;i<tsize;i++) {
		CIMMethod q=((CIMMethod)(methods.elementAt(i)));
		if (q.getName().equalsIgnoreCase(methodName))
		    return(q);
	    }
	}
	return(null);
    }
 
 
 
    /**
       getMethod  - Returns the specified method from the originating
       class given
     */ 
    public CIMMethod getMethod(String name,String originClass)	{
	return(getMethod(originClass+"."+name));
    }	 
				


    /**
       isAssociation - Returns TRUE if the class is an association else FALSE
     */
    public boolean isAssociation() {
	return(associationFlag);
    }


    /**
       setIsAssociation - Sets whether this class is an association 
     */ 
    public void setIsAssociation(boolean isAssociation) {
	associationFlag=isAssociation;
    }
  
    /**
       getQualifiers - Gets the qualifiers
     */ 
    public Vector getQualifiers() {
	return(qualifiers);
    }
 
    /**
       Gets the properties (including overridden ones)
     */	
    public Vector getAllProperties() {
	return(properties);
    }



    /**
       Gets the properties for this CIM class - filters
       out overides
     */  
    public Vector getProperties() {
	Vector prop=new Vector();

	String className = getName();
	
	for(int i=0;i<properties.size();i++) {
	    CIMProperty cp=(CIMProperty)(properties.elementAt(i));
	    //
	    // Not an obvious override, but if the origin class is 
	    // to this classname then return the property
	    String oClass = cp.getOriginClass();
	    if (oClass.equals(className))
		prop.addElement(cp);
	}
	return(prop);  
    }
 
 
    /** 
       Gets the methods for this CIM class (includes overridden ones) 
     */  
    public Vector getAllMethods() {
	if (methods==null)
	    return(emptyMethods);
	return(methods);
    }
 
 
 
    /**
     * Gets the methods - but not overriden ones
     */ 
    public Vector getMethods() {
	Vector meth=new Vector();
	
	if (methods!=null) {
	    int tsize=methods.size();
	    for(int i=0;i<tsize;i++) {
	    CIMMethod cp=(CIMMethod)(methods.elementAt(i));
	    if (cp.getQualifier(CIMQualifier.CIM_QUAL_OVERRIDE)==null)
		meth.addElement(cp);
	    }
	}
	return(meth);	  
    }

    /**
       Adds a property to the class
     */
    public void addProperty(CIMProperty p) {
	properties.addElement(p);
    }

    /**
       Returns the number of properties
     */
    public int numberOfProperties() {
	return(properties.size());
    }

    /**
       Sets the properties for this class (old ones are removed)
     */ 
    public void setProperties(Vector table) {
	properties=table;
    }
    
    /**
       Adds a qualifier
     */
    public void addQualifier(CIMQualifier q) {
	//
	// See if qualifier already present
	//
	for(int i=0;i<qualifiers.size();i++) {
	    CIMQualifier cq=(CIMQualifier)(qualifiers.elementAt(i));
	    
	    if (cq.equals(q)) {
		qualifiers.removeElementAt(i);
		break;
	    }
	}
	qualifiers.addElement(q);
    }
 
    /**
       Checks whether this class has the specified qualifier
      	
       Returns true if qualifier defined
     */ 
    public boolean hasQualifier(CIMQualifier q) {
	int i;
    
	for(i=0;i<qualifiers.size();i++)
	    if (((CIMQualifier)(qualifiers.elementAt(i))).equals(q))
		return(true);

	return(false);
    }
 
    /**
       returns number of qualifiers
     */
    public int numberOfQualifiers() {
	return(qualifiers.size());
    }
    
    /**
       Removes a qualifier
       Returns true if the qualifier removed 
     */ 
    public boolean removeQualifier(CIMQualifier q) {

	for(int i=0;i<qualifiers.size();i++) {
	    CIMQualifier cq=(CIMQualifier)(qualifiers.elementAt(i));
	    
	    if (cq.equals(q)) {
		qualifiers.removeElementAt(i);
		return(true);
	    }
	}
	return(false);
    }
 
 
    /**
       Sets the qualifiers (old qualifiers replaced)
     */ 
    public void setQualifiers(Vector table) {
	for(int i=0;i<table.size();i++) {
	    Object o=table.elementAt(i);
	    if (!(o instanceof CIMQualifier)) {
		//
		// Why doesn't this issue an exception...
		//
		return;
	    }
	}
	qualifiers=table;
    }


    /**
       Adds a method
     */
    public void addMethod(CIMMethod m) {
	//for(int i=0;i<methods.size();i++) {
	//  CIMMethod cm=(CIMMethod)(methods.elementAt(i));
	//  
	//  if (cm.equals(m)) {
	//	methods.removeElementAt(i);
	//	break;
	//  }
	//}
	if (methods==null)
	    methods=new Vector();
	methods.addElement(m);
    }

    /**
       Sets methods (old methods replaced)
     */ 
    public void setMethods(Vector table) {
	methods=table;
    }

    /**
       Returns a new instance appropriately initialized
     */ 
    public CIMInstance newInstance() {
	CIMInstance cInstance=new CIMInstance();

	Vector props=new Vector();
	for(int i=0;i<properties.size();i++) {
	    CIMProperty prop=(CIMProperty)(properties.elementAt(i));
	    props.addElement(prop.clone());
	}
	cInstance.setProperties(props);
	
	Vector quals=new Vector();
	for(int i=0;i<qualifiers.size();i++) {
	    CIMQualifier qual=(CIMQualifier)(qualifiers.elementAt(i));
	    quals.addElement(qual.clone());
	}
	cInstance.setQualifiers(quals);

	cInstance.setClassName(getName());
	return(cInstance);
    }

 
    /**
       Returns an XML representation of this CIM class. 
     */
    public String toXml(boolean includeQualifiers) throws MalformedURLException {
	return(toXml(false,includeQualifiers,true,null));
    }


    
    //
    // SUNDIFF - not supported
    //
    //public String toXml(CIMObjectPath cop) throws MalformedURLException {
    //	  throw new MalformedURLException("");
    //}
 
    //
    // SUNDIFF - not supported
    //
    //public String toXml(CIMObjectPath cop,String host) throws MalformedURLException {
    //	  throw new MalformedURLException("");
    //}


    /**
       Returns the local elements
     */
    public CIMClass localElements() {
	
	CIMClass localClass=new CIMClass();
			
	String className=getName();
	localClass.setName(className);
	localClass.parentClassName=parentClassName;	    // Name of parent class
	localClass.associationFlag=associationFlag;	       // TRUE if an association
	
	if (methods!=null) {
	    int maxMethods=methods.size();
	    for(int i=0;i<maxMethods;i++) {
		CIMMethod cm=(CIMMethod) (methods.elementAt(i)) ;
		localClass.methods.addElement(cm.clone());
	    }
	}
	
	int maxQualifiers=qualifiers.size();
	for(int i=0;i<maxQualifiers;i++) {
	    CIMQualifier cm=(CIMQualifier) (qualifiers.elementAt(i)) ;
	    localClass.qualifiers.addElement(cm.clone());
	}
	
	if (properties!=null) {
	    for(int i=0;i<properties.size();i++) {
		CIMProperty prop=(CIMProperty)(properties.elementAt(i));
		if (prop.getOriginClass().equalsIgnoreCase(className)) 
		    localClass.properties.addElement(prop.clone());
	    }
	}
		
	localClass.isKeyed=isKeyed; 
	return(localClass);	
    }
    
    
    
    public CIMClass filterProperties(java.lang.String[] propertyList,
				 boolean includeQualifier,
				 boolean includeClassOrigin) {
	
	CIMClass localClass=new CIMClass();
				   
	localClass.setName(getName());
	localClass.parentClassName=parentClassName;	    // Name of parent class
	localClass.associationFlag=associationFlag;	       // TRUE if an association
	
	if (methods==null) {
	    int maxMethods=methods.size();
	    for(int i=0;i<maxMethods;i++) {
		CIMMethod cm=(CIMMethod) (methods.elementAt(i)) ;
		localClass.methods.addElement(cm.clone());
	    }
	}
	
	int maxQualifiers=qualifiers.size();
	for(int i=0;i<maxQualifiers;i++) {
	    CIMQualifier cm=(CIMQualifier) (qualifiers.elementAt(i)) ;
	    localClass.qualifiers.addElement(cm.clone());
	}
	
	if (properties!=null) {
	    for(int i=0;i<properties.size();i++) {
		CIMProperty prop=(CIMProperty)(properties.elementAt(i));
		//
		// If propertyList is not NULL then check this is a request property
		//
		if (propertyList!=null) {
		    int j;
		    
		    String pName=prop.getName();
		    for(j=0;j<propertyList.length;j++)
			if (pName.equalsIgnoreCase(propertyList[j]))
			    break;
		    //
		    // Not in list, so skip
		    //
		    if (j==propertyList.length)
			continue;
		}
		localClass.properties.addElement(prop.clone(includeQualifier,
				 includeClassOrigin));
	    }
	}
		
 
	localClass.isKeyed=isKeyed; 
	return(localClass);
	
    }
    
    
    /**
       Returns an XML representation of this CIM class. 
       @return String Managed Object Format (MOF) representation of this class
    */
    public String toXml(boolean localOnly,
			boolean includeQualifiers,
			boolean includeClassOrigin,
			String[] propertyList)	throws MalformedURLException
    {
	StringBuffer text;	
	String cimName=getName();
	
	if (cimName==null)
	    throw new MalformedURLException("class must have name");

	
	if (parentClassName!=null)
	    text=new StringBuffer("<CLASS NAME=\""+cimName+
			    "\" SUPERCLASS=\""+parentClassName+"\">");
	else
	    text=new StringBuffer("<CLASS NAME=\""+cimName+"\">");

	//
	// Process qualifiers
	//
	if (includeQualifiers && qualifiers!=null) {
	    for(int i=0;i<qualifiers.size();i++) {
		CIMQualifier qual=(CIMQualifier)(qualifiers.elementAt(i));
		text=text.append(qual.toXml(localOnly));
	    }
	}
	
	if (properties!=null) {
	    for(int i=0;i<properties.size();i++) {
		CIMProperty prop=(CIMProperty)(properties.elementAt(i));
		//
		// If propertyList is not NULL then check this is a request property
		//
		if (propertyList!=null) {
		    int j;
		    
		    String pName=prop.getName();
		    for(j=0;j<propertyList.length;j++)
			if (pName.equalsIgnoreCase(propertyList[j]))
			    break;
		    //
		    // Not in list, so skip
		    //
		    if (j==propertyList.length)
			continue;
		}
		text=text.append(prop.toXml(localOnly,includeQualifiers,includeClassOrigin));
	    }
	}
		
	if (methods!=null) {
	    for(int i=0;i<methods.size();i++) {
		CIMMethod method=(CIMMethod)(methods.elementAt(i));
		text=text.append(method.toXml(localOnly,includeQualifiers,includeClassOrigin));
	    }
	}
	
	text=text.append("</CLASS>");
	return(text.toString());
    }
    

    
    /**
       Breaks a string of the form x.y into two strings 'x' and 'y'
     */
    private String splitName1(String inName) {
	int i=inName.indexOf(".");
	if (i== -1)
	    return(null);
	
	return(inName.substring(0,i));
    }

    /**
       Breaks a string of the form x.y into two strings 'x' and 'y'
     */
    private String splitName2(String inName) {
	int i=inName.indexOf(".");
	if (i== -1)
	    return(inName);
	
	return(inName.substring(i+1,inName.length()));
    }
    //
    // END NOTSUN
    //
}
