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