Home | History | Annotate | Download | only in serialization
      1 /*
      2  * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a copy
      5  * of this software and associated documentation files (the "Software"), to deal
      6  * in the Software without restriction, including without limitation the rights
      7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8  * copies of the Software, and to permit persons to whom the Software is
      9  * furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice shall be included in
     12  * all copies or substantial portions of the Software.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     20  * SOFTWARE.
     21  *
     22  * Contributor(s): John D. Beatty, Dave Dash, Andre Gerard, F. Hunter, Renaud
     23  * Tognelli
     24  */
     25 
     26 package org.ksoap2.serialization;
     27 
     28 import java.util.*;
     29 
     30 /**
     31  * A simple dynamic object that can be used to build soap calls without
     32  * implementing KvmSerializable
     33  * <p/>
     34  * Essentially, this is what goes inside the body of a soap envelope - it is the
     35  * direct subelement of the body and all further subelements
     36  * <p/>
     37  * Instead of this this class, custom classes can be used if they implement the
     38  * KvmSerializable interface.
     39  */
     40 
     41 public class SoapObject extends AttributeContainer implements KvmSerializable {
     42 
     43     private static final String EMPTY_STRING = "";
     44     /**
     45      * The namespace of this soap object.
     46      */
     47     protected String namespace;
     48     /**
     49      * The name of this soap object.
     50      */
     51     protected String name;
     52     /**
     53      * The Vector of properties (can contain PropertyInfo and SoapObject)
     54      */
     55     protected Vector properties = new Vector();
     56 
     57     // TODO: accessing properties and attributes would work much better if we
     58     // kept a list of known properties instead of iterating through the list
     59     // each time
     60 
     61     /**
     62      * Creates a new <code>SoapObject</code> instance.
     63      */
     64 
     65     public SoapObject() {
     66         this("", "");
     67     }
     68 
     69     /**
     70      * Creates a new <code>SoapObject</code> instance.
     71      *
     72      * @param namespace
     73      *            the namespace for the soap object
     74      * @param name
     75      *            the name of the soap object
     76      */
     77 
     78     public SoapObject(String namespace, String name) {
     79         this.namespace = namespace;
     80         this.name = name;
     81     }
     82 
     83     public boolean equals(Object obj) {
     84         if (!(obj instanceof SoapObject)) {
     85             return false;
     86         }
     87 
     88         SoapObject otherSoapObject = (SoapObject) obj;
     89 
     90         if (!name.equals(otherSoapObject.name)
     91                 || !namespace.equals(otherSoapObject.namespace)) {
     92             return false;
     93         }
     94 
     95         int numProperties = properties.size();
     96         if (numProperties != otherSoapObject.properties.size()) {
     97             return false;
     98         }
     99 
    100         // SoapObjects are only considered the same if properties equals and in the same order
    101         for (int propIndex = 0; propIndex < numProperties; propIndex++) {
    102             Object thisProp = this.properties.elementAt(propIndex);
    103             if (!otherSoapObject.isPropertyEqual(thisProp, propIndex)) {
    104                 return false;
    105             }
    106         }
    107 
    108         return attributesAreEqual(otherSoapObject);
    109     }
    110 
    111     /**
    112      * Helper function for SoapObject.equals
    113      * Checks if a given property and index are the same as in this
    114      *
    115      *  @param otherProp, index
    116      *  @return
    117      */
    118     public boolean isPropertyEqual(Object otherProp, int index) {
    119         if (index >= getPropertyCount()) {
    120             return false;
    121         }
    122         Object thisProp = this.properties.elementAt(index);
    123         if (otherProp instanceof PropertyInfo &&
    124                 thisProp instanceof PropertyInfo) {
    125             // Get both PropertInfos and compare values
    126             PropertyInfo otherPropInfo = (PropertyInfo) otherProp;
    127             PropertyInfo thisPropInfo = (PropertyInfo) thisProp;
    128             return otherPropInfo.getName().equals(thisPropInfo.getName()) &&
    129                     otherPropInfo.getValue().equals(thisPropInfo.getValue());
    130         } else if (otherProp instanceof SoapObject && thisProp instanceof SoapObject) {
    131             SoapObject otherPropSoap = (SoapObject) otherProp;
    132             SoapObject thisPropSoap = (SoapObject) thisProp;
    133             return otherPropSoap.equals(thisPropSoap);
    134         }
    135         return false;
    136     }
    137 
    138     public String getName() {
    139         return name;
    140     }
    141 
    142     public String getNamespace() {
    143         return namespace;
    144     }
    145 
    146     /**
    147      * @inheritDoc
    148      */
    149     public Object getProperty(int index) {
    150         Object prop = properties.elementAt(index);
    151         if (prop instanceof PropertyInfo) {
    152             return ((PropertyInfo) prop).getValue();
    153         } else {
    154             return ((SoapObject) prop);
    155         }
    156     }
    157 
    158     /**
    159      * Get the toString value of the property.
    160      *
    161      * @param index
    162      * @return
    163      */
    164     public String getPropertyAsString(int index) {
    165         PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index);
    166         return propertyInfo.getValue().toString();
    167     }
    168 
    169     /**
    170      * Get the property with the given name
    171      *
    172      * @throws java.lang.RuntimeException
    173      *             if the property does not exist
    174      */
    175     public Object getProperty(String name) {
    176         Integer index = propertyIndex(name);
    177         if (index != null) {
    178             return getProperty(index.intValue());
    179         } else {
    180             throw new RuntimeException("illegal property: " + name);
    181         }
    182     }
    183 
    184     /**
    185      * Get the toString value of the property.
    186      *
    187      * @param name
    188      * @return
    189      */
    190 
    191     public String getPropertyAsString(String name) {
    192         Integer index = propertyIndex(name);
    193         if (index != null) {
    194             return getProperty(index.intValue()).toString();
    195         } else {
    196             throw new RuntimeException("illegal property: " + name);
    197         }
    198     }
    199 
    200     /**
    201      * Knows whether the given property exists
    202      */
    203     public boolean hasProperty(final String name) {
    204         if (propertyIndex(name) != null) {
    205             return true;
    206         } else {
    207             return false;
    208         }
    209     }
    210 
    211     /**
    212      * Get a property without chance of throwing an exception
    213      *
    214      * @return the property if it exists; if not, {@link NullSoapObject} is
    215      *         returned
    216      */
    217     public Object getPropertySafely(final String name) {
    218         Integer i = propertyIndex(name);
    219         if (i != null) {
    220             return getProperty(i.intValue());
    221         } else {
    222             return new NullSoapObject();
    223         }
    224     }
    225 
    226     /**
    227      * Get the toString value of a property without chance of throwing an
    228      * exception
    229      *
    230      * @return the string value of the property if it exists; if not, #EMPTY_STRING is
    231      *         returned
    232      */
    233     public String getPropertySafelyAsString(final String name) {
    234         Integer i = propertyIndex(name);
    235         if (i != null) {
    236             Object foo = getProperty(i.intValue());
    237             if (foo == null) {
    238                 return EMPTY_STRING;
    239             } else {
    240                 return foo.toString();
    241             }
    242         } else {
    243             return EMPTY_STRING;
    244         }
    245     }
    246 
    247     /**
    248      * Get a property without chance of throwing an exception. An object can be
    249      * provided to this method; if the property is not found, this object will
    250      * be returned.
    251      *
    252      * @param defaultThing
    253      *            the object to return if the property is not found
    254      * @return the property if it exists; defaultThing if the property does not
    255      *         exist
    256      */
    257     public Object getPropertySafely(final String name, final Object defaultThing) {
    258         Integer i = propertyIndex(name);
    259         if (i != null) {
    260             return getProperty(i.intValue());
    261         } else {
    262             return defaultThing;
    263         }
    264     }
    265 
    266     /**
    267      * Get the toString value of a property without chance of throwing an
    268      * exception. An object can be provided to this method; if the property is
    269      * not found, this object's string representation will be returned.
    270      *
    271      * @param defaultThing
    272      *            toString of the object to return if the property is not found
    273      * @return the property toString if it exists; defaultThing toString if the
    274      *         property does not exist, if the defaultThing is null #EMPTY_STRING
    275      *         is returned
    276      */
    277     public String getPropertySafelyAsString(final String name,
    278             final Object defaultThing) {
    279         Integer i = propertyIndex(name);
    280         if (i != null) {
    281             Object property = getProperty(i.intValue());
    282             if (property != null) {
    283                 return property.toString();
    284             } else {
    285                 return EMPTY_STRING;
    286             }
    287         } else {
    288             if (defaultThing != null) {
    289                 return defaultThing.toString();
    290             } else {
    291                 return EMPTY_STRING;
    292             }
    293         }
    294     }
    295 
    296     /**
    297      * Get the primitive property with the given name.
    298      *
    299      * @param name
    300      * @return PropertyInfo containing an empty string if property either complex or empty
    301      */
    302     public Object getPrimitiveProperty(final String name) {
    303         Integer index = propertyIndex(name);
    304         if (index != null) {
    305             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
    306             if (propertyInfo.getType() != SoapObject.class) {
    307                 return propertyInfo.getValue();
    308             } else {
    309                 propertyInfo = new PropertyInfo();
    310                 propertyInfo.setType(String.class);
    311                 propertyInfo.setValue(EMPTY_STRING);
    312                 propertyInfo.setName(name);
    313                 return (Object) propertyInfo.getValue();
    314             }
    315         } else {
    316             throw new RuntimeException("illegal property: " + name);
    317         }
    318     }
    319 
    320     /**
    321      * Get the toString value of the primitive property with the given name.
    322      * Returns empty string if property either complex or empty
    323      *
    324      * @param name
    325      * @return the string value of the property
    326      */
    327     public String getPrimitivePropertyAsString(final String name) {
    328         Integer index = propertyIndex(name);
    329         if (index != null) {
    330             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
    331             if (propertyInfo.getType() != SoapObject.class) {
    332                 return propertyInfo.getValue().toString();
    333             } else {
    334                 return EMPTY_STRING;
    335             }
    336         } else {
    337             throw new RuntimeException("illegal property: " + name);
    338         }
    339     }
    340 
    341     /**
    342      * Get the toString value of a primitive property without chance of throwing an
    343      * exception
    344      *
    345      * @param name
    346      * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
    347      *         returned
    348      */
    349     public Object getPrimitivePropertySafely(final String name) {
    350         Integer index = propertyIndex(name);
    351         if (index != null) {
    352             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
    353             if (propertyInfo.getType() != SoapObject.class) {
    354                 return propertyInfo.getValue().toString();
    355             } else {
    356                 propertyInfo = new PropertyInfo();
    357                 propertyInfo.setType(String.class);
    358                 propertyInfo.setValue(EMPTY_STRING);
    359                 propertyInfo.setName(name);
    360                 return (Object) propertyInfo.getValue();
    361             }
    362         } else {
    363             return new NullSoapObject();
    364         }
    365     }
    366 
    367     /**
    368      * Get the toString value of a primitive property without chance of throwing an
    369      * exception
    370      *
    371      * @param name
    372      * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
    373      *         returned
    374      */
    375     public String getPrimitivePropertySafelyAsString(final String name) {
    376         Integer index = propertyIndex(name);
    377         if (index != null) {
    378             PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
    379             if (propertyInfo.getType() != SoapObject.class) {
    380                 return propertyInfo.getValue().toString();
    381             } else {
    382                 return EMPTY_STRING;
    383             }
    384         } else {
    385             return EMPTY_STRING;
    386         }
    387     }
    388 
    389     private Integer propertyIndex(String name) {
    390         if (name != null) {
    391             for (int i = 0; i < properties.size(); i++) {
    392                 if (name.equals(((PropertyInfo) properties.elementAt(i)).getName())) {
    393                     return new Integer(i);
    394                 }
    395             }
    396         }
    397         return null;
    398     }
    399 
    400     /**
    401      * Returns the number of properties
    402      *
    403      * @return the number of properties
    404      */
    405     public int getPropertyCount() {
    406         return properties.size();
    407     }
    408 
    409     /**
    410      * Places PropertyInfo of desired property into a designated PropertyInfo
    411      * object. Just calls #getPropertyInfo and discards any provided properties.
    412      *
    413      * @param index
    414      *            index of desired property
    415      * @param properties
    416      *            this parameter is ignored
    417      * @param propertyInfo
    418      *            designated retainer of desired property
    419      */
    420     public void getPropertyInfo(int index, Hashtable properties, PropertyInfo propertyInfo) {
    421         getPropertyInfo(index, propertyInfo);
    422     }
    423 
    424     /**
    425      * Places PropertyInfo of desired property into a designated PropertyInfo
    426      * object
    427      *
    428      * @param index
    429      *            index of desired property
    430      * @param propertyInfo
    431      *            designated retainer of desired property
    432      */
    433     public void getPropertyInfo(int index, PropertyInfo propertyInfo) {
    434         Object element = properties.elementAt(index);
    435         if (element instanceof PropertyInfo) {
    436             PropertyInfo p = (PropertyInfo) element;
    437             propertyInfo.name = p.name;
    438             propertyInfo.namespace = p.namespace;
    439             propertyInfo.flags = p.flags;
    440             propertyInfo.type = p.type;
    441             propertyInfo.elementType = p.elementType;
    442             propertyInfo.value = p.value;
    443             propertyInfo.multiRef = p.multiRef;
    444         } else {
    445             // SoapObject
    446             propertyInfo.name = null;
    447             propertyInfo.namespace = null;
    448             propertyInfo.flags = 0;
    449             propertyInfo.type = null;
    450             propertyInfo.elementType = null;
    451             propertyInfo.value = element;
    452             propertyInfo.multiRef = false;
    453         }
    454     }
    455 
    456     /**
    457      * Creates a new SoapObject based on this, allows usage of SoapObjects as
    458      * templates. One application is to set the expected return type of a soap
    459      * call if the server does not send explicit type information.
    460      *
    461      * @return a copy of this.
    462      */
    463     public SoapObject newInstance() {
    464         SoapObject o = new SoapObject(namespace, name);
    465         for (int propIndex = 0; propIndex < properties.size(); propIndex++) {
    466             Object prop = properties.elementAt(propIndex);
    467             if (prop instanceof PropertyInfo) {
    468                 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(propIndex);
    469                 PropertyInfo propertyInfoClonned = (PropertyInfo) propertyInfo.clone();
    470                 o.addProperty(propertyInfoClonned);
    471             } else if (prop instanceof SoapObject) {
    472                 o.addSoapObject(((SoapObject) prop).newInstance());
    473             }
    474         }
    475         for (int attribIndex = 0; attribIndex < getAttributeCount(); attribIndex++) {
    476             AttributeInfo newAI = new AttributeInfo();
    477             getAttributeInfo(attribIndex, newAI);
    478             AttributeInfo attributeInfo = newAI; // (AttributeInfo)
    479                                                  // attributes.elementAt(attribIndex);
    480             o.addAttribute(attributeInfo);
    481         }
    482         return o;
    483     }
    484 
    485     /**
    486      * Sets a specified property to a certain value.
    487      *
    488      * @param index
    489      *            the index of the specified property
    490      * @param value
    491      *            the new value of the property
    492      */
    493     public void setProperty(int index, Object value) {
    494         Object prop = properties.elementAt(index);
    495         if (prop instanceof PropertyInfo) {
    496             ((PropertyInfo) prop).setValue(value);
    497         }
    498         // TODO: not sure how you want to handle an exception here if the index points to a SoapObject
    499     }
    500 
    501     /**
    502      * Adds a property (parameter) to the object. This is essentially a sub
    503      * element.
    504      *
    505      * @param name
    506      *            The name of the property
    507      * @param value
    508      *            the value of the property
    509      */
    510     public SoapObject addProperty(String name, Object value) {
    511         PropertyInfo propertyInfo = new PropertyInfo();
    512         propertyInfo.name = name;
    513         propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
    514                 .getClass();
    515         propertyInfo.value = value;
    516         propertyInfo.namespace = namespace;
    517         ///M: HS20 modify by Jungo
    518         return addProperty(propertyInfo);
    519     }
    520 
    521     /**
    522      * Add a property only if the value is not null.
    523      *
    524      * @param name
    525      * @param value
    526      * @return
    527      */
    528     public SoapObject addPropertyIfValue(String name, Object value) {
    529         if (value != null) {
    530             return addProperty(name, value);
    531         } else {
    532             return this;
    533         }
    534     }
    535 
    536     /**
    537      * Add a property only if the value is not null.
    538      *
    539      * @param propertyInfo
    540      * @param value
    541      * @return
    542      */
    543     public SoapObject addPropertyIfValue(PropertyInfo propertyInfo, Object value) {
    544         if (value != null) {
    545             propertyInfo.setValue(value);
    546             return addProperty(propertyInfo);
    547         } else {
    548             return this;
    549         }
    550     }
    551 
    552     /**
    553      * Adds a property (parameter) to the object. This is essentially a sub
    554      * element.
    555      *
    556      * @param propertyInfo
    557      *            designated retainer of desired property
    558      */
    559     public SoapObject addProperty(PropertyInfo propertyInfo) {
    560         properties.addElement(propertyInfo);
    561         return this;
    562     }
    563 
    564     /**
    565      * Ad the propertyInfo only if the value of it is not null.
    566      *
    567      * @param propertyInfo
    568      * @return
    569      */
    570     public SoapObject addPropertyIfValue(PropertyInfo propertyInfo) {
    571         if (propertyInfo.value != null) {
    572             properties.addElement(propertyInfo);
    573             return this;
    574         } else {
    575             return this;
    576         }
    577     }
    578 
    579     /**
    580      * Adds a SoapObject the properties array. This is a sub element to
    581      * allow nested SoapObjects
    582      *
    583      * @param soapObject
    584      *            to be added as a property of the current object
    585      */
    586     public SoapObject addSoapObject(SoapObject soapObject) {
    587         properties.addElement(soapObject);
    588         return this;
    589     }
    590 
    591     /**
    592      * Generate a {@code String} describing this object.
    593      *
    594      * @return
    595      */
    596     public String toString() {
    597         StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{");
    598         for (int i = 0; i < getPropertyCount(); i++) {
    599             Object prop = properties.elementAt(i);
    600             if (prop instanceof PropertyInfo) {
    601                 buf.append(EMPTY_STRING)
    602                         .append(((PropertyInfo) prop).getName())
    603                         .append("=")
    604                         .append(getProperty(i))
    605                         .append("; ");
    606             } else {
    607                 buf.append(((SoapObject) prop).toString());
    608             }
    609         }
    610         buf.append("}");
    611         return buf.toString();
    612     }
    613 }
    614