blob: d0c9fdafe506699cdc9622c0b576575195a71571 [file] [log] [blame]
Ecco Park84808292018-05-08 13:44:14 -07001From 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