1From b5b548a4be9f1ced6f5599b62765216f9ab8af00 Mon Sep 17 00:00:00 2001 2From: Ecco Park <eccopark@google.com> 3Date: Tue, 8 May 2018 13:44:14 -0700 4Subject: [PATCH 1/1] ksoap2 update 5 6Change-Id: Iace4c0f3cb31c9532c5fa0c44c2dc863bd81b23e 7Signed-off-by: Ecco Park <eccopark@google.com> 8--- 9 .../main/java/org/ksoap2/SoapEnvelope.java | 17 +- 10 .../src/main/java/org/ksoap2/SoapFault12.java | 13 +- 11 .../serialization/AttributeContainer.java | 128 +++++- 12 .../java/org/ksoap2/serialization/DM.java | 57 ++- 13 .../ksoap2/serialization/HasAttributes.java | 16 + 14 .../ksoap2/serialization/HasInnerText.java | 17 + 15 .../ksoap2/serialization/KvmSerializable.java | 25 +- 16 .../org/ksoap2/serialization/Marshal.java | 3 +- 17 .../ksoap2/serialization/MarshalBase64.java | 3 +- 18 .../org/ksoap2/serialization/MarshalDate.java | 3 +- 19 .../serialization/MarshalHashtable.java | 8 +- 20 .../org/ksoap2/serialization/SoapObject.java | 340 +++++++++++++- 21 .../ksoap2/serialization/SoapPrimitive.java | 20 +- 22 .../SoapSerializationEnvelope.java | 419 ++++++++++++------ 23 .../org/ksoap2/serialization/ValueWriter.java | 13 + 24 .../ksoap2/transport/ServiceConnection.java | 13 +- 25 .../java/org/ksoap2/transport/Transport.java | 131 ++++-- 26 .../ksoap2/serialization/MarshalFloat.java | 3 +- 27 .../transport/HttpResponseException.java | 60 +++ 28 .../org/ksoap2/transport/HttpTransportSE.java | 348 +++++++-------- 29 .../transport/HttpsServiceConnectionSE.java | 58 ++- 30 .../ksoap2/transport/HttpsTransportSE.java | 81 ++-- 31 .../transport/KeepAliveHttpsTransportSE.java | 20 +- 32 .../ksoap2/transport/ServiceConnectionSE.java | 44 +- 33 24 files changed, 1298 insertions(+), 542 deletions(-) 34 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java 35 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java 36 create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java 37 create mode 100644 ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java 38 39diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java 40index 8a0b894..1c43656 100644 41--- a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java 42+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java 43@@ -54,10 +54,8 @@ public class SoapEnvelope { 44 /** Namespace constant: http://www.w3.org/1999/XMLSchema */ 45 public static final String XSI1999 = "http://www.w3.org/1999/XMLSchema-instance"; 46 47- //public static final String NS20 = "http://www.wi-fi-org/specifications/hotspot2dot0/spp/1.0/"; 48 public static final String NS20 = "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"; 49 50- //public static final String OMADM12 = "http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd"; 51 52 /** 53 * Returns true for the string values "1" and "true", ignoring upper/lower 54@@ -105,9 +103,8 @@ public class SoapEnvelope { 55 /** Xml Schema data namespace, set by the constructor */ 56 public String xsd; 57 58- ///M: HS20 Add by Jungo 59+ // HS20 change 60 public String ns; 61- public String omadm; 62 63 /** 64 * Initializes a SOAP Envelope. The version parameter must be set to one of 65@@ -129,10 +126,8 @@ public class SoapEnvelope { 66 enc = SoapEnvelope.ENC2003; 67 env = SoapEnvelope.ENV2003; 68 } 69- 70+ // HS20 change 71 ns = SoapEnvelope.NS20; 72- //omadm = SoapEnvelope.OMADM12; 73- 74 } 75 76 /** Parses the SOAP envelope from the given parser */ 77@@ -206,13 +201,9 @@ public class SoapEnvelope { 78 * given XML writer. 79 */ 80 public void write(XmlSerializer writer) throws IOException { 81- ///M: HS20 modify by Jungo 82- //writer.setPrefix("i", xsi); 83- //writer.setPrefix("d", xsd); 84- //writer.setPrefix("c", enc); 85- writer.setPrefix("soap", env);//the prefix for namespace env in xml output 86+ // HS 2.0 changes 87+ writer.setPrefix("soap", env); //the prefix for namespace env in xml output 88 writer.setPrefix("spp", ns); 89- //writer.setPrefix("omadm", omadm); 90 91 writer.startTag(env, "Envelope"); 92 writer.startTag(env, "Header"); 93diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java 94index 5667cb4..3f39147 100644 95--- a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java 96+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java 97@@ -72,27 +72,28 @@ public class SoapFault12 extends SoapFault { 98 99 while (parser.nextTag() == XmlPullParser.START_TAG) { 100 String name = parser.getName(); 101+ String namespace = parser.getNamespace(); 102 parser.nextTag(); 103- if (name.equals("Code")) { 104+ if (name.toLowerCase().equals("Code".toLowerCase())) { 105 this.Code = new Node(); 106 this.Code.parse(parser); 107- } else if (name.equals("Reason")) { 108+ } else if (name.toLowerCase().equals("Reason".toLowerCase())) { 109 this.Reason = new Node(); 110 this.Reason.parse(parser); 111- } else if (name.equals("Node")) { 112+ } else if (name.toLowerCase().equals("Node".toLowerCase())) { 113 this.Node = new Node(); 114 this.Node.parse(parser); 115- } else if (name.equals("Role")) { 116+ } else if (name.toLowerCase().equals("Role".toLowerCase())) { 117 this.Role = new Node(); 118 this.Role.parse(parser); 119- } else if (name.equals("Detail")) { 120+ } else if (name.toLowerCase().equals("Detail".toLowerCase())) { 121 this.Detail = new Node(); 122 this.Detail.parse(parser); 123 } else { 124 throw new RuntimeException("unexpected tag:" + name); 125 } 126 127- parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, name); 128+ parser.require(XmlPullParser.END_TAG, namespace, name); 129 } 130 parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, "Fault"); 131 parser.nextTag(); 132diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java 133index 6b83847..34d2723 100644 134--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java 135+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java 136@@ -3,8 +3,8 @@ package org.ksoap2.serialization; 137 138 import java.util.Vector; 139 140-public class AttributeContainer { 141- private Vector attributes = new Vector(); 142+public class AttributeContainer implements HasAttributes{ 143+ protected Vector attributes = new Vector(); 144 145 /** 146 * Places AttributeInfo of desired attribute into a designated AttributeInfo object 147@@ -29,9 +29,9 @@ public class AttributeContainer { 148 return ((AttributeInfo) attributes.elementAt(index)).getValue(); 149 } 150 151- /** 152- * Get the attribute's toString value. 153- */ 154+ /** 155+ * Get the attribute's toString value. 156+ */ 157 public String getAttributeAsString(int index) { 158 AttributeInfo attributeInfo = (AttributeInfo) attributes.elementAt(index); 159 return attributeInfo.getValue().toString(); 160@@ -51,6 +51,20 @@ public class AttributeContainer { 161 } 162 } 163 164+ /** 165+ * Get the attribute with the given name 166+ * 167+ * @throws RuntimeException if the attribute does not exist 168+ */ 169+ public Object getAttribute(String namespace,String name) { 170+ Integer i = attributeIndex(namespace,name); 171+ if (i != null) { 172+ return getAttribute(i.intValue()); 173+ } else { 174+ throw new RuntimeException("illegal property: " + name); 175+ } 176+ } 177+ 178 /** 179 * Get the toString value of the attribute with the given name. 180 * 181@@ -65,6 +79,19 @@ public class AttributeContainer { 182 } 183 } 184 185+ /** 186+ * Get the toString value of the attribute with the given name. 187+ * 188+ * @throws RuntimeException if the attribute does not exist 189+ */ 190+ public String getAttributeAsString(String namespace,String name) { 191+ Integer i = attributeIndex(namespace,name); 192+ if (i != null) { 193+ return getAttribute(i.intValue()).toString(); 194+ } else { 195+ throw new RuntimeException("illegal property: " + name); 196+ } 197+ } 198 /** 199 * Knows whether the given attribute exists 200 */ 201@@ -76,6 +103,16 @@ public class AttributeContainer { 202 } 203 } 204 205+ /** 206+ * Knows whether the given attribute exists 207+ */ 208+ public boolean hasAttribute(final String namespace,final String name) { 209+ if (attributeIndex(namespace,name) != null) { 210+ return true; 211+ } else { 212+ return false; 213+ } 214+ } 215 /** 216 * Get an attribute without chance of throwing an exception 217 * 218@@ -91,6 +128,21 @@ public class AttributeContainer { 219 } 220 } 221 222+ /** 223+ * Get an attribute without chance of throwing an exception 224+ * 225+ * @param name the name of the attribute to retrieve 226+ * @return the value of the attribute if it exists; {@code null} if it does not exist 227+ */ 228+ public Object getAttributeSafely(String namespace,String name) { 229+ Integer i = attributeIndex(namespace,name); 230+ if (i != null) { 231+ return getAttribute(i.intValue()); 232+ } else { 233+ return null; 234+ } 235+ } 236+ 237 /** 238 * Get an attributes' toString value without chance of throwing an 239 * exception. 240@@ -108,6 +160,23 @@ public class AttributeContainer { 241 } 242 } 243 244+ /** 245+ * Get an attributes' toString value without chance of throwing an 246+ * exception. 247+ 248+ * @param name 249+ * @return the value of the attribute,s toString method if it exists; "" 250+ * if it does not exist 251+ */ 252+ public Object getAttributeSafelyAsString(String namespace,String name) { 253+ Integer i = attributeIndex(namespace,name); 254+ if (i != null) { 255+ return getAttribute(i.intValue()).toString(); 256+ } else { 257+ return ""; 258+ } 259+ } 260+ 261 private Integer attributeIndex(String name) { 262 for (int i = 0; i < attributes.size(); i++) { 263 if (name.equals(((AttributeInfo) attributes.elementAt(i)).getName())) { 264@@ -117,6 +186,16 @@ public class AttributeContainer { 265 return null; 266 } 267 268+ private Integer attributeIndex(String namespace,String name) { 269+ for (int i = 0; i < attributes.size(); i++) { 270+ AttributeInfo attrInfo=(AttributeInfo) attributes.elementAt(i); 271+ if (name.equals(attrInfo.getName()) && namespace.equals(attrInfo.getNamespace())) { 272+ return new Integer(i); 273+ } 274+ } 275+ return null; 276+ } 277+ 278 /** 279 * Returns the number of attributes 280 * 281@@ -160,13 +239,25 @@ public class AttributeContainer { 282 * @return {@code this} object. 283 */ 284 public void addAttribute(String name, Object value) { 285+ addAttribute(null,name,value); 286+ } 287+ 288+ /** 289+ * Adds a attribute (parameter) to the object. 290+ * 291+ * @param namespace The namespace of the attribute 292+ * @param name The name of the attribute 293+ * @param value the value of the attribute 294+ * @return {@code this} object. 295+ */ 296+ public void addAttribute(String namespace,String name, Object value) { 297 AttributeInfo attributeInfo = new AttributeInfo(); 298 attributeInfo.name = name; 299+ attributeInfo.namespace = namespace; 300 attributeInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass(); 301 attributeInfo.value = value; 302 addAttribute(attributeInfo); 303 } 304- 305 /** 306 * Add an attribute if the value is not null. 307 * @param name 308@@ -178,6 +269,18 @@ public class AttributeContainer { 309 } 310 } 311 312+ /** 313+ * Add an attribute if the value is not null. 314+ * @param namespace The namespace of the attribute 315+ * @param name 316+ * @param value 317+ */ 318+ public void addAttributeIfValue(String namespace,String name, Object value) { 319+ if (value != null) { 320+ addAttribute(namespace,name, value); 321+ } 322+ } 323+ 324 /** 325 * Add a new attribute by providing an {@link AttributeInfo} object. {@code AttributeInfo} 326 * contains all data about the attribute, including name and value.} 327@@ -198,4 +301,17 @@ public class AttributeContainer { 328 attributes.addElement(attributeInfo); 329 } 330 } 331+ 332+ 333+ public void setAttribute(AttributeInfo info) { 334+ 335+ 336+ } 337+ 338+ 339+ public void getAttribute(int index, AttributeInfo info) { 340+ 341+ 342+ } 343+ 344 } 345diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java 346index 78d4449..255126e 100644 347--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java 348+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java 349@@ -20,9 +20,12 @@ 350 351 package org.ksoap2.serialization; 352 353-import java.io.*; 354-import org.xmlpull.v1.*; 355-import org.ksoap2.*; 356+import java.io.IOException; 357+ 358+import org.ksoap2.SoapEnvelope; 359+import org.xmlpull.v1.XmlPullParser; 360+import org.xmlpull.v1.XmlPullParserException; 361+import org.xmlpull.v1.XmlSerializer; 362 363 /** 364 * This class is not public, so save a few bytes by using a short class name (DM 365@@ -30,8 +33,7 @@ import org.ksoap2.*; 366 */ 367 class DM implements Marshal { 368 369- public Object readInstance(XmlPullParser parser, String namespace, String name, 370- PropertyInfo expected) 371+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo excepted) 372 throws IOException, XmlPullParserException { 373 String text = parser.nextText(); 374 switch (name.charAt(0)) { 375@@ -49,9 +51,10 @@ class DM implements Marshal { 376 } 377 378 /** 379- * Write the instance out. In case it is an AttributeContainer write those our first though. 380- * @param writer 381- * the xml serializer. 382+ * Write the instance out. In case it is an AttributeContainer write those our first though. 383+ * If it HasAttributes then write the attributes and values. 384+ * 385+ * @param writer the xml serializer. 386 * @param instance 387 * @throws IOException 388 */ 389@@ -62,11 +65,43 @@ class DM implements Marshal { 390 for (int counter = 0; counter < cnt; counter++) { 391 AttributeInfo attributeInfo = new AttributeInfo(); 392 attributeContainer.getAttributeInfo(counter, attributeInfo); 393- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), 394- attributeInfo.getValue().toString()); 395+ try { 396+ attributeContainer.getAttribute(counter, attributeInfo); 397+ } catch (Exception e) { 398+ e.printStackTrace(); 399+ } 400+ if (attributeInfo.getValue() != null) { 401+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), 402+ (attributeInfo.getValue() != null) ? attributeInfo.getValue().toString() : ""); 403+ } 404+ } 405+ } else if (instance instanceof HasAttributes) { 406+ HasAttributes soapObject = (HasAttributes) instance; 407+ int cnt = soapObject.getAttributeCount(); 408+ for (int counter = 0; counter < cnt; counter++) { 409+ AttributeInfo attributeInfo = new AttributeInfo(); 410+ soapObject.getAttributeInfo(counter, attributeInfo); 411+ try { 412+ soapObject.getAttribute(counter, attributeInfo); 413+ } catch (Exception e) { 414+ e.printStackTrace(); 415+ } 416+ if (attributeInfo.getValue() != null) { 417+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), 418+ attributeInfo.getValue() != null ? attributeInfo.getValue().toString() : ""); 419+ } 420 } 421 } 422- writer.text(instance.toString()); 423+ 424+ if(instance instanceof ValueWriter) 425+ { 426+ ((ValueWriter)instance).write(writer); 427+ } 428+ else 429+ { 430+ writer.text(instance.toString()); 431+ } 432+ 433 } 434 435 public void register(SoapSerializationEnvelope cm) { 436diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java 437new file mode 100644 438index 0000000..b513138 439--- /dev/null 440+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java 441@@ -0,0 +1,16 @@ 442+package org.ksoap2.serialization; 443+ 444+/** 445+ * Common inteface for classes which want to serialize attributes to outgoing soap message 446+ * 447+ * @author robocik 448+ */ 449+public interface HasAttributes { 450+ int getAttributeCount(); 451+ 452+ void getAttributeInfo(int index, AttributeInfo info); 453+ 454+ void getAttribute(int index, AttributeInfo info); 455+ 456+ void setAttribute(AttributeInfo info); 457+} 458diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java 459new file mode 100644 460index 0000000..b35c35b 461--- /dev/null 462+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java 463@@ -0,0 +1,17 @@ 464+package org.ksoap2.serialization; 465+/** 466+ * Interface for classes requiring inner text of xml tags 467+ * 468+ * @author satansly 469+ */ 470+public interface HasInnerText { 471+ /** 472+ * Gets the inner text of xml tags 473+ */ 474+ Object getInnerText(); 475+ 476+ /** 477+ * @param s String to be set as inner text for an outgoing soap object 478+ */ 479+ void setInnerText(Object s); 480+} 481diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java 482index bded0c0..09d7b32 100644 483--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java 484+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java 485@@ -39,31 +39,26 @@ public interface KvmSerializable { 486 */ 487 Object getProperty(int index); 488 489- /** 490- * @return the number of serializable properties 491+ /** 492+ * @return the number of serializable properties 493 */ 494 int getPropertyCount(); 495 496 /** 497 * Sets the property with the given index to the given value. 498- * 499- * @param index 500- * the index to be set 501- * @param value 502- * the value of the property 503+ * 504+ * @param index the index to be set 505+ * @param value the value of the property 506 */ 507 void setProperty(int index, Object value); 508 509 /** 510 * Fills the given property info record. 511- * 512- * @param index 513- * the index to be queried 514- * @param properties 515- * information about the (de)serializer. Not frequently used. 516- * @param info 517- * The return parameter, to be filled with information about the 518- * property with the given index. 519+ * 520+ * @param index the index to be queried 521+ * @param properties information about the (de)serializer. Not frequently used. 522+ * @param info The return parameter, to be filled with information about the 523+ * property with the given index. 524 */ 525 void getPropertyInfo(int index, Hashtable properties, PropertyInfo info); 526 527diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java 528index cfa9d81..100f107 100644 529--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java 530+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java 531@@ -41,8 +41,7 @@ public interface Marshal { 532 * the namespace. 533 * @return the object read from the xml stream. 534 */ 535- public Object readInstance(XmlPullParser parser, String namespace, String name, 536- PropertyInfo expected) 537+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected) 538 throws IOException, XmlPullParserException; 539 540 /** 541diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java 542index 2f8420c..2239027 100644 543--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java 544+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java 545@@ -31,8 +31,7 @@ import org.xmlpull.v1.*; 546 public class MarshalBase64 implements Marshal { 547 public static Class BYTE_ARRAY_CLASS = new byte[0].getClass(); 548 549- public Object readInstance(XmlPullParser parser, String namespace, String name, 550- PropertyInfo expected) 551+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected) 552 throws IOException, XmlPullParserException { 553 return Base64.decode(parser.nextText()); 554 } 555diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java 556index 3e4fa06..489ba3b 100644 557--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java 558+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java 559@@ -31,8 +31,7 @@ import org.ksoap2.kobjects.isodate.*; 560 public class MarshalDate implements Marshal { 561 public static Class DATE_CLASS = new Date().getClass(); 562 563- public Object readInstance(XmlPullParser parser, String namespace, String name, 564- PropertyInfo expected) 565+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected) 566 throws IOException, XmlPullParserException { 567 return IsoDate.stringToDate(parser.nextText(), IsoDate.DATE_TIME); 568 } 569diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java 570index d2367e9..0c6b53e 100644 571--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java 572+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java 573@@ -46,8 +46,7 @@ public class MarshalHashtable implements Marshal { 574 public static final Class HASHTABLE_CLASS = new Hashtable().getClass(); 575 SoapSerializationEnvelope envelope; 576 577- public Object readInstance(XmlPullParser parser, String namespace, String name, 578- PropertyInfo expected) 579+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected) 580 throws IOException, XmlPullParserException { 581 Hashtable instance = new Hashtable(); 582 String elementName = parser.getName(); 583@@ -81,7 +80,7 @@ public class MarshalHashtable implements Marshal { 584 Object key = keys.nextElement(); 585 item.setProperty(0, key); 586 item.setProperty(1, h.get(key)); 587- envelope.writeObjectBody(writer, item); 588+ envelope.writeObjectBodyWithAttributes(writer, item); 589 writer.endTag("", "item"); 590 } 591 } 592@@ -89,7 +88,6 @@ public class MarshalHashtable implements Marshal { 593 class ItemSoapObject extends SoapObject { 594 Hashtable h; 595 int resolvedIndex = -1; 596- 597 ItemSoapObject(Hashtable h) { 598 super(null, null); 599 this.h = h; 600@@ -107,7 +105,7 @@ public class MarshalHashtable implements Marshal { 601 Object resolved = resolvedIndex == 0 ? getProperty(0) : getProperty(1); 602 if (index == 0) { 603 h.put(value, resolved); 604- } else { 605+ } else { 606 h.put(resolved, value); 607 } 608 } 609diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java 610index 24a1ffe..f11210a 100644 611--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java 612+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java 613@@ -38,7 +38,7 @@ import java.util.*; 614 * KvmSerializable interface. 615 */ 616 617-public class SoapObject extends AttributeContainer implements KvmSerializable { 618+public class SoapObject extends AttributeContainer implements KvmSerializable, HasInnerText { 619 620 private static final String EMPTY_STRING = ""; 621 /** 622@@ -54,6 +54,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 623 */ 624 protected Vector properties = new Vector(); 625 626+ protected Object innerText; 627+ 628 // TODO: accessing properties and attributes would work much better if we 629 // kept a list of known properties instead of iterating through the list 630 // each time 631@@ -181,6 +183,230 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 632 } 633 } 634 635+ /** 636+ * Get the property with the given name 637+ * 638+ * return null 639+ * if the property does not exist 640+ */ 641+ public Object getProperty(String namespace,String name) { 642+ Integer index = propertyIndex(namespace,name); 643+ if (index != null) { 644+ return getProperty(index.intValue()); 645+ } 646+ else { 647+ throw new RuntimeException("illegal property: " + name); 648+ } 649+ } 650+ 651+ /** 652+ * Get a property using namespace and name without chance of throwing an exception 653+ * 654+ * @return the property if it exists; if not, {@link NullSoapObject} is 655+ * returned 656+ */ 657+ public Object getPropertyByNamespaceSafely(final String namespace, final String name) { 658+ Integer i = propertyIndex(namespace,name); 659+ if (i != null) { 660+ return getProperty(i.intValue()); 661+ } else { 662+ return new NullSoapObject(); 663+ } 664+ } 665+ 666+ /** 667+ * Get the toString value of a property without chance of throwing an 668+ * exception 669+ * 670+ * @return the string value of the property if it exists; if not, #EMPTY_STRING is 671+ * returned 672+ */ 673+ public String getPropertyByNamespaceSafelyAsString(final String namespace,final String name) { 674+ Integer i = propertyIndex(namespace,name); 675+ if (i != null) { 676+ Object foo = getProperty(i.intValue()); 677+ if (foo == null) { 678+ return EMPTY_STRING; 679+ } else { 680+ return foo.toString(); 681+ } 682+ } else { 683+ return EMPTY_STRING; 684+ } 685+ } 686+ 687+ /** 688+ * Get a property without chance of throwing an exception. An object can be 689+ * provided to this method; if the property is not found, this object will 690+ * be returned. 691+ * 692+ * @param defaultThing 693+ * the object to return if the property is not found 694+ * @return the property if it exists; defaultThing if the property does not 695+ * exist 696+ */ 697+ public Object getPropertySafely(final String namespace,final String name, final Object defaultThing) { 698+ Integer i = propertyIndex(namespace,name); 699+ if (i != null) { 700+ return getProperty(i.intValue()); 701+ } else { 702+ return defaultThing; 703+ } 704+ } 705+ 706+ /** 707+ * Get the toString value of a property without chance of throwing an 708+ * exception. An object can be provided to this method; if the property is 709+ * not found, this object's string representation will be returned. 710+ * 711+ * @param defaultThing 712+ * toString of the object to return if the property is not found 713+ * @return the property toString if it exists; defaultThing toString if the 714+ * property does not exist, if the defaultThing is null #EMPTY_STRING 715+ * is returned 716+ */ 717+ public String getPropertySafelyAsString(final String namespace,final String name, 718+ final Object defaultThing) { 719+ Integer i = propertyIndex(namespace,name); 720+ if (i != null) { 721+ Object property = getProperty(i.intValue()); 722+ if (property != null) { 723+ return property.toString(); 724+ } else { 725+ return EMPTY_STRING; 726+ } 727+ } else { 728+ if (defaultThing != null) { 729+ return defaultThing.toString(); 730+ } else { 731+ return EMPTY_STRING; 732+ } 733+ } 734+ } 735+ 736+ /** 737+ * Get the primitive property with the given name. 738+ * 739+ * @param name 740+ * @return PropertyInfo containing an empty string if property either complex or empty 741+ */ 742+ public Object getPrimitiveProperty(final String namespace,final String name){ 743+ Integer index = propertyIndex(namespace,name); 744+ if (index != null){ 745+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 746+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 747+ return propertyInfo.getValue(); 748+ } else { 749+ propertyInfo = new PropertyInfo(); 750+ propertyInfo.setType(String.class); 751+ propertyInfo.setValue(EMPTY_STRING); 752+ propertyInfo.setName(name); 753+ propertyInfo.setNamespace(namespace); 754+ return (Object) propertyInfo.getValue(); 755+ } 756+ } else { 757+ throw new RuntimeException("illegal property: " + name); 758+ } 759+ } 760+ 761+ /** 762+ * Get the toString value of the primitive property with the given name. 763+ * Returns empty string if property either complex or empty 764+ * 765+ * @param name 766+ * @return the string value of the property 767+ */ 768+ public String getPrimitivePropertyAsString(final String namespace,final String name){ 769+ Integer index = propertyIndex(namespace,name); 770+ if (index != null){ 771+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 772+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 773+ return propertyInfo.getValue().toString(); 774+ } else { 775+ return EMPTY_STRING; 776+ } 777+ } else { 778+ throw new RuntimeException("illegal property: " + name); 779+ } 780+ } 781+ 782+ /** 783+ * Get the toString value of a primitive property without chance of throwing an 784+ * exception 785+ * 786+ * @param name 787+ * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is 788+ * returned 789+ */ 790+ public Object getPrimitivePropertySafely(final String namespace,final String name) { 791+ Integer index = propertyIndex(namespace,name); 792+ if (index != null){ 793+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 794+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 795+ return propertyInfo.getValue().toString(); 796+ } else { 797+ propertyInfo = new PropertyInfo(); 798+ propertyInfo.setType(String.class); 799+ propertyInfo.setValue(EMPTY_STRING); 800+ propertyInfo.setName(name); 801+ propertyInfo.setNamespace(namespace); 802+ return (Object) propertyInfo.getValue(); 803+ } 804+ } else { 805+ return new NullSoapObject(); 806+ } 807+ } 808+ 809+ /** 810+ * Get the toString value of a primitive property without chance of throwing an 811+ * exception 812+ * 813+ * @param name 814+ * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is 815+ * returned 816+ */ 817+ public String getPrimitivePropertySafelyAsString(final String namespace,final String name) { 818+ Integer index = propertyIndex(namespace,name); 819+ if (index != null){ 820+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 821+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 822+ return propertyInfo.getValue().toString(); 823+ } else { 824+ return EMPTY_STRING; 825+ } 826+ } else { 827+ return EMPTY_STRING; 828+ } 829+ } 830+ 831+ /** 832+ * Knows whether the given property exists 833+ */ 834+ public boolean hasProperty(final String namespace,final String name) { 835+ if (propertyIndex(namespace,name) != null) { 836+ return true; 837+ } else { 838+ return false; 839+ } 840+ } 841+ 842+ /** 843+ * Get the toString value of the property. 844+ * 845+ * @param namespace 846+ * @param name 847+ * @return 848+ */ 849+ 850+ public String getPropertyAsString(String namespace,String name) { 851+ Integer index = propertyIndex(namespace,name); 852+ if (index != null) { 853+ return getProperty(index.intValue()).toString(); 854+ } else { 855+ throw new RuntimeException("illegal property: " + name); 856+ } 857+ } 858+ 859 /** 860 * Get the toString value of the property. 861 * 862@@ -301,9 +527,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 863 */ 864 public Object getPrimitiveProperty(final String name) { 865 Integer index = propertyIndex(name); 866- if (index != null) { 867+ if (index != null){ 868 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 869- if (propertyInfo.getType() != SoapObject.class) { 870+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 871 return propertyInfo.getValue(); 872 } else { 873 propertyInfo = new PropertyInfo(); 874@@ -326,9 +552,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 875 */ 876 public String getPrimitivePropertyAsString(final String name) { 877 Integer index = propertyIndex(name); 878- if (index != null) { 879+ if (index != null){ 880 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 881- if (propertyInfo.getType() != SoapObject.class) { 882+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 883 return propertyInfo.getValue().toString(); 884 } else { 885 return EMPTY_STRING; 886@@ -348,9 +574,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 887 */ 888 public Object getPrimitivePropertySafely(final String name) { 889 Integer index = propertyIndex(name); 890- if (index != null) { 891+ if (index != null){ 892 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 893- if (propertyInfo.getType() != SoapObject.class) { 894+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 895 return propertyInfo.getValue().toString(); 896 } else { 897 propertyInfo = new PropertyInfo(); 898@@ -374,9 +600,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 899 */ 900 public String getPrimitivePropertySafelyAsString(final String name) { 901 Integer index = propertyIndex(name); 902- if (index != null) { 903+ if (index != null){ 904 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue()); 905- if (propertyInfo.getType() != SoapObject.class) { 906+ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){ 907 return propertyInfo.getValue().toString(); 908 } else { 909 return EMPTY_STRING; 910@@ -397,6 +623,18 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 911 return null; 912 } 913 914+ 915+ private Integer propertyIndex(String namespace,String name) { 916+ if (name != null && namespace!=null) { 917+ for (int i = 0; i < properties.size(); i++) { 918+ PropertyInfo info= (PropertyInfo) properties.elementAt(i); 919+ if (name.equals(info.getName()) && namespace.equals(info.getNamespace())) { 920+ return new Integer(i); 921+ } 922+ } 923+ } 924+ return null; 925+ } 926 /** 927 * Returns the number of properties 928 * 929@@ -453,6 +691,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 930 } 931 } 932 933+ public PropertyInfo getPropertyInfo(int index) { 934+ Object element = properties.elementAt(index); 935+ if (element instanceof PropertyInfo) { 936+ PropertyInfo p = (PropertyInfo) element; 937+ return p; 938+ } else { 939+ // SoapObject 940+ return null; 941+ } 942+ } 943+ 944 /** 945 * Creates a new SoapObject based on this, allows usage of SoapObjects as 946 * templates. One application is to set the expected return type of a soap 947@@ -466,17 +715,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 948 Object prop = properties.elementAt(propIndex); 949 if (prop instanceof PropertyInfo) { 950 PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(propIndex); 951- PropertyInfo propertyInfoClonned = (PropertyInfo) propertyInfo.clone(); 952- o.addProperty(propertyInfoClonned); 953- } else if (prop instanceof SoapObject) { 954- o.addSoapObject(((SoapObject) prop).newInstance()); 955+ PropertyInfo propertyInfoClonned = (PropertyInfo)propertyInfo.clone(); 956+ o.addProperty( propertyInfoClonned ); 957+ } else if(prop instanceof SoapObject) { 958+ o.addSoapObject(((SoapObject)prop).newInstance()); 959 } 960 } 961 for (int attribIndex = 0; attribIndex < getAttributeCount(); attribIndex++) { 962 AttributeInfo newAI = new AttributeInfo(); 963 getAttributeInfo(attribIndex, newAI); 964 AttributeInfo attributeInfo = newAI; // (AttributeInfo) 965- // attributes.elementAt(attribIndex); 966+ // attributes.elementAt(attribIndex); 967 o.addAttribute(attributeInfo); 968 } 969 return o; 970@@ -513,11 +762,49 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 971 propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value 972 .getClass(); 973 propertyInfo.value = value; 974+ return addProperty(propertyInfo); 975+ } 976+ 977+ /** 978+ * Adds a property (parameter) to the object. This is essentially a sub 979+ * element. 980+ * 981+ * @param namespace 982+ * The namespace of the property 983+ * @param name 984+ * The name of the property 985+ * @param value 986+ * the value of the property 987+ */ 988+ public SoapObject addProperty(String namespace,String name, Object value) { 989+ PropertyInfo propertyInfo = new PropertyInfo(); 990+ propertyInfo.name = name; 991 propertyInfo.namespace = namespace; 992- ///M: HS20 modify by Jungo 993+ propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value 994+ .getClass(); 995+ propertyInfo.value = value; 996 return addProperty(propertyInfo); 997 } 998 999+ /** 1000+ * Add a property only if the value is not null. 1001+ * 1002+ * @param namespace 1003+ * The namespace of the property 1004+ * @param name 1005+ * The name of the property 1006+ * @param value 1007+ * the value of the property 1008+ * @return 1009+ */ 1010+ public SoapObject addPropertyIfValue(String namespace,String name, Object value) { 1011+ if (value != null) { 1012+ return addProperty(namespace,name, value); 1013+ } else { 1014+ return this; 1015+ } 1016+ } 1017+ 1018 /** 1019 * Add a property only if the value is not null. 1020 * 1021@@ -597,12 +884,12 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 1022 StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{"); 1023 for (int i = 0; i < getPropertyCount(); i++) { 1024 Object prop = properties.elementAt(i); 1025- if (prop instanceof PropertyInfo) { 1026+ if(prop instanceof PropertyInfo) { 1027 buf.append(EMPTY_STRING) 1028- .append(((PropertyInfo) prop).getName()) 1029- .append("=") 1030- .append(getProperty(i)) 1031- .append("; "); 1032+ .append(((PropertyInfo) prop).getName()) 1033+ .append("=") 1034+ .append(getProperty(i)) 1035+ .append("; "); 1036 } else { 1037 buf.append(((SoapObject) prop).toString()); 1038 } 1039@@ -610,4 +897,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable { 1040 buf.append("}"); 1041 return buf.toString(); 1042 } 1043+ public Object getInnerText() { 1044+ return innerText; 1045+ } 1046+ 1047+ public void setInnerText(Object innerText) 1048+ { 1049+ this.innerText=innerText; 1050+ } 1051+ 1052+ public void removePropertyInfo(Object info) 1053+ { 1054+ properties.remove(info); 1055+ } 1056 } 1057diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java 1058index d09f7a7..32fe333 100644 1059--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java 1060+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java 1061@@ -34,11 +34,14 @@ package org.ksoap2.serialization; 1062 */ 1063 1064 public class SoapPrimitive extends AttributeContainer { 1065- String namespace; 1066- String name; 1067- String value; 1068+ protected String namespace; 1069+ protected String name; 1070+ protected Object value; 1071 1072- public SoapPrimitive(String namespace, String name, String value) { 1073+ public static final Object NullSkip = new Object(); 1074+ public static final Object NullNilElement = new Object(); 1075+ 1076+ public SoapPrimitive(String namespace, String name, Object value) { 1077 this.namespace = namespace; 1078 this.name = name; 1079 this.value = value; 1080@@ -50,7 +53,7 @@ public class SoapPrimitive extends AttributeContainer { 1081 } 1082 SoapPrimitive p = (SoapPrimitive) o; 1083 boolean varsEqual = name.equals(p.name) 1084- && (namespace == null ? p.namespace == null : namespace.equals(p.namespace)) 1085+ && (namespace == null ? p.namespace == null:namespace.equals(p.namespace)) 1086 && (value == null ? (p.value == null) : value.equals(p.value)); 1087 return varsEqual && attributesAreEqual(p); 1088 } 1089@@ -60,7 +63,7 @@ public class SoapPrimitive extends AttributeContainer { 1090 } 1091 1092 public String toString() { 1093- return value; 1094+ return value != null ? value.toString() : null; 1095 } 1096 1097 public String getNamespace() { 1098@@ -70,4 +73,9 @@ public class SoapPrimitive extends AttributeContainer { 1099 public String getName() { 1100 return name; 1101 } 1102+ 1103+ public Object getValue() { 1104+ return value; 1105+ } 1106+ 1107 } 1108diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java 1109index dae09d2..ceeb3f4 100644 1110--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java 1111+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java 1112@@ -27,9 +27,9 @@ import org.xmlpull.v1.XmlPullParserException; 1113 import org.xmlpull.v1.XmlSerializer; 1114 1115 import java.io.IOException; 1116+import java.util.ArrayList; 1117 import java.util.Hashtable; 1118 import java.util.Vector; 1119-import java.io.ByteArrayOutputStream; 1120 1121 import org.kxml2.io.*; 1122 1123@@ -43,29 +43,29 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1124 protected static final int QNAME_TYPE = 1; 1125 protected static final int QNAME_NAMESPACE = 0; 1126 protected static final int QNAME_MARSHAL = 3; 1127+ protected static final String NULL_LABEL = "null"; 1128+ protected static final String NIL_LABEL = "nil"; 1129+ static final Marshal DEFAULT_MARSHAL = new DM(); 1130 private static final String ANY_TYPE_LABEL = "anyType"; 1131 private static final String ARRAY_MAPPING_NAME = "Array"; 1132- private static final String NULL_LABEL = "null"; 1133- private static final String NIL_LABEL = "nil"; 1134 private static final String HREF_LABEL = "href"; 1135 private static final String ID_LABEL = "id"; 1136 private static final String ROOT_LABEL = "root"; 1137 private static final String TYPE_LABEL = "type"; 1138 private static final String ITEM_LABEL = "item"; 1139 private static final String ARRAY_TYPE_LABEL = "arrayType"; 1140- static final Marshal DEFAULT_MARSHAL = new DM(); 1141 public Hashtable properties = new Hashtable(); 1142- 1143- Hashtable idMap = new Hashtable(); 1144- Vector multiRef; // = new Vector(); 1145- 1146 /** 1147 * Set this variable to true if you don't want that type definitions for complex types/objects 1148 * are automatically generated (with type "anyType") in the XML-Request, if you don't call the 1149 * Method addMapping. This is needed by some Servers which have problems with these type-definitions. 1150 */ 1151 public boolean implicitTypes; 1152- 1153+ /** 1154+ * If set to true then all properties with null value will be skipped from the soap message. 1155+ * If false then null properties will be sent as <element nil="true" /> 1156+ */ 1157+ public boolean skipNullProperties; 1158 /** 1159 * Set this variable to true for compatibility with what seems to be the default encoding for 1160 * .Net-Services. This feature is an extremely ugly hack. A much better option is to change the 1161@@ -96,9 +96,10 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1162 * Set to true to add and ID and ROOT label to the envelope. Change to false for compatibility with WSDL. 1163 */ 1164 protected boolean addAdornments = true; 1165+ Hashtable idMap = new Hashtable(); 1166+ Vector multiRef; // = new Vector(); 1167 1168- public SoapSerializationEnvelope(int version) 1169- { 1170+ public SoapSerializationEnvelope(int version) { 1171 super(version); 1172 addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS); 1173 DEFAULT_MARSHAL.register(this); 1174@@ -107,23 +108,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1175 /** 1176 * @return the addAdornments 1177 */ 1178- public boolean isAddAdornments() 1179- { 1180+ public boolean isAddAdornments() { 1181 return addAdornments; 1182 } 1183 1184 /** 1185- * @param addAdornments 1186- * the addAdornments to set 1187+ * @param addAdornments the addAdornments to set 1188 */ 1189- public void setAddAdornments(boolean addAdornments) 1190- { 1191+ public void setAddAdornments(boolean addAdornments) { 1192 this.addAdornments = addAdornments; 1193 } 1194 1195 /** 1196 * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut will 1197 * cause #writeBody to skip writing anything redundant. 1198+ * 1199 * @param emptyBody 1200 * @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77" 1201 */ 1202@@ -133,8 +132,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1203 } 1204 } 1205 1206- public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException 1207- { 1208+ public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException { 1209 bodyIn = null; 1210 parser.nextTag(); 1211 if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env) 1212@@ -161,10 +159,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1213 } 1214 } 1215 1216- /** Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. */ 1217+ /** 1218+ * Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. 1219+ */ 1220 protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException, 1221- XmlPullParserException 1222- { 1223+ XmlPullParserException { 1224 for (int counter = 0; counter < parser.getAttributeCount(); counter++) { 1225 String attributeName = parser.getAttributeName(counter); 1226 String value = parser.getAttributeValue(counter); 1227@@ -173,11 +172,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1228 readSerializable(parser, (KvmSerializable) obj); 1229 } 1230 1231- /** Read a KvmSerializable. */ 1232+ /** 1233+ * Read a KvmSerializable. 1234+ */ 1235 protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException, 1236- XmlPullParserException 1237- { 1238- while (parser.nextTag() != XmlPullParser.END_TAG) { 1239+ XmlPullParserException { 1240+ int tag = 0; 1241+ try { 1242+ tag = parser.nextTag(); 1243+ } catch (XmlPullParserException e) { 1244+ if(obj instanceof HasInnerText){ 1245+ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : ""); 1246+ } 1247+ tag = parser.nextTag(); 1248+ } 1249+ while (tag != XmlPullParser.END_TAG) { 1250 String name = parser.getName(); 1251 if (!implicitTypes || !(obj instanceof SoapObject)) { 1252 PropertyInfo info = new PropertyInfo(); 1253@@ -188,8 +197,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1254 info.clear(); 1255 obj.getPropertyInfo(i, properties, info); 1256 1257- if ((name.equals(info.name) && info.namespace == null) 1258- || 1259+ if ((name.equals(info.name) && info.namespace == null) || 1260 (name.equals(info.name) && parser.getNamespace().equals(info.namespace))) { 1261 propertyFound = true; 1262 obj.setProperty(i, read(parser, obj, i, null, null, info)); 1263@@ -199,21 +207,42 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1264 if (!propertyFound) { 1265 if (avoidExceptionForUnknownProperty) { 1266 // Dummy loop to read until corresponding END tag 1267- while (parser.next() != XmlPullParser.END_TAG 1268- || !name.equals(parser.getName())) { 1269+ while (parser.next() != XmlPullParser.END_TAG || !name.equals(parser.getName())) { 1270 } 1271 ; 1272 } else { 1273 throw new RuntimeException("Unknown Property: " + name); 1274 } 1275+ } else { 1276+ if (obj instanceof HasAttributes) { 1277+ HasAttributes soapObject = (HasAttributes) obj; 1278+ int cnt = parser.getAttributeCount(); 1279+ for (int counter = 0; counter < cnt; counter++) { 1280+ AttributeInfo attributeInfo = new AttributeInfo(); 1281+ attributeInfo.setName(parser.getAttributeName(counter)); 1282+ attributeInfo.setValue(parser.getAttributeValue(counter)); 1283+ attributeInfo.setNamespace(parser.getAttributeNamespace(counter)); 1284+ attributeInfo.setType(parser.getAttributeType(counter)); 1285+ soapObject.setAttribute(attributeInfo); 1286+ 1287+ } 1288+ } 1289 } 1290 } else { 1291 // I can only make this work for SoapObjects - hence the check above 1292 // I don't understand namespaces well enough to know whether it is correct in the next line... 1293- ((SoapObject) obj).addProperty(parser.getName(), 1294- read(parser, obj, obj.getPropertyCount(), 1295- ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE)); 1296+ ((SoapObject) obj).addProperty(parser.getName(), read(parser, obj, obj.getPropertyCount(), 1297+ ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE)); 1298+ } 1299+ try { 1300+ tag = parser.nextTag(); 1301+ } catch (XmlPullParserException e) { 1302+ if(obj instanceof HasInnerText){ 1303+ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : ""); 1304+ } 1305+ tag = parser.nextTag(); 1306 } 1307+ 1308 } 1309 parser.require(XmlPullParser.END_TAG, null, null); 1310 } 1311@@ -278,9 +307,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1312 } 1313 1314 while (parser.getEventType() != XmlPullParser.END_TAG) { 1315- so.addProperty(parser.getName(), 1316- read(parser, so, so.getPropertyCount(), null, null, 1317- PropertyInfo.OBJECT_TYPE)); 1318+ so.addProperty(parser.getNamespace(),parser.getName(), read(parser, so, so.getPropertyCount(), 1319+ null, null, PropertyInfo.OBJECT_TYPE)); 1320 parser.nextTag(); 1321 } 1322 result = so; 1323@@ -293,12 +321,15 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1324 if (value == null) { 1325 return dflt; 1326 } 1327- return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1, 1328- value.length() - 1)); 1329+ try { 1330+ return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1, 1331+ value.length() - 1)); 1332+ } catch (Exception ex) { 1333+ return dflt; 1334+ } 1335 } 1336 1337- protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) 1338- throws IOException, 1339+ protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) throws IOException, 1340 XmlPullParserException { 1341 String namespace = null; 1342 String name = null; 1343@@ -337,14 +368,23 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1344 parser.require(XmlPullParser.END_TAG, null, null); 1345 } 1346 1347+ /** 1348+ * This method returns id from the href attribute value. 1349+ * By default we assume that href value looks like this: #id so we basically have to remove the first character. 1350+ * But in theory there could be a different value format, like cid:value, etc... 1351+ */ 1352+ protected String getIdFromHref(String hrefValue) { 1353+ return hrefValue.substring(1); 1354+ } 1355+ 1356 /** 1357 * Builds an object from the XML stream. This method is public for usage in conjuction with Marshal 1358 * subclasses. Precondition: On the start tag of the object or property, so href can be read. 1359 */ 1360 1361- public Object read(XmlPullParser parser, Object owner, int index, String namespace, 1362- String name, 1363- PropertyInfo expected) throws IOException, XmlPullParserException { 1364+ public Object read(XmlPullParser parser, Object owner, int index, String namespace, String name, 1365+ PropertyInfo expected) 1366+ throws IOException, XmlPullParserException { 1367 String elementName = parser.getName(); 1368 String href = parser.getAttributeValue(null, HREF_LABEL); 1369 Object obj; 1370@@ -352,7 +392,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1371 if (owner == null) { 1372 throw new RuntimeException("href at root level?!?"); 1373 } 1374- href = href.substring(1); 1375+ href = getIdFromHref(href); 1376 obj = idMap.get(href); 1377 if (obj == null || obj instanceof FwdRef) { 1378 FwdRef f = new FwdRef(); 1379@@ -402,21 +442,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1380 } 1381 // finally, care about the id.... 1382 if (id != null) { 1383- Object hlp = idMap.get(id); 1384- if (hlp instanceof FwdRef) { 1385- FwdRef f = (FwdRef) hlp; 1386- do { 1387- if (f.obj instanceof KvmSerializable) { 1388- ((KvmSerializable) f.obj).setProperty(f.index, obj); 1389- } else { 1390- ((Vector) f.obj).setElementAt(obj, f.index); 1391- } 1392- f = f.next; 1393- } while (f != null); 1394- } else if (hlp != null) { 1395- throw new RuntimeException("double ID"); 1396- } 1397- idMap.put(id, obj); 1398+ resolveReference(id, obj); 1399+ 1400 } 1401 } 1402 1403@@ -424,12 +451,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1404 return obj; 1405 } 1406 1407+ protected void resolveReference(String id, Object obj) { 1408+ Object hlp = idMap.get(id); 1409+ if (hlp instanceof FwdRef) { 1410+ FwdRef f = (FwdRef) hlp; 1411+ do { 1412+ if (f.obj instanceof KvmSerializable) { 1413+ ((KvmSerializable) f.obj).setProperty(f.index, obj); 1414+ } else { 1415+ ((Vector) f.obj).setElementAt(obj, f.index); 1416+ } 1417+ f = f.next; 1418+ } 1419+ while (f != null); 1420+ } else if (hlp != null) { 1421+ throw new RuntimeException("double ID"); 1422+ } 1423+ idMap.put(id, obj); 1424+ } 1425+ 1426 /** 1427 * Returns a new object read from the given parser. If no mapping is found, null is returned. This method 1428 * is used by the SoapParser in order to convert the XML code to Java objects. 1429 */ 1430- public Object readInstance(XmlPullParser parser, String namespace, String name, 1431- PropertyInfo expected) 1432+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected) 1433 throws IOException, XmlPullParserException { 1434 Object obj = qNameToClass.get(new SoapPrimitive(namespace, name, null)); 1435 if (obj == null) { 1436@@ -448,10 +493,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1437 throw new RuntimeException(e.toString()); 1438 } 1439 } 1440+ if (obj instanceof HasAttributes) { 1441+ HasAttributes soapObject = (HasAttributes) obj; 1442+ int cnt = parser.getAttributeCount(); 1443+ for (int counter = 0; counter < cnt; counter++) { 1444+ 1445+ AttributeInfo attributeInfo = new AttributeInfo(); 1446+ attributeInfo.setName(parser.getAttributeName(counter)); 1447+ attributeInfo.setValue(parser.getAttributeValue(counter)); 1448+ attributeInfo.setNamespace(parser.getAttributeNamespace(counter)); 1449+ attributeInfo.setType(parser.getAttributeType(counter)); 1450+ 1451+ soapObject.setAttribute(attributeInfo); 1452+ 1453+ } 1454+ } 1455+ 1456 // ok, obj is now the instance, fill it.... 1457 if (obj instanceof SoapObject) { 1458 readSerializable(parser, (SoapObject) obj); 1459 } else if (obj instanceof KvmSerializable) { 1460+ 1461+ if(obj instanceof HasInnerText){ 1462+ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : ""); 1463+ } 1464 readSerializable(parser, (KvmSerializable) obj); 1465 } else if (obj instanceof Vector) { 1466 readVector(parser, (Vector) obj, expected.elementType); 1467@@ -476,15 +541,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1468 } 1469 if (type instanceof SoapObject) { 1470 SoapObject so = (SoapObject) type; 1471- return new Object[] { 1472- so.getNamespace(), so.getName(), null, null 1473- }; 1474+ return new Object[]{so.getNamespace(), so.getName(), null, null}; 1475 } 1476 if (type instanceof SoapPrimitive) { 1477 SoapPrimitive sp = (SoapPrimitive) type; 1478- return new Object[] { 1479- sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL 1480- }; 1481+ return new Object[]{sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL}; 1482 } 1483 if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) { 1484 Object[] tmp = (Object[]) classToQName.get(((Class) type).getName()); 1485@@ -492,9 +553,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1486 return tmp; 1487 } 1488 } 1489- return new Object[] { 1490- xsd, ANY_TYPE_LABEL, null, null 1491- }; 1492+ return new Object[]{xsd, ANY_TYPE_LABEL, null, null}; 1493 } 1494 1495 /** 1496@@ -503,11 +562,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1497 */ 1498 public void addMapping(String namespace, String name, Class clazz, Marshal marshal) { 1499 qNameToClass 1500- .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz 1501- : marshal); 1502- classToQName.put(clazz.getName(), new Object[] { 1503- namespace, name, null, marshal 1504- }); 1505+ .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz : marshal); 1506+ classToQName.put(clazz.getName(), new Object[]{namespace, name, null, marshal}); 1507 } 1508 1509 /** 1510@@ -528,11 +584,14 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1511 /** 1512 * Response from the soap call. Pulls the object from the wrapper object and returns it. 1513 * 1514- * @since 2.0.3 1515 * @return response from the soap call. 1516 * @throws SoapFault 1517+ * @since 2.0.3 1518 */ 1519 public Object getResponse() throws SoapFault { 1520+ if (bodyIn == null) { 1521+ return null; 1522+ } 1523 if (bodyIn instanceof SoapFault) { 1524 throw (SoapFault) bodyIn; 1525 } 1526@@ -554,8 +613,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1527 /** 1528 * Serializes the request object to the given XmlSerliazer object 1529 * 1530- * @param writer 1531- * XmlSerializer object to write the body into. 1532+ * @param writer XmlSerializer object to write the body into. 1533 */ 1534 public void writeBody(XmlSerializer writer) throws IOException { 1535 // allow an empty body without any tags in it 1536@@ -564,36 +622,48 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1537 multiRef = new Vector(); 1538 multiRef.addElement(bodyOut); 1539 Object[] qName = getInfo(null, bodyOut); 1540- writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 1541- (String) qName[QNAME_TYPE]); //<spp:sppPostDevData 1542- if (dotNet) { 1543- writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]); 1544- } 1545+ 1546+ writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]); 1547+ 1548+ if (dotNet) { 1549+ writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]); 1550+ } 1551+ 1552 if (addAdornments) { 1553 writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]); 1554 writer.attribute(enc, ROOT_LABEL, "1"); 1555 } 1556- writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); //.... 1557- writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], 1558- (String) qName[QNAME_TYPE]);//</spp:sppPostDevData> 1559+ writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); 1560+ writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]); 1561 } 1562 } 1563 1564- /** 1565- * Writes the body of an SoapObject. This method write the attributes and then calls 1566- * "writeObjectBody (writer, (KvmSerializable)obj);" 1567- */ 1568- public void writeObjectBody(XmlSerializer writer, SoapObject obj) throws IOException { 1569- SoapObject soapObject = (SoapObject) obj; 1570+ private void writeAttributes(XmlSerializer writer, HasAttributes obj) throws IOException { 1571+ HasAttributes soapObject = (HasAttributes) obj; 1572 int cnt = soapObject.getAttributeCount(); 1573 for (int counter = 0; counter < cnt; counter++) { 1574 AttributeInfo attributeInfo = new AttributeInfo(); 1575 soapObject.getAttributeInfo(counter, attributeInfo); 1576- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), attributeInfo 1577- .getValue() 1578- .toString()); 1579+ soapObject.getAttribute(counter, attributeInfo); 1580+ if (attributeInfo.getValue() != null) { 1581+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), 1582+ attributeInfo.getValue().toString()); 1583+ } 1584+ } 1585+ } 1586+ 1587+ public void writeArrayListBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException { 1588+ if (obj instanceof HasAttributes) { 1589+ writeAttributes(writer, (HasAttributes) obj); 1590 } 1591- writeObjectBody(writer, (KvmSerializable) obj); 1592+ writeArrayListBody(writer, (ArrayList) obj); 1593+ } 1594+ 1595+ public void writeObjectBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException { 1596+ if (obj instanceof HasAttributes) { 1597+ writeAttributes(writer, (HasAttributes) obj); 1598+ } 1599+ writeObjectBody(writer, obj); 1600 } 1601 1602 /** 1603@@ -614,9 +684,12 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1604 if (!(prop instanceof SoapObject)) { 1605 // prop is a PropertyInfo 1606 if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) { 1607- writer.startTag(propertyInfo.namespace, propertyInfo.name); 1608- writeProperty(writer, obj.getProperty(i), propertyInfo); 1609- writer.endTag(propertyInfo.namespace, propertyInfo.name); 1610+ Object objValue = obj.getProperty(i); 1611+ if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) { 1612+ writer.startTag(propertyInfo.namespace, propertyInfo.name); 1613+ writeProperty(writer, objValue, propertyInfo); 1614+ writer.endTag(propertyInfo.namespace, propertyInfo.name); 1615+ } 1616 } 1617 } else { 1618 // prop is a SoapObject 1619@@ -633,46 +706,47 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1620 name = (String) qName[QNAME_TYPE]; 1621 } 1622 1623- // treat MO data as CDATA 1624- if (name.equals("DevInfo") || name.equals("DevDetail") 1625- || name.equals("PerProviderSubscription") || // format v4 1626- name.equals("MgmtTree") // format v6 1627- ) { 1628- ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1629- XmlSerializer xw = new KXmlSerializer(); 1630- xw.setOutput(bos, "UTF-8"); 1631- xw.startTag((dotNet) ? "" : namespace, name); 1632- if (!implicitTypes) { 1633- String prefix = writer.getPrefix(namespace, true); 1634- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 1635- } 1636- writeObjectBody(xw, nestedSoap); 1637- xw.endTag((dotNet) ? "" : namespace, name); 1638- xw.flush(); 1639- //bos.write('\r'); 1640- //bos.write('\n'); 1641- bos.flush(); 1642- writer.cdsect(bos.toString()); 1643+ // prefer the namespace from the property info 1644+ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) { 1645+ namespace = propertyInfo.namespace; 1646+ } else { 1647+ namespace = (String) qName[QNAME_NAMESPACE]; 1648+ } 1649+ 1650+ writer.startTag(namespace, name); 1651+ if (!implicitTypes) { 1652+ String prefix = writer.getPrefix(namespace, true); 1653+ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 1654+ } 1655+ writeObjectBodyWithAttributes(writer, nestedSoap); 1656+ writer.endTag(namespace, name); 1657+ } 1658+ } 1659+ writeInnerText(writer, obj); 1660+ 1661+ } 1662+ 1663+ private void writeInnerText(XmlSerializer writer, KvmSerializable obj) throws IOException { 1664+ if(obj instanceof HasInnerText){ 1665+ 1666+ Object value=((HasInnerText)obj).getInnerText(); 1667+ if (value != null) { 1668+ if(value instanceof ValueWriter) 1669+ { 1670+ ((ValueWriter)value).write(writer); 1671 } 1672 else 1673 { 1674- writer.startTag((dotNet) ? "" : namespace, name); 1675- if (!implicitTypes) { 1676- String prefix = writer.getPrefix(namespace, true); 1677- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 1678- } 1679- writeObjectBody(writer, nestedSoap); 1680- writer.endTag((dotNet) ? "" : namespace, name); 1681+ writer.cdsect(value.toString()); 1682 } 1683+ 1684 } 1685 } 1686 } 1687 1688- protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) 1689- throws IOException { 1690- if (obj == null) { 1691- ///M: Modify for HS20 1692- //writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true"); 1693+ protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) throws IOException { 1694+ if (obj == null || obj == SoapPrimitive.NullNilElement) { 1695+ writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true"); 1696 return; 1697 } 1698 Object[] qName = getInfo(null, obj); 1699@@ -692,15 +766,19 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1700 } 1701 } 1702 1703- private void writeElement(XmlSerializer writer, Object element, PropertyInfo type, 1704- Object marshal) 1705+ protected void writeElement(XmlSerializer writer, Object element, PropertyInfo type, Object marshal) 1706 throws IOException { 1707 if (marshal != null) { 1708 ((Marshal) marshal).writeInstance(writer, element); 1709- } else if (element instanceof SoapObject) { 1710- writeObjectBody(writer, (SoapObject) element); 1711- } else if (element instanceof KvmSerializable) { 1712- writeObjectBody(writer, (KvmSerializable) element); 1713+ } else if (element instanceof KvmSerializable || element == SoapPrimitive.NullNilElement 1714+ || element == SoapPrimitive.NullSkip) { 1715+ if (element instanceof ArrayList) { 1716+ writeArrayListBodyWithAttributes(writer, (KvmSerializable) element); 1717+ } else { 1718+ writeObjectBodyWithAttributes(writer, (KvmSerializable) element); 1719+ } 1720+ } else if (element instanceof HasAttributes) { 1721+ writeAttributes(writer, (HasAttributes) element); 1722 } else if (element instanceof Vector) { 1723 writeVectorBody(writer, (Vector) element, type.elementType); 1724 } else { 1725@@ -708,6 +786,65 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1726 } 1727 } 1728 1729+ protected void writeArrayListBody(XmlSerializer writer, ArrayList list) 1730+ throws IOException { 1731+ KvmSerializable obj = (KvmSerializable) list; 1732+ int cnt = list.size(); 1733+ PropertyInfo propertyInfo = new PropertyInfo(); 1734+ String namespace; 1735+ String name; 1736+ String type; 1737+ for (int i = 0; i < cnt; i++) { 1738+ // get the property 1739+ Object prop = obj.getProperty(i); 1740+ // and importantly also get the property info which holds the name potentially! 1741+ obj.getPropertyInfo(i, properties, propertyInfo); 1742+ 1743+ if (!(prop instanceof SoapObject)) { 1744+ // prop is a PropertyInfo 1745+ if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) { 1746+ Object objValue = obj.getProperty(i); 1747+ if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) { 1748+ writer.startTag(propertyInfo.namespace, propertyInfo.name); 1749+ writeProperty(writer, objValue, propertyInfo); 1750+ writer.endTag(propertyInfo.namespace, propertyInfo.name); 1751+ } 1752+ } 1753+ } else { 1754+ 1755+ // prop is a SoapObject 1756+ SoapObject nestedSoap = (SoapObject) prop; 1757+ // lets get the info from the soap object itself 1758+ Object[] qName = getInfo(null, nestedSoap); 1759+ namespace = (String) qName[QNAME_NAMESPACE]; 1760+ type = (String) qName[QNAME_TYPE]; 1761+ 1762+ // prefer the name from the property info 1763+ if (propertyInfo.name != null && propertyInfo.name.length() > 0) { 1764+ name = propertyInfo.name; 1765+ } else { 1766+ name = (String) qName[QNAME_TYPE]; 1767+ } 1768+ 1769+ // prefer the namespace from the property info 1770+ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) { 1771+ namespace = propertyInfo.namespace; 1772+ } else { 1773+ namespace = (String) qName[QNAME_NAMESPACE]; 1774+ } 1775+ 1776+ writer.startTag(namespace, name); 1777+ if (!implicitTypes) { 1778+ String prefix = writer.getPrefix(namespace, true); 1779+ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type); 1780+ } 1781+ writeObjectBodyWithAttributes(writer, nestedSoap); 1782+ writer.endTag(namespace, name); 1783+ } 1784+ } 1785+ writeInnerText(writer, obj); 1786+ } 1787+ 1788 protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType) 1789 throws IOException { 1790 String itemsTagName = ITEM_LABEL; 1791@@ -727,9 +864,13 @@ public class SoapSerializationEnvelope extends SoapEnvelope 1792 1793 // This removes the arrayType attribute from the xml for arrays(required for most .Net services to work) 1794 if (!implicitTypes) { 1795- writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false) 1796- + ":" 1797+ writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false) + ":" 1798 + arrType[1] + "[" + cnt + "]"); 1799+ } else { 1800+ // Get the namespace from mappings if available when arrayType is removed for .Net 1801+ if (itemsNamespace == null) { 1802+ itemsNamespace = (String) arrType[0]; 1803+ } 1804 } 1805 1806 boolean skipped = false; 1807diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java 1808new file mode 100644 1809index 0000000..fdbaa21 1810--- /dev/null 1811+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java 1812@@ -0,0 +1,13 @@ 1813+package org.ksoap2.serialization; 1814+ 1815+import org.xmlpull.v1.XmlSerializer; 1816+ 1817+import java.io.IOException; 1818+ 1819+/** 1820+ * Created by robocik on 2015-09-25. 1821+ */ 1822+public interface ValueWriter 1823+{ 1824+ void write(XmlSerializer writer) throws IOException; 1825+} 1826diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java 1827index 8e14ee7..9dd3837 100644 1828--- a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java 1829+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java 1830@@ -21,8 +21,10 @@ 1831 1832 package org.ksoap2.transport; 1833 1834+import java.io.IOException; 1835+import java.io.InputStream; 1836+import java.io.OutputStream; 1837 import java.util.List; 1838-import java.io.*; 1839 1840 /** 1841 * Interface to allow the abstraction of the raw transport information 1842@@ -58,6 +60,13 @@ public interface ServiceConnection { 1843 */ 1844 public List getResponseProperties() throws IOException; 1845 1846+ /** 1847+ * Returns the numerical HTTP status to the caller 1848+ * @return an integer status value 1849+ * @throws IOException 1850+ */ 1851+ public int getResponseCode() throws IOException; 1852+ 1853 /** 1854 * Set properties on the outgoing connection. 1855 * 1856@@ -88,6 +97,8 @@ public interface ServiceConnection { 1857 **/ 1858 public void setFixedLengthStreamingMode(int contentLength); 1859 1860+ public void setChunkedStreamingMode(); 1861+ 1862 /** 1863 * Open and return the outputStream to the endpoint. 1864 * 1865diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java 1866index b2f6587..2f3d523 100644 1867--- a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java 1868+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java 1869@@ -23,9 +23,13 @@ 1870 1871 package org.ksoap2.transport; 1872 1873+import java.util.HashMap; 1874+import java.util.Iterator; 1875 import java.util.List; 1876 import java.io.*; 1877+import java.net.MalformedURLException; 1878 import java.net.Proxy; 1879+import java.net.URL; 1880 1881 import org.ksoap2.*; 1882 import org.kxml2.io.*; 1883@@ -40,9 +44,9 @@ import org.xmlpull.v1.*; 1884 abstract public class Transport { 1885 1886 /** 1887- * Added to enable web service interactions on the emulator 1888- * to be debugged with Fiddler2 (Windows) but provides utility 1889- * for other proxy requirements. 1890+ * Added to enable web service interactions on the emulator to be debugged 1891+ * with Fiddler2 (Windows) but provides utility for other proxy 1892+ * requirements. 1893 */ 1894 protected Proxy proxy; 1895 protected String url; 1896@@ -61,6 +65,12 @@ abstract public class Transport { 1897 1898 private int bufferLength = ServiceConnection.DEFAULT_BUFFER_SIZE; 1899 1900+ private HashMap prefixes = new HashMap(); 1901+ 1902+ public HashMap getPrefixes() { 1903+ return prefixes; 1904+ } 1905+ 1906 public Transport() { 1907 } 1908 1909@@ -82,9 +92,12 @@ abstract public class Transport { 1910 /** 1911 * Construct the transport object 1912 * 1913- * @param proxy Specifies the proxy server to use for 1914- * accessing the web service or <code>null</code> if a direct connection is available 1915- * @param url Specifies the web service url 1916+ * @param proxy 1917+ * Specifies the proxy server to use for accessing the web 1918+ * service or <code>null</code> if a direct connection is 1919+ * available 1920+ * @param url 1921+ * Specifies the web service url 1922 * 1923 */ 1924 public Transport(Proxy proxy, String url) { 1925@@ -114,23 +127,30 @@ abstract public class Transport { 1926 xp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 1927 xp.setInput(is, null); 1928 envelope.parse(xp); 1929+ /* 1930+ * Fix memory leak when running on android in strict mode. Issue 133 1931+ */ 1932+ is.close(); 1933 } 1934 1935 /** 1936 * Serializes the request. 1937 */ 1938- protected byte[] createRequestData(SoapEnvelope envelope, String encoding) throws IOException { 1939- System.out.println("createRequestData"); 1940+ protected byte[] createRequestData(SoapEnvelope envelope, String encoding) 1941+ throws IOException { 1942 ByteArrayOutputStream bos = new ByteArrayOutputStream(bufferLength); 1943 byte result[] = null; 1944 bos.write(xmlVersionTag.getBytes()); 1945- System.out.println("bos.write"); 1946 XmlSerializer xw = new KXmlSerializer(); 1947- System.out.println("new KXmlSerializer"); 1948+ 1949+ final Iterator keysIter = prefixes.keySet().iterator(); 1950+ 1951 xw.setOutput(bos, encoding); 1952- System.out.println("xw.setOutput"); 1953+ while (keysIter.hasNext()) { 1954+ String key = (String) keysIter.next(); 1955+ xw.setPrefix(key, (String) prefixes.get(key)); 1956+ } 1957 envelope.write(xw); 1958- System.out.println("envelope.write"); 1959 xw.flush(); 1960 bos.write('\r'); 1961 bos.write('\n'); 1962@@ -138,14 +158,14 @@ abstract public class Transport { 1963 result = bos.toByteArray(); 1964 xw = null; 1965 bos = null; 1966- System.out.println("createRequestData end"); 1967 return result; 1968 } 1969 1970 /** 1971 * Serializes the request. 1972 */ 1973- protected byte[] createRequestData(SoapEnvelope envelope) throws IOException { 1974+ protected byte[] createRequestData(SoapEnvelope envelope) 1975+ throws IOException { 1976 return createRequestData(envelope, null); 1977 } 1978 1979@@ -159,6 +179,12 @@ abstract public class Transport { 1980 this.url = url; 1981 } 1982 1983+ public String getUrl() 1984+ { 1985+ return url; 1986+ } 1987+ 1988+ 1989 /** 1990 * Sets the version tag for the outgoing soap call. Example <?xml 1991 * version=\"1.0\" encoding=\"UTF-8\"?> 1992@@ -177,57 +203,94 @@ abstract public class Transport { 1993 } 1994 1995 /** 1996- * Perform a soap call with a given namespace and the given envelope providing 1997- * any extra headers that the user requires such as cookies. Headers that are 1998- * returned by the web service will be returned to the caller in the form of a 1999- * <code>List</code> of <code>HeaderProperty</code> instances. 2000+ * Perform a soap call with a given namespace and the given envelope 2001+ * providing any extra headers that the user requires such as cookies. 2002+ * Headers that are returned by the web service will be returned to the 2003+ * caller in the form of a <code>List</code> of <code>HeaderProperty</code> 2004+ * instances. 2005+ * 2006+ * @param soapAction 2007+ * the namespace with which to perform the call in. 2008+ * @param envelope 2009+ * the envelope the contains the information for the call. 2010+ * @param headers 2011+ * <code>List</code> of <code>HeaderProperty</code> headers to 2012+ * send with the SOAP request. 2013+ * 2014+ * @return Headers returned by the web service as a <code>List</code> of 2015+ * <code>HeaderProperty</code> instances. 2016+ */ 2017+ abstract public List call(String soapAction, SoapEnvelope envelope, 2018+ List headers) throws IOException, XmlPullParserException; 2019+ 2020+ /** 2021+ * Perform a soap call with a given namespace and the given envelope 2022+ * providing any extra headers that the user requires such as cookies. 2023+ * Headers that are returned by the web service will be returned to the 2024+ * caller in the form of a <code>List</code> of <code>HeaderProperty</code> 2025+ * instances. 2026 * 2027- * @param targetNamespace 2028+ * @param soapAction 2029 * the namespace with which to perform the call in. 2030 * @param envelope 2031 * the envelope the contains the information for the call. 2032 * @param headers 2033- * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request. 2034+ * <code>List</code> of <code>HeaderProperty</code> headers to 2035+ * send with the SOAP request. 2036+ * @param outputFile 2037+ * a file to stream the response into rather than parsing it, 2038+ * streaming happens when file is not null 2039 * 2040 * @return Headers returned by the web service as a <code>List</code> of 2041- * <code>HeaderProperty</code> instances. 2042+ * <code>HeaderProperty</code> instances. 2043 */ 2044- abstract public List call(String targetNamespace, SoapEnvelope envelope, List headers) 2045- throws IOException, XmlPullParserException; 2046+ abstract public List call(String soapAction, SoapEnvelope envelope, 2047+ List headers, File outputFile) throws IOException, 2048+ XmlPullParserException; 2049 2050 /** 2051 * Perform a soap call with a given namespace and the given envelope. 2052 * 2053- * @param targetNamespace 2054+ * @param soapAction 2055 * the namespace with which to perform the call in. 2056 * @param envelope 2057 * the envelope the contains the information for the call. 2058 */ 2059- public void call(String targetNamespace, SoapEnvelope envelope) throws IOException, 2060- XmlPullParserException { 2061- call(targetNamespace, envelope, null); 2062+ public void call(String soapAction, SoapEnvelope envelope) 2063+ throws IOException, XmlPullParserException { 2064+ call(soapAction, envelope, null); 2065 } 2066 2067 /** 2068 * Return the name of the host that is specified as the web service target 2069- * 2070+ * 2071 * @return Host name 2072 */ 2073- abstract public String getHost(); 2074+ public String getHost() throws MalformedURLException { 2075+ 2076+ return new URL(url).getHost(); 2077+ } 2078 2079 /** 2080- * Return the port number of the host that is specified as the web service target 2081- * 2082+ * Return the port number of the host that is specified as the web service 2083+ * target 2084+ * 2085 * @return Port number 2086 */ 2087- abstract public int getPort(); 2088+ public int getPort() throws MalformedURLException { 2089+ 2090+ return new URL(url).getPort(); 2091+ } 2092 2093 /** 2094 * Return the path to the web service target 2095- * 2096+ * 2097 * @return The URL's path 2098 */ 2099- abstract public String getPath(); 2100+ public String getPath() throws MalformedURLException { 2101+ 2102+ return new URL(url).getPath(); 2103+ } 2104 2105 abstract public ServiceConnection getServiceConnection() throws IOException; 2106 } 2107diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java 2108index 71c8caa..61a5e2c 100644 2109--- a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java 2110+++ b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java 2111@@ -27,8 +27,7 @@ import org.xmlpull.v1.*; 2112 2113 public class MarshalFloat implements Marshal { 2114 2115- public Object readInstance(XmlPullParser parser, String namespace, String name, 2116- PropertyInfo propertyInfo) 2117+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo propertyInfo) 2118 throws IOException, XmlPullParserException { 2119 String stringValue = parser.nextText(); 2120 Object result; 2121diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java 2122new file mode 100644 2123index 0000000..f7fb866 2124--- /dev/null 2125+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java 2126@@ -0,0 +1,60 @@ 2127+package org.ksoap2.transport; 2128+ 2129+import java.io.IOException; 2130+import java.util.List; 2131+ 2132+/** 2133+ * HttpResponseException is an IOException that is to be thrown when a Http response code is different from 200. 2134+ * It allows for easier retrieval of the Http response code from the connection. 2135+ * 2136+ * @author Rui Pereira <syshex@gmail.com> 2137+ */ 2138+public class HttpResponseException extends IOException { 2139+ 2140+ private int statusCode; 2141+ private List responseHeaders; 2142+ 2143+ public HttpResponseException(int statusCode) { 2144+ super(); 2145+ this.statusCode = statusCode; 2146+ } 2147+ 2148+ public HttpResponseException(String detailMessage, int statusCode) { 2149+ super(detailMessage); 2150+ this.statusCode = statusCode; 2151+ } 2152+ 2153+ public HttpResponseException(String detailMessage, int statusCode,List responseHeaders) { 2154+ super(detailMessage); 2155+ this.statusCode = statusCode; 2156+ this.responseHeaders=responseHeaders; 2157+ } 2158+ 2159+ public HttpResponseException(String message, Throwable cause, int statusCode) { 2160+ super(message, cause); 2161+ this.statusCode = statusCode; 2162+ } 2163+ 2164+ public HttpResponseException(Throwable cause, int statusCode) { 2165+ super(cause); 2166+ this.statusCode = statusCode; 2167+ } 2168+ 2169+ /** 2170+ * Returns the unexpected Http response code 2171+ * 2172+ * @return response code 2173+ */ 2174+ public int getStatusCode() { 2175+ return statusCode; 2176+ } 2177+ 2178+ /** 2179+ * Returns all http headers from this response 2180+ * 2181+ * @return response code 2182+ */ 2183+ public List getResponseHeaders() { 2184+ return responseHeaders; 2185+ } 2186+} 2187diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java 2188index d92ac09..920ca60 100644 2189--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java 2190+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java 2191@@ -25,24 +25,21 @@ 2192 2193 package org.ksoap2.transport; 2194 2195-import java.util.List; 2196-import java.util.zip.GZIPInputStream; 2197+import org.ksoap2.HeaderProperty; 2198+import org.ksoap2.SoapEnvelope; 2199+import org.ksoap2.serialization.*; 2200+import org.xmlpull.v1.XmlPullParserException; 2201+ 2202 import java.io.*; 2203-import java.net.MalformedURLException; 2204 import java.net.Proxy; 2205-import java.net.URL; 2206- 2207-import org.ksoap2.*; 2208-import org.ksoap2.serialization.SoapSerializationEnvelope; 2209-import org.xmlpull.v1.*; 2210+import java.util.*; 2211+import java.util.zip.GZIPInputStream; 2212 2213 /** 2214 * A J2SE based HttpTransport layer. 2215 */ 2216 public class HttpTransportSE extends Transport { 2217 2218- private ServiceConnection serviceConnection; 2219- 2220 /** 2221 * Creates instance of HttpTransportSE with set url 2222 * 2223@@ -107,249 +104,254 @@ public class HttpTransportSE extends Transport { 2224 * the desired soapAction 2225 * @param envelope 2226 * the envelope containing the information for the soap call. 2227+ * @throws HttpResponseException 2228 * @throws IOException 2229 * @throws XmlPullParserException 2230 */ 2231- public void call(String soapAction, SoapEnvelope envelope) throws IOException, 2232- XmlPullParserException { 2233- 2234+ public void call(String soapAction, SoapEnvelope envelope) 2235+ throws HttpResponseException, IOException, XmlPullParserException { 2236+ 2237 call(soapAction, envelope, null); 2238 } 2239 2240+ public List call(String soapAction, SoapEnvelope envelope, List headers) 2241+ throws HttpResponseException, IOException, XmlPullParserException { 2242+ return call(soapAction, envelope, headers, null); 2243+ } 2244+ 2245 /** 2246- * 2247- * set the desired soapAction header field 2248- * 2249+ * Perform a soap call with a given namespace and the given envelope providing 2250+ * any extra headers that the user requires such as cookies. Headers that are 2251+ * returned by the web service will be returned to the caller in the form of a 2252+ * <code>List</code> of <code>HeaderProperty</code> instances. 2253+ * 2254 * @param soapAction 2255- * the desired soapAction 2256+ * the namespace with which to perform the call in. 2257 * @param envelope 2258- * the envelope containing the information for the soap call. 2259+ * the envelope the contains the information for the call. 2260 * @param headers 2261- * a list of HeaderProperties to be http header properties when establishing the connection 2262+ * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request. 2263+ * @param outputFile 2264+ * a file to stream the response into rather than parsing it, streaming happens when file is not null 2265 * 2266- * @return <code>CookieJar</code> with any cookies sent by the server 2267- * @throws IOException 2268- * @throws XmlPullParserException 2269+ * @return Headers returned by the web service as a <code>List</code> of 2270+ * <code>HeaderProperty</code> instances. 2271+ * 2272+ * @throws HttpResponseException 2273+ * an IOException when Http response code is different from 200 2274 */ 2275- public List call(String soapAction, SoapEnvelope envelope, List headers) 2276- throws IOException, XmlPullParserException { 2277+ public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile) 2278+ throws HttpResponseException, IOException, XmlPullParserException { 2279 2280 if (soapAction == null) { 2281 soapAction = "\"\""; 2282 } 2283 2284- System.out.println("call action:" + soapAction); 2285 byte[] requestData = createRequestData(envelope, "UTF-8"); 2286 2287- if (requestData != null) { 2288- requestDump = debug ? new String(requestData) : null; 2289- } 2290- else { 2291- requestDump = null; 2292- } 2293+ requestDump = debug ? new String(requestData) : null; 2294 responseDump = null; 2295- 2296- System.out.println("requestDump:" + requestDump); 2297+ System.out.println("requestDump: " + requestDump); 2298 ServiceConnection connection = getServiceConnection(); 2299- System.out.println("connection:" + connection); 2300 2301 connection.setRequestProperty("User-Agent", USER_AGENT); 2302 // SOAPAction is not a valid header for VER12 so do not add 2303 // it 2304 // @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67 2305- System.out.println("envelope:" + envelope); 2306- if (envelope != null) { 2307- if (envelope.version != SoapSerializationEnvelope.VER12) { 2308- connection.setRequestProperty("SOAPAction", soapAction); 2309- } 2310+ if (envelope.version != SoapSerializationEnvelope.VER12) { 2311+ connection.setRequestProperty("SOAPAction", soapAction); 2312+ } 2313 2314- if (envelope.version == SoapSerializationEnvelope.VER12) { 2315- connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8); 2316- } else { 2317- connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8); 2318- } 2319+ if (envelope.version == SoapSerializationEnvelope.VER12) { 2320+ connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8); 2321+ } else { 2322+ connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8); 2323+ } 2324 2325- connection.setRequestProperty("Connection", "close"); 2326- connection.setRequestProperty("Accept-Encoding", "gzip"); 2327- connection.setRequestProperty("Content-Length", "" + requestData.length); 2328+ // this seems to cause issues so we are removing it 2329+ //connection.setRequestProperty("Connection", "close"); 2330+ connection.setRequestProperty("Accept-Encoding", "gzip"); 2331 2332- //M: Retry for HTTP Authentication 2333- //connection.setFixedLengthStreamingMode(requestData.length); 2334 2335- // Pass the headers provided by the user along with the call 2336- if (headers != null) { 2337- for (int i = 0; i < headers.size(); i++) { 2338- HeaderProperty hp = (HeaderProperty) headers.get(i); 2339- connection.setRequestProperty(hp.getKey(), hp.getValue()); 2340- } 2341+ // Pass the headers provided by the user along with the call 2342+ if (headers != null) { 2343+ for (int i = 0; i < headers.size(); i++) { 2344+ HeaderProperty hp = (HeaderProperty) headers.get(i); 2345+ connection.setRequestProperty(hp.getKey(), hp.getValue()); 2346 } 2347- 2348- connection.setRequestMethod("POST"); 2349- 2350- } 2351- else { 2352- connection.setRequestProperty("Connection", "close"); 2353- connection.setRequestProperty("Accept-Encoding", "gzip"); 2354- connection.setRequestMethod("GET"); 2355 } 2356 2357- if (requestData != null) { 2358- OutputStream os = connection.openOutputStream(); 2359- 2360- os.write(requestData, 0, requestData.length); 2361- os.flush(); 2362- os.close(); 2363- requestData = null; 2364- } 2365- InputStream is; 2366+ connection.setRequestMethod("POST"); 2367+ sendData(requestData, connection,envelope); 2368+ requestData = null; 2369+ InputStream is = null; 2370 List retHeaders = null; 2371+ byte[] buf = null; // To allow releasing the resource after used 2372+ int contentLength = 8192; // To determine the size of the response and adjust buffer size 2373 boolean gZippedContent = false; 2374- boolean bcaCert = false; 2375+ boolean xmlContent = false; 2376+ int status = connection.getResponseCode(); 2377 2378 try { 2379 retHeaders = connection.getResponseProperties(); 2380- System.out.println("[HttpTransportSE] retHeaders = " + retHeaders); 2381+ 2382 for (int i = 0; i < retHeaders.size(); i++) { 2383- HeaderProperty hp = (HeaderProperty) retHeaders.get(i); 2384+ HeaderProperty hp = (HeaderProperty)retHeaders.get(i); 2385 // HTTP response code has null key 2386 if (null == hp.getKey()) { 2387 continue; 2388 } 2389+ 2390+ // If we know the size of the response, we should use the size to initiate vars 2391+ if (hp.getKey().equalsIgnoreCase("content-length") ) { 2392+ if ( hp.getValue() != null ) { 2393+ try { 2394+ contentLength = Integer.parseInt( hp.getValue() ); 2395+ } catch ( NumberFormatException nfe ) { 2396+ contentLength = 8192; 2397+ } 2398+ } 2399+ } 2400+ 2401+ 2402+ // Check the content-type header to see if we're getting back XML, in case of a 2403+ // SOAP fault on 500 codes 2404+ if (hp.getKey().equalsIgnoreCase("Content-Type") 2405+ && hp.getValue().contains("xml")) { 2406+ xmlContent = true; 2407+ } 2408+ 2409+ 2410 // ignoring case since users found that all smaller case is used on some server 2411 // and even if it is wrong according to spec, we rather have it work.. 2412 if (hp.getKey().equalsIgnoreCase("Content-Encoding") 2413- && hp.getValue().equalsIgnoreCase("gzip")) { 2414+ && hp.getValue().equalsIgnoreCase("gzip")) { 2415 gZippedContent = true; 2416 } 2417 } 2418- if (gZippedContent) { 2419- is = getUnZippedInputStream(connection.openInputStream()); 2420- } else { 2421- is = connection.openInputStream(); 2422- } 2423- } catch (IOException e) { 2424- if (gZippedContent) { 2425- is = getUnZippedInputStream(connection.getErrorStream()); 2426- } else { 2427- is = connection.getErrorStream(); 2428- } 2429 2430- if (is == null) { 2431- connection.disconnect(); 2432- throw (e); 2433+ //first check the response code.... 2434+ if (status != 200 && status != 202) { 2435+ //202 is a correct status returned by WCF OneWay operation 2436+ throw new HttpResponseException("HTTP request failed, HTTP status: " + status, status,retHeaders); 2437 } 2438- } 2439- 2440- if (debug) { 2441- ByteArrayOutputStream bos = new ByteArrayOutputStream(); 2442- byte[] buf = new byte[8192]; 2443 2444- while (true) { 2445- int rd = is.read(buf, 0, 8192); 2446- if (rd == -1) { 2447- break; 2448+ if (contentLength > 0) { 2449+ if (gZippedContent) { 2450+ is = getUnZippedInputStream( 2451+ new BufferedInputStream(connection.openInputStream(),contentLength)); 2452+ } else { 2453+ is = new BufferedInputStream(connection.openInputStream(),contentLength); 2454+ } 2455+ } 2456+ } catch (IOException e) { 2457+ if (contentLength > 0) { 2458+ if(gZippedContent) { 2459+ is = getUnZippedInputStream( 2460+ new BufferedInputStream(connection.getErrorStream(),contentLength)); 2461+ } else { 2462+ is = new BufferedInputStream(connection.getErrorStream(),contentLength); 2463 } 2464- bos.write(buf, 0, rd); 2465 } 2466 2467- bos.flush(); 2468- buf = bos.toByteArray(); 2469+ if ( e instanceof HttpResponseException) { 2470+ if (!xmlContent) { 2471+ if (debug && is != null) { 2472+ //go ahead and read the error stream into the debug buffers/file if needed. 2473+ readDebug(is, contentLength, outputFile); 2474+ } 2475 2476- responseDump = new String(buf); 2477+ //we never want to drop through to attempting to parse the HTTP error stream as a SOAP response. 2478+ connection.disconnect(); 2479+ throw e; 2480+ } 2481+ } 2482+ } 2483 2484- System.out.println("responseDump:" + responseDump); 2485- is.close(); 2486- is = new ByteArrayInputStream(buf); 2487+ if (debug) { 2488+ is = readDebug(is, contentLength, outputFile); 2489 } 2490 2491- if (envelope != null) { 2492- parseResponse(envelope, is); 2493+ if(is!=null) 2494+ { 2495+ parseResponse(envelope, is,retHeaders); 2496 } 2497 2498+ // release all resources 2499+ // input stream is will be released inside parseResponse 2500+ is = null; 2501+ buf = null; 2502+ //This fixes Issue 173 read my explanation here: https://code.google.com/p/ksoap2-android/issues/detail?id=173 2503+ connection.disconnect(); 2504+ connection = null; 2505 return retHeaders; 2506 } 2507 2508- private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException { 2509- /* workaround for Android 2.3 2510- (see http://stackoverflow.com/questions/5131016/) 2511- */ 2512- try { 2513- return (GZIPInputStream) inputStream; 2514- } catch (ClassCastException e) { 2515- return new GZIPInputStream(inputStream); 2516- } 2517- } 2518+ protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope) 2519+ throws IOException 2520+ { 2521+ connection.setRequestProperty("Content-Length", "" + requestData.length); 2522+ connection.setFixedLengthStreamingMode(requestData.length); 2523 2524- public ServiceConnection getServiceConnection() throws IOException { 2525- if (serviceConnection == null) { 2526- System.out.println("new ServiceConnectionSE:" + proxy + " " + url + " " + timeout); 2527- serviceConnection = new ServiceConnectionSE(proxy, url, timeout); 2528- } 2529- return serviceConnection; 2530+ OutputStream os = connection.openOutputStream(); 2531+ os.write(requestData, 0, requestData.length); 2532+ os.flush(); 2533+ os.close(); 2534 } 2535 2536- public String getHost() { 2537- 2538- String retVal = null; 2539- 2540- try { 2541- retVal = new URL(url).getHost(); 2542- } catch (MalformedURLException e) { 2543- e.printStackTrace(); 2544- } 2545- 2546- return retVal; 2547+ protected void parseResponse(SoapEnvelope envelope, InputStream is,List returnedHeaders) 2548+ throws XmlPullParserException, IOException 2549+ { 2550+ parseResponse(envelope, is); 2551 } 2552 2553- public int getPort() { 2554- 2555- int retVal = -1; 2556 2557- try { 2558- retVal = new URL(url).getPort(); 2559- } catch (MalformedURLException e) { 2560- e.printStackTrace(); 2561+ private InputStream readDebug(InputStream is, int contentLength, File outputFile) throws IOException { 2562+ OutputStream bos; 2563+ if (outputFile != null) { 2564+ bos = new FileOutputStream(outputFile); 2565+ } else { 2566+ // If known use the size if not use default value 2567+ bos = new ByteArrayOutputStream( (contentLength > 0 ) ? contentLength : 256*1024); 2568 } 2569 2570- return retVal; 2571- } 2572- 2573- public String getPath() { 2574- 2575- String retVal = null; 2576+ byte[] buf = new byte[256]; 2577 2578- try { 2579- retVal = new URL(url).getPath(); 2580- } catch (MalformedURLException e) { 2581- e.printStackTrace(); 2582+ while (true) { 2583+ int rd = is.read(buf, 0, 256); 2584+ if (rd == -1) { 2585+ break; 2586+ } 2587+ bos.write(buf, 0, rd); 2588 } 2589 2590- return retVal; 2591- } 2592- 2593- public String getQuery() { 2594- 2595- String retVal = null; 2596- 2597- try { 2598- retVal = new URL(url).getQuery(); 2599- } catch (MalformedURLException e) { 2600- e.printStackTrace(); 2601+ bos.flush(); 2602+ if (bos instanceof ByteArrayOutputStream) { 2603+ buf = ((ByteArrayOutputStream) bos).toByteArray(); 2604+ } 2605+ bos = null; 2606+ responseDump = new String(buf); 2607+ is.close(); 2608+ System.out.println("responseDump: " + requestDump); 2609+ if (outputFile != null) { 2610+ return new FileInputStream(outputFile); 2611+ } else { 2612+ return new ByteArrayInputStream(buf); 2613 } 2614- 2615- return retVal; 2616 } 2617 2618- /** 2619- * @hide 2620- */ 2621- public byte[] getRequestData(SoapEnvelope envelope, String encoding) { 2622+ private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException { 2623+ /* workaround for Android 2.3 2624+ (see http://stackoverflow.com/questions/5131016/) 2625+ */ 2626 try { 2627- return createRequestData(envelope, encoding); 2628- } catch (Exception e) { 2629- e.printStackTrace(); 2630+ return (GZIPInputStream) inputStream; 2631+ } catch (ClassCastException e) { 2632+ return new GZIPInputStream(inputStream); 2633 } 2634+ } 2635 2636- return null; 2637+ public ServiceConnection getServiceConnection() throws IOException { 2638+ return new ServiceConnectionSE(proxy, url, timeout); 2639 } 2640 } 2641diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java 2642index 9ad9ba9..376c7d8 100644 2643--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java 2644+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java 2645@@ -1,21 +1,16 @@ 2646 2647 package org.ksoap2.transport; 2648 2649+import org.ksoap2.HeaderProperty; 2650+ 2651+import javax.net.ssl.HttpsURLConnection; 2652+import javax.net.ssl.SSLSocketFactory; 2653 import java.io.IOException; 2654 import java.io.InputStream; 2655 import java.io.OutputStream; 2656+import java.net.Proxy; 2657 import java.net.URL; 2658-import java.util.Iterator; 2659-import java.util.LinkedList; 2660-import java.util.Map; 2661-import java.util.List; 2662-import java.util.Set; 2663- 2664-import javax.net.ssl.HostnameVerifier; 2665-import javax.net.ssl.SSLSocketFactory; 2666-import javax.net.ssl.HttpsURLConnection; 2667-//import com.android.okhttp.internal.http.HttpsURLConnectionImpl; 2668-import org.ksoap2.HeaderProperty; 2669+import java.util.*; 2670 2671 /** 2672 * HttpsServiceConnectionSE is a service connection that uses a https url connection and requires explicit setting of 2673@@ -49,10 +44,29 @@ public class HttpsServiceConnectionSE implements ServiceConnection { 2674 * @param timeout the timeout for the connection in milliseconds 2675 * @throws IOException 2676 */ 2677- public HttpsServiceConnectionSE(String host, int port, String file, 2678- int timeout) throws IOException { 2679- connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file) 2680- .openConnection(); 2681+ public HttpsServiceConnectionSE(String host, int port, String file, int timeout) throws IOException { 2682+ this(null, host, port, file, timeout); 2683+ } 2684+ 2685+ /** 2686+ * Create the transport with the supplied parameters. 2687+ * @param proxy proxy server to use 2688+ * @param host the name of the host e.g. webservices.somewhere.com 2689+ * @param port the http port to connect on 2690+ * @param file the path to the file on the webserver that represents the 2691+ * webservice e.g. /api/services/myservice.jsp 2692+ * @param timeout the timeout for the connection in milliseconds 2693+ * @throws IOException 2694+ */ 2695+ public HttpsServiceConnectionSE(Proxy proxy, String host, int port, String file, int timeout) throws IOException { 2696+ 2697+ if (proxy == null) { 2698+ connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection(); 2699+ } else { 2700+ connection = 2701+ (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection(proxy); 2702+ } 2703+ 2704 updateConnectionParameters(timeout); 2705 } 2706 2707@@ -89,6 +103,10 @@ public class HttpsServiceConnectionSE implements ServiceConnection { 2708 return retList; 2709 } 2710 2711+ public int getResponseCode() throws IOException { 2712+ return connection.getResponseCode(); 2713+ } 2714+ 2715 public void setRequestProperty(String key, String value) { 2716 connection.setRequestProperty(key, value); 2717 } 2718@@ -101,6 +119,11 @@ public class HttpsServiceConnectionSE implements ServiceConnection { 2719 connection.setFixedLengthStreamingMode(contentLength); 2720 } 2721 2722+ public void setChunkedStreamingMode() { 2723+ connection.setChunkedStreamingMode(0); 2724+ } 2725+ 2726+ 2727 public OutputStream openOutputStream() throws IOException { 2728 return connection.getOutputStream(); 2729 } 2730@@ -128,9 +151,4 @@ public class HttpsServiceConnectionSE implements ServiceConnection { 2731 public void setSSLSocketFactory(SSLSocketFactory sf) { 2732 connection.setSSLSocketFactory(sf); 2733 } 2734- 2735- public void setHostnameVerifier(HostnameVerifier v) { 2736- connection.setHostnameVerifier(v); 2737- } 2738- 2739 } 2740diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java 2741index d220ac9..a7d7023 100644 2742--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java 2743+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java 2744@@ -1,8 +1,8 @@ 2745- 2746 package org.ksoap2.transport; 2747 2748 import java.io.IOException; 2749 import java.net.MalformedURLException; 2750+import java.net.Proxy; 2751 import java.net.URL; 2752 2753 /** 2754@@ -14,17 +14,31 @@ import java.net.URL; 2755 public class HttpsTransportSE extends HttpTransportSE { 2756 2757 static final String PROTOCOL = "https"; 2758+ private static final String PROTOCOL_FULL = PROTOCOL + "://"; 2759+ 2760+ //connection instance, used for setting the SSLSocketFactory 2761+ private HttpsServiceConnectionSE connection; 2762 2763- private ServiceConnection serviceConnection = null; 2764- private final String host; 2765- private final int port; 2766- private final String file; 2767- private final int timeout; 2768+ protected final String host; 2769+ protected final int port; 2770+ protected final String file; 2771 2772- public HttpsTransportSE(String host, int port, String file, int timeout) { 2773- super(HttpsTransportSE.PROTOCOL + "://" + host + ":" + port + file); 2774- System.out.println("Establistion connection to: " + HttpsTransportSE.PROTOCOL + "://" 2775- + host + ":" + port + file); 2776+ public HttpsTransportSE (String host, int port, String file, int timeout) { 2777+ super(HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file, timeout); 2778+ this.host = host; 2779+ this.port = port; 2780+ this.file = file; 2781+ } 2782+ 2783+ /** 2784+ * Creates instance of HttpTransportSE with set url and defines a 2785+ * proxy server to use to access it 2786+ * 2787+ * @param proxy 2788+ * Proxy information or <code>null</code> for direct access 2789+ */ 2790+ public HttpsTransportSE(Proxy proxy, String host, int port, String file, int timeout) { 2791+ super(proxy, HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file); 2792 this.host = host; 2793 this.port = port; 2794 this.file = file; 2795@@ -37,48 +51,11 @@ public class HttpsTransportSE extends HttpTransportSE { 2796 */ 2797 public ServiceConnection getServiceConnection() throws IOException 2798 { 2799- if (serviceConnection == null) { 2800- serviceConnection = new HttpsServiceConnectionSE(host, port, file, timeout); 2801- } 2802- return serviceConnection; 2803- } 2804- 2805- public String getHost() { 2806- 2807- String retVal = null; 2808- 2809- try { 2810- retVal = new URL(url).getHost(); 2811- } catch (MalformedURLException e) { 2812- e.printStackTrace(); 2813+ if(connection != null) { 2814+ return connection; 2815+ } else { 2816+ connection = new HttpsServiceConnectionSE(proxy, host, port, file, timeout); 2817+ return connection; 2818 } 2819- 2820- return retVal; 2821- } 2822- 2823- public int getPort() { 2824- 2825- int retVal = -1; 2826- 2827- try { 2828- retVal = new URL(url).getPort(); 2829- } catch (MalformedURLException e) { 2830- e.printStackTrace(); 2831- } 2832- 2833- return retVal; 2834- } 2835- 2836- public String getPath() { 2837- 2838- String retVal = null; 2839- 2840- try { 2841- retVal = new URL(url).getPath(); 2842- } catch (MalformedURLException e) { 2843- e.printStackTrace(); 2844- } 2845- 2846- return retVal; 2847 } 2848 } 2849diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java 2850index 287fed1..65ba582 100644 2851--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java 2852+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java 2853@@ -24,18 +24,8 @@ import java.io.IOException; 2854 */ 2855 public class KeepAliveHttpsTransportSE extends HttpsTransportSE 2856 { 2857- private final String host; 2858- private final int port; 2859- private final String file; 2860- private final int timeout; 2861- private ServiceConnection serviceConnection; 2862- 2863- public KeepAliveHttpsTransportSE(String host, int port, String file, int timeout) { 2864+ public KeepAliveHttpsTransportSE (String host, int port, String file, int timeout) { 2865 super(host, port, file, timeout); 2866- this.host = host; 2867- this.port = port; 2868- this.file = file; 2869- this.timeout = timeout; 2870 } 2871 2872 /** 2873@@ -47,11 +37,9 @@ public class KeepAliveHttpsTransportSE extends HttpsTransportSE 2874 //@Override 2875 public ServiceConnection getServiceConnection() throws IOException 2876 { 2877- if (serviceConnection == null) { 2878- serviceConnection = new HttpsServiceConnectionSEIgnoringConnectionClose(host, port, 2879- file, timeout); 2880- serviceConnection.setRequestProperty("Connection", "keep-alive"); 2881- } 2882+ ServiceConnection serviceConnection = 2883+ new HttpsServiceConnectionSEIgnoringConnectionClose(host, port, file, timeout); 2884+ serviceConnection.setRequestProperty("Connection", "keep-alive"); 2885 return serviceConnection; 2886 } 2887 2888diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java 2889index 029ee9a..bfdfe11 100644 2890--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java 2891+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java 2892@@ -21,16 +21,16 @@ 2893 2894 package org.ksoap2.transport; 2895 2896-import java.io.*; 2897-import java.net.*; 2898-import java.util.Iterator; 2899-import java.util.LinkedList; 2900-import java.util.List; 2901-import java.util.Map; 2902-import java.util.Set; 2903- 2904 import org.ksoap2.HeaderProperty; 2905 2906+import java.io.IOException; 2907+import java.io.InputStream; 2908+import java.io.OutputStream; 2909+import java.net.HttpURLConnection; 2910+import java.net.Proxy; 2911+import java.net.URL; 2912+import java.util.*; 2913+ 2914 /** 2915 * Connection for J2SE environments. 2916 */ 2917@@ -80,23 +80,29 @@ public class ServiceConnectionSE implements ServiceConnection { 2918 connection.disconnect(); 2919 } 2920 2921- public List getResponseProperties() { 2922- Map properties = connection.getHeaderFields(); 2923- Set keys = properties.keySet(); 2924+ public List getResponseProperties() throws IOException { 2925 List retList = new LinkedList(); 2926 2927- for (Iterator i = keys.iterator(); i.hasNext();) { 2928- String key = (String) i.next(); 2929- List values = (List) properties.get(key); 2930- 2931- for (int j = 0; j < values.size(); j++) { 2932- retList.add(new HeaderProperty(key, (String) values.get(j))); 2933+ Map properties = connection.getHeaderFields(); 2934+ if(properties != null) { 2935+ Set keys = properties.keySet(); 2936+ for (Iterator i = keys.iterator(); i.hasNext();) { 2937+ String key = (String) i.next(); 2938+ List values = (List) properties.get(key); 2939+ 2940+ for (int j = 0; j < values.size(); j++) { 2941+ retList.add(new HeaderProperty(key, (String) values.get(j))); 2942+ } 2943 } 2944 } 2945 2946 return retList; 2947 } 2948 2949+ public int getResponseCode() throws IOException { 2950+ return connection.getResponseCode(); 2951+ } 2952+ 2953 public void setRequestProperty(String string, String soapAction) { 2954 connection.setRequestProperty(string, soapAction); 2955 } 2956@@ -116,6 +122,10 @@ public class ServiceConnectionSE implements ServiceConnection { 2957 connection.setFixedLengthStreamingMode(contentLength); 2958 } 2959 2960+ public void setChunkedStreamingMode() { 2961+ connection.setChunkedStreamingMode(0); 2962+ } 2963+ 2964 public OutputStream openOutputStream() throws IOException { 2965 return connection.getOutputStream(); 2966 } 2967-- 29682.17.0.441.gb46fe60e1d-goog 2969 2970