summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2019-09-04 13:33:44 -0700
committerXin Li <delphij@google.com>2019-09-04 13:33:44 -0700
commitb511c7d0256aa4a68859790b01d1c8fdc06204e0 (patch)
tree202f22b06b90bff2fc9dd36abff5017cb012cbbe
parent6d71f5eade618c487f7112aa240c1fc75def0416 (diff)
parent03c95c428e18adaf7aa33e49cac88cc530e0d6cf (diff)
downloadksoap2-b511c7d0256aa4a68859790b01d1c8fdc06204e0.tar.gz
DO NOT MERGE - Merge Android 10 into masterndk-sysroot-r21
Bug: 139893257 Change-Id: Id46d6370b1569ed342f5e31e36c98fa78f256c9a
-rw-r--r--0001-ksoap2-update.patch2969
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java17
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java13
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java128
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java56
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java16
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java17
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java25
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java21
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java4
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java5
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java4
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java402
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java20
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java509
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java13
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java55
-rw-r--r--ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java150
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java2
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java61
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java364
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java93
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java85
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java44
-rw-r--r--ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java58
25 files changed, 4417 insertions, 714 deletions
diff --git a/0001-ksoap2-update.patch b/0001-ksoap2-update.patch
new file mode 100644
index 0000000..d0c9fda
--- /dev/null
+++ b/0001-ksoap2-update.patch
@@ -0,0 +1,2969 @@
+From b5b548a4be9f1ced6f5599b62765216f9ab8af00 Mon Sep 17 00:00:00 2001
+From: Ecco Park <eccopark@google.com>
+Date: Tue, 8 May 2018 13:44:14 -0700
+Subject: [PATCH 1/1] ksoap2 update
+
+Change-Id: Iace4c0f3cb31c9532c5fa0c44c2dc863bd81b23e
+Signed-off-by: Ecco Park <eccopark@google.com>
+---
+ .../main/java/org/ksoap2/SoapEnvelope.java | 17 +-
+ .../src/main/java/org/ksoap2/SoapFault12.java | 13 +-
+ .../serialization/AttributeContainer.java | 128 +++++-
+ .../java/org/ksoap2/serialization/DM.java | 57 ++-
+ .../ksoap2/serialization/HasAttributes.java | 16 +
+ .../ksoap2/serialization/HasInnerText.java | 17 +
+ .../ksoap2/serialization/KvmSerializable.java | 25 +-
+ .../org/ksoap2/serialization/Marshal.java | 3 +-
+ .../ksoap2/serialization/MarshalBase64.java | 3 +-
+ .../org/ksoap2/serialization/MarshalDate.java | 3 +-
+ .../serialization/MarshalHashtable.java | 8 +-
+ .../org/ksoap2/serialization/SoapObject.java | 340 +++++++++++++-
+ .../ksoap2/serialization/SoapPrimitive.java | 20 +-
+ .../SoapSerializationEnvelope.java | 419 ++++++++++++------
+ .../org/ksoap2/serialization/ValueWriter.java | 13 +
+ .../ksoap2/transport/ServiceConnection.java | 13 +-
+ .../java/org/ksoap2/transport/Transport.java | 131 ++++--
+ .../ksoap2/serialization/MarshalFloat.java | 3 +-
+ .../transport/HttpResponseException.java | 60 +++
+ .../org/ksoap2/transport/HttpTransportSE.java | 348 +++++++--------
+ .../transport/HttpsServiceConnectionSE.java | 58 ++-
+ .../ksoap2/transport/HttpsTransportSE.java | 81 ++--
+ .../transport/KeepAliveHttpsTransportSE.java | 20 +-
+ .../ksoap2/transport/ServiceConnectionSE.java | 44 +-
+ 24 files changed, 1298 insertions(+), 542 deletions(-)
+ create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
+ create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
+ create mode 100644 ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
+ create mode 100644 ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
+
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
+index 8a0b894..1c43656 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
+@@ -54,10 +54,8 @@ public class SoapEnvelope {
+ /** Namespace constant: http://www.w3.org/1999/XMLSchema */
+ public static final String XSI1999 = "http://www.w3.org/1999/XMLSchema-instance";
+
+- //public static final String NS20 = "http://www.wi-fi-org/specifications/hotspot2dot0/spp/1.0/";
+ public static final String NS20 = "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp";
+
+- //public static final String OMADM12 = "http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd";
+
+ /**
+ * Returns true for the string values "1" and "true", ignoring upper/lower
+@@ -105,9 +103,8 @@ public class SoapEnvelope {
+ /** Xml Schema data namespace, set by the constructor */
+ public String xsd;
+
+- ///M: HS20 Add by Jungo
++ // HS20 change
+ public String ns;
+- public String omadm;
+
+ /**
+ * Initializes a SOAP Envelope. The version parameter must be set to one of
+@@ -129,10 +126,8 @@ public class SoapEnvelope {
+ enc = SoapEnvelope.ENC2003;
+ env = SoapEnvelope.ENV2003;
+ }
+-
++ // HS20 change
+ ns = SoapEnvelope.NS20;
+- //omadm = SoapEnvelope.OMADM12;
+-
+ }
+
+ /** Parses the SOAP envelope from the given parser */
+@@ -206,13 +201,9 @@ public class SoapEnvelope {
+ * given XML writer.
+ */
+ public void write(XmlSerializer writer) throws IOException {
+- ///M: HS20 modify by Jungo
+- //writer.setPrefix("i", xsi);
+- //writer.setPrefix("d", xsd);
+- //writer.setPrefix("c", enc);
+- writer.setPrefix("soap", env);//the prefix for namespace env in xml output
++ // HS 2.0 changes
++ writer.setPrefix("soap", env); //the prefix for namespace env in xml output
+ writer.setPrefix("spp", ns);
+- //writer.setPrefix("omadm", omadm);
+
+ writer.startTag(env, "Envelope");
+ writer.startTag(env, "Header");
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
+index 5667cb4..3f39147 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
+@@ -72,27 +72,28 @@ public class SoapFault12 extends SoapFault {
+
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ String name = parser.getName();
++ String namespace = parser.getNamespace();
+ parser.nextTag();
+- if (name.equals("Code")) {
++ if (name.toLowerCase().equals("Code".toLowerCase())) {
+ this.Code = new Node();
+ this.Code.parse(parser);
+- } else if (name.equals("Reason")) {
++ } else if (name.toLowerCase().equals("Reason".toLowerCase())) {
+ this.Reason = new Node();
+ this.Reason.parse(parser);
+- } else if (name.equals("Node")) {
++ } else if (name.toLowerCase().equals("Node".toLowerCase())) {
+ this.Node = new Node();
+ this.Node.parse(parser);
+- } else if (name.equals("Role")) {
++ } else if (name.toLowerCase().equals("Role".toLowerCase())) {
+ this.Role = new Node();
+ this.Role.parse(parser);
+- } else if (name.equals("Detail")) {
++ } else if (name.toLowerCase().equals("Detail".toLowerCase())) {
+ this.Detail = new Node();
+ this.Detail.parse(parser);
+ } else {
+ throw new RuntimeException("unexpected tag:" + name);
+ }
+
+- parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, name);
++ parser.require(XmlPullParser.END_TAG, namespace, name);
+ }
+ parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, "Fault");
+ parser.nextTag();
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
+index 6b83847..34d2723 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
+@@ -3,8 +3,8 @@ package org.ksoap2.serialization;
+
+ import java.util.Vector;
+
+-public class AttributeContainer {
+- private Vector attributes = new Vector();
++public class AttributeContainer implements HasAttributes{
++ protected Vector attributes = new Vector();
+
+ /**
+ * Places AttributeInfo of desired attribute into a designated AttributeInfo object
+@@ -29,9 +29,9 @@ public class AttributeContainer {
+ return ((AttributeInfo) attributes.elementAt(index)).getValue();
+ }
+
+- /**
+- * Get the attribute's toString value.
+- */
++ /**
++ * Get the attribute's toString value.
++ */
+ public String getAttributeAsString(int index) {
+ AttributeInfo attributeInfo = (AttributeInfo) attributes.elementAt(index);
+ return attributeInfo.getValue().toString();
+@@ -51,6 +51,20 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Get the attribute with the given name
++ *
++ * @throws RuntimeException if the attribute does not exist
++ */
++ public Object getAttribute(String namespace,String name) {
++ Integer i = attributeIndex(namespace,name);
++ if (i != null) {
++ return getAttribute(i.intValue());
++ } else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
++
+ /**
+ * Get the toString value of the attribute with the given name.
+ *
+@@ -65,6 +79,19 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Get the toString value of the attribute with the given name.
++ *
++ * @throws RuntimeException if the attribute does not exist
++ */
++ public String getAttributeAsString(String namespace,String name) {
++ Integer i = attributeIndex(namespace,name);
++ if (i != null) {
++ return getAttribute(i.intValue()).toString();
++ } else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
+ /**
+ * Knows whether the given attribute exists
+ */
+@@ -76,6 +103,16 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Knows whether the given attribute exists
++ */
++ public boolean hasAttribute(final String namespace,final String name) {
++ if (attributeIndex(namespace,name) != null) {
++ return true;
++ } else {
++ return false;
++ }
++ }
+ /**
+ * Get an attribute without chance of throwing an exception
+ *
+@@ -91,6 +128,21 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Get an attribute without chance of throwing an exception
++ *
++ * @param name the name of the attribute to retrieve
++ * @return the value of the attribute if it exists; {@code null} if it does not exist
++ */
++ public Object getAttributeSafely(String namespace,String name) {
++ Integer i = attributeIndex(namespace,name);
++ if (i != null) {
++ return getAttribute(i.intValue());
++ } else {
++ return null;
++ }
++ }
++
+ /**
+ * Get an attributes' toString value without chance of throwing an
+ * exception.
+@@ -108,6 +160,23 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Get an attributes' toString value without chance of throwing an
++ * exception.
++
++ * @param name
++ * @return the value of the attribute,s toString method if it exists; ""
++ * if it does not exist
++ */
++ public Object getAttributeSafelyAsString(String namespace,String name) {
++ Integer i = attributeIndex(namespace,name);
++ if (i != null) {
++ return getAttribute(i.intValue()).toString();
++ } else {
++ return "";
++ }
++ }
++
+ private Integer attributeIndex(String name) {
+ for (int i = 0; i < attributes.size(); i++) {
+ if (name.equals(((AttributeInfo) attributes.elementAt(i)).getName())) {
+@@ -117,6 +186,16 @@ public class AttributeContainer {
+ return null;
+ }
+
++ private Integer attributeIndex(String namespace,String name) {
++ for (int i = 0; i < attributes.size(); i++) {
++ AttributeInfo attrInfo=(AttributeInfo) attributes.elementAt(i);
++ if (name.equals(attrInfo.getName()) && namespace.equals(attrInfo.getNamespace())) {
++ return new Integer(i);
++ }
++ }
++ return null;
++ }
++
+ /**
+ * Returns the number of attributes
+ *
+@@ -160,13 +239,25 @@ public class AttributeContainer {
+ * @return {@code this} object.
+ */
+ public void addAttribute(String name, Object value) {
++ addAttribute(null,name,value);
++ }
++
++ /**
++ * Adds a attribute (parameter) to the object.
++ *
++ * @param namespace The namespace of the attribute
++ * @param name The name of the attribute
++ * @param value the value of the attribute
++ * @return {@code this} object.
++ */
++ public void addAttribute(String namespace,String name, Object value) {
+ AttributeInfo attributeInfo = new AttributeInfo();
+ attributeInfo.name = name;
++ attributeInfo.namespace = namespace;
+ attributeInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass();
+ attributeInfo.value = value;
+ addAttribute(attributeInfo);
+ }
+-
+ /**
+ * Add an attribute if the value is not null.
+ * @param name
+@@ -178,6 +269,18 @@ public class AttributeContainer {
+ }
+ }
+
++ /**
++ * Add an attribute if the value is not null.
++ * @param namespace The namespace of the attribute
++ * @param name
++ * @param value
++ */
++ public void addAttributeIfValue(String namespace,String name, Object value) {
++ if (value != null) {
++ addAttribute(namespace,name, value);
++ }
++ }
++
+ /**
+ * Add a new attribute by providing an {@link AttributeInfo} object. {@code AttributeInfo}
+ * contains all data about the attribute, including name and value.}
+@@ -198,4 +301,17 @@ public class AttributeContainer {
+ attributes.addElement(attributeInfo);
+ }
+ }
++
++
++ public void setAttribute(AttributeInfo info) {
++
++
++ }
++
++
++ public void getAttribute(int index, AttributeInfo info) {
++
++
++ }
++
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
+index 78d4449..255126e 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
+@@ -20,9 +20,12 @@
+
+ package org.ksoap2.serialization;
+
+-import java.io.*;
+-import org.xmlpull.v1.*;
+-import org.ksoap2.*;
++import java.io.IOException;
++
++import org.ksoap2.SoapEnvelope;
++import org.xmlpull.v1.XmlPullParser;
++import org.xmlpull.v1.XmlPullParserException;
++import org.xmlpull.v1.XmlSerializer;
+
+ /**
+ * This class is not public, so save a few bytes by using a short class name (DM
+@@ -30,8 +33,7 @@ import org.ksoap2.*;
+ */
+ class DM implements Marshal {
+
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo excepted)
+ throws IOException, XmlPullParserException {
+ String text = parser.nextText();
+ switch (name.charAt(0)) {
+@@ -49,9 +51,10 @@ class DM implements Marshal {
+ }
+
+ /**
+- * Write the instance out. In case it is an AttributeContainer write those our first though.
+- * @param writer
+- * the xml serializer.
++ * Write the instance out. In case it is an AttributeContainer write those our first though.
++ * If it HasAttributes then write the attributes and values.
++ *
++ * @param writer the xml serializer.
+ * @param instance
+ * @throws IOException
+ */
+@@ -62,11 +65,43 @@ class DM implements Marshal {
+ for (int counter = 0; counter < cnt; counter++) {
+ AttributeInfo attributeInfo = new AttributeInfo();
+ attributeContainer.getAttributeInfo(counter, attributeInfo);
+- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
+- attributeInfo.getValue().toString());
++ try {
++ attributeContainer.getAttribute(counter, attributeInfo);
++ } catch (Exception e) {
++ e.printStackTrace();
++ }
++ if (attributeInfo.getValue() != null) {
++ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
++ (attributeInfo.getValue() != null) ? attributeInfo.getValue().toString() : "");
++ }
++ }
++ } else if (instance instanceof HasAttributes) {
++ HasAttributes soapObject = (HasAttributes) instance;
++ int cnt = soapObject.getAttributeCount();
++ for (int counter = 0; counter < cnt; counter++) {
++ AttributeInfo attributeInfo = new AttributeInfo();
++ soapObject.getAttributeInfo(counter, attributeInfo);
++ try {
++ soapObject.getAttribute(counter, attributeInfo);
++ } catch (Exception e) {
++ e.printStackTrace();
++ }
++ if (attributeInfo.getValue() != null) {
++ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
++ attributeInfo.getValue() != null ? attributeInfo.getValue().toString() : "");
++ }
+ }
+ }
+- writer.text(instance.toString());
++
++ if(instance instanceof ValueWriter)
++ {
++ ((ValueWriter)instance).write(writer);
++ }
++ else
++ {
++ writer.text(instance.toString());
++ }
++
+ }
+
+ public void register(SoapSerializationEnvelope cm) {
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
+new file mode 100644
+index 0000000..b513138
+--- /dev/null
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
+@@ -0,0 +1,16 @@
++package org.ksoap2.serialization;
++
++/**
++ * Common inteface for classes which want to serialize attributes to outgoing soap message
++ *
++ * @author robocik
++ */
++public interface HasAttributes {
++ int getAttributeCount();
++
++ void getAttributeInfo(int index, AttributeInfo info);
++
++ void getAttribute(int index, AttributeInfo info);
++
++ void setAttribute(AttributeInfo info);
++}
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
+new file mode 100644
+index 0000000..b35c35b
+--- /dev/null
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
+@@ -0,0 +1,17 @@
++package org.ksoap2.serialization;
++/**
++ * Interface for classes requiring inner text of xml tags
++ *
++ * @author satansly
++ */
++public interface HasInnerText {
++ /**
++ * Gets the inner text of xml tags
++ */
++ Object getInnerText();
++
++ /**
++ * @param s String to be set as inner text for an outgoing soap object
++ */
++ void setInnerText(Object s);
++}
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
+index bded0c0..09d7b32 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
+@@ -39,31 +39,26 @@ public interface KvmSerializable {
+ */
+ Object getProperty(int index);
+
+- /**
+- * @return the number of serializable properties
++ /**
++ * @return the number of serializable properties
+ */
+ int getPropertyCount();
+
+ /**
+ * Sets the property with the given index to the given value.
+- *
+- * @param index
+- * the index to be set
+- * @param value
+- * the value of the property
++ *
++ * @param index the index to be set
++ * @param value the value of the property
+ */
+ void setProperty(int index, Object value);
+
+ /**
+ * Fills the given property info record.
+- *
+- * @param index
+- * the index to be queried
+- * @param properties
+- * information about the (de)serializer. Not frequently used.
+- * @param info
+- * The return parameter, to be filled with information about the
+- * property with the given index.
++ *
++ * @param index the index to be queried
++ * @param properties information about the (de)serializer. Not frequently used.
++ * @param info The return parameter, to be filled with information about the
++ * property with the given index.
+ */
+ void getPropertyInfo(int index, Hashtable properties, PropertyInfo info);
+
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
+index cfa9d81..100f107 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
+@@ -41,8 +41,7 @@ public interface Marshal {
+ * the namespace.
+ * @return the object read from the xml stream.
+ */
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
+ throws IOException, XmlPullParserException;
+
+ /**
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
+index 2f8420c..2239027 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
+@@ -31,8 +31,7 @@ import org.xmlpull.v1.*;
+ public class MarshalBase64 implements Marshal {
+ public static Class BYTE_ARRAY_CLASS = new byte[0].getClass();
+
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
+ throws IOException, XmlPullParserException {
+ return Base64.decode(parser.nextText());
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
+index 3e4fa06..489ba3b 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
+@@ -31,8 +31,7 @@ import org.ksoap2.kobjects.isodate.*;
+ public class MarshalDate implements Marshal {
+ public static Class DATE_CLASS = new Date().getClass();
+
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
+ throws IOException, XmlPullParserException {
+ return IsoDate.stringToDate(parser.nextText(), IsoDate.DATE_TIME);
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
+index d2367e9..0c6b53e 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
+@@ -46,8 +46,7 @@ public class MarshalHashtable implements Marshal {
+ public static final Class HASHTABLE_CLASS = new Hashtable().getClass();
+ SoapSerializationEnvelope envelope;
+
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
+ throws IOException, XmlPullParserException {
+ Hashtable instance = new Hashtable();
+ String elementName = parser.getName();
+@@ -81,7 +80,7 @@ public class MarshalHashtable implements Marshal {
+ Object key = keys.nextElement();
+ item.setProperty(0, key);
+ item.setProperty(1, h.get(key));
+- envelope.writeObjectBody(writer, item);
++ envelope.writeObjectBodyWithAttributes(writer, item);
+ writer.endTag("", "item");
+ }
+ }
+@@ -89,7 +88,6 @@ public class MarshalHashtable implements Marshal {
+ class ItemSoapObject extends SoapObject {
+ Hashtable h;
+ int resolvedIndex = -1;
+-
+ ItemSoapObject(Hashtable h) {
+ super(null, null);
+ this.h = h;
+@@ -107,7 +105,7 @@ public class MarshalHashtable implements Marshal {
+ Object resolved = resolvedIndex == 0 ? getProperty(0) : getProperty(1);
+ if (index == 0) {
+ h.put(value, resolved);
+- } else {
++ } else {
+ h.put(resolved, value);
+ }
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
+index 24a1ffe..f11210a 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
+@@ -38,7 +38,7 @@ import java.util.*;
+ * KvmSerializable interface.
+ */
+
+-public class SoapObject extends AttributeContainer implements KvmSerializable {
++public class SoapObject extends AttributeContainer implements KvmSerializable, HasInnerText {
+
+ private static final String EMPTY_STRING = "";
+ /**
+@@ -54,6 +54,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ */
+ protected Vector properties = new Vector();
+
++ protected Object innerText;
++
+ // TODO: accessing properties and attributes would work much better if we
+ // kept a list of known properties instead of iterating through the list
+ // each time
+@@ -181,6 +183,230 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ }
+ }
+
++ /**
++ * Get the property with the given name
++ *
++ * return null
++ * if the property does not exist
++ */
++ public Object getProperty(String namespace,String name) {
++ Integer index = propertyIndex(namespace,name);
++ if (index != null) {
++ return getProperty(index.intValue());
++ }
++ else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
++
++ /**
++ * Get a property using namespace and name without chance of throwing an exception
++ *
++ * @return the property if it exists; if not, {@link NullSoapObject} is
++ * returned
++ */
++ public Object getPropertyByNamespaceSafely(final String namespace, final String name) {
++ Integer i = propertyIndex(namespace,name);
++ if (i != null) {
++ return getProperty(i.intValue());
++ } else {
++ return new NullSoapObject();
++ }
++ }
++
++ /**
++ * Get the toString value of a property without chance of throwing an
++ * exception
++ *
++ * @return the string value of the property if it exists; if not, #EMPTY_STRING is
++ * returned
++ */
++ public String getPropertyByNamespaceSafelyAsString(final String namespace,final String name) {
++ Integer i = propertyIndex(namespace,name);
++ if (i != null) {
++ Object foo = getProperty(i.intValue());
++ if (foo == null) {
++ return EMPTY_STRING;
++ } else {
++ return foo.toString();
++ }
++ } else {
++ return EMPTY_STRING;
++ }
++ }
++
++ /**
++ * Get a property without chance of throwing an exception. An object can be
++ * provided to this method; if the property is not found, this object will
++ * be returned.
++ *
++ * @param defaultThing
++ * the object to return if the property is not found
++ * @return the property if it exists; defaultThing if the property does not
++ * exist
++ */
++ public Object getPropertySafely(final String namespace,final String name, final Object defaultThing) {
++ Integer i = propertyIndex(namespace,name);
++ if (i != null) {
++ return getProperty(i.intValue());
++ } else {
++ return defaultThing;
++ }
++ }
++
++ /**
++ * Get the toString value of a property without chance of throwing an
++ * exception. An object can be provided to this method; if the property is
++ * not found, this object's string representation will be returned.
++ *
++ * @param defaultThing
++ * toString of the object to return if the property is not found
++ * @return the property toString if it exists; defaultThing toString if the
++ * property does not exist, if the defaultThing is null #EMPTY_STRING
++ * is returned
++ */
++ public String getPropertySafelyAsString(final String namespace,final String name,
++ final Object defaultThing) {
++ Integer i = propertyIndex(namespace,name);
++ if (i != null) {
++ Object property = getProperty(i.intValue());
++ if (property != null) {
++ return property.toString();
++ } else {
++ return EMPTY_STRING;
++ }
++ } else {
++ if (defaultThing != null) {
++ return defaultThing.toString();
++ } else {
++ return EMPTY_STRING;
++ }
++ }
++ }
++
++ /**
++ * Get the primitive property with the given name.
++ *
++ * @param name
++ * @return PropertyInfo containing an empty string if property either complex or empty
++ */
++ public Object getPrimitiveProperty(final String namespace,final String name){
++ Integer index = propertyIndex(namespace,name);
++ if (index != null){
++ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
++ return propertyInfo.getValue();
++ } else {
++ propertyInfo = new PropertyInfo();
++ propertyInfo.setType(String.class);
++ propertyInfo.setValue(EMPTY_STRING);
++ propertyInfo.setName(name);
++ propertyInfo.setNamespace(namespace);
++ return (Object) propertyInfo.getValue();
++ }
++ } else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
++
++ /**
++ * Get the toString value of the primitive property with the given name.
++ * Returns empty string if property either complex or empty
++ *
++ * @param name
++ * @return the string value of the property
++ */
++ public String getPrimitivePropertyAsString(final String namespace,final String name){
++ Integer index = propertyIndex(namespace,name);
++ if (index != null){
++ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
++ return propertyInfo.getValue().toString();
++ } else {
++ return EMPTY_STRING;
++ }
++ } else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
++
++ /**
++ * Get the toString value of a primitive property without chance of throwing an
++ * exception
++ *
++ * @param name
++ * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
++ * returned
++ */
++ public Object getPrimitivePropertySafely(final String namespace,final String name) {
++ Integer index = propertyIndex(namespace,name);
++ if (index != null){
++ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
++ return propertyInfo.getValue().toString();
++ } else {
++ propertyInfo = new PropertyInfo();
++ propertyInfo.setType(String.class);
++ propertyInfo.setValue(EMPTY_STRING);
++ propertyInfo.setName(name);
++ propertyInfo.setNamespace(namespace);
++ return (Object) propertyInfo.getValue();
++ }
++ } else {
++ return new NullSoapObject();
++ }
++ }
++
++ /**
++ * Get the toString value of a primitive property without chance of throwing an
++ * exception
++ *
++ * @param name
++ * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
++ * returned
++ */
++ public String getPrimitivePropertySafelyAsString(final String namespace,final String name) {
++ Integer index = propertyIndex(namespace,name);
++ if (index != null){
++ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
++ return propertyInfo.getValue().toString();
++ } else {
++ return EMPTY_STRING;
++ }
++ } else {
++ return EMPTY_STRING;
++ }
++ }
++
++ /**
++ * Knows whether the given property exists
++ */
++ public boolean hasProperty(final String namespace,final String name) {
++ if (propertyIndex(namespace,name) != null) {
++ return true;
++ } else {
++ return false;
++ }
++ }
++
++ /**
++ * Get the toString value of the property.
++ *
++ * @param namespace
++ * @param name
++ * @return
++ */
++
++ public String getPropertyAsString(String namespace,String name) {
++ Integer index = propertyIndex(namespace,name);
++ if (index != null) {
++ return getProperty(index.intValue()).toString();
++ } else {
++ throw new RuntimeException("illegal property: " + name);
++ }
++ }
++
+ /**
+ * Get the toString value of the property.
+ *
+@@ -301,9 +527,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ */
+ public Object getPrimitiveProperty(final String name) {
+ Integer index = propertyIndex(name);
+- if (index != null) {
++ if (index != null){
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+- if (propertyInfo.getType() != SoapObject.class) {
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
+ return propertyInfo.getValue();
+ } else {
+ propertyInfo = new PropertyInfo();
+@@ -326,9 +552,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ */
+ public String getPrimitivePropertyAsString(final String name) {
+ Integer index = propertyIndex(name);
+- if (index != null) {
++ if (index != null){
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+- if (propertyInfo.getType() != SoapObject.class) {
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
+ return propertyInfo.getValue().toString();
+ } else {
+ return EMPTY_STRING;
+@@ -348,9 +574,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ */
+ public Object getPrimitivePropertySafely(final String name) {
+ Integer index = propertyIndex(name);
+- if (index != null) {
++ if (index != null){
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+- if (propertyInfo.getType() != SoapObject.class) {
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
+ return propertyInfo.getValue().toString();
+ } else {
+ propertyInfo = new PropertyInfo();
+@@ -374,9 +600,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ */
+ public String getPrimitivePropertySafelyAsString(final String name) {
+ Integer index = propertyIndex(name);
+- if (index != null) {
++ if (index != null){
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+- if (propertyInfo.getType() != SoapObject.class) {
++ if (propertyInfo.getType()!=SoapObject.class && propertyInfo.getValue()!=null){
+ return propertyInfo.getValue().toString();
+ } else {
+ return EMPTY_STRING;
+@@ -397,6 +623,18 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ return null;
+ }
+
++
++ private Integer propertyIndex(String namespace,String name) {
++ if (name != null && namespace!=null) {
++ for (int i = 0; i < properties.size(); i++) {
++ PropertyInfo info= (PropertyInfo) properties.elementAt(i);
++ if (name.equals(info.getName()) && namespace.equals(info.getNamespace())) {
++ return new Integer(i);
++ }
++ }
++ }
++ return null;
++ }
+ /**
+ * Returns the number of properties
+ *
+@@ -453,6 +691,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ }
+ }
+
++ public PropertyInfo getPropertyInfo(int index) {
++ Object element = properties.elementAt(index);
++ if (element instanceof PropertyInfo) {
++ PropertyInfo p = (PropertyInfo) element;
++ return p;
++ } else {
++ // SoapObject
++ return null;
++ }
++ }
++
+ /**
+ * Creates a new SoapObject based on this, allows usage of SoapObjects as
+ * templates. One application is to set the expected return type of a soap
+@@ -466,17 +715,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ Object prop = properties.elementAt(propIndex);
+ if (prop instanceof PropertyInfo) {
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(propIndex);
+- PropertyInfo propertyInfoClonned = (PropertyInfo) propertyInfo.clone();
+- o.addProperty(propertyInfoClonned);
+- } else if (prop instanceof SoapObject) {
+- o.addSoapObject(((SoapObject) prop).newInstance());
++ PropertyInfo propertyInfoClonned = (PropertyInfo)propertyInfo.clone();
++ o.addProperty( propertyInfoClonned );
++ } else if(prop instanceof SoapObject) {
++ o.addSoapObject(((SoapObject)prop).newInstance());
+ }
+ }
+ for (int attribIndex = 0; attribIndex < getAttributeCount(); attribIndex++) {
+ AttributeInfo newAI = new AttributeInfo();
+ getAttributeInfo(attribIndex, newAI);
+ AttributeInfo attributeInfo = newAI; // (AttributeInfo)
+- // attributes.elementAt(attribIndex);
++ // attributes.elementAt(attribIndex);
+ o.addAttribute(attributeInfo);
+ }
+ return o;
+@@ -513,11 +762,49 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
+ .getClass();
+ propertyInfo.value = value;
++ return addProperty(propertyInfo);
++ }
++
++ /**
++ * Adds a property (parameter) to the object. This is essentially a sub
++ * element.
++ *
++ * @param namespace
++ * The namespace of the property
++ * @param name
++ * The name of the property
++ * @param value
++ * the value of the property
++ */
++ public SoapObject addProperty(String namespace,String name, Object value) {
++ PropertyInfo propertyInfo = new PropertyInfo();
++ propertyInfo.name = name;
+ propertyInfo.namespace = namespace;
+- ///M: HS20 modify by Jungo
++ propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
++ .getClass();
++ propertyInfo.value = value;
+ return addProperty(propertyInfo);
+ }
+
++ /**
++ * Add a property only if the value is not null.
++ *
++ * @param namespace
++ * The namespace of the property
++ * @param name
++ * The name of the property
++ * @param value
++ * the value of the property
++ * @return
++ */
++ public SoapObject addPropertyIfValue(String namespace,String name, Object value) {
++ if (value != null) {
++ return addProperty(namespace,name, value);
++ } else {
++ return this;
++ }
++ }
++
+ /**
+ * Add a property only if the value is not null.
+ *
+@@ -597,12 +884,12 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{");
+ for (int i = 0; i < getPropertyCount(); i++) {
+ Object prop = properties.elementAt(i);
+- if (prop instanceof PropertyInfo) {
++ if(prop instanceof PropertyInfo) {
+ buf.append(EMPTY_STRING)
+- .append(((PropertyInfo) prop).getName())
+- .append("=")
+- .append(getProperty(i))
+- .append("; ");
++ .append(((PropertyInfo) prop).getName())
++ .append("=")
++ .append(getProperty(i))
++ .append("; ");
+ } else {
+ buf.append(((SoapObject) prop).toString());
+ }
+@@ -610,4 +897,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
+ buf.append("}");
+ return buf.toString();
+ }
++ public Object getInnerText() {
++ return innerText;
++ }
++
++ public void setInnerText(Object innerText)
++ {
++ this.innerText=innerText;
++ }
++
++ public void removePropertyInfo(Object info)
++ {
++ properties.remove(info);
++ }
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
+index d09f7a7..32fe333 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
+@@ -34,11 +34,14 @@ package org.ksoap2.serialization;
+ */
+
+ public class SoapPrimitive extends AttributeContainer {
+- String namespace;
+- String name;
+- String value;
++ protected String namespace;
++ protected String name;
++ protected Object value;
+
+- public SoapPrimitive(String namespace, String name, String value) {
++ public static final Object NullSkip = new Object();
++ public static final Object NullNilElement = new Object();
++
++ public SoapPrimitive(String namespace, String name, Object value) {
+ this.namespace = namespace;
+ this.name = name;
+ this.value = value;
+@@ -50,7 +53,7 @@ public class SoapPrimitive extends AttributeContainer {
+ }
+ SoapPrimitive p = (SoapPrimitive) o;
+ boolean varsEqual = name.equals(p.name)
+- && (namespace == null ? p.namespace == null : namespace.equals(p.namespace))
++ && (namespace == null ? p.namespace == null:namespace.equals(p.namespace))
+ && (value == null ? (p.value == null) : value.equals(p.value));
+ return varsEqual && attributesAreEqual(p);
+ }
+@@ -60,7 +63,7 @@ public class SoapPrimitive extends AttributeContainer {
+ }
+
+ public String toString() {
+- return value;
++ return value != null ? value.toString() : null;
+ }
+
+ public String getNamespace() {
+@@ -70,4 +73,9 @@ public class SoapPrimitive extends AttributeContainer {
+ public String getName() {
+ return name;
+ }
++
++ public Object getValue() {
++ return value;
++ }
++
+ }
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
+index dae09d2..ceeb3f4 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
+@@ -27,9 +27,9 @@ import org.xmlpull.v1.XmlPullParserException;
+ import org.xmlpull.v1.XmlSerializer;
+
+ import java.io.IOException;
++import java.util.ArrayList;
+ import java.util.Hashtable;
+ import java.util.Vector;
+-import java.io.ByteArrayOutputStream;
+
+ import org.kxml2.io.*;
+
+@@ -43,29 +43,29 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ protected static final int QNAME_TYPE = 1;
+ protected static final int QNAME_NAMESPACE = 0;
+ protected static final int QNAME_MARSHAL = 3;
++ protected static final String NULL_LABEL = "null";
++ protected static final String NIL_LABEL = "nil";
++ static final Marshal DEFAULT_MARSHAL = new DM();
+ private static final String ANY_TYPE_LABEL = "anyType";
+ private static final String ARRAY_MAPPING_NAME = "Array";
+- private static final String NULL_LABEL = "null";
+- private static final String NIL_LABEL = "nil";
+ private static final String HREF_LABEL = "href";
+ private static final String ID_LABEL = "id";
+ private static final String ROOT_LABEL = "root";
+ private static final String TYPE_LABEL = "type";
+ private static final String ITEM_LABEL = "item";
+ private static final String ARRAY_TYPE_LABEL = "arrayType";
+- static final Marshal DEFAULT_MARSHAL = new DM();
+ public Hashtable properties = new Hashtable();
+-
+- Hashtable idMap = new Hashtable();
+- Vector multiRef; // = new Vector();
+-
+ /**
+ * Set this variable to true if you don't want that type definitions for complex types/objects
+ * are automatically generated (with type "anyType") in the XML-Request, if you don't call the
+ * Method addMapping. This is needed by some Servers which have problems with these type-definitions.
+ */
+ public boolean implicitTypes;
+-
++ /**
++ * If set to true then all properties with null value will be skipped from the soap message.
++ * If false then null properties will be sent as <element nil="true" />
++ */
++ public boolean skipNullProperties;
+ /**
+ * Set this variable to true for compatibility with what seems to be the default encoding for
+ * .Net-Services. This feature is an extremely ugly hack. A much better option is to change the
+@@ -96,9 +96,10 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ * Set to true to add and ID and ROOT label to the envelope. Change to false for compatibility with WSDL.
+ */
+ protected boolean addAdornments = true;
++ Hashtable idMap = new Hashtable();
++ Vector multiRef; // = new Vector();
+
+- public SoapSerializationEnvelope(int version)
+- {
++ public SoapSerializationEnvelope(int version) {
+ super(version);
+ addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS);
+ DEFAULT_MARSHAL.register(this);
+@@ -107,23 +108,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ /**
+ * @return the addAdornments
+ */
+- public boolean isAddAdornments()
+- {
++ public boolean isAddAdornments() {
+ return addAdornments;
+ }
+
+ /**
+- * @param addAdornments
+- * the addAdornments to set
++ * @param addAdornments the addAdornments to set
+ */
+- public void setAddAdornments(boolean addAdornments)
+- {
++ public void setAddAdornments(boolean addAdornments) {
+ this.addAdornments = addAdornments;
+ }
+
+ /**
+ * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut will
+ * cause #writeBody to skip writing anything redundant.
++ *
+ * @param emptyBody
+ * @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77"
+ */
+@@ -133,8 +132,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ }
+
+- public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException
+- {
++ public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException {
+ bodyIn = null;
+ parser.nextTag();
+ if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env)
+@@ -161,10 +159,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ }
+
+- /** Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. */
++ /**
++ * Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable.
++ */
+ protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException,
+- XmlPullParserException
+- {
++ XmlPullParserException {
+ for (int counter = 0; counter < parser.getAttributeCount(); counter++) {
+ String attributeName = parser.getAttributeName(counter);
+ String value = parser.getAttributeValue(counter);
+@@ -173,11 +172,21 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ readSerializable(parser, (KvmSerializable) obj);
+ }
+
+- /** Read a KvmSerializable. */
++ /**
++ * Read a KvmSerializable.
++ */
+ protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException,
+- XmlPullParserException
+- {
+- while (parser.nextTag() != XmlPullParser.END_TAG) {
++ XmlPullParserException {
++ int tag = 0;
++ try {
++ tag = parser.nextTag();
++ } catch (XmlPullParserException e) {
++ if(obj instanceof HasInnerText){
++ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
++ }
++ tag = parser.nextTag();
++ }
++ while (tag != XmlPullParser.END_TAG) {
+ String name = parser.getName();
+ if (!implicitTypes || !(obj instanceof SoapObject)) {
+ PropertyInfo info = new PropertyInfo();
+@@ -188,8 +197,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ info.clear();
+ obj.getPropertyInfo(i, properties, info);
+
+- if ((name.equals(info.name) && info.namespace == null)
+- ||
++ if ((name.equals(info.name) && info.namespace == null) ||
+ (name.equals(info.name) && parser.getNamespace().equals(info.namespace))) {
+ propertyFound = true;
+ obj.setProperty(i, read(parser, obj, i, null, null, info));
+@@ -199,21 +207,42 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ if (!propertyFound) {
+ if (avoidExceptionForUnknownProperty) {
+ // Dummy loop to read until corresponding END tag
+- while (parser.next() != XmlPullParser.END_TAG
+- || !name.equals(parser.getName())) {
++ while (parser.next() != XmlPullParser.END_TAG || !name.equals(parser.getName())) {
+ }
+ ;
+ } else {
+ throw new RuntimeException("Unknown Property: " + name);
+ }
++ } else {
++ if (obj instanceof HasAttributes) {
++ HasAttributes soapObject = (HasAttributes) obj;
++ int cnt = parser.getAttributeCount();
++ for (int counter = 0; counter < cnt; counter++) {
++ AttributeInfo attributeInfo = new AttributeInfo();
++ attributeInfo.setName(parser.getAttributeName(counter));
++ attributeInfo.setValue(parser.getAttributeValue(counter));
++ attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
++ attributeInfo.setType(parser.getAttributeType(counter));
++ soapObject.setAttribute(attributeInfo);
++
++ }
++ }
+ }
+ } else {
+ // I can only make this work for SoapObjects - hence the check above
+ // I don't understand namespaces well enough to know whether it is correct in the next line...
+- ((SoapObject) obj).addProperty(parser.getName(),
+- read(parser, obj, obj.getPropertyCount(),
+- ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE));
++ ((SoapObject) obj).addProperty(parser.getName(), read(parser, obj, obj.getPropertyCount(),
++ ((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE));
++ }
++ try {
++ tag = parser.nextTag();
++ } catch (XmlPullParserException e) {
++ if(obj instanceof HasInnerText){
++ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
++ }
++ tag = parser.nextTag();
+ }
++
+ }
+ parser.require(XmlPullParser.END_TAG, null, null);
+ }
+@@ -278,9 +307,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+
+ while (parser.getEventType() != XmlPullParser.END_TAG) {
+- so.addProperty(parser.getName(),
+- read(parser, so, so.getPropertyCount(), null, null,
+- PropertyInfo.OBJECT_TYPE));
++ so.addProperty(parser.getNamespace(),parser.getName(), read(parser, so, so.getPropertyCount(),
++ null, null, PropertyInfo.OBJECT_TYPE));
+ parser.nextTag();
+ }
+ result = so;
+@@ -293,12 +321,15 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ if (value == null) {
+ return dflt;
+ }
+- return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
+- value.length() - 1));
++ try {
++ return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
++ value.length() - 1));
++ } catch (Exception ex) {
++ return dflt;
++ }
+ }
+
+- protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType)
+- throws IOException,
++ protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType) throws IOException,
+ XmlPullParserException {
+ String namespace = null;
+ String name = null;
+@@ -337,14 +368,23 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ parser.require(XmlPullParser.END_TAG, null, null);
+ }
+
++ /**
++ * This method returns id from the href attribute value.
++ * By default we assume that href value looks like this: #id so we basically have to remove the first character.
++ * But in theory there could be a different value format, like cid:value, etc...
++ */
++ protected String getIdFromHref(String hrefValue) {
++ return hrefValue.substring(1);
++ }
++
+ /**
+ * Builds an object from the XML stream. This method is public for usage in conjuction with Marshal
+ * subclasses. Precondition: On the start tag of the object or property, so href can be read.
+ */
+
+- public Object read(XmlPullParser parser, Object owner, int index, String namespace,
+- String name,
+- PropertyInfo expected) throws IOException, XmlPullParserException {
++ public Object read(XmlPullParser parser, Object owner, int index, String namespace, String name,
++ PropertyInfo expected)
++ throws IOException, XmlPullParserException {
+ String elementName = parser.getName();
+ String href = parser.getAttributeValue(null, HREF_LABEL);
+ Object obj;
+@@ -352,7 +392,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ if (owner == null) {
+ throw new RuntimeException("href at root level?!?");
+ }
+- href = href.substring(1);
++ href = getIdFromHref(href);
+ obj = idMap.get(href);
+ if (obj == null || obj instanceof FwdRef) {
+ FwdRef f = new FwdRef();
+@@ -402,21 +442,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ // finally, care about the id....
+ if (id != null) {
+- Object hlp = idMap.get(id);
+- if (hlp instanceof FwdRef) {
+- FwdRef f = (FwdRef) hlp;
+- do {
+- if (f.obj instanceof KvmSerializable) {
+- ((KvmSerializable) f.obj).setProperty(f.index, obj);
+- } else {
+- ((Vector) f.obj).setElementAt(obj, f.index);
+- }
+- f = f.next;
+- } while (f != null);
+- } else if (hlp != null) {
+- throw new RuntimeException("double ID");
+- }
+- idMap.put(id, obj);
++ resolveReference(id, obj);
++
+ }
+ }
+
+@@ -424,12 +451,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ return obj;
+ }
+
++ protected void resolveReference(String id, Object obj) {
++ Object hlp = idMap.get(id);
++ if (hlp instanceof FwdRef) {
++ FwdRef f = (FwdRef) hlp;
++ do {
++ if (f.obj instanceof KvmSerializable) {
++ ((KvmSerializable) f.obj).setProperty(f.index, obj);
++ } else {
++ ((Vector) f.obj).setElementAt(obj, f.index);
++ }
++ f = f.next;
++ }
++ while (f != null);
++ } else if (hlp != null) {
++ throw new RuntimeException("double ID");
++ }
++ idMap.put(id, obj);
++ }
++
+ /**
+ * Returns a new object read from the given parser. If no mapping is found, null is returned. This method
+ * is used by the SoapParser in order to convert the XML code to Java objects.
+ */
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo expected)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo expected)
+ throws IOException, XmlPullParserException {
+ Object obj = qNameToClass.get(new SoapPrimitive(namespace, name, null));
+ if (obj == null) {
+@@ -448,10 +493,30 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ throw new RuntimeException(e.toString());
+ }
+ }
++ if (obj instanceof HasAttributes) {
++ HasAttributes soapObject = (HasAttributes) obj;
++ int cnt = parser.getAttributeCount();
++ for (int counter = 0; counter < cnt; counter++) {
++
++ AttributeInfo attributeInfo = new AttributeInfo();
++ attributeInfo.setName(parser.getAttributeName(counter));
++ attributeInfo.setValue(parser.getAttributeValue(counter));
++ attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
++ attributeInfo.setType(parser.getAttributeType(counter));
++
++ soapObject.setAttribute(attributeInfo);
++
++ }
++ }
++
+ // ok, obj is now the instance, fill it....
+ if (obj instanceof SoapObject) {
+ readSerializable(parser, (SoapObject) obj);
+ } else if (obj instanceof KvmSerializable) {
++
++ if(obj instanceof HasInnerText){
++ ((HasInnerText)obj).setInnerText((parser.getText() != null) ? parser.getText() : "");
++ }
+ readSerializable(parser, (KvmSerializable) obj);
+ } else if (obj instanceof Vector) {
+ readVector(parser, (Vector) obj, expected.elementType);
+@@ -476,15 +541,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ if (type instanceof SoapObject) {
+ SoapObject so = (SoapObject) type;
+- return new Object[] {
+- so.getNamespace(), so.getName(), null, null
+- };
++ return new Object[]{so.getNamespace(), so.getName(), null, null};
+ }
+ if (type instanceof SoapPrimitive) {
+ SoapPrimitive sp = (SoapPrimitive) type;
+- return new Object[] {
+- sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL
+- };
++ return new Object[]{sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL};
+ }
+ if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) {
+ Object[] tmp = (Object[]) classToQName.get(((Class) type).getName());
+@@ -492,9 +553,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ return tmp;
+ }
+ }
+- return new Object[] {
+- xsd, ANY_TYPE_LABEL, null, null
+- };
++ return new Object[]{xsd, ANY_TYPE_LABEL, null, null};
+ }
+
+ /**
+@@ -503,11 +562,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ */
+ public void addMapping(String namespace, String name, Class clazz, Marshal marshal) {
+ qNameToClass
+- .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz
+- : marshal);
+- classToQName.put(clazz.getName(), new Object[] {
+- namespace, name, null, marshal
+- });
++ .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz : marshal);
++ classToQName.put(clazz.getName(), new Object[]{namespace, name, null, marshal});
+ }
+
+ /**
+@@ -528,11 +584,14 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ /**
+ * Response from the soap call. Pulls the object from the wrapper object and returns it.
+ *
+- * @since 2.0.3
+ * @return response from the soap call.
+ * @throws SoapFault
++ * @since 2.0.3
+ */
+ public Object getResponse() throws SoapFault {
++ if (bodyIn == null) {
++ return null;
++ }
+ if (bodyIn instanceof SoapFault) {
+ throw (SoapFault) bodyIn;
+ }
+@@ -554,8 +613,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ /**
+ * Serializes the request object to the given XmlSerliazer object
+ *
+- * @param writer
+- * XmlSerializer object to write the body into.
++ * @param writer XmlSerializer object to write the body into.
+ */
+ public void writeBody(XmlSerializer writer) throws IOException {
+ // allow an empty body without any tags in it
+@@ -564,36 +622,48 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ multiRef = new Vector();
+ multiRef.addElement(bodyOut);
+ Object[] qName = getInfo(null, bodyOut);
+- writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
+- (String) qName[QNAME_TYPE]); //<spp:sppPostDevData
+- if (dotNet) {
+- writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]);
+- }
++
++ writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]);
++
++ if (dotNet) {
++ writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]);
++ }
++
+ if (addAdornments) {
+ writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]);
+ writer.attribute(enc, ROOT_LABEL, "1");
+ }
+- writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); //....
+- writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
+- (String) qName[QNAME_TYPE]);//</spp:sppPostDevData>
++ writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]);
++ writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE], (String) qName[QNAME_TYPE]);
+ }
+ }
+
+- /**
+- * Writes the body of an SoapObject. This method write the attributes and then calls
+- * "writeObjectBody (writer, (KvmSerializable)obj);"
+- */
+- public void writeObjectBody(XmlSerializer writer, SoapObject obj) throws IOException {
+- SoapObject soapObject = (SoapObject) obj;
++ private void writeAttributes(XmlSerializer writer, HasAttributes obj) throws IOException {
++ HasAttributes soapObject = (HasAttributes) obj;
+ int cnt = soapObject.getAttributeCount();
+ for (int counter = 0; counter < cnt; counter++) {
+ AttributeInfo attributeInfo = new AttributeInfo();
+ soapObject.getAttributeInfo(counter, attributeInfo);
+- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), attributeInfo
+- .getValue()
+- .toString());
++ soapObject.getAttribute(counter, attributeInfo);
++ if (attributeInfo.getValue() != null) {
++ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
++ attributeInfo.getValue().toString());
++ }
++ }
++ }
++
++ public void writeArrayListBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException {
++ if (obj instanceof HasAttributes) {
++ writeAttributes(writer, (HasAttributes) obj);
+ }
+- writeObjectBody(writer, (KvmSerializable) obj);
++ writeArrayListBody(writer, (ArrayList) obj);
++ }
++
++ public void writeObjectBodyWithAttributes(XmlSerializer writer, KvmSerializable obj) throws IOException {
++ if (obj instanceof HasAttributes) {
++ writeAttributes(writer, (HasAttributes) obj);
++ }
++ writeObjectBody(writer, obj);
+ }
+
+ /**
+@@ -614,9 +684,12 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ if (!(prop instanceof SoapObject)) {
+ // prop is a PropertyInfo
+ if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
+- writer.startTag(propertyInfo.namespace, propertyInfo.name);
+- writeProperty(writer, obj.getProperty(i), propertyInfo);
+- writer.endTag(propertyInfo.namespace, propertyInfo.name);
++ Object objValue = obj.getProperty(i);
++ if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) {
++ writer.startTag(propertyInfo.namespace, propertyInfo.name);
++ writeProperty(writer, objValue, propertyInfo);
++ writer.endTag(propertyInfo.namespace, propertyInfo.name);
++ }
+ }
+ } else {
+ // prop is a SoapObject
+@@ -633,46 +706,47 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ name = (String) qName[QNAME_TYPE];
+ }
+
+- // treat MO data as CDATA
+- if (name.equals("DevInfo") || name.equals("DevDetail")
+- || name.equals("PerProviderSubscription") || // format v4
+- name.equals("MgmtTree") // format v6
+- ) {
+- ByteArrayOutputStream bos = new ByteArrayOutputStream();
+- XmlSerializer xw = new KXmlSerializer();
+- xw.setOutput(bos, "UTF-8");
+- xw.startTag((dotNet) ? "" : namespace, name);
+- if (!implicitTypes) {
+- String prefix = writer.getPrefix(namespace, true);
+- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
+- }
+- writeObjectBody(xw, nestedSoap);
+- xw.endTag((dotNet) ? "" : namespace, name);
+- xw.flush();
+- //bos.write('\r');
+- //bos.write('\n');
+- bos.flush();
+- writer.cdsect(bos.toString());
++ // prefer the namespace from the property info
++ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
++ namespace = propertyInfo.namespace;
++ } else {
++ namespace = (String) qName[QNAME_NAMESPACE];
++ }
++
++ writer.startTag(namespace, name);
++ if (!implicitTypes) {
++ String prefix = writer.getPrefix(namespace, true);
++ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
++ }
++ writeObjectBodyWithAttributes(writer, nestedSoap);
++ writer.endTag(namespace, name);
++ }
++ }
++ writeInnerText(writer, obj);
++
++ }
++
++ private void writeInnerText(XmlSerializer writer, KvmSerializable obj) throws IOException {
++ if(obj instanceof HasInnerText){
++
++ Object value=((HasInnerText)obj).getInnerText();
++ if (value != null) {
++ if(value instanceof ValueWriter)
++ {
++ ((ValueWriter)value).write(writer);
+ }
+ else
+ {
+- writer.startTag((dotNet) ? "" : namespace, name);
+- if (!implicitTypes) {
+- String prefix = writer.getPrefix(namespace, true);
+- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
+- }
+- writeObjectBody(writer, nestedSoap);
+- writer.endTag((dotNet) ? "" : namespace, name);
++ writer.cdsect(value.toString());
+ }
++
+ }
+ }
+ }
+
+- protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type)
+- throws IOException {
+- if (obj == null) {
+- ///M: Modify for HS20
+- //writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
++ protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type) throws IOException {
++ if (obj == null || obj == SoapPrimitive.NullNilElement) {
++ writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
+ return;
+ }
+ Object[] qName = getInfo(null, obj);
+@@ -692,15 +766,19 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ }
+
+- private void writeElement(XmlSerializer writer, Object element, PropertyInfo type,
+- Object marshal)
++ protected void writeElement(XmlSerializer writer, Object element, PropertyInfo type, Object marshal)
+ throws IOException {
+ if (marshal != null) {
+ ((Marshal) marshal).writeInstance(writer, element);
+- } else if (element instanceof SoapObject) {
+- writeObjectBody(writer, (SoapObject) element);
+- } else if (element instanceof KvmSerializable) {
+- writeObjectBody(writer, (KvmSerializable) element);
++ } else if (element instanceof KvmSerializable || element == SoapPrimitive.NullNilElement
++ || element == SoapPrimitive.NullSkip) {
++ if (element instanceof ArrayList) {
++ writeArrayListBodyWithAttributes(writer, (KvmSerializable) element);
++ } else {
++ writeObjectBodyWithAttributes(writer, (KvmSerializable) element);
++ }
++ } else if (element instanceof HasAttributes) {
++ writeAttributes(writer, (HasAttributes) element);
+ } else if (element instanceof Vector) {
+ writeVectorBody(writer, (Vector) element, type.elementType);
+ } else {
+@@ -708,6 +786,65 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+ }
+ }
+
++ protected void writeArrayListBody(XmlSerializer writer, ArrayList list)
++ throws IOException {
++ KvmSerializable obj = (KvmSerializable) list;
++ int cnt = list.size();
++ PropertyInfo propertyInfo = new PropertyInfo();
++ String namespace;
++ String name;
++ String type;
++ for (int i = 0; i < cnt; i++) {
++ // get the property
++ Object prop = obj.getProperty(i);
++ // and importantly also get the property info which holds the name potentially!
++ obj.getPropertyInfo(i, properties, propertyInfo);
++
++ if (!(prop instanceof SoapObject)) {
++ // prop is a PropertyInfo
++ if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
++ Object objValue = obj.getProperty(i);
++ if ((prop != null || !skipNullProperties) && (objValue != SoapPrimitive.NullSkip)) {
++ writer.startTag(propertyInfo.namespace, propertyInfo.name);
++ writeProperty(writer, objValue, propertyInfo);
++ writer.endTag(propertyInfo.namespace, propertyInfo.name);
++ }
++ }
++ } else {
++
++ // prop is a SoapObject
++ SoapObject nestedSoap = (SoapObject) prop;
++ // lets get the info from the soap object itself
++ Object[] qName = getInfo(null, nestedSoap);
++ namespace = (String) qName[QNAME_NAMESPACE];
++ type = (String) qName[QNAME_TYPE];
++
++ // prefer the name from the property info
++ if (propertyInfo.name != null && propertyInfo.name.length() > 0) {
++ name = propertyInfo.name;
++ } else {
++ name = (String) qName[QNAME_TYPE];
++ }
++
++ // prefer the namespace from the property info
++ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
++ namespace = propertyInfo.namespace;
++ } else {
++ namespace = (String) qName[QNAME_NAMESPACE];
++ }
++
++ writer.startTag(namespace, name);
++ if (!implicitTypes) {
++ String prefix = writer.getPrefix(namespace, true);
++ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
++ }
++ writeObjectBodyWithAttributes(writer, nestedSoap);
++ writer.endTag(namespace, name);
++ }
++ }
++ writeInnerText(writer, obj);
++ }
++
+ protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType)
+ throws IOException {
+ String itemsTagName = ITEM_LABEL;
+@@ -727,9 +864,13 @@ public class SoapSerializationEnvelope extends SoapEnvelope
+
+ // This removes the arrayType attribute from the xml for arrays(required for most .Net services to work)
+ if (!implicitTypes) {
+- writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false)
+- + ":"
++ writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false) + ":"
+ + arrType[1] + "[" + cnt + "]");
++ } else {
++ // Get the namespace from mappings if available when arrayType is removed for .Net
++ if (itemsNamespace == null) {
++ itemsNamespace = (String) arrType[0];
++ }
+ }
+
+ boolean skipped = false;
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
+new file mode 100644
+index 0000000..fdbaa21
+--- /dev/null
++++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
+@@ -0,0 +1,13 @@
++package org.ksoap2.serialization;
++
++import org.xmlpull.v1.XmlSerializer;
++
++import java.io.IOException;
++
++/**
++ * Created by robocik on 2015-09-25.
++ */
++public interface ValueWriter
++{
++ void write(XmlSerializer writer) throws IOException;
++}
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
+index 8e14ee7..9dd3837 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
+@@ -21,8 +21,10 @@
+
+ package org.ksoap2.transport;
+
++import java.io.IOException;
++import java.io.InputStream;
++import java.io.OutputStream;
+ import java.util.List;
+-import java.io.*;
+
+ /**
+ * Interface to allow the abstraction of the raw transport information
+@@ -58,6 +60,13 @@ public interface ServiceConnection {
+ */
+ public List getResponseProperties() throws IOException;
+
++ /**
++ * Returns the numerical HTTP status to the caller
++ * @return an integer status value
++ * @throws IOException
++ */
++ public int getResponseCode() throws IOException;
++
+ /**
+ * Set properties on the outgoing connection.
+ *
+@@ -88,6 +97,8 @@ public interface ServiceConnection {
+ **/
+ public void setFixedLengthStreamingMode(int contentLength);
+
++ public void setChunkedStreamingMode();
++
+ /**
+ * Open and return the outputStream to the endpoint.
+ *
+diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
+index b2f6587..2f3d523 100644
+--- a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
++++ b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
+@@ -23,9 +23,13 @@
+
+ package org.ksoap2.transport;
+
++import java.util.HashMap;
++import java.util.Iterator;
+ import java.util.List;
+ import java.io.*;
++import java.net.MalformedURLException;
+ import java.net.Proxy;
++import java.net.URL;
+
+ import org.ksoap2.*;
+ import org.kxml2.io.*;
+@@ -40,9 +44,9 @@ import org.xmlpull.v1.*;
+ abstract public class Transport {
+
+ /**
+- * Added to enable web service interactions on the emulator
+- * to be debugged with Fiddler2 (Windows) but provides utility
+- * for other proxy requirements.
++ * Added to enable web service interactions on the emulator to be debugged
++ * with Fiddler2 (Windows) but provides utility for other proxy
++ * requirements.
+ */
+ protected Proxy proxy;
+ protected String url;
+@@ -61,6 +65,12 @@ abstract public class Transport {
+
+ private int bufferLength = ServiceConnection.DEFAULT_BUFFER_SIZE;
+
++ private HashMap prefixes = new HashMap();
++
++ public HashMap getPrefixes() {
++ return prefixes;
++ }
++
+ public Transport() {
+ }
+
+@@ -82,9 +92,12 @@ abstract public class Transport {
+ /**
+ * Construct the transport object
+ *
+- * @param proxy Specifies the proxy server to use for
+- * accessing the web service or <code>null</code> if a direct connection is available
+- * @param url Specifies the web service url
++ * @param proxy
++ * Specifies the proxy server to use for accessing the web
++ * service or <code>null</code> if a direct connection is
++ * available
++ * @param url
++ * Specifies the web service url
+ *
+ */
+ public Transport(Proxy proxy, String url) {
+@@ -114,23 +127,30 @@ abstract public class Transport {
+ xp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ xp.setInput(is, null);
+ envelope.parse(xp);
++ /*
++ * Fix memory leak when running on android in strict mode. Issue 133
++ */
++ is.close();
+ }
+
+ /**
+ * Serializes the request.
+ */
+- protected byte[] createRequestData(SoapEnvelope envelope, String encoding) throws IOException {
+- System.out.println("createRequestData");
++ protected byte[] createRequestData(SoapEnvelope envelope, String encoding)
++ throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(bufferLength);
+ byte result[] = null;
+ bos.write(xmlVersionTag.getBytes());
+- System.out.println("bos.write");
+ XmlSerializer xw = new KXmlSerializer();
+- System.out.println("new KXmlSerializer");
++
++ final Iterator keysIter = prefixes.keySet().iterator();
++
+ xw.setOutput(bos, encoding);
+- System.out.println("xw.setOutput");
++ while (keysIter.hasNext()) {
++ String key = (String) keysIter.next();
++ xw.setPrefix(key, (String) prefixes.get(key));
++ }
+ envelope.write(xw);
+- System.out.println("envelope.write");
+ xw.flush();
+ bos.write('\r');
+ bos.write('\n');
+@@ -138,14 +158,14 @@ abstract public class Transport {
+ result = bos.toByteArray();
+ xw = null;
+ bos = null;
+- System.out.println("createRequestData end");
+ return result;
+ }
+
+ /**
+ * Serializes the request.
+ */
+- protected byte[] createRequestData(SoapEnvelope envelope) throws IOException {
++ protected byte[] createRequestData(SoapEnvelope envelope)
++ throws IOException {
+ return createRequestData(envelope, null);
+ }
+
+@@ -159,6 +179,12 @@ abstract public class Transport {
+ this.url = url;
+ }
+
++ public String getUrl()
++ {
++ return url;
++ }
++
++
+ /**
+ * Sets the version tag for the outgoing soap call. Example <?xml
+ * version=\"1.0\" encoding=\"UTF-8\"?>
+@@ -177,57 +203,94 @@ abstract public class Transport {
+ }
+
+ /**
+- * Perform a soap call with a given namespace and the given envelope providing
+- * any extra headers that the user requires such as cookies. Headers that are
+- * returned by the web service will be returned to the caller in the form of a
+- * <code>List</code> of <code>HeaderProperty</code> instances.
++ * Perform a soap call with a given namespace and the given envelope
++ * providing any extra headers that the user requires such as cookies.
++ * Headers that are returned by the web service will be returned to the
++ * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
++ * instances.
++ *
++ * @param soapAction
++ * the namespace with which to perform the call in.
++ * @param envelope
++ * the envelope the contains the information for the call.
++ * @param headers
++ * <code>List</code> of <code>HeaderProperty</code> headers to
++ * send with the SOAP request.
++ *
++ * @return Headers returned by the web service as a <code>List</code> of
++ * <code>HeaderProperty</code> instances.
++ */
++ abstract public List call(String soapAction, SoapEnvelope envelope,
++ List headers) throws IOException, XmlPullParserException;
++
++ /**
++ * Perform a soap call with a given namespace and the given envelope
++ * providing any extra headers that the user requires such as cookies.
++ * Headers that are returned by the web service will be returned to the
++ * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
++ * instances.
+ *
+- * @param targetNamespace
++ * @param soapAction
+ * the namespace with which to perform the call in.
+ * @param envelope
+ * the envelope the contains the information for the call.
+ * @param headers
+- * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
++ * <code>List</code> of <code>HeaderProperty</code> headers to
++ * send with the SOAP request.
++ * @param outputFile
++ * a file to stream the response into rather than parsing it,
++ * streaming happens when file is not null
+ *
+ * @return Headers returned by the web service as a <code>List</code> of
+- * <code>HeaderProperty</code> instances.
++ * <code>HeaderProperty</code> instances.
+ */
+- abstract public List call(String targetNamespace, SoapEnvelope envelope, List headers)
+- throws IOException, XmlPullParserException;
++ abstract public List call(String soapAction, SoapEnvelope envelope,
++ List headers, File outputFile) throws IOException,
++ XmlPullParserException;
+
+ /**
+ * Perform a soap call with a given namespace and the given envelope.
+ *
+- * @param targetNamespace
++ * @param soapAction
+ * the namespace with which to perform the call in.
+ * @param envelope
+ * the envelope the contains the information for the call.
+ */
+- public void call(String targetNamespace, SoapEnvelope envelope) throws IOException,
+- XmlPullParserException {
+- call(targetNamespace, envelope, null);
++ public void call(String soapAction, SoapEnvelope envelope)
++ throws IOException, XmlPullParserException {
++ call(soapAction, envelope, null);
+ }
+
+ /**
+ * Return the name of the host that is specified as the web service target
+- *
++ *
+ * @return Host name
+ */
+- abstract public String getHost();
++ public String getHost() throws MalformedURLException {
++
++ return new URL(url).getHost();
++ }
+
+ /**
+- * Return the port number of the host that is specified as the web service target
+- *
++ * Return the port number of the host that is specified as the web service
++ * target
++ *
+ * @return Port number
+ */
+- abstract public int getPort();
++ public int getPort() throws MalformedURLException {
++
++ return new URL(url).getPort();
++ }
+
+ /**
+ * Return the path to the web service target
+- *
++ *
+ * @return The URL's path
+ */
+- abstract public String getPath();
++ public String getPath() throws MalformedURLException {
++
++ return new URL(url).getPath();
++ }
+
+ abstract public ServiceConnection getServiceConnection() throws IOException;
+ }
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
+index 71c8caa..61a5e2c 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
+@@ -27,8 +27,7 @@ import org.xmlpull.v1.*;
+
+ public class MarshalFloat implements Marshal {
+
+- public Object readInstance(XmlPullParser parser, String namespace, String name,
+- PropertyInfo propertyInfo)
++ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo propertyInfo)
+ throws IOException, XmlPullParserException {
+ String stringValue = parser.nextText();
+ Object result;
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
+new file mode 100644
+index 0000000..f7fb866
+--- /dev/null
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
+@@ -0,0 +1,60 @@
++package org.ksoap2.transport;
++
++import java.io.IOException;
++import java.util.List;
++
++/**
++ * HttpResponseException is an IOException that is to be thrown when a Http response code is different from 200.
++ * It allows for easier retrieval of the Http response code from the connection.
++ *
++ * @author Rui Pereira <syshex@gmail.com>
++ */
++public class HttpResponseException extends IOException {
++
++ private int statusCode;
++ private List responseHeaders;
++
++ public HttpResponseException(int statusCode) {
++ super();
++ this.statusCode = statusCode;
++ }
++
++ public HttpResponseException(String detailMessage, int statusCode) {
++ super(detailMessage);
++ this.statusCode = statusCode;
++ }
++
++ public HttpResponseException(String detailMessage, int statusCode,List responseHeaders) {
++ super(detailMessage);
++ this.statusCode = statusCode;
++ this.responseHeaders=responseHeaders;
++ }
++
++ public HttpResponseException(String message, Throwable cause, int statusCode) {
++ super(message, cause);
++ this.statusCode = statusCode;
++ }
++
++ public HttpResponseException(Throwable cause, int statusCode) {
++ super(cause);
++ this.statusCode = statusCode;
++ }
++
++ /**
++ * Returns the unexpected Http response code
++ *
++ * @return response code
++ */
++ public int getStatusCode() {
++ return statusCode;
++ }
++
++ /**
++ * Returns all http headers from this response
++ *
++ * @return response code
++ */
++ public List getResponseHeaders() {
++ return responseHeaders;
++ }
++}
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
+index d92ac09..920ca60 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
+@@ -25,24 +25,21 @@
+
+ package org.ksoap2.transport;
+
+-import java.util.List;
+-import java.util.zip.GZIPInputStream;
++import org.ksoap2.HeaderProperty;
++import org.ksoap2.SoapEnvelope;
++import org.ksoap2.serialization.*;
++import org.xmlpull.v1.XmlPullParserException;
++
+ import java.io.*;
+-import java.net.MalformedURLException;
+ import java.net.Proxy;
+-import java.net.URL;
+-
+-import org.ksoap2.*;
+-import org.ksoap2.serialization.SoapSerializationEnvelope;
+-import org.xmlpull.v1.*;
++import java.util.*;
++import java.util.zip.GZIPInputStream;
+
+ /**
+ * A J2SE based HttpTransport layer.
+ */
+ public class HttpTransportSE extends Transport {
+
+- private ServiceConnection serviceConnection;
+-
+ /**
+ * Creates instance of HttpTransportSE with set url
+ *
+@@ -107,249 +104,254 @@ public class HttpTransportSE extends Transport {
+ * the desired soapAction
+ * @param envelope
+ * the envelope containing the information for the soap call.
++ * @throws HttpResponseException
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+- public void call(String soapAction, SoapEnvelope envelope) throws IOException,
+- XmlPullParserException {
+-
++ public void call(String soapAction, SoapEnvelope envelope)
++ throws HttpResponseException, IOException, XmlPullParserException {
++
+ call(soapAction, envelope, null);
+ }
+
++ public List call(String soapAction, SoapEnvelope envelope, List headers)
++ throws HttpResponseException, IOException, XmlPullParserException {
++ return call(soapAction, envelope, headers, null);
++ }
++
+ /**
+- *
+- * set the desired soapAction header field
+- *
++ * Perform a soap call with a given namespace and the given envelope providing
++ * any extra headers that the user requires such as cookies. Headers that are
++ * returned by the web service will be returned to the caller in the form of a
++ * <code>List</code> of <code>HeaderProperty</code> instances.
++ *
+ * @param soapAction
+- * the desired soapAction
++ * the namespace with which to perform the call in.
+ * @param envelope
+- * the envelope containing the information for the soap call.
++ * the envelope the contains the information for the call.
+ * @param headers
+- * a list of HeaderProperties to be http header properties when establishing the connection
++ * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
++ * @param outputFile
++ * a file to stream the response into rather than parsing it, streaming happens when file is not null
+ *
+- * @return <code>CookieJar</code> with any cookies sent by the server
+- * @throws IOException
+- * @throws XmlPullParserException
++ * @return Headers returned by the web service as a <code>List</code> of
++ * <code>HeaderProperty</code> instances.
++ *
++ * @throws HttpResponseException
++ * an IOException when Http response code is different from 200
+ */
+- public List call(String soapAction, SoapEnvelope envelope, List headers)
+- throws IOException, XmlPullParserException {
++ public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
++ throws HttpResponseException, IOException, XmlPullParserException {
+
+ if (soapAction == null) {
+ soapAction = "\"\"";
+ }
+
+- System.out.println("call action:" + soapAction);
+ byte[] requestData = createRequestData(envelope, "UTF-8");
+
+- if (requestData != null) {
+- requestDump = debug ? new String(requestData) : null;
+- }
+- else {
+- requestDump = null;
+- }
++ requestDump = debug ? new String(requestData) : null;
+ responseDump = null;
+-
+- System.out.println("requestDump:" + requestDump);
++ System.out.println("requestDump: " + requestDump);
+ ServiceConnection connection = getServiceConnection();
+- System.out.println("connection:" + connection);
+
+ connection.setRequestProperty("User-Agent", USER_AGENT);
+ // SOAPAction is not a valid header for VER12 so do not add
+ // it
+ // @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
+- System.out.println("envelope:" + envelope);
+- if (envelope != null) {
+- if (envelope.version != SoapSerializationEnvelope.VER12) {
+- connection.setRequestProperty("SOAPAction", soapAction);
+- }
++ if (envelope.version != SoapSerializationEnvelope.VER12) {
++ connection.setRequestProperty("SOAPAction", soapAction);
++ }
+
+- if (envelope.version == SoapSerializationEnvelope.VER12) {
+- connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
+- } else {
+- connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
+- }
++ if (envelope.version == SoapSerializationEnvelope.VER12) {
++ connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
++ } else {
++ connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
++ }
+
+- connection.setRequestProperty("Connection", "close");
+- connection.setRequestProperty("Accept-Encoding", "gzip");
+- connection.setRequestProperty("Content-Length", "" + requestData.length);
++ // this seems to cause issues so we are removing it
++ //connection.setRequestProperty("Connection", "close");
++ connection.setRequestProperty("Accept-Encoding", "gzip");
+
+- //M: Retry for HTTP Authentication
+- //connection.setFixedLengthStreamingMode(requestData.length);
+
+- // Pass the headers provided by the user along with the call
+- if (headers != null) {
+- for (int i = 0; i < headers.size(); i++) {
+- HeaderProperty hp = (HeaderProperty) headers.get(i);
+- connection.setRequestProperty(hp.getKey(), hp.getValue());
+- }
++ // Pass the headers provided by the user along with the call
++ if (headers != null) {
++ for (int i = 0; i < headers.size(); i++) {
++ HeaderProperty hp = (HeaderProperty) headers.get(i);
++ connection.setRequestProperty(hp.getKey(), hp.getValue());
+ }
+-
+- connection.setRequestMethod("POST");
+-
+- }
+- else {
+- connection.setRequestProperty("Connection", "close");
+- connection.setRequestProperty("Accept-Encoding", "gzip");
+- connection.setRequestMethod("GET");
+ }
+
+- if (requestData != null) {
+- OutputStream os = connection.openOutputStream();
+-
+- os.write(requestData, 0, requestData.length);
+- os.flush();
+- os.close();
+- requestData = null;
+- }
+- InputStream is;
++ connection.setRequestMethod("POST");
++ sendData(requestData, connection,envelope);
++ requestData = null;
++ InputStream is = null;
+ List retHeaders = null;
++ byte[] buf = null; // To allow releasing the resource after used
++ int contentLength = 8192; // To determine the size of the response and adjust buffer size
+ boolean gZippedContent = false;
+- boolean bcaCert = false;
++ boolean xmlContent = false;
++ int status = connection.getResponseCode();
+
+ try {
+ retHeaders = connection.getResponseProperties();
+- System.out.println("[HttpTransportSE] retHeaders = " + retHeaders);
++
+ for (int i = 0; i < retHeaders.size(); i++) {
+- HeaderProperty hp = (HeaderProperty) retHeaders.get(i);
++ HeaderProperty hp = (HeaderProperty)retHeaders.get(i);
+ // HTTP response code has null key
+ if (null == hp.getKey()) {
+ continue;
+ }
++
++ // If we know the size of the response, we should use the size to initiate vars
++ if (hp.getKey().equalsIgnoreCase("content-length") ) {
++ if ( hp.getValue() != null ) {
++ try {
++ contentLength = Integer.parseInt( hp.getValue() );
++ } catch ( NumberFormatException nfe ) {
++ contentLength = 8192;
++ }
++ }
++ }
++
++
++ // Check the content-type header to see if we're getting back XML, in case of a
++ // SOAP fault on 500 codes
++ if (hp.getKey().equalsIgnoreCase("Content-Type")
++ && hp.getValue().contains("xml")) {
++ xmlContent = true;
++ }
++
++
+ // ignoring case since users found that all smaller case is used on some server
+ // and even if it is wrong according to spec, we rather have it work..
+ if (hp.getKey().equalsIgnoreCase("Content-Encoding")
+- && hp.getValue().equalsIgnoreCase("gzip")) {
++ && hp.getValue().equalsIgnoreCase("gzip")) {
+ gZippedContent = true;
+ }
+ }
+- if (gZippedContent) {
+- is = getUnZippedInputStream(connection.openInputStream());
+- } else {
+- is = connection.openInputStream();
+- }
+- } catch (IOException e) {
+- if (gZippedContent) {
+- is = getUnZippedInputStream(connection.getErrorStream());
+- } else {
+- is = connection.getErrorStream();
+- }
+
+- if (is == null) {
+- connection.disconnect();
+- throw (e);
++ //first check the response code....
++ if (status != 200 && status != 202) {
++ //202 is a correct status returned by WCF OneWay operation
++ throw new HttpResponseException("HTTP request failed, HTTP status: " + status, status,retHeaders);
+ }
+- }
+-
+- if (debug) {
+- ByteArrayOutputStream bos = new ByteArrayOutputStream();
+- byte[] buf = new byte[8192];
+
+- while (true) {
+- int rd = is.read(buf, 0, 8192);
+- if (rd == -1) {
+- break;
++ if (contentLength > 0) {
++ if (gZippedContent) {
++ is = getUnZippedInputStream(
++ new BufferedInputStream(connection.openInputStream(),contentLength));
++ } else {
++ is = new BufferedInputStream(connection.openInputStream(),contentLength);
++ }
++ }
++ } catch (IOException e) {
++ if (contentLength > 0) {
++ if(gZippedContent) {
++ is = getUnZippedInputStream(
++ new BufferedInputStream(connection.getErrorStream(),contentLength));
++ } else {
++ is = new BufferedInputStream(connection.getErrorStream(),contentLength);
+ }
+- bos.write(buf, 0, rd);
+ }
+
+- bos.flush();
+- buf = bos.toByteArray();
++ if ( e instanceof HttpResponseException) {
++ if (!xmlContent) {
++ if (debug && is != null) {
++ //go ahead and read the error stream into the debug buffers/file if needed.
++ readDebug(is, contentLength, outputFile);
++ }
+
+- responseDump = new String(buf);
++ //we never want to drop through to attempting to parse the HTTP error stream as a SOAP response.
++ connection.disconnect();
++ throw e;
++ }
++ }
++ }
+
+- System.out.println("responseDump:" + responseDump);
+- is.close();
+- is = new ByteArrayInputStream(buf);
++ if (debug) {
++ is = readDebug(is, contentLength, outputFile);
+ }
+
+- if (envelope != null) {
+- parseResponse(envelope, is);
++ if(is!=null)
++ {
++ parseResponse(envelope, is,retHeaders);
+ }
+
++ // release all resources
++ // input stream is will be released inside parseResponse
++ is = null;
++ buf = null;
++ //This fixes Issue 173 read my explanation here: https://code.google.com/p/ksoap2-android/issues/detail?id=173
++ connection.disconnect();
++ connection = null;
+ return retHeaders;
+ }
+
+- private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
+- /* workaround for Android 2.3
+- (see http://stackoverflow.com/questions/5131016/)
+- */
+- try {
+- return (GZIPInputStream) inputStream;
+- } catch (ClassCastException e) {
+- return new GZIPInputStream(inputStream);
+- }
+- }
++ protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope)
++ throws IOException
++ {
++ connection.setRequestProperty("Content-Length", "" + requestData.length);
++ connection.setFixedLengthStreamingMode(requestData.length);
+
+- public ServiceConnection getServiceConnection() throws IOException {
+- if (serviceConnection == null) {
+- System.out.println("new ServiceConnectionSE:" + proxy + " " + url + " " + timeout);
+- serviceConnection = new ServiceConnectionSE(proxy, url, timeout);
+- }
+- return serviceConnection;
++ OutputStream os = connection.openOutputStream();
++ os.write(requestData, 0, requestData.length);
++ os.flush();
++ os.close();
+ }
+
+- public String getHost() {
+-
+- String retVal = null;
+-
+- try {
+- retVal = new URL(url).getHost();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
+- }
+-
+- return retVal;
++ protected void parseResponse(SoapEnvelope envelope, InputStream is,List returnedHeaders)
++ throws XmlPullParserException, IOException
++ {
++ parseResponse(envelope, is);
+ }
+
+- public int getPort() {
+-
+- int retVal = -1;
+
+- try {
+- retVal = new URL(url).getPort();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
++ private InputStream readDebug(InputStream is, int contentLength, File outputFile) throws IOException {
++ OutputStream bos;
++ if (outputFile != null) {
++ bos = new FileOutputStream(outputFile);
++ } else {
++ // If known use the size if not use default value
++ bos = new ByteArrayOutputStream( (contentLength > 0 ) ? contentLength : 256*1024);
+ }
+
+- return retVal;
+- }
+-
+- public String getPath() {
+-
+- String retVal = null;
++ byte[] buf = new byte[256];
+
+- try {
+- retVal = new URL(url).getPath();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
++ while (true) {
++ int rd = is.read(buf, 0, 256);
++ if (rd == -1) {
++ break;
++ }
++ bos.write(buf, 0, rd);
+ }
+
+- return retVal;
+- }
+-
+- public String getQuery() {
+-
+- String retVal = null;
+-
+- try {
+- retVal = new URL(url).getQuery();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
++ bos.flush();
++ if (bos instanceof ByteArrayOutputStream) {
++ buf = ((ByteArrayOutputStream) bos).toByteArray();
++ }
++ bos = null;
++ responseDump = new String(buf);
++ is.close();
++ System.out.println("responseDump: " + requestDump);
++ if (outputFile != null) {
++ return new FileInputStream(outputFile);
++ } else {
++ return new ByteArrayInputStream(buf);
+ }
+-
+- return retVal;
+ }
+
+- /**
+- * @hide
+- */
+- public byte[] getRequestData(SoapEnvelope envelope, String encoding) {
++ private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
++ /* workaround for Android 2.3
++ (see http://stackoverflow.com/questions/5131016/)
++ */
+ try {
+- return createRequestData(envelope, encoding);
+- } catch (Exception e) {
+- e.printStackTrace();
++ return (GZIPInputStream) inputStream;
++ } catch (ClassCastException e) {
++ return new GZIPInputStream(inputStream);
+ }
++ }
+
+- return null;
++ public ServiceConnection getServiceConnection() throws IOException {
++ return new ServiceConnectionSE(proxy, url, timeout);
+ }
+ }
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
+index 9ad9ba9..376c7d8 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
+@@ -1,21 +1,16 @@
+
+ package org.ksoap2.transport;
+
++import org.ksoap2.HeaderProperty;
++
++import javax.net.ssl.HttpsURLConnection;
++import javax.net.ssl.SSLSocketFactory;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.OutputStream;
++import java.net.Proxy;
+ import java.net.URL;
+-import java.util.Iterator;
+-import java.util.LinkedList;
+-import java.util.Map;
+-import java.util.List;
+-import java.util.Set;
+-
+-import javax.net.ssl.HostnameVerifier;
+-import javax.net.ssl.SSLSocketFactory;
+-import javax.net.ssl.HttpsURLConnection;
+-//import com.android.okhttp.internal.http.HttpsURLConnectionImpl;
+-import org.ksoap2.HeaderProperty;
++import java.util.*;
+
+ /**
+ * HttpsServiceConnectionSE is a service connection that uses a https url connection and requires explicit setting of
+@@ -49,10 +44,29 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
+ * @param timeout the timeout for the connection in milliseconds
+ * @throws IOException
+ */
+- public HttpsServiceConnectionSE(String host, int port, String file,
+- int timeout) throws IOException {
+- connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file)
+- .openConnection();
++ public HttpsServiceConnectionSE(String host, int port, String file, int timeout) throws IOException {
++ this(null, host, port, file, timeout);
++ }
++
++ /**
++ * Create the transport with the supplied parameters.
++ * @param proxy proxy server to use
++ * @param host the name of the host e.g. webservices.somewhere.com
++ * @param port the http port to connect on
++ * @param file the path to the file on the webserver that represents the
++ * webservice e.g. /api/services/myservice.jsp
++ * @param timeout the timeout for the connection in milliseconds
++ * @throws IOException
++ */
++ public HttpsServiceConnectionSE(Proxy proxy, String host, int port, String file, int timeout) throws IOException {
++
++ if (proxy == null) {
++ connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection();
++ } else {
++ connection =
++ (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file).openConnection(proxy);
++ }
++
+ updateConnectionParameters(timeout);
+ }
+
+@@ -89,6 +103,10 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
+ return retList;
+ }
+
++ public int getResponseCode() throws IOException {
++ return connection.getResponseCode();
++ }
++
+ public void setRequestProperty(String key, String value) {
+ connection.setRequestProperty(key, value);
+ }
+@@ -101,6 +119,11 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
+ connection.setFixedLengthStreamingMode(contentLength);
+ }
+
++ public void setChunkedStreamingMode() {
++ connection.setChunkedStreamingMode(0);
++ }
++
++
+ public OutputStream openOutputStream() throws IOException {
+ return connection.getOutputStream();
+ }
+@@ -128,9 +151,4 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
+ public void setSSLSocketFactory(SSLSocketFactory sf) {
+ connection.setSSLSocketFactory(sf);
+ }
+-
+- public void setHostnameVerifier(HostnameVerifier v) {
+- connection.setHostnameVerifier(v);
+- }
+-
+ }
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
+index d220ac9..a7d7023 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
+@@ -1,8 +1,8 @@
+-
+ package org.ksoap2.transport;
+
+ import java.io.IOException;
+ import java.net.MalformedURLException;
++import java.net.Proxy;
+ import java.net.URL;
+
+ /**
+@@ -14,17 +14,31 @@ import java.net.URL;
+ public class HttpsTransportSE extends HttpTransportSE {
+
+ static final String PROTOCOL = "https";
++ private static final String PROTOCOL_FULL = PROTOCOL + "://";
++
++ //connection instance, used for setting the SSLSocketFactory
++ private HttpsServiceConnectionSE connection;
+
+- private ServiceConnection serviceConnection = null;
+- private final String host;
+- private final int port;
+- private final String file;
+- private final int timeout;
++ protected final String host;
++ protected final int port;
++ protected final String file;
+
+- public HttpsTransportSE(String host, int port, String file, int timeout) {
+- super(HttpsTransportSE.PROTOCOL + "://" + host + ":" + port + file);
+- System.out.println("Establistion connection to: " + HttpsTransportSE.PROTOCOL + "://"
+- + host + ":" + port + file);
++ public HttpsTransportSE (String host, int port, String file, int timeout) {
++ super(HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file, timeout);
++ this.host = host;
++ this.port = port;
++ this.file = file;
++ }
++
++ /**
++ * Creates instance of HttpTransportSE with set url and defines a
++ * proxy server to use to access it
++ *
++ * @param proxy
++ * Proxy information or <code>null</code> for direct access
++ */
++ public HttpsTransportSE(Proxy proxy, String host, int port, String file, int timeout) {
++ super(proxy, HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file);
+ this.host = host;
+ this.port = port;
+ this.file = file;
+@@ -37,48 +51,11 @@ public class HttpsTransportSE extends HttpTransportSE {
+ */
+ public ServiceConnection getServiceConnection() throws IOException
+ {
+- if (serviceConnection == null) {
+- serviceConnection = new HttpsServiceConnectionSE(host, port, file, timeout);
+- }
+- return serviceConnection;
+- }
+-
+- public String getHost() {
+-
+- String retVal = null;
+-
+- try {
+- retVal = new URL(url).getHost();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
++ if(connection != null) {
++ return connection;
++ } else {
++ connection = new HttpsServiceConnectionSE(proxy, host, port, file, timeout);
++ return connection;
+ }
+-
+- return retVal;
+- }
+-
+- public int getPort() {
+-
+- int retVal = -1;
+-
+- try {
+- retVal = new URL(url).getPort();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
+- }
+-
+- return retVal;
+- }
+-
+- public String getPath() {
+-
+- String retVal = null;
+-
+- try {
+- retVal = new URL(url).getPath();
+- } catch (MalformedURLException e) {
+- e.printStackTrace();
+- }
+-
+- return retVal;
+ }
+ }
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
+index 287fed1..65ba582 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
+@@ -24,18 +24,8 @@ import java.io.IOException;
+ */
+ public class KeepAliveHttpsTransportSE extends HttpsTransportSE
+ {
+- private final String host;
+- private final int port;
+- private final String file;
+- private final int timeout;
+- private ServiceConnection serviceConnection;
+-
+- public KeepAliveHttpsTransportSE(String host, int port, String file, int timeout) {
++ public KeepAliveHttpsTransportSE (String host, int port, String file, int timeout) {
+ super(host, port, file, timeout);
+- this.host = host;
+- this.port = port;
+- this.file = file;
+- this.timeout = timeout;
+ }
+
+ /**
+@@ -47,11 +37,9 @@ public class KeepAliveHttpsTransportSE extends HttpsTransportSE
+ //@Override
+ public ServiceConnection getServiceConnection() throws IOException
+ {
+- if (serviceConnection == null) {
+- serviceConnection = new HttpsServiceConnectionSEIgnoringConnectionClose(host, port,
+- file, timeout);
+- serviceConnection.setRequestProperty("Connection", "keep-alive");
+- }
++ ServiceConnection serviceConnection =
++ new HttpsServiceConnectionSEIgnoringConnectionClose(host, port, file, timeout);
++ serviceConnection.setRequestProperty("Connection", "keep-alive");
+ return serviceConnection;
+ }
+
+diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
+index 029ee9a..bfdfe11 100644
+--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
++++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
+@@ -21,16 +21,16 @@
+
+ package org.ksoap2.transport;
+
+-import java.io.*;
+-import java.net.*;
+-import java.util.Iterator;
+-import java.util.LinkedList;
+-import java.util.List;
+-import java.util.Map;
+-import java.util.Set;
+-
+ import org.ksoap2.HeaderProperty;
+
++import java.io.IOException;
++import java.io.InputStream;
++import java.io.OutputStream;
++import java.net.HttpURLConnection;
++import java.net.Proxy;
++import java.net.URL;
++import java.util.*;
++
+ /**
+ * Connection for J2SE environments.
+ */
+@@ -80,23 +80,29 @@ public class ServiceConnectionSE implements ServiceConnection {
+ connection.disconnect();
+ }
+
+- public List getResponseProperties() {
+- Map properties = connection.getHeaderFields();
+- Set keys = properties.keySet();
++ public List getResponseProperties() throws IOException {
+ List retList = new LinkedList();
+
+- for (Iterator i = keys.iterator(); i.hasNext();) {
+- String key = (String) i.next();
+- List values = (List) properties.get(key);
+-
+- for (int j = 0; j < values.size(); j++) {
+- retList.add(new HeaderProperty(key, (String) values.get(j)));
++ Map properties = connection.getHeaderFields();
++ if(properties != null) {
++ Set keys = properties.keySet();
++ for (Iterator i = keys.iterator(); i.hasNext();) {
++ String key = (String) i.next();
++ List values = (List) properties.get(key);
++
++ for (int j = 0; j < values.size(); j++) {
++ retList.add(new HeaderProperty(key, (String) values.get(j)));
++ }
+ }
+ }
+
+ return retList;
+ }
+
++ public int getResponseCode() throws IOException {
++ return connection.getResponseCode();
++ }
++
+ public void setRequestProperty(String string, String soapAction) {
+ connection.setRequestProperty(string, soapAction);
+ }
+@@ -116,6 +122,10 @@ public class ServiceConnectionSE implements ServiceConnection {
+ connection.setFixedLengthStreamingMode(contentLength);
+ }
+
++ public void setChunkedStreamingMode() {
++ connection.setChunkedStreamingMode(0);
++ }
++
+ public OutputStream openOutputStream() throws IOException {
+ return connection.getOutputStream();
+ }
+--
+2.17.0.441.gb46fe60e1d-goog
+
diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
index 8a0b894..1c43656 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapEnvelope.java
@@ -54,10 +54,8 @@ public class SoapEnvelope {
/** Namespace constant: http://www.w3.org/1999/XMLSchema */
public static final String XSI1999 = "http://www.w3.org/1999/XMLSchema-instance";
- //public static final String NS20 = "http://www.wi-fi-org/specifications/hotspot2dot0/spp/1.0/";
public static final String NS20 = "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp";
- //public static final String OMADM12 = "http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd";
/**
* Returns true for the string values "1" and "true", ignoring upper/lower
@@ -105,9 +103,8 @@ public class SoapEnvelope {
/** Xml Schema data namespace, set by the constructor */
public String xsd;
- ///M: HS20 Add by Jungo
+ // HS20 change
public String ns;
- public String omadm;
/**
* Initializes a SOAP Envelope. The version parameter must be set to one of
@@ -129,10 +126,8 @@ public class SoapEnvelope {
enc = SoapEnvelope.ENC2003;
env = SoapEnvelope.ENV2003;
}
-
+ // HS20 change
ns = SoapEnvelope.NS20;
- //omadm = SoapEnvelope.OMADM12;
-
}
/** Parses the SOAP envelope from the given parser */
@@ -206,13 +201,9 @@ public class SoapEnvelope {
* given XML writer.
*/
public void write(XmlSerializer writer) throws IOException {
- ///M: HS20 modify by Jungo
- //writer.setPrefix("i", xsi);
- //writer.setPrefix("d", xsd);
- //writer.setPrefix("c", enc);
- writer.setPrefix("soap", env);//the prefix for namespace env in xml output
+ // HS 2.0 changes
+ writer.setPrefix("soap", env); //the prefix for namespace env in xml output
writer.setPrefix("spp", ns);
- //writer.setPrefix("omadm", omadm);
writer.startTag(env, "Envelope");
writer.startTag(env, "Header");
diff --git a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
index 5667cb4..3f39147 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/SoapFault12.java
@@ -72,27 +72,28 @@ public class SoapFault12 extends SoapFault {
while (parser.nextTag() == XmlPullParser.START_TAG) {
String name = parser.getName();
+ String namespace = parser.getNamespace();
parser.nextTag();
- if (name.equals("Code")) {
+ if (name.toLowerCase().equals("Code".toLowerCase())) {
this.Code = new Node();
this.Code.parse(parser);
- } else if (name.equals("Reason")) {
+ } else if (name.toLowerCase().equals("Reason".toLowerCase())) {
this.Reason = new Node();
this.Reason.parse(parser);
- } else if (name.equals("Node")) {
+ } else if (name.toLowerCase().equals("Node".toLowerCase())) {
this.Node = new Node();
this.Node.parse(parser);
- } else if (name.equals("Role")) {
+ } else if (name.toLowerCase().equals("Role".toLowerCase())) {
this.Role = new Node();
this.Role.parse(parser);
- } else if (name.equals("Detail")) {
+ } else if (name.toLowerCase().equals("Detail".toLowerCase())) {
this.Detail = new Node();
this.Detail.parse(parser);
} else {
throw new RuntimeException("unexpected tag:" + name);
}
- parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, name);
+ parser.require(XmlPullParser.END_TAG, namespace, name);
}
parser.require(XmlPullParser.END_TAG, SoapEnvelope.ENV2003, "Fault");
parser.nextTag();
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
index 6b83847..34d2723 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/AttributeContainer.java
@@ -3,8 +3,8 @@ package org.ksoap2.serialization;
import java.util.Vector;
-public class AttributeContainer {
- private Vector attributes = new Vector();
+public class AttributeContainer implements HasAttributes{
+ protected Vector attributes = new Vector();
/**
* Places AttributeInfo of desired attribute into a designated AttributeInfo object
@@ -29,9 +29,9 @@ public class AttributeContainer {
return ((AttributeInfo) attributes.elementAt(index)).getValue();
}
- /**
- * Get the attribute's toString value.
- */
+ /**
+ * Get the attribute's toString value.
+ */
public String getAttributeAsString(int index) {
AttributeInfo attributeInfo = (AttributeInfo) attributes.elementAt(index);
return attributeInfo.getValue().toString();
@@ -52,6 +52,20 @@ public class AttributeContainer {
}
/**
+ * Get the attribute with the given name
+ *
+ * @throws RuntimeException if the attribute does not exist
+ */
+ public Object getAttribute(String namespace,String name) {
+ Integer i = attributeIndex(namespace,name);
+ if (i != null) {
+ return getAttribute(i.intValue());
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+
+ /**
* Get the toString value of the attribute with the given name.
*
* @throws RuntimeException if the attribute does not exist
@@ -66,6 +80,19 @@ public class AttributeContainer {
}
/**
+ * Get the toString value of the attribute with the given name.
+ *
+ * @throws RuntimeException if the attribute does not exist
+ */
+ public String getAttributeAsString(String namespace,String name) {
+ Integer i = attributeIndex(namespace,name);
+ if (i != null) {
+ return getAttribute(i.intValue()).toString();
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+ /**
* Knows whether the given attribute exists
*/
public boolean hasAttribute(final String name) {
@@ -77,6 +104,16 @@ public class AttributeContainer {
}
/**
+ * Knows whether the given attribute exists
+ */
+ public boolean hasAttribute(final String namespace,final String name) {
+ if (attributeIndex(namespace,name) != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ /**
* Get an attribute without chance of throwing an exception
*
* @param name the name of the attribute to retrieve
@@ -92,6 +129,21 @@ public class AttributeContainer {
}
/**
+ * Get an attribute without chance of throwing an exception
+ *
+ * @param name the name of the attribute to retrieve
+ * @return the value of the attribute if it exists; {@code null} if it does not exist
+ */
+ public Object getAttributeSafely(String namespace,String name) {
+ Integer i = attributeIndex(namespace,name);
+ if (i != null) {
+ return getAttribute(i.intValue());
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Get an attributes' toString value without chance of throwing an
* exception.
@@ -108,6 +160,23 @@ public class AttributeContainer {
}
}
+ /**
+ * Get an attributes' toString value without chance of throwing an
+ * exception.
+
+ * @param name
+ * @return the value of the attribute,s toString method if it exists; ""
+ * if it does not exist
+ */
+ public Object getAttributeSafelyAsString(String namespace,String name) {
+ Integer i = attributeIndex(namespace,name);
+ if (i != null) {
+ return getAttribute(i.intValue()).toString();
+ } else {
+ return "";
+ }
+ }
+
private Integer attributeIndex(String name) {
for (int i = 0; i < attributes.size(); i++) {
if (name.equals(((AttributeInfo) attributes.elementAt(i)).getName())) {
@@ -117,6 +186,16 @@ public class AttributeContainer {
return null;
}
+ private Integer attributeIndex(String namespace,String name) {
+ for (int i = 0; i < attributes.size(); i++) {
+ AttributeInfo attrInfo=(AttributeInfo) attributes.elementAt(i);
+ if (name.equals(attrInfo.getName()) && namespace.equals(attrInfo.getNamespace())) {
+ return new Integer(i);
+ }
+ }
+ return null;
+ }
+
/**
* Returns the number of attributes
*
@@ -160,13 +239,25 @@ public class AttributeContainer {
* @return {@code this} object.
*/
public void addAttribute(String name, Object value) {
+ addAttribute(null,name,value);
+ }
+
+ /**
+ * Adds a attribute (parameter) to the object.
+ *
+ * @param namespace The namespace of the attribute
+ * @param name The name of the attribute
+ * @param value the value of the attribute
+ * @return {@code this} object.
+ */
+ public void addAttribute(String namespace,String name, Object value) {
AttributeInfo attributeInfo = new AttributeInfo();
attributeInfo.name = name;
+ attributeInfo.namespace = namespace;
attributeInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value.getClass();
attributeInfo.value = value;
addAttribute(attributeInfo);
}
-
/**
* Add an attribute if the value is not null.
* @param name
@@ -179,6 +270,18 @@ public class AttributeContainer {
}
/**
+ * Add an attribute if the value is not null.
+ * @param namespace The namespace of the attribute
+ * @param name
+ * @param value
+ */
+ public void addAttributeIfValue(String namespace,String name, Object value) {
+ if (value != null) {
+ addAttribute(namespace,name, value);
+ }
+ }
+
+ /**
* Add a new attribute by providing an {@link AttributeInfo} object. {@code AttributeInfo}
* contains all data about the attribute, including name and value.}
*
@@ -198,4 +301,17 @@ public class AttributeContainer {
attributes.addElement(attributeInfo);
}
}
+
+
+ public void setAttribute(AttributeInfo info) {
+
+
+ }
+
+
+ public void getAttribute(int index, AttributeInfo info) {
+
+
+ }
+
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
index 78d4449..79379b3 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/DM.java
@@ -20,9 +20,12 @@
package org.ksoap2.serialization;
-import java.io.*;
-import org.xmlpull.v1.*;
-import org.ksoap2.*;
+import java.io.IOException;
+
+import org.ksoap2.SoapEnvelope;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
/**
* This class is not public, so save a few bytes by using a short class name (DM
@@ -30,8 +33,7 @@ import org.ksoap2.*;
*/
class DM implements Marshal {
- public Object readInstance(XmlPullParser parser, String namespace, String name,
- PropertyInfo expected)
+ public Object readInstance(XmlPullParser parser, String namespace, String name, PropertyInfo excepted)
throws IOException, XmlPullParserException {
String text = parser.nextText();
switch (name.charAt(0)) {
@@ -50,10 +52,9 @@ class DM implements Marshal {
/**
* Write the instance out. In case it is an AttributeContainer write those our first though.
- * @param writer
- * the xml serializer.
- * @param instance
- * @throws IOException
+ * If it HasAttributes then write the attributes and values.
+ *
+ * @param writer the xml serializer.
*/
public void writeInstance(XmlSerializer writer, Object instance) throws IOException {
if (instance instanceof AttributeContainer) {
@@ -62,11 +63,42 @@ class DM implements Marshal {
for (int counter = 0; counter < cnt; counter++) {
AttributeInfo attributeInfo = new AttributeInfo();
attributeContainer.getAttributeInfo(counter, attributeInfo);
- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
- attributeInfo.getValue().toString());
+ try {
+ attributeContainer.getAttribute(counter, attributeInfo);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (attributeInfo.getValue() != null) {
+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
+ (attributeInfo.getValue() != null) ? attributeInfo.getValue().toString()
+ : "");
+ }
+ }
+ } else if (instance instanceof HasAttributes) {
+ HasAttributes soapObject = (HasAttributes) instance;
+ int cnt = soapObject.getAttributeCount();
+ for (int counter = 0; counter < cnt; counter++) {
+ AttributeInfo attributeInfo = new AttributeInfo();
+ soapObject.getAttributeInfo(counter, attributeInfo);
+ try {
+ soapObject.getAttribute(counter, attributeInfo);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (attributeInfo.getValue() != null) {
+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
+ attributeInfo.getValue() != null ? attributeInfo.getValue().toString()
+ : "");
+ }
}
}
- writer.text(instance.toString());
+
+ if (instance instanceof ValueWriter) {
+ ((ValueWriter) instance).write(writer);
+ } else {
+ writer.text(instance.toString());
+ }
+
}
public void register(SoapSerializationEnvelope cm) {
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
new file mode 100644
index 0000000..e4a893c
--- /dev/null
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasAttributes.java
@@ -0,0 +1,16 @@
+package org.ksoap2.serialization;
+
+/**
+ * Common inteface for classes which want to serialize attributes to outgoing soap message
+ *
+ * @author robocik
+ */
+public interface HasAttributes {
+ int getAttributeCount();
+
+ void getAttributeInfo(int index, AttributeInfo info);
+
+ void getAttribute(int index, AttributeInfo info);
+
+ void setAttribute(AttributeInfo info);
+}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
new file mode 100644
index 0000000..b35c35b
--- /dev/null
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/HasInnerText.java
@@ -0,0 +1,17 @@
+package org.ksoap2.serialization;
+/**
+ * Interface for classes requiring inner text of xml tags
+ *
+ * @author satansly
+ */
+public interface HasInnerText {
+ /**
+ * Gets the inner text of xml tags
+ */
+ Object getInnerText();
+
+ /**
+ * @param s String to be set as inner text for an outgoing soap object
+ */
+ void setInnerText(Object s);
+}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
index bded0c0..09d7b32 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/KvmSerializable.java
@@ -39,31 +39,26 @@ public interface KvmSerializable {
*/
Object getProperty(int index);
- /**
- * @return the number of serializable properties
+ /**
+ * @return the number of serializable properties
*/
int getPropertyCount();
/**
* Sets the property with the given index to the given value.
- *
- * @param index
- * the index to be set
- * @param value
- * the value of the property
+ *
+ * @param index the index to be set
+ * @param value the value of the property
*/
void setProperty(int index, Object value);
/**
* Fills the given property info record.
- *
- * @param index
- * the index to be queried
- * @param properties
- * information about the (de)serializer. Not frequently used.
- * @param info
- * The return parameter, to be filled with information about the
- * property with the given index.
+ *
+ * @param index the index to be queried
+ * @param properties information about the (de)serializer. Not frequently used.
+ * @param info The return parameter, to be filled with information about the
+ * property with the given index.
*/
void getPropertyInfo(int index, Hashtable properties, PropertyInfo info);
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
index cfa9d81..771d922 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/Marshal.java
@@ -34,11 +34,9 @@ public interface Marshal {
* please note that the start and and tag must be consumed. This is not
* symmetric to writeInstance, but otherwise it would not be possible to
* access the attributes of the start tag here.
- *
- * @param parser
- * the xml parser
- * @param namespace
- * the namespace.
+ *
+ * @param parser the xml parser
+ * @param namespace the namespace.
* @return the object read from the xml stream.
*/
public Object readInstance(XmlPullParser parser, String namespace, String name,
@@ -50,19 +48,16 @@ public interface Marshal {
* readInstance, it is not neccessary to care about the surrounding start
* and end tags. Additional attributes must be writen before anything else
* is written.
- *
- * @param writer
- * the xml serializer.
- * @param instance
- * the instance to write to the writer.
+ *
+ * @param writer the xml serializer.
+ * @param instance the instance to write to the writer.
*/
public void writeInstance(XmlSerializer writer, Object instance) throws IOException;
/**
* Register this Marshal with Envelope
- *
- * @param envelope
- * the soap serialization envelope.
+ *
+ * @param envelope the soap serialization envelope.
*/
public void register(SoapSerializationEnvelope envelope);
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
index 2f8420c..5a41a3f 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalBase64.java
@@ -25,8 +25,8 @@ import org.ksoap2.*;
import org.ksoap2.kobjects.base64.*;
import org.xmlpull.v1.*;
-/**
- * Base64 (de)serializer
+/**
+ * Base64 (de)serializer
*/
public class MarshalBase64 implements Marshal {
public static Class BYTE_ARRAY_CLASS = new byte[0].getClass();
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
index 3e4fa06..f1c9927 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalDate.java
@@ -25,8 +25,8 @@ import java.io.*;
import org.xmlpull.v1.*;
import org.ksoap2.kobjects.isodate.*;
-/**
- * Marshal class for Dates.
+/**
+ * Marshal class for Dates.
*/
public class MarshalDate implements Marshal {
public static Class DATE_CLASS = new Date().getClass();
@@ -44,5 +44,4 @@ public class MarshalDate implements Marshal {
public void register(SoapSerializationEnvelope cm) {
cm.addMapping(cm.xsd, "dateTime", MarshalDate.DATE_CLASS, this);
}
-
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
index d2367e9..f31995b 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/MarshalHashtable.java
@@ -76,12 +76,12 @@ public class MarshalHashtable implements Marshal {
SoapObject item = new SoapObject(null, null);
item.addProperty("key", null);
item.addProperty("value", null);
- for (Enumeration keys = h.keys(); keys.hasMoreElements();) {
+ for (Enumeration keys = h.keys(); keys.hasMoreElements(); ) {
writer.startTag("", "item");
Object key = keys.nextElement();
item.setProperty(0, key);
item.setProperty(1, h.get(key));
- envelope.writeObjectBody(writer, item);
+ envelope.writeObjectBodyWithAttributes(writer, item);
writer.endTag("", "item");
}
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
index 24a1ffe..dfe3d36 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapObject.java
@@ -1,16 +1,16 @@
/*
* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
- *
+ *
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -18,7 +18,7 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
- *
+ *
* Contributor(s): John D. Beatty, Dave Dash, Andre Gerard, F. Hunter, Renaud
* Tognelli
*/
@@ -38,7 +38,7 @@ import java.util.*;
* KvmSerializable interface.
*/
-public class SoapObject extends AttributeContainer implements KvmSerializable {
+public class SoapObject extends AttributeContainer implements KvmSerializable, HasInnerText {
private static final String EMPTY_STRING = "";
/**
@@ -54,6 +54,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
*/
protected Vector properties = new Vector();
+ protected Object innerText;
+
// TODO: accessing properties and attributes would work much better if we
// kept a list of known properties instead of iterating through the list
// each time
@@ -69,10 +71,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Creates a new <code>SoapObject</code> instance.
*
- * @param namespace
- * the namespace for the soap object
- * @param name
- * the name of the soap object
+ * @param namespace the namespace for the soap object
+ * @param name the name of the soap object
*/
public SoapObject(String namespace, String name) {
@@ -112,8 +112,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Helper function for SoapObject.equals
* Checks if a given property and index are the same as in this
*
- * @param otherProp, index
- * @return
+ * @param otherProp, index
*/
public boolean isPropertyEqual(Object otherProp, int index) {
if (index >= getPropertyCount()) {
@@ -157,9 +156,6 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Get the toString value of the property.
- *
- * @param index
- * @return
*/
public String getPropertyAsString(int index) {
PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index);
@@ -169,8 +165,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Get the property with the given name
*
- * @throws java.lang.RuntimeException
- * if the property does not exist
+ * @throws java.lang.RuntimeException if the property does not exist
*/
public Object getProperty(String name) {
Integer index = propertyIndex(name);
@@ -182,10 +177,223 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
}
/**
- * Get the toString value of the property.
+ * Get the property with the given name
+ *
+ * return null
+ * if the property does not exist
+ */
+ public Object getProperty(String namespace, String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ return getProperty(index.intValue());
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+
+ /**
+ * Get a property using namespace and name without chance of throwing an exception
+ *
+ * @return the property if it exists; if not, {@link NullSoapObject} is
+ * returned
+ */
+ public Object getPropertyByNamespaceSafely(final String namespace, final String name) {
+ Integer i = propertyIndex(namespace, name);
+ if (i != null) {
+ return getProperty(i.intValue());
+ } else {
+ return new NullSoapObject();
+ }
+ }
+
+ /**
+ * Get the toString value of a property without chance of throwing an
+ * exception
+ *
+ * @return the string value of the property if it exists; if not, #EMPTY_STRING is
+ * returned
+ */
+ public String getPropertyByNamespaceSafelyAsString(final String namespace, final String name) {
+ Integer i = propertyIndex(namespace, name);
+ if (i != null) {
+ Object foo = getProperty(i.intValue());
+ if (foo == null) {
+ return EMPTY_STRING;
+ } else {
+ return foo.toString();
+ }
+ } else {
+ return EMPTY_STRING;
+ }
+ }
+
+ /**
+ * Get a property without chance of throwing an exception. An object can be
+ * provided to this method; if the property is not found, this object will
+ * be returned.
+ *
+ * @param defaultThing the object to return if the property is not found
+ * @return the property if it exists; defaultThing if the property does not
+ * exist
+ */
+ public Object getPropertySafely(final String namespace, final String name,
+ final Object defaultThing) {
+ Integer i = propertyIndex(namespace, name);
+ if (i != null) {
+ return getProperty(i.intValue());
+ } else {
+ return defaultThing;
+ }
+ }
+
+ /**
+ * Get the toString value of a property without chance of throwing an
+ * exception. An object can be provided to this method; if the property is
+ * not found, this object's string representation will be returned.
+ *
+ * @param defaultThing toString of the object to return if the property is not found
+ * @return the property toString if it exists; defaultThing toString if the
+ * property does not exist, if the defaultThing is null #EMPTY_STRING
+ * is returned
+ */
+ public String getPropertySafelyAsString(final String namespace, final String name,
+ final Object defaultThing) {
+ Integer i = propertyIndex(namespace, name);
+ if (i != null) {
+ Object property = getProperty(i.intValue());
+ if (property != null) {
+ return property.toString();
+ } else {
+ return EMPTY_STRING;
+ }
+ } else {
+ if (defaultThing != null) {
+ return defaultThing.toString();
+ } else {
+ return EMPTY_STRING;
+ }
+ }
+ }
+
+ /**
+ * Get the primitive property with the given name.
*
- * @param name
- * @return
+ * @return PropertyInfo containing an empty string if property either complex or empty
+ */
+ public Object getPrimitiveProperty(final String namespace, final String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
+ return propertyInfo.getValue();
+ } else {
+ propertyInfo = new PropertyInfo();
+ propertyInfo.setType(String.class);
+ propertyInfo.setValue(EMPTY_STRING);
+ propertyInfo.setName(name);
+ propertyInfo.setNamespace(namespace);
+ return (Object) propertyInfo.getValue();
+ }
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+
+ /**
+ * Get the toString value of the primitive property with the given name.
+ * Returns empty string if property either complex or empty
+ *
+ * @return the string value of the property
+ */
+ public String getPrimitivePropertyAsString(final String namespace, final String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
+ return propertyInfo.getValue().toString();
+ } else {
+ return EMPTY_STRING;
+ }
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+
+ /**
+ * Get the toString value of a primitive property without chance of throwing an
+ * exception
+ *
+ * @return the string value of the property if it exists and is primitive; if not,
+ * #EMPTY_STRING is
+ * returned
+ */
+ public Object getPrimitivePropertySafely(final String namespace, final String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
+ return propertyInfo.getValue().toString();
+ } else {
+ propertyInfo = new PropertyInfo();
+ propertyInfo.setType(String.class);
+ propertyInfo.setValue(EMPTY_STRING);
+ propertyInfo.setName(name);
+ propertyInfo.setNamespace(namespace);
+ return (Object) propertyInfo.getValue();
+ }
+ } else {
+ return new NullSoapObject();
+ }
+ }
+
+ /**
+ * Get the toString value of a primitive property without chance of throwing an
+ * exception
+ *
+ * @return the string value of the property if it exists and is primitive; if not,
+ * #EMPTY_STRING is
+ * returned
+ */
+ public String getPrimitivePropertySafelyAsString(final String namespace, final String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
+ return propertyInfo.getValue().toString();
+ } else {
+ return EMPTY_STRING;
+ }
+ } else {
+ return EMPTY_STRING;
+ }
+ }
+
+ /**
+ * Knows whether the given property exists
+ */
+ public boolean hasProperty(final String namespace, final String name) {
+ if (propertyIndex(namespace, name) != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the toString value of the property.
+ */
+
+ public String getPropertyAsString(String namespace, String name) {
+ Integer index = propertyIndex(namespace, name);
+ if (index != null) {
+ return getProperty(index.intValue()).toString();
+ } else {
+ throw new RuntimeException("illegal property: " + name);
+ }
+ }
+
+ /**
+ * Get the toString value of the property.
*/
public String getPropertyAsString(String name) {
@@ -212,7 +420,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Get a property without chance of throwing an exception
*
* @return the property if it exists; if not, {@link NullSoapObject} is
- * returned
+ * returned
*/
public Object getPropertySafely(final String name) {
Integer i = propertyIndex(name);
@@ -228,7 +436,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* exception
*
* @return the string value of the property if it exists; if not, #EMPTY_STRING is
- * returned
+ * returned
*/
public String getPropertySafelyAsString(final String name) {
Integer i = propertyIndex(name);
@@ -249,10 +457,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* provided to this method; if the property is not found, this object will
* be returned.
*
- * @param defaultThing
- * the object to return if the property is not found
+ * @param defaultThing the object to return if the property is not found
* @return the property if it exists; defaultThing if the property does not
- * exist
+ * exist
*/
public Object getPropertySafely(final String name, final Object defaultThing) {
Integer i = propertyIndex(name);
@@ -268,11 +475,10 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* exception. An object can be provided to this method; if the property is
* not found, this object's string representation will be returned.
*
- * @param defaultThing
- * toString of the object to return if the property is not found
+ * @param defaultThing toString of the object to return if the property is not found
* @return the property toString if it exists; defaultThing toString if the
- * property does not exist, if the defaultThing is null #EMPTY_STRING
- * is returned
+ * property does not exist, if the defaultThing is null #EMPTY_STRING
+ * is returned
*/
public String getPropertySafelyAsString(final String name,
final Object defaultThing) {
@@ -296,14 +502,13 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Get the primitive property with the given name.
*
- * @param name
* @return PropertyInfo containing an empty string if property either complex or empty
*/
public Object getPrimitiveProperty(final String name) {
Integer index = propertyIndex(name);
if (index != null) {
PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
- if (propertyInfo.getType() != SoapObject.class) {
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
return propertyInfo.getValue();
} else {
propertyInfo = new PropertyInfo();
@@ -321,14 +526,13 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Get the toString value of the primitive property with the given name.
* Returns empty string if property either complex or empty
*
- * @param name
* @return the string value of the property
*/
public String getPrimitivePropertyAsString(final String name) {
Integer index = propertyIndex(name);
if (index != null) {
PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
- if (propertyInfo.getType() != SoapObject.class) {
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
return propertyInfo.getValue().toString();
} else {
return EMPTY_STRING;
@@ -342,15 +546,15 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Get the toString value of a primitive property without chance of throwing an
* exception
*
- * @param name
- * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
- * returned
+ * @return the string value of the property if it exists and is primitive; if not,
+ * #EMPTY_STRING is
+ * returned
*/
public Object getPrimitivePropertySafely(final String name) {
Integer index = propertyIndex(name);
if (index != null) {
PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
- if (propertyInfo.getType() != SoapObject.class) {
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
return propertyInfo.getValue().toString();
} else {
propertyInfo = new PropertyInfo();
@@ -368,15 +572,15 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Get the toString value of a primitive property without chance of throwing an
* exception
*
- * @param name
- * @return the string value of the property if it exists and is primitive; if not, #EMPTY_STRING is
- * returned
+ * @return the string value of the property if it exists and is primitive; if not,
+ * #EMPTY_STRING is
+ * returned
*/
public String getPrimitivePropertySafelyAsString(final String name) {
Integer index = propertyIndex(name);
if (index != null) {
PropertyInfo propertyInfo = (PropertyInfo) properties.elementAt(index.intValue());
- if (propertyInfo.getType() != SoapObject.class) {
+ if (propertyInfo.getType() != SoapObject.class && propertyInfo.getValue() != null) {
return propertyInfo.getValue().toString();
} else {
return EMPTY_STRING;
@@ -397,6 +601,19 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
return null;
}
+
+ private Integer propertyIndex(String namespace, String name) {
+ if (name != null && namespace != null) {
+ for (int i = 0; i < properties.size(); i++) {
+ PropertyInfo info = (PropertyInfo) properties.elementAt(i);
+ if (name.equals(info.getName()) && namespace.equals(info.getNamespace())) {
+ return new Integer(i);
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Returns the number of properties
*
@@ -410,12 +627,9 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Places PropertyInfo of desired property into a designated PropertyInfo
* object. Just calls #getPropertyInfo and discards any provided properties.
*
- * @param index
- * index of desired property
- * @param properties
- * this parameter is ignored
- * @param propertyInfo
- * designated retainer of desired property
+ * @param index index of desired property
+ * @param properties this parameter is ignored
+ * @param propertyInfo designated retainer of desired property
*/
public void getPropertyInfo(int index, Hashtable properties, PropertyInfo propertyInfo) {
getPropertyInfo(index, propertyInfo);
@@ -425,10 +639,8 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Places PropertyInfo of desired property into a designated PropertyInfo
* object
*
- * @param index
- * index of desired property
- * @param propertyInfo
- * designated retainer of desired property
+ * @param index index of desired property
+ * @param propertyInfo designated retainer of desired property
*/
public void getPropertyInfo(int index, PropertyInfo propertyInfo) {
Object element = properties.elementAt(index);
@@ -453,6 +665,17 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
}
}
+ public PropertyInfo getPropertyInfo(int index) {
+ Object element = properties.elementAt(index);
+ if (element instanceof PropertyInfo) {
+ PropertyInfo p = (PropertyInfo) element;
+ return p;
+ } else {
+ // SoapObject
+ return null;
+ }
+ }
+
/**
* Creates a new SoapObject based on this, allows usage of SoapObjects as
* templates. One application is to set the expected return type of a soap
@@ -476,7 +699,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
AttributeInfo newAI = new AttributeInfo();
getAttributeInfo(attribIndex, newAI);
AttributeInfo attributeInfo = newAI; // (AttributeInfo)
- // attributes.elementAt(attribIndex);
+ // attributes.elementAt(attribIndex);
o.addAttribute(attributeInfo);
}
return o;
@@ -485,27 +708,24 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Sets a specified property to a certain value.
*
- * @param index
- * the index of the specified property
- * @param value
- * the new value of the property
+ * @param index the index of the specified property
+ * @param value the new value of the property
*/
public void setProperty(int index, Object value) {
Object prop = properties.elementAt(index);
if (prop instanceof PropertyInfo) {
((PropertyInfo) prop).setValue(value);
}
- // TODO: not sure how you want to handle an exception here if the index points to a SoapObject
+ // TODO: not sure how you want to handle an exception here if the index points to a
+ // SoapObject
}
/**
* Adds a property (parameter) to the object. This is essentially a sub
* element.
*
- * @param name
- * The name of the property
- * @param value
- * the value of the property
+ * @param name The name of the property
+ * @param value the value of the property
*/
public SoapObject addProperty(String name, Object value) {
PropertyInfo propertyInfo = new PropertyInfo();
@@ -513,17 +733,44 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
.getClass();
propertyInfo.value = value;
+ return addProperty(propertyInfo);
+ }
+
+ /**
+ * Adds a property (parameter) to the object. This is essentially a sub
+ * element.
+ *
+ * @param namespace The namespace of the property
+ * @param name The name of the property
+ * @param value the value of the property
+ */
+ public SoapObject addProperty(String namespace, String name, Object value) {
+ PropertyInfo propertyInfo = new PropertyInfo();
+ propertyInfo.name = name;
propertyInfo.namespace = namespace;
- ///M: HS20 modify by Jungo
+ propertyInfo.type = value == null ? PropertyInfo.OBJECT_CLASS : value
+ .getClass();
+ propertyInfo.value = value;
return addProperty(propertyInfo);
}
/**
* Add a property only if the value is not null.
*
- * @param name
- * @param value
- * @return
+ * @param namespace The namespace of the property
+ * @param name The name of the property
+ * @param value the value of the property
+ */
+ public SoapObject addPropertyIfValue(String namespace, String name, Object value) {
+ if (value != null) {
+ return addProperty(namespace, name, value);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Add a property only if the value is not null.
*/
public SoapObject addPropertyIfValue(String name, Object value) {
if (value != null) {
@@ -535,10 +782,6 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Add a property only if the value is not null.
- *
- * @param propertyInfo
- * @param value
- * @return
*/
public SoapObject addPropertyIfValue(PropertyInfo propertyInfo, Object value) {
if (value != null) {
@@ -553,8 +796,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Adds a property (parameter) to the object. This is essentially a sub
* element.
*
- * @param propertyInfo
- * designated retainer of desired property
+ * @param propertyInfo designated retainer of desired property
*/
public SoapObject addProperty(PropertyInfo propertyInfo) {
properties.addElement(propertyInfo);
@@ -563,9 +805,6 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Ad the propertyInfo only if the value of it is not null.
- *
- * @param propertyInfo
- * @return
*/
public SoapObject addPropertyIfValue(PropertyInfo propertyInfo) {
if (propertyInfo.value != null) {
@@ -580,8 +819,7 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
* Adds a SoapObject the properties array. This is a sub element to
* allow nested SoapObjects
*
- * @param soapObject
- * to be added as a property of the current object
+ * @param soapObject to be added as a property of the current object
*/
public SoapObject addSoapObject(SoapObject soapObject) {
properties.addElement(soapObject);
@@ -590,8 +828,6 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
/**
* Generate a {@code String} describing this object.
- *
- * @return
*/
public String toString() {
StringBuffer buf = new StringBuffer(EMPTY_STRING + name + "{");
@@ -610,4 +846,16 @@ public class SoapObject extends AttributeContainer implements KvmSerializable {
buf.append("}");
return buf.toString();
}
+
+ public Object getInnerText() {
+ return innerText;
+ }
+
+ public void setInnerText(Object innerText) {
+ this.innerText = innerText;
+ }
+
+ public void removePropertyInfo(Object info) {
+ properties.remove(info);
+ }
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
index d09f7a7..75ed6f5 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapPrimitive.java
@@ -23,7 +23,7 @@ package org.ksoap2.serialization;
/**
* A class that is used to encapsulate primitive types (represented by a string
* in XML serialization).
- *
+ *
* Basically, the SoapPrimitive class encapsulates "unknown" primitive types
* (similar to SoapObject encapsulating unknown complex types). For example, new
* SoapPrimitive (classMap.xsd, "float", "12.3") allows you to send a float from
@@ -34,11 +34,14 @@ package org.ksoap2.serialization;
*/
public class SoapPrimitive extends AttributeContainer {
- String namespace;
- String name;
- String value;
+ protected String namespace;
+ protected String name;
+ protected Object value;
+
+ public static final Object NullSkip = new Object();
+ public static final Object NullNilElement = new Object();
- public SoapPrimitive(String namespace, String name, String value) {
+ public SoapPrimitive(String namespace, String name, Object value) {
this.namespace = namespace;
this.name = name;
this.value = value;
@@ -60,7 +63,7 @@ public class SoapPrimitive extends AttributeContainer {
}
public String toString() {
- return value;
+ return value != null ? value.toString() : null;
}
public String getNamespace() {
@@ -70,4 +73,9 @@ public class SoapPrimitive extends AttributeContainer {
public String getName() {
return name;
}
+
+ public Object getValue() {
+ return value;
+ }
+
}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
index 03cca04..39978b4 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/SoapSerializationEnvelope.java
@@ -2,18 +2,26 @@
* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- * associated documentation files (the "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the
* following conditions:
*
- * The above copyright notice and this permission notice shall be included in all copies or substantial
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial
* portions of the Software.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
- * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
- * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT
+ * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO
+ * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@@ -26,46 +34,44 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import libcore.util.XmlObjectFactory;
-
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Vector;
-import java.io.ByteArrayOutputStream;
/**
* @author Stefan Haustein
*
- * This class extends the SoapEnvelope with Soap Serialization functionality.
+ * This class extends the SoapEnvelope with Soap Serialization functionality.
*/
-public class SoapSerializationEnvelope extends SoapEnvelope
-{
+public class SoapSerializationEnvelope extends SoapEnvelope {
protected static final int QNAME_TYPE = 1;
protected static final int QNAME_NAMESPACE = 0;
protected static final int QNAME_MARSHAL = 3;
+ protected static final String NULL_LABEL = "null";
+ protected static final String NIL_LABEL = "nil";
+ static final Marshal DEFAULT_MARSHAL = new DM();
private static final String ANY_TYPE_LABEL = "anyType";
private static final String ARRAY_MAPPING_NAME = "Array";
- private static final String NULL_LABEL = "null";
- private static final String NIL_LABEL = "nil";
private static final String HREF_LABEL = "href";
private static final String ID_LABEL = "id";
private static final String ROOT_LABEL = "root";
private static final String TYPE_LABEL = "type";
private static final String ITEM_LABEL = "item";
private static final String ARRAY_TYPE_LABEL = "arrayType";
- static final Marshal DEFAULT_MARSHAL = new DM();
public Hashtable properties = new Hashtable();
-
- Hashtable idMap = new Hashtable();
- Vector multiRef; // = new Vector();
-
/**
* Set this variable to true if you don't want that type definitions for complex types/objects
* are automatically generated (with type "anyType") in the XML-Request, if you don't call the
- * Method addMapping. This is needed by some Servers which have problems with these type-definitions.
+ * Method addMapping. This is needed by some Servers which have problems with these
+ * type-definitions.
*/
public boolean implicitTypes;
-
+ /**
+ * If set to true then all properties with null value will be skipped from the soap message.
+ * If false then null properties will be sent as <element nil="true" />
+ */
+ public boolean skipNullProperties;
/**
* Set this variable to true for compatibility with what seems to be the default encoding for
* .Net-Services. This feature is an extremely ugly hack. A much better option is to change the
@@ -93,12 +99,14 @@ public class SoapSerializationEnvelope extends SoapEnvelope
protected Hashtable classToQName = new Hashtable();
/**
- * Set to true to add and ID and ROOT label to the envelope. Change to false for compatibility with WSDL.
+ * Set to true to add and ID and ROOT label to the envelope. Change to false for
+ * compatibility with WSDL.
*/
protected boolean addAdornments = true;
+ Hashtable idMap = new Hashtable();
+ Vector multiRef; // = new Vector();
- public SoapSerializationEnvelope(int version)
- {
+ public SoapSerializationEnvelope(int version) {
super(version);
addMapping(enc, ARRAY_MAPPING_NAME, PropertyInfo.VECTOR_CLASS);
DEFAULT_MARSHAL.register(this);
@@ -107,24 +115,22 @@ public class SoapSerializationEnvelope extends SoapEnvelope
/**
* @return the addAdornments
*/
- public boolean isAddAdornments()
- {
+ public boolean isAddAdornments() {
return addAdornments;
}
/**
- * @param addAdornments
- * the addAdornments to set
+ * @param addAdornments the addAdornments to set
*/
- public void setAddAdornments(boolean addAdornments)
- {
+ public void setAddAdornments(boolean addAdornments) {
this.addAdornments = addAdornments;
}
/**
- * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut will
+ * Set the bodyOut to be empty so that no un-needed xml is create. The null value for bodyOut
+ * will
* cause #writeBody to skip writing anything redundant.
- * @param emptyBody
+ *
* @see "http://code.google.com/p/ksoap2-android/issues/detail?id=77"
*/
public void setBodyOutEmpty(boolean emptyBody) {
@@ -133,8 +139,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
}
- public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException
- {
+ public void parseBody(XmlPullParser parser) throws IOException, XmlPullParserException {
bodyIn = null;
parser.nextTag();
if (parser.getEventType() == XmlPullParser.START_TAG && parser.getNamespace().equals(env)
@@ -161,10 +166,12 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
}
- /** Read a SoapObject. This extracts any attributes and then reads the object as a KvmSerializable. */
+ /**
+ * Read a SoapObject. This extracts any attributes and then reads the object as a
+ * KvmSerializable.
+ */
protected void readSerializable(XmlPullParser parser, SoapObject obj) throws IOException,
- XmlPullParserException
- {
+ XmlPullParserException {
for (int counter = 0; counter < parser.getAttributeCount(); counter++) {
String attributeName = parser.getAttributeName(counter);
String value = parser.getAttributeValue(counter);
@@ -173,11 +180,22 @@ public class SoapSerializationEnvelope extends SoapEnvelope
readSerializable(parser, (KvmSerializable) obj);
}
- /** Read a KvmSerializable. */
+ /**
+ * Read a KvmSerializable.
+ */
protected void readSerializable(XmlPullParser parser, KvmSerializable obj) throws IOException,
- XmlPullParserException
- {
- while (parser.nextTag() != XmlPullParser.END_TAG) {
+ XmlPullParserException {
+ int tag = 0;
+ try {
+ tag = parser.nextTag();
+ } catch (XmlPullParserException e) {
+ if (obj instanceof HasInnerText) {
+ ((HasInnerText) obj).setInnerText(
+ (parser.getText() != null) ? parser.getText() : "");
+ }
+ tag = parser.nextTag();
+ }
+ while (tag != XmlPullParser.END_TAG) {
String name = parser.getName();
if (!implicitTypes || !(obj instanceof SoapObject)) {
PropertyInfo info = new PropertyInfo();
@@ -188,9 +206,9 @@ public class SoapSerializationEnvelope extends SoapEnvelope
info.clear();
obj.getPropertyInfo(i, properties, info);
- if ((name.equals(info.name) && info.namespace == null)
- ||
- (name.equals(info.name) && parser.getNamespace().equals(info.namespace))) {
+ if ((name.equals(info.name) && info.namespace == null) ||
+ (name.equals(info.name) && parser.getNamespace().equals(
+ info.namespace))) {
propertyFound = true;
obj.setProperty(i, read(parser, obj, i, null, null, info));
}
@@ -199,35 +217,56 @@ public class SoapSerializationEnvelope extends SoapEnvelope
if (!propertyFound) {
if (avoidExceptionForUnknownProperty) {
// Dummy loop to read until corresponding END tag
- while (parser.next() != XmlPullParser.END_TAG
- || !name.equals(parser.getName())) {
+ while (parser.next() != XmlPullParser.END_TAG || !name.equals(
+ parser.getName())) {
}
;
} else {
throw new RuntimeException("Unknown Property: " + name);
}
+ } else {
+ if (obj instanceof HasAttributes) {
+ HasAttributes soapObject = (HasAttributes) obj;
+ int cnt = parser.getAttributeCount();
+ for (int counter = 0; counter < cnt; counter++) {
+ AttributeInfo attributeInfo = new AttributeInfo();
+ attributeInfo.setName(parser.getAttributeName(counter));
+ attributeInfo.setValue(parser.getAttributeValue(counter));
+ attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
+ attributeInfo.setType(parser.getAttributeType(counter));
+ soapObject.setAttribute(attributeInfo);
+
+ }
+ }
}
} else {
// I can only make this work for SoapObjects - hence the check above
- // I don't understand namespaces well enough to know whether it is correct in the next line...
+ // I don't understand namespaces well enough to know whether it is correct in the
+ // next line...
((SoapObject) obj).addProperty(parser.getName(),
read(parser, obj, obj.getPropertyCount(),
((SoapObject) obj).getNamespace(), name, PropertyInfo.OBJECT_TYPE));
}
+ try {
+ tag = parser.nextTag();
+ } catch (XmlPullParserException e) {
+ if (obj instanceof HasInnerText) {
+ ((HasInnerText) obj).setInnerText(
+ (parser.getText() != null) ? parser.getText() : "");
+ }
+ tag = parser.nextTag();
+ }
+
}
parser.require(XmlPullParser.END_TAG, null, null);
}
/**
- * If the type of the object cannot be determined, and thus no Marshal class can handle the object, this
+ * If the type of the object cannot be determined, and thus no Marshal class can handle the
+ * object, this
* method is called. It will build either a SoapPrimitive or a SoapObject
*
- * @param parser
- * @param typeNamespace
- * @param typeName
* @return unknownObject wrapped as a SoapPrimitive or SoapObject
- * @throws IOException
- * @throws XmlPullParserException
*/
protected Object readUnknown(XmlPullParser parser, String typeNamespace, String typeName)
@@ -237,7 +276,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
// cache the attribute info list from the current element before we move on
Vector attributeInfoVector = new Vector();
- for (int attributeCount = 0; attributeCount < parser.getAttributeCount(); attributeCount++) {
+ for (int attributeCount = 0; attributeCount < parser.getAttributeCount();
+ attributeCount++) {
AttributeInfo attributeInfo = new AttributeInfo();
attributeInfo.setName(parser.getAttributeName(attributeCount));
attributeInfo.setValue(parser.getAttributeValue(attributeCount));
@@ -253,14 +293,16 @@ public class SoapSerializationEnvelope extends SoapEnvelope
text = parser.getText();
SoapPrimitive sp = new SoapPrimitive(typeNamespace, typeName, text);
result = sp;
- // apply all the cached attribute info list before we add the property and descend further for parsing
+ // apply all the cached attribute info list before we add the property and descend
+ // further for parsing
for (int i = 0; i < attributeInfoVector.size(); i++) {
sp.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i));
}
parser.next();
} else if (parser.getEventType() == XmlPullParser.END_TAG) {
SoapObject so = new SoapObject(typeNamespace, typeName);
- // apply all the cached attribute info list before we add the property and descend further for parsing
+ // apply all the cached attribute info list before we add the property and descend
+ // further for parsing
for (int i = 0; i < attributeInfoVector.size(); i++) {
so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i));
}
@@ -272,15 +314,16 @@ public class SoapSerializationEnvelope extends SoapEnvelope
throw new RuntimeException("Malformed input: Mixed content");
}
SoapObject so = new SoapObject(typeNamespace, typeName);
- // apply all the cached attribute info list before we add the property and descend further for parsing
+ // apply all the cached attribute info list before we add the property and descend
+ // further for parsing
for (int i = 0; i < attributeInfoVector.size(); i++) {
so.addAttribute((AttributeInfo) attributeInfoVector.elementAt(i));
}
while (parser.getEventType() != XmlPullParser.END_TAG) {
- so.addProperty(parser.getName(),
- read(parser, so, so.getPropertyCount(), null, null,
- PropertyInfo.OBJECT_TYPE));
+ so.addProperty(parser.getNamespace(), parser.getName(),
+ read(parser, so, so.getPropertyCount(),
+ null, null, PropertyInfo.OBJECT_TYPE));
parser.nextTag();
}
result = so;
@@ -293,8 +336,12 @@ public class SoapSerializationEnvelope extends SoapEnvelope
if (value == null) {
return dflt;
}
- return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
- value.length() - 1));
+ try {
+ return value.length() - start < 3 ? dflt : Integer.parseInt(value.substring(start + 1,
+ value.length() - 1));
+ } catch (Exception ex) {
+ return dflt;
+ }
}
protected void readVector(XmlPullParser parser, Vector v, PropertyInfo elementType)
@@ -338,13 +385,24 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
/**
- * Builds an object from the XML stream. This method is public for usage in conjuction with Marshal
+ * This method returns id from the href attribute value.
+ * By default we assume that href value looks like this: #id so we basically have to remove
+ * the first character.
+ * But in theory there could be a different value format, like cid:value, etc...
+ */
+ protected String getIdFromHref(String hrefValue) {
+ return hrefValue.substring(1);
+ }
+
+ /**
+ * Builds an object from the XML stream. This method is public for usage in conjuction with
+ * Marshal
* subclasses. Precondition: On the start tag of the object or property, so href can be read.
*/
- public Object read(XmlPullParser parser, Object owner, int index, String namespace,
- String name,
- PropertyInfo expected) throws IOException, XmlPullParserException {
+ public Object read(XmlPullParser parser, Object owner, int index, String namespace, String name,
+ PropertyInfo expected)
+ throws IOException, XmlPullParserException {
String elementName = parser.getName();
String href = parser.getAttributeValue(null, HREF_LABEL);
Object obj;
@@ -352,7 +410,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
if (owner == null) {
throw new RuntimeException("href at root level?!?");
}
- href = href.substring(1);
+ href = getIdFromHref(href);
obj = idMap.get(href);
if (obj == null || obj instanceof FwdRef) {
FwdRef f = new FwdRef();
@@ -402,21 +460,8 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
// finally, care about the id....
if (id != null) {
- Object hlp = idMap.get(id);
- if (hlp instanceof FwdRef) {
- FwdRef f = (FwdRef) hlp;
- do {
- if (f.obj instanceof KvmSerializable) {
- ((KvmSerializable) f.obj).setProperty(f.index, obj);
- } else {
- ((Vector) f.obj).setElementAt(obj, f.index);
- }
- f = f.next;
- } while (f != null);
- } else if (hlp != null) {
- throw new RuntimeException("double ID");
- }
- idMap.put(id, obj);
+ resolveReference(id, obj);
+
}
}
@@ -424,8 +469,28 @@ public class SoapSerializationEnvelope extends SoapEnvelope
return obj;
}
+ protected void resolveReference(String id, Object obj) {
+ Object hlp = idMap.get(id);
+ if (hlp instanceof FwdRef) {
+ FwdRef f = (FwdRef) hlp;
+ do {
+ if (f.obj instanceof KvmSerializable) {
+ ((KvmSerializable) f.obj).setProperty(f.index, obj);
+ } else {
+ ((Vector) f.obj).setElementAt(obj, f.index);
+ }
+ f = f.next;
+ }
+ while (f != null);
+ } else if (hlp != null) {
+ throw new RuntimeException("double ID");
+ }
+ idMap.put(id, obj);
+ }
+
/**
- * Returns a new object read from the given parser. If no mapping is found, null is returned. This method
+ * Returns a new object read from the given parser. If no mapping is found, null is returned.
+ * This method
* is used by the SoapParser in order to convert the XML code to Java objects.
*/
public Object readInstance(XmlPullParser parser, String namespace, String name,
@@ -448,10 +513,31 @@ public class SoapSerializationEnvelope extends SoapEnvelope
throw new RuntimeException(e.toString());
}
}
+ if (obj instanceof HasAttributes) {
+ HasAttributes soapObject = (HasAttributes) obj;
+ int cnt = parser.getAttributeCount();
+ for (int counter = 0; counter < cnt; counter++) {
+
+ AttributeInfo attributeInfo = new AttributeInfo();
+ attributeInfo.setName(parser.getAttributeName(counter));
+ attributeInfo.setValue(parser.getAttributeValue(counter));
+ attributeInfo.setNamespace(parser.getAttributeNamespace(counter));
+ attributeInfo.setType(parser.getAttributeType(counter));
+
+ soapObject.setAttribute(attributeInfo);
+
+ }
+ }
+
// ok, obj is now the instance, fill it....
if (obj instanceof SoapObject) {
readSerializable(parser, (SoapObject) obj);
} else if (obj instanceof KvmSerializable) {
+
+ if (obj instanceof HasInnerText) {
+ ((HasInnerText) obj).setInnerText(
+ (parser.getText() != null) ? parser.getText() : "");
+ }
readSerializable(parser, (KvmSerializable) obj);
} else if (obj instanceof Vector) {
readVector(parser, (Vector) obj, expected.elementType);
@@ -462,8 +548,10 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
/**
- * Returns a string array containing the namespace, name, id and Marshal object for the given java object.
- * This method is used by the SoapWriter in order to map Java objects to the corresponding SOAP section
+ * Returns a string array containing the namespace, name, id and Marshal object for the given
+ * java object.
+ * This method is used by the SoapWriter in order to map Java objects to the corresponding
+ * SOAP section
* five XML code.
*/
public Object[] getInfo(Object type, Object instance) {
@@ -476,15 +564,11 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
if (type instanceof SoapObject) {
SoapObject so = (SoapObject) type;
- return new Object[] {
- so.getNamespace(), so.getName(), null, null
- };
+ return new Object[]{so.getNamespace(), so.getName(), null, null};
}
if (type instanceof SoapPrimitive) {
SoapPrimitive sp = (SoapPrimitive) type;
- return new Object[] {
- sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL
- };
+ return new Object[]{sp.getNamespace(), sp.getName(), null, DEFAULT_MARSHAL};
}
if ((type instanceof Class) && type != PropertyInfo.OBJECT_CLASS) {
Object[] tmp = (Object[]) classToQName.get(((Class) type).getName());
@@ -492,22 +576,19 @@ public class SoapSerializationEnvelope extends SoapEnvelope
return tmp;
}
}
- return new Object[] {
- xsd, ANY_TYPE_LABEL, null, null
- };
+ return new Object[]{xsd, ANY_TYPE_LABEL, null, null};
}
/**
- * Defines a direct mapping from a namespace and name to a java class (and vice versa), using the given
+ * Defines a direct mapping from a namespace and name to a java class (and vice versa), using
+ * the given
* marshal mechanism
*/
public void addMapping(String namespace, String name, Class clazz, Marshal marshal) {
qNameToClass
- .put(new SoapPrimitive(namespace, name, null), marshal == null ? (Object) clazz
- : marshal);
- classToQName.put(clazz.getName(), new Object[] {
- namespace, name, null, marshal
- });
+ .put(new SoapPrimitive(namespace, name, null),
+ marshal == null ? (Object) clazz : marshal);
+ classToQName.put(clazz.getName(), new Object[]{namespace, name, null, marshal});
}
/**
@@ -518,8 +599,10 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
/**
- * Adds a SoapObject to the class map. During parsing, objects of the given type (namespace/name) will be
- * mapped to corresponding copies of the given SoapObject, maintaining the structure of the template.
+ * Adds a SoapObject to the class map. During parsing, objects of the given type
+ * (namespace/name) will be
+ * mapped to corresponding copies of the given SoapObject, maintaining the structure of the
+ * template.
*/
public void addTemplate(SoapObject so) {
qNameToClass.put(new SoapPrimitive(so.namespace, so.name, null), so);
@@ -528,11 +611,13 @@ public class SoapSerializationEnvelope extends SoapEnvelope
/**
* Response from the soap call. Pulls the object from the wrapper object and returns it.
*
- * @since 2.0.3
* @return response from the soap call.
- * @throws SoapFault
+ * @since 2.0.3
*/
public Object getResponse() throws SoapFault {
+ if (bodyIn == null) {
+ return null;
+ }
if (bodyIn instanceof SoapFault) {
throw (SoapFault) bodyIn;
}
@@ -554,8 +639,7 @@ public class SoapSerializationEnvelope extends SoapEnvelope
/**
* Serializes the request object to the given XmlSerliazer object
*
- * @param writer
- * XmlSerializer object to write the body into.
+ * @param writer XmlSerializer object to write the body into.
*/
public void writeBody(XmlSerializer writer) throws IOException {
// allow an empty body without any tags in it
@@ -564,40 +648,57 @@ public class SoapSerializationEnvelope extends SoapEnvelope
multiRef = new Vector();
multiRef.addElement(bodyOut);
Object[] qName = getInfo(null, bodyOut);
+
writer.startTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
- (String) qName[QNAME_TYPE]); //<spp:sppPostDevData
+ (String) qName[QNAME_TYPE]);
+
if (dotNet) {
writer.attribute(null, "xmlns", (String) qName[QNAME_NAMESPACE]);
}
+
if (addAdornments) {
writer.attribute(null, ID_LABEL, qName[2] == null ? ("o" + 0) : (String) qName[2]);
writer.attribute(enc, ROOT_LABEL, "1");
}
- writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]); //....
+ writeElement(writer, bodyOut, null, qName[QNAME_MARSHAL]);
writer.endTag((dotNet) ? "" : (String) qName[QNAME_NAMESPACE],
- (String) qName[QNAME_TYPE]);//</spp:sppPostDevData>
+ (String) qName[QNAME_TYPE]);
}
}
- /**
- * Writes the body of an SoapObject. This method write the attributes and then calls
- * "writeObjectBody (writer, (KvmSerializable)obj);"
- */
- public void writeObjectBody(XmlSerializer writer, SoapObject obj) throws IOException {
- SoapObject soapObject = (SoapObject) obj;
+ private void writeAttributes(XmlSerializer writer, HasAttributes obj) throws IOException {
+ HasAttributes soapObject = (HasAttributes) obj;
int cnt = soapObject.getAttributeCount();
for (int counter = 0; counter < cnt; counter++) {
AttributeInfo attributeInfo = new AttributeInfo();
soapObject.getAttributeInfo(counter, attributeInfo);
- writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(), attributeInfo
- .getValue()
- .toString());
+ soapObject.getAttribute(counter, attributeInfo);
+ if (attributeInfo.getValue() != null) {
+ writer.attribute(attributeInfo.getNamespace(), attributeInfo.getName(),
+ attributeInfo.getValue().toString());
+ }
}
- writeObjectBody(writer, (KvmSerializable) obj);
+ }
+
+ public void writeArrayListBodyWithAttributes(XmlSerializer writer, KvmSerializable obj)
+ throws IOException {
+ if (obj instanceof HasAttributes) {
+ writeAttributes(writer, (HasAttributes) obj);
+ }
+ writeArrayListBody(writer, (ArrayList) obj);
+ }
+
+ public void writeObjectBodyWithAttributes(XmlSerializer writer, KvmSerializable obj)
+ throws IOException {
+ if (obj instanceof HasAttributes) {
+ writeAttributes(writer, (HasAttributes) obj);
+ }
+ writeObjectBody(writer, obj);
}
/**
- * Writes the body of an KvmSerializable object. This method is public for access from Marshal subclasses.
+ * Writes the body of an KvmSerializable object. This method is public for access from
+ * Marshal subclasses.
*/
public void writeObjectBody(XmlSerializer writer, KvmSerializable obj) throws IOException {
int cnt = obj.getPropertyCount();
@@ -614,9 +715,13 @@ public class SoapSerializationEnvelope extends SoapEnvelope
if (!(prop instanceof SoapObject)) {
// prop is a PropertyInfo
if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
- writer.startTag(propertyInfo.namespace, propertyInfo.name);
- writeProperty(writer, obj.getProperty(i), propertyInfo);
- writer.endTag(propertyInfo.namespace, propertyInfo.name);
+ Object objValue = obj.getProperty(i);
+ if ((prop != null || !skipNullProperties) && (objValue
+ != SoapPrimitive.NullSkip)) {
+ writer.startTag(propertyInfo.namespace, propertyInfo.name);
+ writeProperty(writer, objValue, propertyInfo);
+ writer.endTag(propertyInfo.namespace, propertyInfo.name);
+ }
}
} else {
// prop is a SoapObject
@@ -633,48 +738,45 @@ public class SoapSerializationEnvelope extends SoapEnvelope
name = (String) qName[QNAME_TYPE];
}
- // treat MO data as CDATA
- if (name.equals("DevInfo") || name.equals("DevDetail")
- || name.equals("PerProviderSubscription") || // format v4
- name.equals("MgmtTree") // format v6
- ) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- // Android-changed: Use XmlObjectFactory instead of a specific implementation.
- // XmlSerializer xw = new KXmlSerializer();
- XmlSerializer xw = XmlObjectFactory.newXmlSerializer();
- xw.setOutput(bos, "UTF-8");
- xw.startTag((dotNet) ? "" : namespace, name);
- if (!implicitTypes) {
- String prefix = writer.getPrefix(namespace, true);
- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
- }
- writeObjectBody(xw, nestedSoap);
- xw.endTag((dotNet) ? "" : namespace, name);
- xw.flush();
- //bos.write('\r');
- //bos.write('\n');
- bos.flush();
- writer.cdsect(bos.toString());
+ // prefer the namespace from the property info
+ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
+ namespace = propertyInfo.namespace;
+ } else {
+ namespace = (String) qName[QNAME_NAMESPACE];
}
- else
- {
- writer.startTag((dotNet) ? "" : namespace, name);
- if (!implicitTypes) {
- String prefix = writer.getPrefix(namespace, true);
- writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
- }
- writeObjectBody(writer, nestedSoap);
- writer.endTag((dotNet) ? "" : namespace, name);
+
+ writer.startTag(namespace, name);
+ if (!implicitTypes) {
+ String prefix = writer.getPrefix(namespace, true);
+ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
}
+ writeObjectBodyWithAttributes(writer, nestedSoap);
+ writer.endTag(namespace, name);
+ }
+ }
+ writeInnerText(writer, obj);
+
+ }
+
+ private void writeInnerText(XmlSerializer writer, KvmSerializable obj) throws IOException {
+ if (obj instanceof HasInnerText) {
+
+ Object value = ((HasInnerText) obj).getInnerText();
+ if (value != null) {
+ if (value instanceof ValueWriter) {
+ ((ValueWriter) value).write(writer);
+ } else {
+ writer.cdsect(value.toString());
+ }
+
}
}
}
protected void writeProperty(XmlSerializer writer, Object obj, PropertyInfo type)
throws IOException {
- if (obj == null) {
- ///M: Modify for HS20
- //writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
+ if (obj == null || obj == SoapPrimitive.NullNilElement) {
+ writer.attribute(xsi, version >= VER12 ? NIL_LABEL : NULL_LABEL, "true");
return;
}
Object[] qName = getInfo(null, obj);
@@ -694,15 +796,20 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
}
- private void writeElement(XmlSerializer writer, Object element, PropertyInfo type,
+ protected void writeElement(XmlSerializer writer, Object element, PropertyInfo type,
Object marshal)
throws IOException {
if (marshal != null) {
((Marshal) marshal).writeInstance(writer, element);
- } else if (element instanceof SoapObject) {
- writeObjectBody(writer, (SoapObject) element);
- } else if (element instanceof KvmSerializable) {
- writeObjectBody(writer, (KvmSerializable) element);
+ } else if (element instanceof KvmSerializable || element == SoapPrimitive.NullNilElement
+ || element == SoapPrimitive.NullSkip) {
+ if (element instanceof ArrayList) {
+ writeArrayListBodyWithAttributes(writer, (KvmSerializable) element);
+ } else {
+ writeObjectBodyWithAttributes(writer, (KvmSerializable) element);
+ }
+ } else if (element instanceof HasAttributes) {
+ writeAttributes(writer, (HasAttributes) element);
} else if (element instanceof Vector) {
writeVectorBody(writer, (Vector) element, type.elementType);
} else {
@@ -710,6 +817,66 @@ public class SoapSerializationEnvelope extends SoapEnvelope
}
}
+ protected void writeArrayListBody(XmlSerializer writer, ArrayList list)
+ throws IOException {
+ KvmSerializable obj = (KvmSerializable) list;
+ int cnt = list.size();
+ PropertyInfo propertyInfo = new PropertyInfo();
+ String namespace;
+ String name;
+ String type;
+ for (int i = 0; i < cnt; i++) {
+ // get the property
+ Object prop = obj.getProperty(i);
+ // and importantly also get the property info which holds the name potentially!
+ obj.getPropertyInfo(i, properties, propertyInfo);
+
+ if (!(prop instanceof SoapObject)) {
+ // prop is a PropertyInfo
+ if ((propertyInfo.flags & PropertyInfo.TRANSIENT) == 0) {
+ Object objValue = obj.getProperty(i);
+ if ((prop != null || !skipNullProperties) && (objValue
+ != SoapPrimitive.NullSkip)) {
+ writer.startTag(propertyInfo.namespace, propertyInfo.name);
+ writeProperty(writer, objValue, propertyInfo);
+ writer.endTag(propertyInfo.namespace, propertyInfo.name);
+ }
+ }
+ } else {
+
+ // prop is a SoapObject
+ SoapObject nestedSoap = (SoapObject) prop;
+ // lets get the info from the soap object itself
+ Object[] qName = getInfo(null, nestedSoap);
+ namespace = (String) qName[QNAME_NAMESPACE];
+ type = (String) qName[QNAME_TYPE];
+
+ // prefer the name from the property info
+ if (propertyInfo.name != null && propertyInfo.name.length() > 0) {
+ name = propertyInfo.name;
+ } else {
+ name = (String) qName[QNAME_TYPE];
+ }
+
+ // prefer the namespace from the property info
+ if (propertyInfo.namespace != null && propertyInfo.namespace.length() > 0) {
+ namespace = propertyInfo.namespace;
+ } else {
+ namespace = (String) qName[QNAME_NAMESPACE];
+ }
+
+ writer.startTag(namespace, name);
+ if (!implicitTypes) {
+ String prefix = writer.getPrefix(namespace, true);
+ writer.attribute(xsi, TYPE_LABEL, prefix + ":" + type);
+ }
+ writeObjectBodyWithAttributes(writer, nestedSoap);
+ writer.endTag(namespace, name);
+ }
+ }
+ writeInnerText(writer, obj);
+ }
+
protected void writeVectorBody(XmlSerializer writer, Vector vector, PropertyInfo elementType)
throws IOException {
String itemsTagName = ITEM_LABEL;
@@ -727,11 +894,17 @@ public class SoapSerializationEnvelope extends SoapEnvelope
int cnt = vector.size();
Object[] arrType = getInfo(elementType.type, null);
- // This removes the arrayType attribute from the xml for arrays(required for most .Net services to work)
+ // This removes the arrayType attribute from the xml for arrays(required for most .Net
+ // services to work)
if (!implicitTypes) {
- writer.attribute(enc, ARRAY_TYPE_LABEL, writer.getPrefix((String) arrType[0], false)
- + ":"
- + arrType[1] + "[" + cnt + "]");
+ writer.attribute(enc, ARRAY_TYPE_LABEL,
+ writer.getPrefix((String) arrType[0], false) + ":"
+ + arrType[1] + "[" + cnt + "]");
+ } else {
+ // Get the namespace from mappings if available when arrayType is removed for .Net
+ if (itemsNamespace == null) {
+ itemsNamespace = (String) arrType[0];
+ }
}
boolean skipped = false;
diff --git a/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
new file mode 100644
index 0000000..fdbaa21
--- /dev/null
+++ b/ksoap2-base/src/main/java/org/ksoap2/serialization/ValueWriter.java
@@ -0,0 +1,13 @@
+package org.ksoap2.serialization;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * Created by robocik on 2015-09-25.
+ */
+public interface ValueWriter
+{
+ void write(XmlSerializer writer) throws IOException;
+}
diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
index 8e14ee7..50feac5 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/ServiceConnection.java
@@ -1,6 +1,6 @@
/* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
* Copyright (c) 2006, James Seigel, Calgary, AB., Canada
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -21,8 +21,10 @@
package org.ksoap2.transport;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.List;
-import java.io.*;
/**
* Interface to allow the abstraction of the raw transport information
@@ -34,53 +36,50 @@ public interface ServiceConnection {
/**
* Make an outgoing connection.
- *
- * @exception IOException
*/
public void connect() throws IOException;
/**
* Disconnect from the outgoing connection
- *
- * @exception IOException
*/
public void disconnect() throws IOException;
/**
* Returns to the caller all of the headers that were returned with the
- * response to the SOAP request. Primarily this gives the caller an
+ * response to the SOAP request. Primarily this gives the caller an
* opportunity to save the cookies for later use.
- *
- * @return List of HeaderProperty instances that were returned as part of the http response as http header
+ *
+ * @return List of HeaderProperty instances that were returned as part of the http response
+ * as http header
* properties
- *
- * @exception IOException
*/
public List getResponseProperties() throws IOException;
/**
+ * Returns the numerical HTTP status to the caller
+ *
+ * @return an integer status value
+ */
+ public int getResponseCode() throws IOException;
+
+ /**
* Set properties on the outgoing connection.
- *
- * @param propertyName
- * the name of the property to set. For HTTP connections these
- * are the request properties in the HTTP Header.
- * @param value
- * the string to set the property header to.
- * @exception IOException
+ *
+ * @param propertyName the name of the property to set. For HTTP connections these
+ * are the request properties in the HTTP Header.
+ * @param value the string to set the property header to.
*/
public void setRequestProperty(String propertyName, String value) throws IOException;
/**
* Sets how to make the requests. For HTTP this is typically POST or GET.
- *
- * @param requestMethodType
- * the type of request method to make the soap call with.
- * @exception IOException
+ *
+ * @param requestMethodType the type of request method to make the soap call with.
*/
public void setRequestMethod(String requestMethodType) throws IOException;
/**
- * If the length of a HTTP request body is known ahead, sets fixed length
+ * If the length of a HTTP request body is known ahead, sets fixed length
* to enable streaming without buffering. Sets after connection will cause an exception.
*
* @param contentLength the fixed length of the HTTP request body
@@ -88,10 +87,11 @@ public interface ServiceConnection {
**/
public void setFixedLengthStreamingMode(int contentLength);
+ public void setChunkedStreamingMode();
+
/**
* Open and return the outputStream to the endpoint.
- *
- * @exception IOException
+ *
* @return the output stream to write the soap message to.
*/
public OutputStream openOutputStream() throws IOException;
@@ -99,10 +99,9 @@ public interface ServiceConnection {
/**
* Opens and returns the inputstream from which to parse the result of the
* soap call.
- *
- * @exception IOException
+ *
* @return the inputstream containing the xml to parse the result from the
- * call from.
+ * call from.
*/
public InputStream openInputStream() throws IOException;
diff --git a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
index 5ccd18f..d92e8d8 100644
--- a/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
+++ b/ksoap2-base/src/main/java/org/ksoap2/transport/Transport.java
@@ -1,7 +1,7 @@
-/**
+/**
* Copyright (c) 2006, James Seigel, Calgary, AB., Canada
* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -18,14 +18,18 @@
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
+ * IN THE SOFTWARE.
*/
package org.ksoap2.transport;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.io.*;
+import java.net.MalformedURLException;
import java.net.Proxy;
+import java.net.URL;
import libcore.util.XmlObjectFactory;
@@ -41,9 +45,9 @@ import org.xmlpull.v1.*;
abstract public class Transport {
/**
- * Added to enable web service interactions on the emulator
- * to be debugged with Fiddler2 (Windows) but provides utility
- * for other proxy requirements.
+ * Added to enable web service interactions on the emulator to be debugged
+ * with Fiddler2 (Windows) but provides utility for other proxy
+ * requirements.
*/
protected Proxy proxy;
protected String url;
@@ -57,11 +61,18 @@ abstract public class Transport {
private String xmlVersionTag = "";
protected static final String CONTENT_TYPE_XML_CHARSET_UTF_8 = "text/xml;charset=utf-8";
- protected static final String CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8 = "application/soap+xml;charset=utf-8";
+ protected static final String CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8 =
+ "application/soap+xml;charset=utf-8";
protected static final String USER_AGENT = "ksoap2-android/2.6.0+";
private int bufferLength = ServiceConnection.DEFAULT_BUFFER_SIZE;
+ private HashMap prefixes = new HashMap();
+
+ public HashMap getPrefixes() {
+ return prefixes;
+ }
+
public Transport() {
}
@@ -82,11 +93,14 @@ abstract public class Transport {
/**
* Construct the transport object
- *
- * @param proxy Specifies the proxy server to use for
- * accessing the web service or <code>null</code> if a direct connection is available
- * @param url Specifies the web service url
- *
+ *
+ * @param proxy
+ * Specifies the proxy server to use for accessing the web
+ * service or <code>null</code> if a direct connection is
+ * available
+ * @param url
+ * Specifies the web service url
+ *
*/
public Transport(Proxy proxy, String url) {
this.proxy = proxy;
@@ -117,27 +131,31 @@ abstract public class Transport {
xp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
xp.setInput(is, null);
envelope.parse(xp);
+ /*
+ * Fix memory leak when running on android in strict mode. Issue 133
+ */
+ is.close();
}
/**
* Serializes the request.
*/
- protected byte[] createRequestData(SoapEnvelope envelope, String encoding) throws IOException {
- System.out.println("createRequestData");
+ protected byte[] createRequestData(SoapEnvelope envelope, String encoding)
+ throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(bufferLength);
byte result[] = null;
bos.write(xmlVersionTag.getBytes());
- System.out.println("bos.write");
- // BEGIN Android-changed: Use XmlObjectFactory instead of a specific implementation.
+ // Android-changed: Use XmlObjectFactory instead of a specific implementation.
// XmlSerializer xw = new KXmlSerializer();
- // System.out.println("new KXmlSerializer");
XmlSerializer xw = XmlObjectFactory.newXmlSerializer();
- System.out.println("newXmlSerializer()");
- // END Android-changed: Use XmlObjectFactory instead of a specific implementation.
+
+ final Iterator keysIter = prefixes.keySet().iterator();
xw.setOutput(bos, encoding);
- System.out.println("xw.setOutput");
+ while (keysIter.hasNext()) {
+ String key = (String) keysIter.next();
+ xw.setPrefix(key, (String) prefixes.get(key));
+ }
envelope.write(xw);
- System.out.println("envelope.write");
xw.flush();
bos.write('\r');
bos.write('\n');
@@ -145,20 +163,20 @@ abstract public class Transport {
result = bos.toByteArray();
xw = null;
bos = null;
- System.out.println("createRequestData end");
return result;
}
/**
* Serializes the request.
*/
- protected byte[] createRequestData(SoapEnvelope envelope) throws IOException {
+ protected byte[] createRequestData(SoapEnvelope envelope)
+ throws IOException {
return createRequestData(envelope, null);
}
/**
* Set the target url.
- *
+ *
* @param url
* the target url.
*/
@@ -166,10 +184,15 @@ abstract public class Transport {
this.url = url;
}
+ public String getUrl() {
+ return url;
+ }
+
+
/**
* Sets the version tag for the outgoing soap call. Example <?xml
* version=\"1.0\" encoding=\"UTF-8\"?>
- *
+ *
* @param tag
* the xml string to set at the top of the soap message.
*/
@@ -184,35 +207,62 @@ abstract public class Transport {
}
/**
- * Perform a soap call with a given namespace and the given envelope providing
- * any extra headers that the user requires such as cookies. Headers that are
- * returned by the web service will be returned to the caller in the form of a
- * <code>List</code> of <code>HeaderProperty</code> instances.
- *
- * @param targetNamespace
+ * Perform a soap call with a given namespace and the given envelope
+ * providing any extra headers that the user requires such as cookies.
+ * Headers that are returned by the web service will be returned to the
+ * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
+ * instances.
+ *
+ * @param soapAction
+ * the namespace with which to perform the call in.
+ * @param envelope
+ * the envelope the contains the information for the call.
+ * @param headers
+ * <code>List</code> of <code>HeaderProperty</code> headers to
+ * send with the SOAP request.
+ *
+ * @return Headers returned by the web service as a <code>List</code> of
+ * <code>HeaderProperty</code> instances.
+ */
+ abstract public List call(String soapAction, SoapEnvelope envelope,
+ List headers) throws IOException, XmlPullParserException;
+
+ /**
+ * Perform a soap call with a given namespace and the given envelope
+ * providing any extra headers that the user requires such as cookies.
+ * Headers that are returned by the web service will be returned to the
+ * caller in the form of a <code>List</code> of <code>HeaderProperty</code>
+ * instances.
+ *
+ * @param soapAction
* the namespace with which to perform the call in.
* @param envelope
* the envelope the contains the information for the call.
* @param headers
- * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
- *
+ * <code>List</code> of <code>HeaderProperty</code> headers to
+ * send with the SOAP request.
+ * @param outputFile
+ * a file to stream the response into rather than parsing it,
+ * streaming happens when file is not null
+ *
* @return Headers returned by the web service as a <code>List</code> of
- * <code>HeaderProperty</code> instances.
+ * <code>HeaderProperty</code> instances.
*/
- abstract public List call(String targetNamespace, SoapEnvelope envelope, List headers)
- throws IOException, XmlPullParserException;
+ abstract public List call(String soapAction, SoapEnvelope envelope,
+ List headers, File outputFile) throws IOException,
+ XmlPullParserException;
/**
* Perform a soap call with a given namespace and the given envelope.
- *
- * @param targetNamespace
+ *
+ * @param soapAction
* the namespace with which to perform the call in.
* @param envelope
* the envelope the contains the information for the call.
*/
- public void call(String targetNamespace, SoapEnvelope envelope) throws IOException,
- XmlPullParserException {
- call(targetNamespace, envelope, null);
+ public void call(String soapAction, SoapEnvelope envelope)
+ throws IOException, XmlPullParserException {
+ call(soapAction, envelope, null);
}
/**
@@ -220,21 +270,31 @@ abstract public class Transport {
*
* @return Host name
*/
- abstract public String getHost();
+ public String getHost() throws MalformedURLException {
+
+ return new URL(url).getHost();
+ }
/**
- * Return the port number of the host that is specified as the web service target
+ * Return the port number of the host that is specified as the web service
+ * target
*
* @return Port number
*/
- abstract public int getPort();
+ public int getPort() throws MalformedURLException {
+
+ return new URL(url).getPort();
+ }
/**
* Return the path to the web service target
*
* @return The URL's path
*/
- abstract public String getPath();
+ public String getPath() throws MalformedURLException {
+
+ return new URL(url).getPath();
+ }
abstract public ServiceConnection getServiceConnection() throws IOException;
}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
index 71c8caa..3671519 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/serialization/MarshalFloat.java
@@ -1,6 +1,6 @@
/* Copyright (c) 2003,2004 Stefan Haustein, Oberhausen, Rhld., Germany
* Copyright (c) 2006, James Seigel, Calgary, AB., Canada
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
new file mode 100644
index 0000000..a128f13
--- /dev/null
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpResponseException.java
@@ -0,0 +1,61 @@
+package org.ksoap2.transport;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * HttpResponseException is an IOException that is to be thrown when a Http response code is
+ * different from 200.
+ * It allows for easier retrieval of the Http response code from the connection.
+ *
+ * @author Rui Pereira <syshex@gmail.com>
+ */
+public class HttpResponseException extends IOException {
+
+ private int statusCode;
+ private List responseHeaders;
+
+ public HttpResponseException(int statusCode) {
+ super();
+ this.statusCode = statusCode;
+ }
+
+ public HttpResponseException(String detailMessage, int statusCode) {
+ super(detailMessage);
+ this.statusCode = statusCode;
+ }
+
+ public HttpResponseException(String detailMessage, int statusCode, List responseHeaders) {
+ super(detailMessage);
+ this.statusCode = statusCode;
+ this.responseHeaders = responseHeaders;
+ }
+
+ public HttpResponseException(String message, Throwable cause, int statusCode) {
+ super(message, cause);
+ this.statusCode = statusCode;
+ }
+
+ public HttpResponseException(Throwable cause, int statusCode) {
+ super(cause);
+ this.statusCode = statusCode;
+ }
+
+ /**
+ * Returns the unexpected Http response code
+ *
+ * @return response code
+ */
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /**
+ * Returns all http headers from this response
+ *
+ * @return response code
+ */
+ public List getResponseHeaders() {
+ return responseHeaders;
+ }
+}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
index d92ac09..979e90a 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpTransportSE.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
+ * Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -17,35 +17,32 @@
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
+ * IN THE SOFTWARE.
*
- * Contributor(s): John D. Beatty, Dave Dash, F. Hunter, Alexander Krebs,
- * Lars Mehrmann, Sean McDaniel, Thomas Strang, Renaud Tognelli
- * */
+ * Contributor(s): John D. Beatty, Dave Dash, F. Hunter, Alexander Krebs,
+ * Lars Mehrmann, Sean McDaniel, Thomas Strang, Renaud Tognelli
+ */
package org.ksoap2.transport;
-import java.util.List;
-import java.util.zip.GZIPInputStream;
+import org.ksoap2.HeaderProperty;
+import org.ksoap2.SoapEnvelope;
+import org.ksoap2.serialization.*;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.*;
-import java.net.MalformedURLException;
import java.net.Proxy;
-import java.net.URL;
-
-import org.ksoap2.*;
-import org.ksoap2.serialization.SoapSerializationEnvelope;
-import org.xmlpull.v1.*;
+import java.util.*;
+import java.util.zip.GZIPInputStream;
/**
* A J2SE based HttpTransport layer.
*/
public class HttpTransportSE extends Transport {
- private ServiceConnection serviceConnection;
-
/**
* Creates instance of HttpTransportSE with set url
- *
+ *
* @param url
* the destination to POST SOAP data
*/
@@ -56,7 +53,7 @@ public class HttpTransportSE extends Transport {
/**
* Creates instance of HttpTransportSE with set url and defines a
* proxy server to use to access it
- *
+ *
* @param proxy
* Proxy information or <code>null</code> for direct access
* @param url
@@ -68,7 +65,7 @@ public class HttpTransportSE extends Transport {
/**
* Creates instance of HttpTransportSE with set url
- *
+ *
* @param url
* the destination to POST SOAP data
* @param timeout
@@ -84,7 +81,7 @@ public class HttpTransportSE extends Transport {
/**
* Creates instance of HttpTransportSE with set url
- *
+ *
* @param url
* the destination to POST SOAP data
* @param timeout
@@ -102,119 +99,130 @@ public class HttpTransportSE extends Transport {
/**
* set the desired soapAction header field
- *
+ *
* @param soapAction
* the desired soapAction
* @param envelope
* the envelope containing the information for the soap call.
+ * @throws HttpResponseException
* @throws IOException
* @throws XmlPullParserException
*/
- public void call(String soapAction, SoapEnvelope envelope) throws IOException,
- XmlPullParserException {
+ public void call(String soapAction, SoapEnvelope envelope)
+ throws HttpResponseException, IOException, XmlPullParserException {
call(soapAction, envelope, null);
}
+ public List call(String soapAction, SoapEnvelope envelope, List headers)
+ throws HttpResponseException, IOException, XmlPullParserException {
+ return call(soapAction, envelope, headers, null);
+ }
+
/**
- *
- * set the desired soapAction header field
- *
+ * Perform a soap call with a given namespace and the given envelope providing
+ * any extra headers that the user requires such as cookies. Headers that are
+ * returned by the web service will be returned to the caller in the form of a
+ * <code>List</code> of <code>HeaderProperty</code> instances.
+ *
* @param soapAction
- * the desired soapAction
+ * the namespace with which to perform the call in.
* @param envelope
- * the envelope containing the information for the soap call.
+ * the envelope the contains the information for the call.
* @param headers
- * a list of HeaderProperties to be http header properties when establishing the connection
+ * <code>List</code> of <code>HeaderProperty</code> headers to send with the SOAP request.
+ * @param outputFile
+ * a file to stream the response into rather than parsing it, streaming happens
+ * when file is not null
*
- * @return <code>CookieJar</code> with any cookies sent by the server
- * @throws IOException
- * @throws XmlPullParserException
+ * @return Headers returned by the web service as a <code>List</code> of
+ * <code>HeaderProperty</code> instances.
+ *
+ * @throws HttpResponseException
+ * an IOException when Http response code is different from 200
*/
- public List call(String soapAction, SoapEnvelope envelope, List headers)
- throws IOException, XmlPullParserException {
+ public List call(String soapAction, SoapEnvelope envelope, List headers, File outputFile)
+ throws HttpResponseException, IOException, XmlPullParserException {
if (soapAction == null) {
soapAction = "\"\"";
}
- System.out.println("call action:" + soapAction);
byte[] requestData = createRequestData(envelope, "UTF-8");
- if (requestData != null) {
- requestDump = debug ? new String(requestData) : null;
- }
- else {
- requestDump = null;
- }
+ requestDump = debug ? new String(requestData) : null;
responseDump = null;
-
- System.out.println("requestDump:" + requestDump);
+ // System.out.println("requestDump: " + requestDump);
ServiceConnection connection = getServiceConnection();
- System.out.println("connection:" + connection);
connection.setRequestProperty("User-Agent", USER_AGENT);
// SOAPAction is not a valid header for VER12 so do not add
// it
// @see "http://code.google.com/p/ksoap2-android/issues/detail?id=67
- System.out.println("envelope:" + envelope);
- if (envelope != null) {
- if (envelope.version != SoapSerializationEnvelope.VER12) {
- connection.setRequestProperty("SOAPAction", soapAction);
- }
+ if (envelope.version != SoapSerializationEnvelope.VER12) {
+ connection.setRequestProperty("SOAPAction", soapAction);
+ }
- if (envelope.version == SoapSerializationEnvelope.VER12) {
- connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
- } else {
- connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
- }
+ if (envelope.version == SoapSerializationEnvelope.VER12) {
+ connection.setRequestProperty("Content-Type", CONTENT_TYPE_SOAP_XML_CHARSET_UTF_8);
+ } else {
+ connection.setRequestProperty("Content-Type", CONTENT_TYPE_XML_CHARSET_UTF_8);
+ }
- connection.setRequestProperty("Connection", "close");
- connection.setRequestProperty("Accept-Encoding", "gzip");
- connection.setRequestProperty("Content-Length", "" + requestData.length);
+ // this seems to cause issues so we are removing it
+ //connection.setRequestProperty("Connection", "close");
+ connection.setRequestProperty("Accept-Encoding", "gzip");
- //M: Retry for HTTP Authentication
- //connection.setFixedLengthStreamingMode(requestData.length);
- // Pass the headers provided by the user along with the call
- if (headers != null) {
- for (int i = 0; i < headers.size(); i++) {
- HeaderProperty hp = (HeaderProperty) headers.get(i);
- connection.setRequestProperty(hp.getKey(), hp.getValue());
- }
+ // Pass the headers provided by the user along with the call
+ if (headers != null) {
+ for (int i = 0; i < headers.size(); i++) {
+ HeaderProperty hp = (HeaderProperty) headers.get(i);
+ connection.setRequestProperty(hp.getKey(), hp.getValue());
}
-
- connection.setRequestMethod("POST");
-
- }
- else {
- connection.setRequestProperty("Connection", "close");
- connection.setRequestProperty("Accept-Encoding", "gzip");
- connection.setRequestMethod("GET");
}
- if (requestData != null) {
- OutputStream os = connection.openOutputStream();
-
- os.write(requestData, 0, requestData.length);
- os.flush();
- os.close();
- requestData = null;
- }
- InputStream is;
+ connection.setRequestMethod("POST");
+ sendData(requestData, connection, envelope);
+ requestData = null;
+ InputStream is = null;
List retHeaders = null;
+ byte[] buf = null; // To allow releasing the resource after used
+ int contentLength = 8192; // To determine the size of the response and adjust buffer size
boolean gZippedContent = false;
- boolean bcaCert = false;
+ boolean xmlContent = false;
+ int status = connection.getResponseCode();
try {
retHeaders = connection.getResponseProperties();
- System.out.println("[HttpTransportSE] retHeaders = " + retHeaders);
+
for (int i = 0; i < retHeaders.size(); i++) {
HeaderProperty hp = (HeaderProperty) retHeaders.get(i);
// HTTP response code has null key
if (null == hp.getKey()) {
continue;
}
+
+ // If we know the size of the response, we should use the size to initiate vars
+ if (hp.getKey().equalsIgnoreCase("content-length")) {
+ if (hp.getValue() != null) {
+ try {
+ contentLength = Integer.parseInt(hp.getValue());
+ } catch (NumberFormatException nfe) {
+ contentLength = 8192;
+ }
+ }
+ }
+
+
+ // Check the content-type header to see if we're getting back XML, in case of a
+ // SOAP fault on 500 codes
+ if (hp.getKey().equalsIgnoreCase("Content-Type")
+ && hp.getValue().contains("xml")) {
+ xmlContent = true;
+ }
+
+
// ignoring case since users found that all smaller case is used on some server
// and even if it is wrong according to spec, we rather have it work..
if (hp.getKey().equalsIgnoreCase("Content-Encoding")
@@ -222,134 +230,130 @@ public class HttpTransportSE extends Transport {
gZippedContent = true;
}
}
- if (gZippedContent) {
- is = getUnZippedInputStream(connection.openInputStream());
- } else {
- is = connection.openInputStream();
+
+ //first check the response code....
+ if (status != 200 && status != 202) {
+ //202 is a correct status returned by WCF OneWay operation
+ throw new HttpResponseException("HTTP request failed, HTTP status: " + status,
+ status, retHeaders);
+ }
+
+ if (contentLength > 0) {
+ if (gZippedContent) {
+ is = getUnZippedInputStream(
+ new BufferedInputStream(connection.openInputStream(), contentLength));
+ } else {
+ is = new BufferedInputStream(connection.openInputStream(), contentLength);
+ }
}
} catch (IOException e) {
- if (gZippedContent) {
- is = getUnZippedInputStream(connection.getErrorStream());
- } else {
- is = connection.getErrorStream();
+ if (contentLength > 0) {
+ if (gZippedContent) {
+ is = getUnZippedInputStream(
+ new BufferedInputStream(connection.getErrorStream(), contentLength));
+ } else {
+ is = new BufferedInputStream(connection.getErrorStream(), contentLength);
+ }
}
- if (is == null) {
- connection.disconnect();
- throw (e);
+ if (e instanceof HttpResponseException) {
+ if (!xmlContent) {
+ if (debug && is != null) {
+ //go ahead and read the error stream into the debug buffers/file if needed.
+ readDebug(is, contentLength, outputFile);
+ }
+
+ //we never want to drop through to attempting to parse the HTTP error stream
+ // as a SOAP response.
+ connection.disconnect();
+ throw e;
+ }
}
}
if (debug) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- byte[] buf = new byte[8192];
-
- while (true) {
- int rd = is.read(buf, 0, 8192);
- if (rd == -1) {
- break;
- }
- bos.write(buf, 0, rd);
- }
-
- bos.flush();
- buf = bos.toByteArray();
-
- responseDump = new String(buf);
-
- System.out.println("responseDump:" + responseDump);
- is.close();
- is = new ByteArrayInputStream(buf);
+ is = readDebug(is, contentLength, outputFile);
}
- if (envelope != null) {
- parseResponse(envelope, is);
+ if (is != null) {
+ parseResponse(envelope, is, retHeaders);
}
+ // release all resources
+ // input stream is will be released inside parseResponse
+ is = null;
+ buf = null;
+ //This fixes Issue 173 read my explanation here: https://code.google
+ // .com/p/ksoap2-android/issues/detail?id=173
+ connection.disconnect();
+ connection = null;
return retHeaders;
}
- private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
- /* workaround for Android 2.3
- (see http://stackoverflow.com/questions/5131016/)
- */
- try {
- return (GZIPInputStream) inputStream;
- } catch (ClassCastException e) {
- return new GZIPInputStream(inputStream);
- }
- }
+ protected void sendData(byte[] requestData, ServiceConnection connection, SoapEnvelope envelope)
+ throws IOException {
+ connection.setRequestProperty("Content-Length", "" + requestData.length);
+ connection.setFixedLengthStreamingMode(requestData.length);
- public ServiceConnection getServiceConnection() throws IOException {
- if (serviceConnection == null) {
- System.out.println("new ServiceConnectionSE:" + proxy + " " + url + " " + timeout);
- serviceConnection = new ServiceConnectionSE(proxy, url, timeout);
- }
- return serviceConnection;
+ OutputStream os = connection.openOutputStream();
+ os.write(requestData, 0, requestData.length);
+ os.flush();
+ os.close();
}
- public String getHost() {
-
- String retVal = null;
-
- try {
- retVal = new URL(url).getHost();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
-
- return retVal;
+ protected void parseResponse(SoapEnvelope envelope, InputStream is, List returnedHeaders)
+ throws XmlPullParserException, IOException {
+ parseResponse(envelope, is);
}
- public int getPort() {
-
- int retVal = -1;
- try {
- retVal = new URL(url).getPort();
- } catch (MalformedURLException e) {
- e.printStackTrace();
+ private InputStream readDebug(InputStream is, int contentLength, File outputFile)
+ throws IOException {
+ OutputStream bos;
+ if (outputFile != null) {
+ bos = new FileOutputStream(outputFile);
+ } else {
+ // If known use the size if not use default value
+ bos = new ByteArrayOutputStream((contentLength > 0) ? contentLength : 256 * 1024);
}
- return retVal;
- }
-
- public String getPath() {
+ byte[] buf = new byte[256];
- String retVal = null;
-
- try {
- retVal = new URL(url).getPath();
- } catch (MalformedURLException e) {
- e.printStackTrace();
+ while (true) {
+ int rd = is.read(buf, 0, 256);
+ if (rd == -1) {
+ break;
+ }
+ bos.write(buf, 0, rd);
}
- return retVal;
- }
-
- public String getQuery() {
-
- String retVal = null;
-
- try {
- retVal = new URL(url).getQuery();
- } catch (MalformedURLException e) {
- e.printStackTrace();
+ bos.flush();
+ if (bos instanceof ByteArrayOutputStream) {
+ buf = ((ByteArrayOutputStream) bos).toByteArray();
+ }
+ bos = null;
+ responseDump = new String(buf);
+ is.close();
+ // System.out.println("responseDump: " + requestDump);
+ if (outputFile != null) {
+ return new FileInputStream(outputFile);
+ } else {
+ return new ByteArrayInputStream(buf);
}
-
- return retVal;
}
- /**
- * @hide
- */
- public byte[] getRequestData(SoapEnvelope envelope, String encoding) {
+ private InputStream getUnZippedInputStream(InputStream inputStream) throws IOException {
+ /* workaround for Android 2.3
+ (see http://stackoverflow.com/questions/5131016/)
+ */
try {
- return createRequestData(envelope, encoding);
- } catch (Exception e) {
- e.printStackTrace();
+ return (GZIPInputStream) inputStream;
+ } catch (ClassCastException e) {
+ return new GZIPInputStream(inputStream);
}
+ }
- return null;
+ public ServiceConnection getServiceConnection() throws IOException {
+ return new ServiceConnectionSE(proxy, url, timeout);
}
}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
index 9ad9ba9..4ad6e7b 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsServiceConnectionSE.java
@@ -1,40 +1,37 @@
-
package org.ksoap2.transport;
+import org.ksoap2.HeaderProperty;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.Proxy;
import java.net.URL;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.List;
-import java.util.Set;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.HttpsURLConnection;
-//import com.android.okhttp.internal.http.HttpsURLConnectionImpl;
-import org.ksoap2.HeaderProperty;
+import java.util.*;
/**
- * HttpsServiceConnectionSE is a service connection that uses a https url connection and requires explicit setting of
+ * HttpsServiceConnectionSE is a service connection that uses a https url connection and requires
+ * explicit setting of
* host, port and file.
*
- * The explicit setting is necessary since pure url passing and letting the Java URL class parse the string does not
+ * The explicit setting is necessary since pure url passing and letting the Java URL class parse
+ * the string does not
* work properly on Android.
*
* Links for reference:
+ *
+ * @author Manfred Moser <manfred@simpligility.com>
* @see "http://stackoverflow.com/questions/2820284/ssl-on-android-strange-issue"
- * @see "http://stackoverflow.com/questions/2899079/custom-ssl-handling-stopped-working-on-android-2-2-froyo"
+ * @see "http://stackoverflow.com/questions/2899079/custom-ssl-handling-stopped-working-on
+ * -android-2-2-froyo"
* @see "http://code.google.com/p/android/issues/detail?id=2690"
* @see "http://code.google.com/p/android/issues/detail?id=2764"
- *
* @see "https://gist.github.com/908048" There can be problems with the
* certificate of theof the server on older android versions. You can disable
* SSL for the versions only e.g. with an approach like this.
- *
- * @author Manfred Moser <manfred@simpligility.com>
*/
public class HttpsServiceConnectionSE implements ServiceConnection {
@@ -42,23 +39,47 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
/**
* Create the transport with the supplied parameters.
- * @param host the name of the host e.g. webservices.somewhere.com
- * @param port the http port to connect on
- * @param file the path to the file on the webserver that represents the
- * webservice e.g. /api/services/myservice.jsp
+ *
+ * @param host the name of the host e.g. webservices.somewhere.com
+ * @param port the http port to connect on
+ * @param file the path to the file on the webserver that represents the
+ * webservice e.g. /api/services/myservice.jsp
* @param timeout the timeout for the connection in milliseconds
- * @throws IOException
*/
- public HttpsServiceConnectionSE(String host, int port, String file,
- int timeout) throws IOException {
- connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port, file)
- .openConnection();
+ public HttpsServiceConnectionSE(String host, int port, String file, int timeout)
+ throws IOException {
+ this(null, host, port, file, timeout);
+ }
+
+ /**
+ * Create the transport with the supplied parameters.
+ *
+ * @param proxy proxy server to use
+ * @param host the name of the host e.g. webservices.somewhere.com
+ * @param port the http port to connect on
+ * @param file the path to the file on the webserver that represents the
+ * webservice e.g. /api/services/myservice.jsp
+ * @param timeout the timeout for the connection in milliseconds
+ */
+ public HttpsServiceConnectionSE(Proxy proxy, String host, int port, String file, int timeout)
+ throws IOException {
+
+ if (proxy == null) {
+ connection = (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port,
+ file).openConnection();
+ } else {
+ connection =
+ (HttpsURLConnection) new URL(HttpsTransportSE.PROTOCOL, host, port,
+ file).openConnection(proxy);
+ }
+
updateConnectionParameters(timeout);
}
private void updateConnectionParameters(int timeout) {
connection.setConnectTimeout(timeout);
- connection.setReadTimeout(timeout); // even if we connect fine we want to time out if we cant read anything..
+ connection.setReadTimeout(
+ timeout); // even if we connect fine we want to time out if we cant read anything..
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
@@ -77,7 +98,7 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
Set keys = properties.keySet();
List retList = new LinkedList();
- for (Iterator i = keys.iterator(); i.hasNext();) {
+ for (Iterator i = keys.iterator(); i.hasNext(); ) {
String key = (String) i.next();
List values = (List) properties.get(key);
@@ -89,6 +110,10 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
return retList;
}
+ public int getResponseCode() throws IOException {
+ return connection.getResponseCode();
+ }
+
public void setRequestProperty(String key, String value) {
connection.setRequestProperty(key, value);
}
@@ -101,6 +126,11 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
connection.setFixedLengthStreamingMode(contentLength);
}
+ public void setChunkedStreamingMode() {
+ connection.setChunkedStreamingMode(0);
+ }
+
+
public OutputStream openOutputStream() throws IOException {
return connection.getOutputStream();
}
@@ -128,9 +158,4 @@ public class HttpsServiceConnectionSE implements ServiceConnection {
public void setSSLSocketFactory(SSLSocketFactory sf) {
connection.setSSLSocketFactory(sf);
}
-
- public void setHostnameVerifier(HostnameVerifier v) {
- connection.setHostnameVerifier(v);
- }
-
}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
index d220ac9..12b840f 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/HttpsTransportSE.java
@@ -1,12 +1,13 @@
-
package org.ksoap2.transport;
import java.io.IOException;
import java.net.MalformedURLException;
+import java.net.Proxy;
import java.net.URL;
/**
- * HttpsTransportSE is a simple transport for https protocal based connections. It creates a #HttpsServiceConnectionSE
+ * HttpsTransportSE is a simple transport for https protocal based connections. It creates a
+ * #HttpsServiceConnectionSE
* with the provided parameters.
*
* @author Manfred Moser <manfred@simpligility.com>
@@ -14,17 +15,30 @@ import java.net.URL;
public class HttpsTransportSE extends HttpTransportSE {
static final String PROTOCOL = "https";
+ private static final String PROTOCOL_FULL = PROTOCOL + "://";
+
+ //connection instance, used for setting the SSLSocketFactory
+ private HttpsServiceConnectionSE connection;
- private ServiceConnection serviceConnection = null;
- private final String host;
- private final int port;
- private final String file;
- private final int timeout;
+ protected final String host;
+ protected final int port;
+ protected final String file;
public HttpsTransportSE(String host, int port, String file, int timeout) {
- super(HttpsTransportSE.PROTOCOL + "://" + host + ":" + port + file);
- System.out.println("Establistion connection to: " + HttpsTransportSE.PROTOCOL + "://"
- + host + ":" + port + file);
+ super(HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file, timeout);
+ this.host = host;
+ this.port = port;
+ this.file = file;
+ }
+
+ /**
+ * Creates instance of HttpTransportSE with set url and defines a
+ * proxy server to use to access it
+ *
+ * @param proxy Proxy information or <code>null</code> for direct access
+ */
+ public HttpsTransportSE(Proxy proxy, String host, int port, String file, int timeout) {
+ super(proxy, HttpsTransportSE.PROTOCOL_FULL + host + ":" + port + file);
this.host = host;
this.port = port;
this.file = file;
@@ -33,52 +47,15 @@ public class HttpsTransportSE extends HttpTransportSE {
/**
* Returns the HttpsServiceConnectionSE and creates it if necessary
+ *
* @see org.ksoap2.transport.HttpsTransportSE#getServiceConnection()
*/
- public ServiceConnection getServiceConnection() throws IOException
- {
- if (serviceConnection == null) {
- serviceConnection = new HttpsServiceConnectionSE(host, port, file, timeout);
- }
- return serviceConnection;
- }
-
- public String getHost() {
-
- String retVal = null;
-
- try {
- retVal = new URL(url).getHost();
- } catch (MalformedURLException e) {
- e.printStackTrace();
+ public ServiceConnection getServiceConnection() throws IOException {
+ if (connection != null) {
+ return connection;
+ } else {
+ connection = new HttpsServiceConnectionSE(proxy, host, port, file, timeout);
+ return connection;
}
-
- return retVal;
- }
-
- public int getPort() {
-
- int retVal = -1;
-
- try {
- retVal = new URL(url).getPort();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
-
- return retVal;
- }
-
- public String getPath() {
-
- String retVal = null;
-
- try {
- retVal = new URL(url).getPath();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- }
-
- return retVal;
}
}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
index 287fed1..84dc448 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/KeepAliveHttpsTransportSE.java
@@ -11,47 +11,39 @@ package org.ksoap2.transport;
import java.io.IOException;
/**
- * KeepAliveHttpsTransport deals with the problems with the Android ssl libraries having trouble with certificates and
- * certificate authorities somehow messing up connecting/needing reconnects. Added as generic class for SE since it
- * might be useful in SE environments as well and can be used as an example to create your own transport
+ * KeepAliveHttpsTransport deals with the problems with the Android ssl libraries having trouble
+ * with certificates and
+ * certificate authorities somehow messing up connecting/needing reconnects. Added as generic
+ * class for SE since it
+ * might be useful in SE environments as well and can be used as an example to create your own
+ * transport
* implementations.
*
* @author Manfred Moser <manfred@simpligility.com>
- *
- * @see "http://groups.google.com/group/android-developers/browse_thread/thread/3dcf62e7886a213/21f912bb90a011d6"
+ * @see "http://groups.google.com/group/android-developers/browse_thread/thread/3dcf62e7886a213
+ * /21f912bb90a011d6"
* @see "http://code.google.com/p/android/issues/detail?id=7074"
* @see "http://crazybob.org/2010_02_01_crazyboblee_archive.html"
*/
-public class KeepAliveHttpsTransportSE extends HttpsTransportSE
-{
- private final String host;
- private final int port;
- private final String file;
- private final int timeout;
- private ServiceConnection serviceConnection;
-
+public class KeepAliveHttpsTransportSE extends HttpsTransportSE {
public KeepAliveHttpsTransportSE(String host, int port, String file, int timeout) {
super(host, port, file, timeout);
- this.host = host;
- this.port = port;
- this.file = file;
- this.timeout = timeout;
}
/**
- * Get a service connection. Returns an implementation of {@link org.ksoap2.transport.ServiceConnectionSE} that
- * ignores "Connection: close" request property setting and has "Connection: keep-alive" always set and is uses
+ * Get a service connection. Returns an implementation of
+ * {@link org.ksoap2.transport.ServiceConnectionSE} that
+ * ignores "Connection: close" request property setting and has "Connection: keep-alive"
+ * always set and is uses
* a https connection.
+ *
* @see org.ksoap2.transport.HttpTransportSE#getServiceConnection()
*/
//@Override
- public ServiceConnection getServiceConnection() throws IOException
- {
- if (serviceConnection == null) {
- serviceConnection = new HttpsServiceConnectionSEIgnoringConnectionClose(host, port,
- file, timeout);
- serviceConnection.setRequestProperty("Connection", "keep-alive");
- }
+ public ServiceConnection getServiceConnection() throws IOException {
+ ServiceConnection serviceConnection =
+ new HttpsServiceConnectionSEIgnoringConnectionClose(host, port, file, timeout);
+ serviceConnection.setRequestProperty("Connection", "keep-alive");
return serviceConnection;
}
diff --git a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
index 029ee9a..e63bc9c 100644
--- a/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
+++ b/ksoap2-j2se/src/main/java/org/ksoap2/transport/ServiceConnectionSE.java
@@ -1,6 +1,6 @@
/* Copyright (c) 2003,2004, Stefan Haustein, Oberhausen, Rhld., Germany
* Copyright (c) 2006, James Seigel, Calgary, AB., Canada
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
@@ -21,16 +21,16 @@
package org.ksoap2.transport;
-import java.io.*;
-import java.net.*;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import org.ksoap2.HeaderProperty;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.*;
+
/**
* Connection for J2SE environments.
*/
@@ -40,8 +40,8 @@ public class ServiceConnectionSE implements ServiceConnection {
/**
* Constructor taking the url to the endpoint for this soap communication
+ *
* @param url the url to open the connection to.
- * @throws IOException
*/
public ServiceConnectionSE(String url) throws IOException {
this(null, url, ServiceConnection.DEFAULT_TIMEOUT);
@@ -53,9 +53,10 @@ public class ServiceConnectionSE implements ServiceConnection {
/**
* Constructor taking the url to the endpoint for this soap communication
- * @param url the url to open the connection to.
+ *
+ * @param url the url to open the connection to.
* @param timeout the connection and read timeout for the http connection in milliseconds
- * @throws IOException // 20 seconds
+ * @throws IOException // 20 seconds
*/
public ServiceConnectionSE(String url, int timeout) throws IOException {
this(null, url, timeout);
@@ -69,7 +70,8 @@ public class ServiceConnectionSE implements ServiceConnection {
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(timeout);
- connection.setReadTimeout(timeout); // even if we connect fine we want to time out if we cant read anything..
+ connection.setReadTimeout(
+ timeout); // even if we connect fine we want to time out if we cant read anything..
}
public void connect() throws IOException {
@@ -80,23 +82,29 @@ public class ServiceConnectionSE implements ServiceConnection {
connection.disconnect();
}
- public List getResponseProperties() {
- Map properties = connection.getHeaderFields();
- Set keys = properties.keySet();
+ public List getResponseProperties() throws IOException {
List retList = new LinkedList();
- for (Iterator i = keys.iterator(); i.hasNext();) {
- String key = (String) i.next();
- List values = (List) properties.get(key);
-
- for (int j = 0; j < values.size(); j++) {
- retList.add(new HeaderProperty(key, (String) values.get(j)));
+ Map properties = connection.getHeaderFields();
+ if (properties != null) {
+ Set keys = properties.keySet();
+ for (Iterator i = keys.iterator(); i.hasNext(); ) {
+ String key = (String) i.next();
+ List values = (List) properties.get(key);
+
+ for (int j = 0; j < values.size(); j++) {
+ retList.add(new HeaderProperty(key, (String) values.get(j)));
+ }
}
}
return retList;
}
+ public int getResponseCode() throws IOException {
+ return connection.getResponseCode();
+ }
+
public void setRequestProperty(String string, String soapAction) {
connection.setRequestProperty(string, soapAction);
}
@@ -106,7 +114,7 @@ public class ServiceConnectionSE implements ServiceConnection {
}
/**
- * If the length of a HTTP request body is known ahead, sets fixed length
+ * If the length of a HTTP request body is known ahead, sets fixed length
* to enable streaming without buffering. Sets after connection will cause an exception.
*
* @param contentLength the fixed length of the HTTP request body
@@ -116,6 +124,10 @@ public class ServiceConnectionSE implements ServiceConnection {
connection.setFixedLengthStreamingMode(contentLength);
}
+ public void setChunkedStreamingMode() {
+ connection.setChunkedStreamingMode(0);
+ }
+
public OutputStream openOutputStream() throws IOException {
return connection.getOutputStream();
}