/* * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Contributor(s): John D. Beatty, Dave Dash, Andre Gerard, F. Hunter, Renaud * Tognelli */ package org.ksoap2.serialization; import java.util.*; /** * A simple dynamic object that can be used to build soap calls without * implementing KvmSerializable * <p/> * Essentially, this is what goes inside the body of a soap envelope - it is the * direct subelement of the body and all further subelements * <p/> * Instead of this this class, custom classes can be used if they implement the * KvmSerializable interface. */ @SuppressWarnings({"rawtypes", "unchecked"}) public class SoapObject extends AttributeContainer implements KvmSerializable { private static final String EMPTY_STRING = ""; /** * The namespace of this soap object. */ protected String namespace; /** * The name of this soap object. */ protected String name; /** * The Vector of properties (can contain PropertyInfo and SoapObject) */ protected Vector properties = new Vector(); // TODO: accessing properties and attributes would work much better if we // kept a list of known properties instead of iterating through the list // each time /** * Creates a new <code>SoapObject</code> instance. */ public SoapObject() { this("",""); } /** * Creates a new <code>SoapObject</code> instance. * * @param namespace * the namespace for the soap object * @param name * the name of the soap object */ public SoapObject(String namespace, String name) { this.namespace = namespace; this.name = name; } public boolean equals(Object obj) { if (!(obj instanceof SoapObject)) { return false; } SoapObject otherSoapObject = (SoapObject) obj; if (!name.equals(otherSoapObject.name) || !namespace.equals(otherSoapObject.namespace)) { return false; } int numProperties = properties.size(); if (numProperties != otherSoapObject.properties.size()) { return false; } // SoapObjects are only considered the same if properties equals and in the same order for (int propIndex = 0; propIndex < numProperties; propIndex++) { Object thisProp = this.properties.elementAt(propIndex); if(!otherSoapObject.isPropertyEqual(thisProp, propIndex)) { return false; } } return attributesAreEqual(otherSoapObject); } /** * Helper function for SoapObject.equals * Checks if a given property and index are the same as in this * * @param otherProp, index * @return */ public boolean isPropertyEqual(Object otherProp, int index) { if(index >= getPropertyCount()) { return false; } Object thisProp = this.properties.elementAt(index); if(otherProp instanceof PropertyInfo && thisProp instanceof PropertyInfo) { // Get both PropertInfos and compare values PropertyInfo otherPropInfo = (PropertyInfo)otherProp; PropertyInfo thisPropInfo = (PropertyInfo)thisProp; return otherPropInfo.getName().equals(thisPropInfo.getName()) && otherPropInfo.getValue().equals(thisPropInfo.getValue()); } else if (otherProp instanceof SoapObject && thisProp instanceof SoapObject) { SoapObject otherPropSoap = (SoapObject)otherProp; SoapObject thisPropSoap = (SoapObject)thisProp; return otherPropSoap.equals(thisPropSoap); } return false; } public String getName() { return name; } public String getNamespace() { return namespace; } /** * @inheritDoc */ public Object getProperty(int index) { Object prop = properties.elementAt(index); if(prop instanceof PropertyInfo) { return ((PropertyInfo)prop).getValue(); } else { return ((SoapObject)prop); } } /** * Get the toString value of the property. * * @param index * @return */ public String getPropertyAsString(int index) { PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index); return propertyInfo.getValue().toString(); } /** * Get the property with the given name * * @throws java.lang.RuntimeException * if the property does not exist */ public Object getProperty(String name) { Integer index = propertyIndex(name); if (index != null) { return getProperty(index.intValue()); } else { throw new RuntimeException("illegal property: " + name); } } /** * Get the toString value of the property. * * @param name * @return */ public String getPropertyAsString(String name) { Integer index = propertyIndex(name); if (index != null) { return getProperty(index.intValue()).toString(); } else { throw new RuntimeException("illegal property: " + name); } } /** * Knows whether the given property exists */ public boolean hasProperty(final String name) { if (propertyIndex(name) != null) { return true; } else { return false; } } /** * Get a property without chance of throwing an exception * * @return the property if it exists; if not, {@link NullSoapObject} is * returned */ public Object getPropertySafely(final String name) { Integer i = propertyIndex(name); if (i != null) { return getProperty(i.intValue()); } else { return new NullSoapObject(); } } /** * Get the toString value of a property without chance of throwing an * exception * * @return the string value of the property if it exists; if not, #EMPTY_STRING is * returned */ public String getPropertySafelyAsString(final String name) { Integer i = propertyIndex(name); if (i != null) { Object foo = getProperty(i.intValue()); if (foo == null) { return EMPTY_STRING; } else { return foo.toString(); } } else { return EMPTY_STRING; } } /** * Get a property without chance of throwing an exception. An object can be * provided to this method; if the property is not found, this object will * be returned. * * @param defaultThing * the object to return if the property is not found * @return the property if it exists; defaultThing if the property does not * exist */ public Object getPropertySafely(final String name, final Object defaultThing) { Integer i = propertyIndex(name); if (i != null) { return getProperty(i.intValue()); } else { return defaultThing; } } /** * Get the toString value of a property without chance of throwing an * exception. An object can be provided to this method; if the property is * not found, this object's string representation will be returned. * * @param defaultThing * toString of the object to return if the property is not found * @return the property toString if it exists; defaultThing toString if the * property does not exist, if the defaultThing is null #EMPTY_STRING * is returned */ public String getPropertySafelyAsString(final String name, final Object defaultThing) { Integer i = propertyIndex(name); if (i != null) { Object property = getProperty(i.intValue()); if (property != null) { return property.toString(); } else { return EMPTY_STRING; } } else { if (defaultThing != null) { return defaultThing.toString(); } else { return EMPTY_STRING; } } } /** * Get the primitive property with the given name. * * @param name * @return PropertyInfo containing an empty string if property either complex or empty */ public Object getPrimitiveProperty(final String name){ Integer index = propertyIndex(name); if (index != null){ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); if (propertyInfo.getType()!=SoapObject.class){ return propertyInfo.getValue(); } else { propertyInfo = new PropertyInfo(); propertyInfo.setType(String.class); propertyInfo.setValue(EMPTY_STRING); propertyInfo.setName(name); return (Object) propertyInfo.getValue(); } } else { throw new RuntimeException("illegal property: " + name); } } /** * Get the toString value of the primitive property with the given name. * Returns empty string if property either complex or empty * * @param name * @return the string value of the property */ public String getPrimitivePropertyAsString(final String name){ Integer index = propertyIndex(name); if (index != null){ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); if (propertyInfo.getType()!=SoapObject.class){ return propertyInfo.getValue().toString(); } else { return EMPTY_STRING; } } else { throw new RuntimeException("illegal property: " + name); } } /** * Get the toString value of a primitive property without chance of throwing an * exception * * @param name * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is * returned */ public Object getPrimitivePropertySafely(final String name) { Integer index = propertyIndex(name); if (index != null){ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); if (propertyInfo.getType()!=SoapObject.class){ return propertyInfo.getValue().toString(); } else { propertyInfo = new PropertyInfo(); propertyInfo.setType(String.class); propertyInfo.setValue(EMPTY_STRING); propertyInfo.setName(name); return (Object) propertyInfo.getValue(); } } else { return new NullSoapObject(); } } /** * Get the toString value of a primitive property without chance of throwing an * exception * * @param name * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is * returned */ public String getPrimitivePropertySafelyAsString(final String name) { Integer index = propertyIndex(name); if (index != null){ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); if (propertyInfo.getType()!=SoapObject.class){ return propertyInfo.getValue().toString(); } else { return EMPTY_STRING; } } else { return EMPTY_STRING; } } private Integer propertyIndex(String name) { if (name != null) { for (int i = 0; i < properties.size(); i++) { if (name.equals(((PropertyInfo) properties.elementAt(i)).getName())) { return new Integer(i); } } } return null; } /** * Returns the number of properties * * @return the number of properties */ public int getPropertyCount() { return properties.size(); } /** * Places PropertyInfo of desired property into a designated PropertyInfo * object. Just calls #getPropertyInfo and discards any provided properties. * * @param index * index of desired property * @param properties * this parameter is ignored * @param propertyInfo * designated retainer of desired property */ public void getPropertyInfo(int index, Hashtable properties, PropertyInfo propertyInfo) { getPropertyInfo(index, propertyInfo); } /** * Places PropertyInfo of desired property into a designated PropertyInfo * object * * @param index * index of desired property * @param propertyInfo * designated retainer of desired property */ public void getPropertyInfo(int index, PropertyInfo propertyInfo) { Object element = properties.elementAt(index); if (element instanceof PropertyInfo) { PropertyInfo p = (PropertyInfo) element; propertyInfo.name = p.name; propertyInfo.namespace = p.namespace; propertyInfo.flags = p.flags; propertyInfo.type = p.type; propertyInfo.elementType = p.elementType; propertyInfo.value = p.value; propertyInfo.multiRef = p.multiRef; } else { // SoapObject propertyInfo.name = null; propertyInfo.namespace = null; propertyInfo.flags = 0; propertyInfo.type = null; propertyInfo.elementType = null; propertyInfo.value = element; propertyInfo.multiRef = false; } } /** * Creates a new SoapObject based on this, allows usage of SoapObjects as * templates. One application is to set the expected return type of a soap * call if the server does not send explicit type information. * * @return a copy of this. */ public SoapObject newInstance() { SoapObject o = new SoapObject(namespace, name); for (int propIndex = 0; propIndex < properties.size(); propIndex++) { Object prop = properties.elementAt(propIndex); if(prop instanceof PropertyInfo) { PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(propIndex); PropertyInfo propertyInfoClonned = (PropertyInfo)propertyInfo.clone(); o.addProperty( propertyInfoClonned ); } else if(prop instanceof SoapObject) { o.addSoapObject(((SoapObject)prop).newInstance()); } } for (int attribIndex = 0; attribIndex < getAttributeCount(); attribIndex++) { AttributeInfo newAI = new AttributeInfo(); getAttributeInfo(attribIndex, newAI); AttributeInfo attributeInfo = newAI; // (AttributeInfo) // attributes.elementAt(attribIndex); o.addAttribute(attributeInfo); } return o; } /** * Sets a specified property to a certain value. * * @param index * the index of the specified property * @param value * the new value of the property */ public void setProperty(int index, Object value) { Object prop = properties.elementAt(index); if(prop instanceof PropertyInfo) { ((PropertyInfo) prop).setValue(value); } // TODO: not sure how you want to handle an exception here if the index points to a SoapObject } /** * Adds a property (parameter) to the object. This is essentially a sub * element. * * @param name * The name of the property * @param value * the value of the property */ public SoapObject addProperty(String name, Object value) { PropertyInfo propertyInfo = new PropertyInfo(); propertyInfo.name = name; propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value .getClass(); propertyInfo.value = value; return addProperty(propertyInfo); } /** * Add a property only if the value is not null. * * @param name * @param value * @return */ public SoapObject addPropertyIfValue(String name, Object value) { if (value != null) { return addProperty(name, value); } else { return this; } } /** * Add a property only if the value is not null. * * @param propertyInfo * @param value * @return */ public SoapObject addPropertyIfValue(PropertyInfo propertyInfo, Object value) { if (value != null) { propertyInfo.setValue(value); return addProperty(propertyInfo); } else { return this; } } /** * Adds a property (parameter) to the object. This is essentially a sub * element. * * @param propertyInfo * designated retainer of desired property */ public SoapObject addProperty(PropertyInfo propertyInfo) { properties.addElement(propertyInfo); return this; } /** * Ad the propertyInfo only if the value of it is not null. * * @param propertyInfo * @return */ public SoapObject addPropertyIfValue(PropertyInfo propertyInfo) { if (propertyInfo.value != null) { properties.addElement(propertyInfo); return this; } else { return this; } } /** * Adds a SoapObject the properties array. This is a sub element to * allow nested SoapObjects * * @param soapObject * to be added as a property of the current object */ public SoapObject addSoapObject(SoapObject soapObject) { properties.addElement(soapObject); return this; } /** * Generate a {@code String} describing this object. * * @return */ public String toString() { StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{"); for (int i = 0; i < getPropertyCount(); i++) { Object prop = properties.elementAt(i); if(prop instanceof PropertyInfo) { buf.append(EMPTY_STRING) .append(((PropertyInfo) prop).getName()) .append("=") .append(getProperty(i)) .append("; "); } else { buf.append(((SoapObject) prop).toString()); } } buf.append("}"); return buf.toString(); } }