//
// CIMInstance.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 CIMInstance.java
// 
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s): Peter Rivera, Angela Lee
//
//
// 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: CIMInstance.java,v 1.1 2003/07/17 18:32:55 kumpf Exp $
//
// $Log: CIMInstance.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:23  karl
// import of SNIA 0_7
//
// Revision 1.12  2001/03/31 12:03:39  davidsi
// - toString() now calls the CIMValue.toString() method rather than the
//   object obtained from CIMValue.
//
// Revision 1.11  2001/03/19 17:56:16  davidsi
// - Add cache to getName() to prevent rebuilding string each time.
//
// Revision 1.10  2001/01/02 19:31:30  davidsi
// - Fixed misleading comment about key generation.
// - Added check for null when looking for a property.
//
// Revision 1.9  2000/11/06 17:59:47  davidsi
// - Fixed toString() to return more information on the instance.
//
// Revision 1.8  2000/08/16 05:59:52  shawn
// merge P_TOMCAT31 branch
//
// Revision 1.7.6.1  2000/08/10 00:03:27  shawn
// head merge
//
// Revision 1.7  2000/07/10 17:02:31  davidsi
// - Fixed formatting.
//
// Revision 1.6  2000/05/19 14:25:45  davidsi
// - Fixed bug in updatePropertyValue() which was failing to force
//   a rebuild of the key lists when a keyed property value changed.
//
// Revision 1.5  2000/05/11 10:12:19  davidsi
// - Fixed tabs.
//
// 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:07  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.util.Hashtable;
import java.io.Serializable;
import org.snia.wbemcmd.xml.XMLNode;
import org.snia.wbemcmd.xml.XMLParameters;
import org.snia.wbemcmd.cimom.CIMOMEscape;

/**
    Creates and instantiates an instance of a CIM class.  Use this interface
    to describe a managed object that belongs to a particular class.  
    Instances contain actual data.  Clients use CIMInstance in conjunction
    with the CIMClient instance methods like createInstance or setInstance
    to manipulate instances within a namespace.
 */

public class CIMInstance extends CIMElement implements Serializable {

    private Vector keys=null;
    private String owningClassName=null;
    private Vector properties = new Vector();	// are CIMProperty
    private Vector qualifiers = new Vector();	// are CIMQualifier
    private String aliasName=null;

   /** 
        Constructor - Create a CIM Instance object.
        @return  Instance created
    */
    public CIMInstance() {
    }



   /** 
        Constructor - Create a CIMInstance with a given name.
        @param String the new instance to be created.
        @return New instance
    */
    public CIMInstance(String name) {
	setName(name);
    }

    /**
        Constructor - Create a CIMInstance using a XML definition
        @param node The node containing an XML definition of CIMInstance
     */
    public CIMInstance(org.w3c.dom.Node node) {
	XMLNode result=XMLNode.getXMLNode(node);
	String superClassName;
	String inClassName;

	if (result.getToken()!=XMLNode.XML_ELEMENT_INSTANCE)
	    throw new java.lang.InstantiationError("Not an instance node");
	try {
	    inClassName=result.mustGetAttribute(XMLParameters.classParamName);
	    setClassName(inClassName);
	    // 
	    // 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 ) {
		    properties.addElement(new CIMProperty(result));
		} else {
		    break;
		}
	    }
	} catch (CIMException ce) {
	    throw new java.lang.InstantiationError(ce.toString());
	}
    }


    /**
        Returns a String representation of the CIMInstance.
        @return String representation of the CIMInstance
     */
    public String toString() {
	int i;
	String outVal;
	StringBuffer temp=new StringBuffer("instance of "+owningClassName+" {\n");
		
	for(i=0;i<properties.size();i++) {
	    CIMProperty cp=((CIMProperty)(properties.elementAt(i)));
	    CIMValue val=cp.getValue();
	    if (val==null)
		outVal="null";
	    else
		outVal=val.toString();
	    temp.append(cp.getName()+" = "+outVal+";\n");
	}
	temp.append("}\n");
	return(temp.toString());
    }
    


    /**
        Returns a MOF representation of the CIMInstance.
        @return String mof representation of the CIMInstance
     */
    public String toMOF() {
	int i;
	
	String ret=new String();
	
	if (qualifiers.size()!=0) {
	     ret=ret.concat("[\n");
	     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("INSTANCE OF ");
	ret=ret.concat(owningClassName);

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



    /**
        Returns the class name of the instance
        @return String with the class name.
     */
    public String getClassName()  {
	return(owningClassName);
    }



    /**
        Sets the name of the class 
        @param String class name.
     */ 
    public void setClassName(String iclassName){
	owningClassName=iclassName;
   }


    /** 
        getQualifiers - Retrieves the qualifier object defined for this 
                        instance.
        @return Vector  list of qualifier objects for the CIMInstance.
     */
    public Vector getQualifiers() {
	return(qualifiers);
    }


    /**
        Sets the qualifiers
        @param Vector  qualifier objects.
     */
    public void setQualifiers(Vector table) {
	//
	// Only make the assign if all the qualifiers are
	// real CIMQualifiers.
	//
	int tsize=table.size();
	for(int i=0;i<tsize;i++)
	    if (!(table.elementAt(i) instanceof CIMQualifier))
		return;
	qualifiers=table;
    }


    /**
        Gets the properties list
        @return Vector  properties list
     */
    public Vector getProperties() {

	return(properties);
    }



    /**
        Sets the properties  for this instance
        @param Vector  list of properties.
     */ 
    public void setProperties(Vector itable) {
	int tsize=itable.size();
	properties=new Vector();
	
	//
	// Note: The property gets cloned because it embeds
	// a value that will vary on a per instance basis
	//
	for(int i=0;i<tsize;i++) {
	    properties.addElement(
			((CIMProperty)itable.elementAt(i)).clone());
	}
	//
	// Remake object path information
	//
	keys=null;
	
    }



    /**
        Returns a property as specified by the name/origin class.
        @param String name  - name of the property
        @param String originClass  - class origin
        @return CIMProperty property object the specified name
     */
    public CIMProperty getProperty(String name, String originClass) {
	CIMValue cimValue;
	int tsize=properties.size();
	if ( (name == null) || (originClass == null) )
	    return( null );
	for(int i=0;i<tsize;i++) {
	    CIMProperty cp=((CIMProperty)(properties.elementAt(i)));
	    if (originClass.equalsIgnoreCase(cp.getOriginClass()) &&
		name.equalsIgnoreCase(cp.getName()))
		return(cp);
	}
	return(null);
    }

    /**
        Returns a property as specified by the name.
        @param String name  - name of the property
        @return CIMProperty property object the specified name
     */
    public CIMProperty getProperty(String propertyName) {
	if (propertyName==null)
	    return(null);
	int tsize=properties.size();
	for(int i=0;i<tsize;i++) {
	    CIMProperty cp=((CIMProperty)(properties.elementAt(i)));
	    String pname=cp.getName();
	    if (pname!=null && propertyName.equalsIgnoreCase(pname))
		return(cp);
	}
	return(null);
    }



    /**
        Gets the alias name (returns null if there isn't one)
        @return String the alias of the instance if exist else null.
     */
    public String getAlias() {
	return(aliasName);
    }


    /**
        Sets the alias for the CIM instance
        @param String alias name
     */
    public void setAlias(String ialiasName){
	if (ialiasName!=null)
	    aliasName=new String(ialiasName);
	else
	    aliasName=null;
    }




    /**
        Gets the name of this instance.
        @return String The name is made by concating all key 
                       properties and their values
     */
    public String getName() {
	// if this is not the first time we've been called then this
	// should have already been generated
	String retString = super.getName();
	if (retString==null) {
	    boolean hasdone=false;
	    retString =  owningClassName+".";
	
	    for(int i=0;i<properties.size();i++) {
		CIMProperty cp=(CIMProperty)(properties.elementAt(i));
		if (cp.getType().isReferenceType()) {
		    //
		    // If its a reference property then its an implied key
		    //
		    if (hasdone)
			retString=retString.concat(",");
		    CIMValue cv=cp.getValue();
		    if (cv==null)
			return(null);
		    retString=retString.concat(cp.getName()+"=\""+
			CIMOMEscape.escape(cv.getValue().toString())+"\"");
		    hasdone=true;		    
		} else {
		    //
		    // Assumed its not an association
		    Vector cv=cp.getQualifiers();	    // CIMQualifer
		    for(int j=0;j<cv.size();j++) {
			CIMQualifier cq=(CIMQualifier)(cv.elementAt(j));	
		     
			if (cq.getName().equalsIgnoreCase(CIMQualifier.CIM_QUAL_KEY)) {
			    if (hasdone)
				retString=retString.concat(",");
			    retString=retString.concat(cp.getName()+"="+
						       cp.getValue());
			    hasdone=true;
			}
		    }
		}
	    }
	    // set the string for future use
	    super.setName(retString);
	}
	return(retString);
    }


    /**
        Returns the list of key-value pairs for this instance
        @return Vector  list of key-value pairs for this instance
     */
    public Vector getKeyValuePairs() {
	if (keys!=null)
	    return(keys);
	keys=new Vector();
	int tsize=properties.size();
	for(int i=0;i<tsize;i++) {
	    CIMProperty cp=((CIMProperty)(properties.elementAt(i)));
	    //
	    // Note that a REF type is an implied key even if not
	    // explicitly stated
	    //
	    if (cp.getQualifier("key")!=null)
		keys.addElement(cp);
	    else if (cp.getType().isReferenceType())
		keys.addElement(cp);
	}
	return(keys);
    }


    /**
        Updates the property values for the instance
        @param Vector  list of property values
     */
    public void updatePropertyValues(Vector v){
	int tsize=v.size();
	for(int i=0;i<tsize;i++) {
	    updatePropertyValue((CIMProperty)(v.elementAt(i)));
	}
    }


    
    /**
        Updates a single property value for the instance
        @param CIMProperty  a CIM Property value
     */
    public void updatePropertyValue(CIMProperty pe) {
	String name=pe.getName();
	int tsize=properties.size();
	for(int i=0;i<tsize;i++) {
	    CIMProperty cp=(CIMProperty)(properties.elementAt(i));
	    String rname=cp.getName();

	    if (rname!=null && rname.equalsIgnoreCase(name)) {
		cp.setValue(pe.getValue());
		if (keys!=null && cp.getQualifier("key")!=null) {
			//
			// If keys are not null and this is a key
			// rebuild the key list next time its requested
			//
			keys=null;
		}
		return;
	    }
	}
    }


    /**
        sets property value for the CIM value
        @param String property name to set
        @param CIMValue a CIMProperty value
     */
    public void setProperty(String name, CIMValue cv) {
	int tsize=properties.size();
	for(int i=0;i<tsize;i++) {
	    CIMProperty cp=(CIMProperty)(properties.elementAt(i));
	    String rname=cp.getName();

	    if (rname!=null && rname.equalsIgnoreCase(name)) {
		//
		// Fix up object path if its a key
		//
		if (keys!=null && cp.getQualifier("key")!=null)
		    keys=null;
	
		cp.setValue(cv);
		return;
	    }
	}
	//
	// Not found so add it
	//
	CIMProperty cp=new CIMProperty();
	cp.setName(name);
	cp.setValue(cv);
	cp.setType(cv.getType());
	properties.addElement(cp);
	//
	// Fix up object path if its a key
	//
	if (cp.getQualifier("key")!=null)
	    keys=null;
    }



    /**
        toXml - returns a XML representation of the CIMInstance. 
        @return String XML Representation
     */
    public String toXml() throws MalformedURLException{
	return(toXml(null,false,true,true,null));
    }


    
    //
    // SUNDIFF
    //
    /**
        toXml - returns a XML representation of the CIMInstance. 
        @param CIMObjectPath Object path of this CIM instance
        @return String XML Representation
     */
    public String toXml(CIMObjectPath cop) throws MalformedURLException{
	return(toXml(cop,false,true,true,null));
    }

    //
    // SUNDIFF
    //
    //public String toXml(CIMObjectPath cop,String host) throws MalformedURLException {
    //	  throw new MalformedURLException("");
    //}
    
    //
    // NOT SUN
    //


    /**
        toXml - returns a XML representation of the CIMInstance. 
                you can also specify propertyList for certain set of
                properties you are interested in.

        @param CIMObjectPath Object path of this CIM instance
        @param boolean localonly flag
        @param boolean includeQualifiers flag
        @param boolean includeClassOrigin flag
        @param String property list

        @return String XML Representation
     */
    public String toXml(CIMObjectPath cop,
			boolean localOnly,
			boolean includeQualifiers,
			boolean includeClassOrigin,
			String[] propertyList ) throws MalformedURLException{
	StringBuffer text;	
	
	if (cop!=null) {
	    CIMNameSpace ns=cop.getFullNameSpace();
	    text=new StringBuffer("<INSTANCEPATH>"+ns.toXml(false)+
		 cop.toXml()+"</INSTANCEPATH>");
	} else
	    text=new StringBuffer();
	
	//
	// NOTE: don't write the object path here!
	//
	
	if (owningClassName!=null)
	    text=text.append("<INSTANCE CLASSNAME=\""+owningClassName+"\">");
	else
	    throw new MalformedURLException("instance has no class name");

	//
	// 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));
	    }
	}
	text=text.append("</INSTANCE>");
	return(text.toString());
    }
    
    /**
        localElements - returns only the local elements for this instance. 
        @return CIMInstance instance with the local elements
     */ 
    public CIMInstance localElements() {
	CIMInstance ci=new CIMInstance();
	ci.owningClassName=owningClassName;
	ci.aliasName=aliasName;     

	//
	// Process qualifiers
	//
	if (qualifiers!=null) {
	    ci.setQualifiers(qualifiers);
	}
	
	if (properties!=null) {
	    for(int i=0;i<properties.size();i++) {
		CIMProperty prop=(CIMProperty)(properties.elementAt(i));
		if (prop.getOriginClass().equalsIgnoreCase(owningClassName)) 
		    ci.properties.addElement(prop.clone());
	    }
	}
	return(ci);
    }
    
    
    
    /**
        Returns a new CIMInstance with properties filtered according to 
        the input parameters. Class origin and qualifiers can also be specified. 

        @param String property list array you are interested in
        @param boolean includeQualifiers flag
        @param boolean includeClassOrigin flag

        @return CIMInstance instance with the local elements
     */
    public CIMInstance filterProperties(
			      String[] propertyList,
				boolean includeQualifiers,
				boolean includeClassOrigin ){
	
	CIMInstance ci=new CIMInstance();
	ci.owningClassName=owningClassName;
	ci.aliasName=aliasName;     

	//
	// Process qualifiers
	//
	if (includeQualifiers && qualifiers!=null) {
	    ci.setQualifiers(qualifiers);
	}
	
	if (properties!=null) {
	    Vector v=new Vector();
	    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;
		}
		ci.properties.addElement(prop.clone(includeQualifiers,
				 includeClassOrigin));
	    }
	}
	return(ci);
    }
    
    
}
