//
// CIMObjectPath.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 CIMObjectPath.java
// 
// The Initial Developer of the Original Code is David Simons.
//
// Contributor(s):
//
// 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: CIMObjectPath.java,v 1.2 2003/08/12 00:46:28 kumpf Exp $
//
// $Log: CIMObjectPath.java,v $
// Revision 1.2  2003/08/12 00:46:28  kumpf
// HP-BP: Fixed Bug No. 417
//
// Revision 1.1  2003/07/17 18:32:55  kumpf
// HP-BAPU: PEP-64 CIM Client and CIM Listener Java Interface
//
//
// HP Java Client: 1.1  2003/01/09 18:14:30 [ Bapu Patil]
//    - Add support to handle EMPTY XML tags 
//
// Revision 1.2  2001/11/25 20:27:08  david
// - Updates for query/indication support from Adrian
//
// Revision 1.1.1.1  2001/10/31 18:14:30  karl
// import of SNIA 0_7
//
// Revision 1.16  2001/07/22 14:34:28  davidsi
// - From Jean Lamontagne - using toString().hashCode() is potentially
//   problematic because it assumes the keys will be correctly ordered.
//   For the moment it is safer to rely on the objectName.hashCode().
//
// Revision 1.15  2001/07/22 13:29:57  davidsi
// - Further updates from Angela.
//
// Revision 1.14  2001/07/07 16:29:23  davidsi
// - Make the not set value less like to clash with a real value by adding
//   non standard characters to it.
//
// Revision 1.13  2001/07/07 15:47:00  davidsi
// - From Angela Lee. Significant reworking to ensure all values are
//   correctly set - now rather than defaulting to null/"" use
//   an explicit not set value.
//
// Revision 1.12  2001/07/07 13:41:45  davidsi
// - From Daniel Abramovich, fixed equals() method for handling non
//   instance cases.
//
// Revision 1.11  2001/06/23 13:57:20  davidsi
// - Fixed hashCode for CIMObjectPath (Tatsuya Kainuma)
//
// Revision 1.10  2001/03/19 17:34:49  davidsi
// - Key values are now compared as strings without reference to their
//   types.
//
// Revision 1.9  2000/11/06 18:07:39  davidsi
// - Fixed error IDs.
// - Changes to suport the movement of the escaping functionality to
//   CIMOMEscape.
//
// 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/19 16:48:57  davidsi
// - Revised escaping mechanisms for object paths.
//
// Revision 1.6  2000/05/19 14:43:12  davidsi
// - Fixed the assumption that key components will always be in the same
//   order when performing a comparison - they may not be.
// - Convert key values to strings prior to comparing. In some (rare)
//   cases the values may be stored as different types but be
//   representing the same values (strictly one shouldn't need to
//   do this but some providers can be inconsistent about how they
//   type properties)
//
// Revision 1.5  2000/05/11 10:12:20  davidsi
// - Fixed tabs.
//
// Revision 1.4  2000/05/06 17:04:22  davidsi
// - Modifications to bring classes inline with the SUN WBEM APIs 2.2.
//
// Revision 1.3  2000/04/09 13:17:58  davidsi
// - Switched to StringBuffer (from String) for XML generation.
// - Switched to forcing a <KEYBINDING> tag - no longer parse
//   singleton property values without a property name.
//
// 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.util.Vector;
import java.net.MalformedURLException;
import java.io.Serializable;
import java.util.StringTokenizer;
import org.snia.wbemcmd.xml.XMLNode;
import org.snia.wbemcmd.xml.XMLClass;
import org.snia.wbemcmd.xml.XMLCIM;

import org.snia.wbemcmd.cimom.CIMOMEscape;

/** 
    Creates and instantiates a path name of a CIM object.  The CIM object
    path is a reference to a CIM element.  It is only valid in context of
    an active connection to a CIM Server on a host.  In order to uniquely
    identify a given object on a host, a CIM object path contains the
    following parts:

    <ul>
    <li>Host - name of host whose repository contains the object</li>
    <li>NameSpace - the namespace which contains the object</li>
    <li>ClassName - name of the object's class</li>
    <li>KeyBindings - key/value pairs which uniquely identify an instance</li>
    </ul>

    For example, the object path:

    <pre>
    //atp:5989/root/cimv2:TennisPlayer.first="Patrick",last="Rafter"
    </pre>

    has the following parts:

    <ul>
    <li>//atp:9999 - host name and port number 
    <li>/root/cimv2 - namespace 
    <li>TennisPlayer - class name</li>
    <li>first="Patrick",last="Rafter" -  key/value pairs to identify 
                                         the instance</li>
    </ul>

    Note that the host and namespace argument may be empty since object names
    need not necessarily include a namespace path according to the standard.

    Notice that the keys in the object name may appear in any order.
    That is, the following object names refer to the same object:

    <pre>
    TennisPlayer.first="Patrick",last="Rafter"
    TennisPlayer.last="Rafter",first="Patrick"
    </pre>

    And since CIM is not case sensitive, the following refer to the same
    object:

    <pre>
    TennisPlayer.first="Patrick",last="Rafter"
    tennisplayer.FIRST="Patrick",Last="Rafter"
    </pre>

    The CIM standard leaves it an open question whether object paths may have
    spaces around delimiters (like '.', '=', and ','). We assume they cannot.
    So the following is an invalid object path:

    <pre>
    TennisPlayer . first = "Patrick", last="Rafter"
    </pre>

    We require that the '.', '=', and ',' have no spaces around them.

    For reasons of efficiency, the key bindings are internally sorted
    during initialization. This allows the key bindings to be compared
    more easily. This means that when the string is converted back to
    string (by calling toString()) that the keys may have been rearranged.

    If it begins with "//" then we assume the namespace part is present and
    process it that way.

    It should also be noted that an object name may refer to an instance or
    a class. Here is an example of each:

    <pre>
    TennisPlayer.first="Patrick",last="Rafter"
    TennisPlayer
    </pre>

    In the second case--when it refers to a class--the key bindings are
    omitted.
 */

public class CIMObjectPath extends Object implements Serializable {

    ///////////////////////////////////////////////////////////////////////////
    // Public Static Data
    ///////////////////////////////////////////////////////////////////////////

    /** invalid value indicating the data has not been initialized by 
	the user who instantiated the object.  See comment block for 
	constructors below for details.					**/
    public final static String NOT_SET = "NotSet";

    public static final String EMPTY = "";


    ///////////////////////////////////////////////////////////////////////////
    // Private Member Data
    ///////////////////////////////////////////////////////////////////////////

    // The default case (where we lack the host/namespace/objectName 
    // information):
    //
    // 1. can not use the default CIMNameSpace instance since that is
    //    specifically 
    //	  for the local host environment.
    // 2. Sun SDK (and therefore JSR-000048) behavior is to return an 
    //    empty string for getNameSpace() & getHost().
    // 3. For now, we will return an invalid string for uninitialized values so 
    //	  that all the callers can be identified and corrected.
    //
    CIMNameSpace nameSpace = new CIMNameSpace(CIMObjectPath.NOT_SET, 
					      CIMObjectPath.NOT_SET);
    String hostNameString = CIMObjectPath.NOT_SET;
    String namespaceString = CIMObjectPath.NOT_SET;
    String objectName = CIMObjectPath.NOT_SET;
    Vector keys= new Vector();

    
    ///////////////////////////////////////////////////////////////////////////
    // Constructors
    //
    //	[NOTE]: JSR-000048 does not have a good interface to this object and 
    //	it allows its construction without valid host name & namespace data.
    //	1. The caller is responsible for setting the correct host name and 
    //	   namespace before using the constructed instance object.
    //	2. Until that is done, the namespace data will contain NOT_SET strings
    //	   as the host name and namespace.  This of course is an invalid
    //     situation and some operations will not work properly.
    ///////////////////////////////////////////////////////////////////////////
    
 
    /** Default constructor. */
    public CIMObjectPath() {
    }
    
    /**
	CIMObjectPath -  Constructs an object path to access the specified object
     */
    public CIMObjectPath(String on) {
	// If there is a namespace but it will be set via CIMClient on
	// the next call
	this(on, (String)null);
    }
 
    /**
       CIMObjectPath  - Constructs an object path to access the specified object (a
       qualifier or class) in a particular namespace
     */ 
    public CIMObjectPath(String on,String ns) {
	if (on != null)
	    setObjectName(on);
	if (ns != null)
	    setNameSpace(ns);
    } 
 
    /** 
       CIMObjectPath - Constructs a path for an instance. The vector should be
       the keys and the string a class name.
     */ 
    public CIMObjectPath(String iClassName,Vector v) {
	// If there is a namespace but it will be set via CIMClient on
	// the next call
	setObjectName(iClassName);
	keys=v;
    }			   
    
    
    /**
       CIMObjectPath -  Create an instance using a XML definition
      
       @param node The node containing an XML definition of CIMObjectPath
     */
    public CIMObjectPath(org.w3c.dom.Node inNode) {
	XMLNode node=XMLNode.getXMLNode(inNode);
	int token=node.getToken();
	
	try {
	    if (token==XMLNode.XML_ELEMENT_LOCALCLASSPATH 
		|| token==XMLNode.XML_ELEMENT_CLASSPATH) {
		boolean dolocal=false;
		if (node.getToken()!=XMLNode.XML_ELEMENT_LOCALCLASSPATH)
		    dolocal=true;
	
		XMLNode localNameNode=node.mustChildElementChild(XMLNode.XML_ELEMENT_NAMESPACEPATH);
		//
		// Get NameSpace path
		//
		if (!dolocal) {
		    localNameNode.mustElement(XMLNode.XML_ELEMENT_HOST);
		    setHost(localNameNode.getUnescapeText());
		}
		localNameNode=localNameNode.mustNextElement(XMLNode.XML_ELEMENT_LOCALNAMESPACEPATH);
		String readNS=XMLClass.getNameSpace(localNameNode.getChild());
		setNameSpace(readNS);
		node=node.mustNextElement(XMLNode.XML_ELEMENT_CLASSNAME);
		String className=node.mustGetAttribute("Name"); 
		setObjectName(className);
		return;
	    }
	    //
	    // Must be an instance path or an error...
	    //
	    boolean dolocal=false;
	    XMLNode pathNode;
	
	    if (token==XMLNode.XML_ELEMENT_LOCALINSTANCEPATH) {
		dolocal=true;
		node=node.getChild();	    // check for null below!
		pathNode=node;
	    } else if (token==XMLNode.XML_ELEMENT_INSTANCEPATH) {
		node=node.mustChildElement(XMLNode.XML_ELEMENT_NAMESPACEPATH);
		pathNode=node.getChild();
		pathNode.mustElement(XMLNode.XML_ELEMENT_HOST);
		setHost(pathNode.getUnescapeText());
		pathNode=pathNode.getNext();
	    } else
		throw new java.lang.InstantiationError(
				    "Required <INSTANCEPATH> or  <CLASSPATH>");
	
	    //
	    // Make sure nodes are valid before continuing..
	    //
	    if (pathNode==null)
		throw new CIMException(CIMException.CIM_ERR_FAILED,
				    "Required <LOCALNAMESPACEPATH>");
	    if (node==null)
		throw new CIMException(CIMException.CIM_ERR_FAILED,
				    "Required <INSTANCENAME>");
	    //
	    // Get NameSpace path
	    //
	    pathNode.mustElement(XMLNode.XML_ELEMENT_LOCALNAMESPACEPATH);
	    setNameSpace(XMLClass.getNameSpace(pathNode.getChild()));
	    node=node.mustNextElement(XMLNode.XML_ELEMENT_INSTANCENAME);

	    XMLClass.getInstanceName(node,this);
	} catch (CIMException ce) {
	    throw new java.lang.InstantiationError(ce.toString());
	}
    }
    
    
    /** 
       Adds a further key to a path. 
       @param name  name of the key property
       @param value the CIMValue of the key property
     */
    public void addKey(String keyname,CIMValue value) {
	CIMProperty cp=new CIMProperty(keyname,value);
	cp.setType(value.getType());
	keys.addElement(cp);
    }
    
    


    /**
       Returns the keys for this CIMObjectPath object.
       @return Vector of keys
     */
    public Vector getKeys() {
	return(keys);
    }
 
    /** 
       Sets the keys for this path (vector contains CIMProperty instances)

       @param Vector of keys
     */
    public void setKeys(Vector v) {
	if (v!=null)
	    keys=v;
	else
            keys=new Vector(); //fix Bug#417
    }	
    
    
    
    /**
       Returns the current namespace
       @param string namespace name
     */
    public String getNameSpace() {
	return(namespaceString);
    }
    
    /**
        Gets the host for this CIMObjectPath. 
        @return String hostname 
     */
    public String getHost() {
	return(hostNameString);
    }
    
    
    /**
        Returns the object name for this path
        @return String object name can be either a class name or 
		       a qualifier type name. 
     */
    public String getObjectName() {
	return(objectName);
    }
    
    
    
    /***
     * parse
     * 
     * Converts a string representation of an object path
     * 
     * @param instanceName
     *    The object path to convert - assumed to be to an instance
     * @throws
     *    CIMException an error occurred parsing the path
     */ 
    public void parse(String instanceName) throws CIMException {
	char []values=instanceName.toCharArray();
	int i;
	int keystart=0;
	String text;
	int equalspos;
	Vector tmpkeys=new Vector();
	boolean singleKey=true;
	boolean inquote=false;
	String keyvalue;
	boolean nstype=true;
	boolean nshdl=false;
	int classNamePos=0;	    // starting position of classname
	String nameSpaceType=null;  // namespace type
	String nameSpaceHdl=null;   // handle for namespace
	
	singleKey=false;	// set this to TRUE if it is intended that
				// a property value can be specified without
				// its name when there is only one key.
	
	for(i=0;i<values.length;i++) {
	    //
	    // One can come across '.' in the namespace handle because
	    // it may be an IP address
	    //
	    if (values[i]=='.' && !nshdl)
		break;
	    if (values[i]==':') {
		if (nstype) {
		    // found namespace type
		    nameSpaceType=new String(values,0,i);
		    classNamePos=i+1;
		    nstype=false;
		    nshdl=true;
		} else {
		    // found namespace handle
		    nameSpaceHdl=new String(values,classNamePos,i-classNamePos);
		    classNamePos=i+1;
		    nshdl=false;
		}
	    }
	}

	//
	// There must be a '.' in the instance name if it is valid
	// as this delimits the class name
	//
	if (i==values.length)
	    throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" did not have a '.' as a class delimeter");
	
	//
	// If a namespace type was found then there must have also been
	// a handle
	//
	if (!nstype && nshdl) 
	    throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" did not have a namespace handle");

	//
	// Only http namespace types are supported
	if (nameSpaceType!=null && !nameSpaceType.equalsIgnoreCase("HTTP"))
	    throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" does not represent a HTTP namespace");

	    
	String className=new String(values,classNamePos,i-classNamePos);
    
	//
	// Now extract keys
	//
	i++;
	equalspos=0;
	for(keystart=i;i<values.length;i++) {
	    char ch=values[i];
		//
		// Skip escaped characters
		//
	    if (i<values.length-1 && ch=='\\') {
		i++;
		continue;
	    }
	    //
	    // Check for quotes
	    //
	    if (ch=='\"')
		inquote = !inquote;
	    if (inquote)
		continue;
	    
	    if (ch=='=')
		equalspos=i+1;
	    if (ch==',' || (i+1==values.length) ) {
		if ((i+1==values.length) && equalspos==0 && singleKey) {
		    //
		    // This is the special case of when its classname.value
		    //
		    CIMProperty cp=new CIMProperty();
		    cp.setName("test it");
		    //
		    // Generally there will be quotes but for integer values
		    // they are not strictly necessary so check for them
		    //
		    if (values[keystart]!='\"')
			keyvalue=new String(values,keystart,i-keystart+1);
		    else
			keyvalue=new String(values,keystart+1,i-keystart+1-2);
		    cp.setValue(new CIMValue(keyvalue,
					    CIMDataType.getPredefinedType(
					    CIMDataType.STRING)
					    ));
		    cp.setType(CIMDataType.getPredefinedType(
					CIMDataType.STRING));
		    tmpkeys.addElement(cp);
		    break;
		}
		
		if (i+1==values.length)
		    i++;
		
		if (equalspos==0) {
		    throw new CIMException(CIMException.CIM_ERR_NOT_FOUND,
			"Bad key in string ("+instanceName+")");
		}
		String keyprop=new String(values,keystart,equalspos-keystart-1);
		//
		// Generally there will be quotes but for integer values
		// they are not strictly necessary so check for them
		//
		if (values[equalspos]!='\"')
		    keyvalue=new String(values,equalspos,i-equalspos);
		else
		    keyvalue=CIMOMEscape.unEscape(new String(values,equalspos+1,i-equalspos-2));
		i++;
		equalspos=0;
		keystart=i;
		CIMProperty cp=new CIMProperty();
		cp.setName(keyprop);
		cp.setValue(new CIMValue(keyvalue,
				CIMDataType.getPredefinedType(
				CIMDataType.STRING) ));
		cp.setType(CIMDataType.getPredefinedType(
				    CIMDataType.STRING));
		tmpkeys.addElement(cp);
		singleKey=false;
	    }
	}
    
	
	if (nameSpaceHdl!=null) {
	    String hostName=null;
	    String newNameSpace=null;
	    
	    if (!nameSpaceHdl.substring(0,2).equals("//"))
		throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" does not specify a host");
	    StringTokenizer stok=new StringTokenizer(nameSpaceHdl,"/",false);
	    for (int tokenNumber=0;stok.hasMoreTokens();tokenNumber++) {
		String token=stok.nextToken();
		if (tokenNumber==0) {
		    hostName=token;
		} else if (tokenNumber==1) {
		    newNameSpace=token;
		} else {
		    newNameSpace=newNameSpace.concat("/"+token);
		}
	    }
	    if (hostName==null)
		throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" does not specify a host");
	    if (newNameSpace==null)
		throw new CIMException(CIMException.CIM_ERR_INVALID_PARAMETER,
			"Instance name \""+instanceName+"\" does not specify a namespace");
	    setNameSpace(newNameSpace);
	    setHost(hostName);
	} else
	    nameSpace= new CIMNameSpace(CIMObjectPath.NOT_SET, 
					      CIMObjectPath.NOT_SET);
    
	//
	// Now assign the values to this instance
	//
	objectName=className;
	keys=tmpkeys;
    }
 
    /**
       Set the host for this CIMObjectPath object.
       @param String name of the host
     */
    public void setHost(String host) {
	if (host!=null) {
	    hostNameString = host;
	    nameSpace.setHost(host);
	}
    }
    
    /**
       Sets the namespace for the object
       @param String namespace
     */
    public void setNameSpace(String ns) {
	if (ns!=null) {
	    namespaceString = ns;
	    nameSpace.setNameSpace(ns);
	}
    }

    /**
     * Assigns the object name as a string
     * 
     * @param op
     *    The object name to assign
     */
    public void setObjectName(String objectName) {
	this.objectName=objectName;
    }
    
    /** 
       Returns the object name represented by this reference. The returned
       string is formed from the hostname, namespace, classname
       and keybindings defined for this CIMObjectPath object.
       the form of the name is:

            <pre>
                "//" + hostname + "/" + namespace + ":" + classname +"." +
                        (keyname) + "=" (keyvalue) +"," ...
            </pre>

       The building includes the escaping of special characters.
       @return String representation of the CIMObjectPath 
    */
    public String toString() {
	if ( !(keys.isEmpty()) ) {
	    String ret=objectName;
	    
	    for(int i=0;i<keys.size();i++) {
		CIMProperty cp=(CIMProperty)(keys.elementAt(i));
		if (i>0) 
		    ret=ret.concat(","+cp.getName()+"=\""+
				   CIMOMEscape.escape(cp.getValue().getValue().toString())+
				   "\"");
		else
		    ret=ret.concat("."+cp.getName()+"=\""+
				   CIMOMEscape.escape(cp.getValue().getValue().toString())+
				   "\"");
	    }
	    return(ret);
	}
	return(objectName);
    }

 
    /**
       Computes the hash code for this object path
       @return int hastcode 
     */ 
    public int hashCode() {
	return objectName.hashCode();
    }

    /**
        Returns true if this reference is identical to the one given
        by the o argument. Since CIMObjectPaths are normalized when they
        are created, any differences in the ordering of keybindings is accounted
        for as are the case insensitivity characteristics defined by
        the specification
        @param Object CIMObjectPath for comparison
        @return True if the objects are have identical components
     */
    public boolean equals(Object o) {
	if (!(o instanceof CIMObjectPath))
	    return(false);
	CIMObjectPath cop=(CIMObjectPath)o;

	// not the same object name...
	if (! objectName.equalsIgnoreCase(cop.objectName))
	    return(false);
	// different namespace...
	else if (! getNameSpace().equalsIgnoreCase(cop.getNameSpace()))
	    return(false);
	// different host name...
	else if (! getHost().equalsIgnoreCase(cop.getHost()))
	    return(false);

	// both have one or more keys
	if (!keys.isEmpty() && !cop.keys.isEmpty()) {
	    //
	    // An instance path
	    //

	    if (keys.size()!=cop.keys.size()) {
		return(false);
	    }
	    if (keys.size()==1) {
		//
		// Special case for a single, just use class name and
		// value
		//
		CIMProperty cp1=(CIMProperty)(keys.elementAt(0));
		CIMProperty cp2=(CIMProperty)(cop.keys.elementAt(0));
		CIMValue cv1 = cp1.getValue();
		CIMValue cv2 = cp2.getValue();
		if (cv1==null && cv2==null)
		    return(true);
		if (cv1==null || cv2==null)
		    return(false);
		return (cv1.toString().equals(cv2.toString()));
	    }
			
	    int maxNoKeys=keys.size();
	    for(int i=0;i<maxNoKeys;i++) {
		CIMProperty cp1=(CIMProperty)(keys.elementAt(i));
					       
		//
		// In general keys will be in the same order, so
		// try the same index first...
		// 
		CIMProperty cp2=(CIMProperty)(cop.keys.elementAt(i));
		if (!cp1.getName().equalsIgnoreCase(cp2.getName())) {
			//
			// No match so start searching for the 
			// equivalent key
			//
			int j;
	    		for(j=0;j<maxNoKeys;j++) {
				cp2=(CIMProperty)(cop.keys.elementAt(j));
				if (cp1.getName().equalsIgnoreCase(
							cp2.getName()))
					break;
			}
			//
			// Not found, failed to match
			//
			if (j==maxNoKeys)
				return(false);
		}
		CIMValue cv1 = cp1.getValue();
		CIMValue cv2 = cp2.getValue();
		if (cv1==null && cv2==null)
		    continue;
		if (cv1==null || cv2==null)
		    return(false);
		if ( !(cv1.toString().equals(cv2.toString())))
		    return(false);
	    }

	    return(true);
	} else if ( (keys.isEmpty() && !cop.keys.isEmpty()) ||
			(!keys.isEmpty() && cop.keys.isEmpty()) ) {
	    // only one has one or more keys
	    return(false);
	} else {
	    // both are empty
	    return(true);
	}
    }
    
    /**
        Returns true if this reference is identical to the one given
        by the o argument. Since CIMObjectPaths are normalized when they
        are created, any differences in the ordering of keybindings is accounted
        for as are the case insensitivity characteristics defined by
        the specification
        @param Object CIMObjectPath for comparison
        @param boolean ignore Class Origins
        @return True if the objects are have identical components
     */
    public boolean equals(Object o,boolean ignoreClassOrigins) {
	//
	// SUNDIFF - unclear why classorigins would ever be checked!
	//
	return(equals(o));
    }

    /**
       Returns the object name represented by this reference. The returned
       string is formed from the hostname, namespace, classname
       and keybindings defined for this CIMObjectPath object.
       the form of the name is:
       @return String XML representation of the CIMObjectPath 
    */
    public String toXml() throws MalformedURLException {
	StringBuffer ret;
	
	//
	// Instance path
	//
	ret=new StringBuffer("<INSTANCENAME CLASSNAME=\""+
			 objectName+"\">");

	if (keys==null || keys.size()==0) {
	    //
	    // TODO-SUN
	    // This only supports writing instance paths, but does the
	    // sun implementation write class paths as well?
	    //
	    // support for query: empty objects, nothing to project (AS)
	    ret=ret.append("</INSTANCENAME>");
	    return(ret.toString());
	    //throw new MalformedURLException("Bad path to object");
	}
	
	int numkeys=keys.size();
	    //
	    // If keys >1 then must use KEYBINDING - we also use it for
	    // the key==1 case - most implementations can't cope with
	    // a single KEYVALUE without a KEYBINDING element
	    //
	    if (numkeys>0) {	
		for(int i=0;i<numkeys;i++) {
		    CIMProperty cp=(CIMProperty)(keys.elementAt(i));
		    String name=cp.getName();
		    ret=ret.append("<KEYBINDING NAME=\""+name+"\">"
					+outputKEYVALUE(cp)
					+"</KEYBINDING>");
		}
	    }
	    else if (numkeys==1) {
		//
		// Keys =1, so go with KEYVALUE
		//
		CIMProperty cp=(CIMProperty)(keys.elementAt(0));
		ret=ret.append(outputKEYVALUE(cp));
	} else {
		//
		// No keys, so no instances
		// 
		throw new MalformedURLException("No instance path because no keys");
	    }
	    ret=ret.append("</INSTANCENAME>");
	
	return(ret.toString());
    }

    /*
       get the KEYVALUE for property 

        <!ELEMENT KEYVALUE (#PCDATA)>
         <!ATTLIST KEYVALUE
         VALUETYPE (string|boolean|numeric)  'string'>
       @param CIMProperty  cim property
       @return String key value
    */
    private String outputKEYVALUE(CIMProperty cp) throws MalformedURLException	{
	CIMDataType dtype=cp.getType();
	String type;
	
	if (dtype.isArrayType()) 
	    throw new MalformedURLException("An array cannot be a KEY");
	
	if (dtype.isReferenceType()) {
	    //
	    // Is a reference
	    //
	    return(cp.getValue().toXml());
	} else {
	    //
	    // Regular key value
	    int cd=dtype.getType();
	
	    if (cd==CIMDataType.CHAR16 || cd==CIMDataType.DATETIME || cd==CIMDataType.STRING)
		type="string";
	    else if (cd==CIMDataType.BOOLEAN)
		type="boolean";
	    else
		type="numeric";
	    CIMValue keyCIMValue=cp.getValue();
	    if (keyCIMValue==null)
	    {
		throw new MalformedURLException("No key value");
            }

	    // the following -
	    //  String keyvalue=keyCIMValue.getValue().toString();
	    // has been changed as below to handle EMPTY XML value  as
	    //
	    Object cimvalueObj =keyCIMValue.getValue();
      	    String keyvalue=cimvalueObj.toString();
	    return("<KEYVALUE VALUETYPE=\""+type+"\">"+
		        XMLCIM.xmlCIMEscape.XMLEscape(keyvalue)+
		   "</KEYVALUE>");
	}
    }

    //
    // NOT SUN
    //
    

    ///////////////////////////////////////////////////////////////////////////
    // [NOTE]: This is not aligned with JSR-000048, and the instance 
    // returned may not be a valid one if the user has not set the 
    // namespace and hostname correctly!!!
    ///////////////////////////////////////////////////////////////////////////
    

    /***
     * @deprecated
     */
    public CIMNameSpace getFullNameSpace() {
	return(nameSpace);
    }
    
    
    /**
       Main - For testing purposes
     */
    public static void main(String[] args) {
	int i;
        for(i=0;i<args.length;i++) {
            System.err.println("Processing arg#"+i+" : "+args[i]);
            try {
                CIMObjectPath cop=new CIMObjectPath();
                cop.setNameSpace("\\root\\cimv2");
                cop.setHost("myHostName");
                cop.parse(args[i]);
                System.err.println("PATH="+cop.toString());
                CIMNameSpace ns=cop.getFullNameSpace();
                System.err.println("NAMESPACE="+ns.toString());
            } catch (CIMException e) {
                e.printStackTrace(System.err);
                System.err.println("XML parse failed:"+e);
            }
	}
    }
}
