RE: Fixes to SoapParser

"Mike Jones" <[email protected]> Sat, 2 Aug 2003 16:46:35 -0700
Newsgroups gmane.comp.java.enhydra.ksoap
Message-ID <!~!UENERkVCMDkAAQACAAAAAAAAAAAAAAAAABgAAAAAAAAAq5r+Gdo6e0SOWnHo8vu74qKCAAAQAAAAE/[email protected]>
Until I find a diff tool, here is the whole parser class with changes.
What this class does is takes a soap message with an array, coded using
the href tag as shown below. The implementation is not the best
algorithm, but the first question is this:

Should this style of array be supported by ksoap?

If the answer is yes, then proposals for a better implementation might
be in order.

Mike

"<SOAP-ENV:Envelope
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" 
	xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" 
	xmlns:SOAPENC=\"http://schemas.xmlsoap.org/soap/encoding/\"
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" 
	xmlns:clr=\"http://schemas.microsoft.com/soap/encoding/clr/1.0\"

	
SOAPENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r<S
OAP-ENV:Body>
<SOAP-ENC:Array SOAP-ENC:arrayType=\"a1:Command2[2]\" 	
	
xmlns:a1=\"http://schemas.microsoft.com/clr/nsassem/SoapExperiment/SoapE
xperiment\">
<item href=\"#ref-3\"/>
<item href=\"#ref-4\"/>
</SOAP-ENC:Array>
<a1:Command2 id=\"ref-3\" 
	
xmlns:a1=\"http://schemas.microsoft.com/clr/nsassem/SoapExperiment/SoapE
xperiment\">

<foo1 xsi:type=\"xsd:int\">1</foo1>
<foo2 xsi:type=\"xsd:long\">8</foo2>
<foo3 xsi:type=\"xsd:boolean\">true</foo3>
<foo4 xsi:type=\"xsd:string\">My or my!</foo4>
<foo5 xsi:type=\"xsd:double\">4.3</foo5>

</a1:Command2>\r\n<a1:Command2 id=\"ref-4\" 
	
xmlns:a1=\"http://schemas.microsoft.com/clr/nsassem/SoapExperiment/SoapE
xperiment\">
<foo1 xsi:type=\"xsd:int\">2</foo1>
<foo2 xsi:type=\"xsd:long\">8</foo2>
<foo3 xsi:type=\"xsd:boolean\">true</foo3>
<foo4 xsi:type=\"xsd:string\">My or my!</foo4>
<foo5 xsi:type=\"xsd:double\">4.3</foo5>
</a1:Command2>

</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


/* kSOAP
 *
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License
 * on the Enhydra web site ( http://www.enhydra.org/ ).
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific terms governing rights and limitations
 * under the License.
 *
 * The Initial Developer of kSOAP is Stefan Haustein. Copyright (C)
 * 2000, 2001 Stefan Haustein, D-46045 Oberhausen (Rhld.),
 * Germany. All Rights Reserved.
 *
 * Contributor(s): John D. Beatty, F. Hunter, Renaud Tognelli, Sean
McDaniel
 *
 * */

package org.ksoap;

import java.util.*;
import java.io.*;

import org.kxml.*;
import org.kxml.io.*;
import org.kxml.parser.*;
import org.kobjects.serialization.*;


/** A SOAP parser. Limitations:
    <ul>
    <li>Partial arrays are not yet supported</li>
    <li>Multi-Dimensional Arrays are not supported</li>
    <li>The hrefs must be local</li>
    </ul>

      */

public class SoapParser {

    ClassMap classMap;
    Hashtable idMap = new Hashtable ();
    Hashtable arrayMap = new Hashtable ();
    public AbstractXmlParser parser;


    class FwdRef {
	FwdRef next;
	Object obj;
	int index;
    }


    public SoapParser (AbstractXmlParser parser,
		       ClassMap classMap) {

	this.parser = parser;
	this.classMap = classMap;
    }




    /** Extracts namespace and name and calls readBody for actual
reading */

    public Object read () throws IOException {

	Object root = null;

	//System.out.println ("start parsing....");


	while (true) {
	    parser.skip ();
	    int type = parser.peek ().getType ();

	    if (type == Xml.END_TAG || type == Xml.END_DOCUMENT)
		break;
	    else if (type != Xml.START_TAG)
		throw new IOException ("Unexpected event: "+parser.peek
());

	    StartTag start = (StartTag) parser.peek ();

	    //	String name = namespaceMap.getPackage
(start.getNamespace ())
	    //	+ "." + start.getName ();

	    Object o = read
		(null, -1,
		 start.getNamespace (), start.getName (),
		 ElementType.OBJECT_TYPE);

	    Attribute attr = start.getAttribute (classMap.enc, "root");
	    if ((attr != null && attr.getValue ().equals ("1"))
		|| root == null)
		root = o;
	}

	//System.out.println ("leaving root read");

	return root;
    }




    /** Builds an object from the XML stream. This method
        is public for usage in conjuction with Marshal subclasses. */

    public Object read (Object owner, int index,
			String namespace, String name,
			ElementType expected)

	throws IOException {

	//	System.out.println ("in read");


	StartTag start = (StartTag) parser.peek ();

	// determine wire element type

	String href = start.getValueDefault ("href", null);
	Object obj = null;

	if (href != null) {
	    if (owner == null)
		throw new RuntimeException ("href at root level?!?");

	    href = href.substring (1);
	    obj = idMap.get (href);

	    if (obj == null || obj instanceof FwdRef) {

		FwdRef f = new FwdRef ();
		f.next = (FwdRef) obj;
		f.obj = owner;
		f.index = index;
		idMap.put (href, f);
		obj = null;
	    }

	    parser.read (); // start tag
	    parser.skip ();
	    parser.read (Xml.END_TAG, null, null);
	}
	else {
	    Attribute attr = start.getAttribute
		(classMap.xsi, "nil");

        if (attr == null) attr = start.getAttribute(classMap.xsi,
"null");

	    if (attr != null && Soap.stringToBoolean (attr.getValue ()))
{
		obj = null;
		parser.read (); // start tag
		parser.skip ();
		parser.read (Xml.END_TAG, null, null);
	    }
	    else {

		attr = start.getAttribute
		    (classMap.xsi, "type");

		if (attr != null) {
		    String type = attr.getValue ();
		    int cut = type.indexOf (':');

		    name = type.substring (cut + 1);
		    String prefix = cut == -1 ? "" : type.substring (0,
cut);
		    namespace = start.getPrefixMap ().getNamespace
(prefix);

			obj = classMap.readInstance
				(this, namespace, name, expected);
		}
		else if (name == null && namespace == null) {
			attr = start.getAttribute (classMap.enc,
"arrayType");
		    if (attr != null) {
			namespace = classMap.enc;
			name = "Array";
			obj = classMap.readInstance
				(this, namespace, name, expected);
			String arrayType = attr.getValue();
			
			if (arrayType != null && arrayType.length() > 0)
{
				int cut = arrayType.indexOf(':');
				int end = arrayType.indexOf('[');
				String nm = arrayType.substring(cut + 1,
end);
				String prefix = cut == -1 ? "" :
arrayType.substring(0, cut);
				// This does not get the namespace.
				String ns =
start.getPrefixMap().getNamespace(prefix);
				SoapPrimitive primitive = new
SoapPrimitive(ns, nm, null);
				arrayMap.put(obj, primitive);
			}
			}
		    else {
				String id = start.getValueDefault ("id",
null);
				if (id != null) {
					Object hlp = idMap.get (id);
					if (hlp instanceof FwdRef) {
						FwdRef f = (FwdRef) hlp;
						if
(arrayMap.containsKey(f.obj)) {
							SoapPrimitive
primitive = (SoapPrimitive) arrayMap.get(f.obj);
							namespace =
primitive.getNamespace();
							name =
primitive.getName();
							obj =
classMap.readInstance
								(this,
primitive.getNamespace(), primitive.getName(), expected);
						}
					}
				}
				else {
					Object [] names =
classMap.getInfo(expected.type, null);

					namespace = (String) names [0];
					name = (String) names [1];
			
					obj = classMap.readInstance
						(this, namespace, name,
expected);
				}
		    }
		}
		else {
			obj = classMap.readInstance
				(this, namespace, name, expected);
		}

		if (obj == null)
		    obj = readUnknown (namespace, name);
	    }

	    // finally, care about the id....
	    String id = start.getValueDefault ("id", null);

	    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
			{
				if (arrayMap.containsKey(f.obj))
				{
					// The hash changes when adding
elements, so we need to rehash.
					// This is not efficient code
and a better strategy is needed!
					SoapPrimitive primitive =
(SoapPrimitive) arrayMap.get(f.obj);
					arrayMap.remove(f.obj);
					((Vector) f.obj)
					.setElementAt (obj, f.index);
					arrayMap.put(f.obj, primitive);
				}
				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);
	    }
	}

	//	System.out.println ("leaving read");

	return obj;
    }



    protected void readSerializable (KvmSerializable obj)
	throws IOException {

	parser.read (); // read start tag

	int testIndex = -1; // inc at beg. of loop for perf. reasons
	int sourceIndex = 0;
	int cnt = obj.getPropertyCount ();
	PropertyInfo info = new PropertyInfo ();

	while (true) {
	    parser.skip ();
	    if (parser.peek ().getType () == Xml.END_TAG) break;

	    StartTag start = (StartTag) parser.peek ();
	    String name = start.getName ();
	    //	    System.out.println ("tagname:"+name);

	    int countdown = cnt;

	    while (true) {
		if (countdown-- == 0)
		    throw new RuntimeException ("Unknwon Property:
"+name);

		if (++testIndex >= cnt) testIndex = 0;

		obj.getPropertyInfo (testIndex, info);
		if (info.name == null 
		    ? testIndex == sourceIndex
		    : info.name.equals (name)) break;

	    }

	    obj.setProperty 
		(testIndex, read (obj, testIndex, null, null, info));

	    sourceIndex = 0;
	}

	parser.read (Xml.END_TAG, null, null);
    }


    public Object readUnknown (String namespace,
			       String name) throws IOException {

	parser.read (); // start tag
	parser.skip ();

	Object result;

	if (parser.peek ().getType () == Xml.TEXT)
	     result = new SoapPrimitive
		 (namespace, name, parser.readText ());
	else {
	    SoapObject so = new SoapObject (namespace, name);

	    while (parser.peek ().getType () != Xml.END_TAG) {
		so.addProperty (parser.peek ().getName (),
				read (so, so.getPropertyCount (),
				      null, null,
				      ElementType.OBJECT_TYPE));
		parser.skip ();
	    }
	    result = so;
	}

	parser.read (Xml.END_TAG, null, null);

	return result;
    }

    private int getIndex (Attribute attr, int start, int dflt) {
	if (attr == null) return dflt;
	String value = attr.getValue ();
	return value.length() - start < 3 
	    ? dflt
	    : Integer.parseInt (value.substring (start+1,
value.length()-1));
    }
    

    public void readVector (Vector v,
			    ElementType elementType) throws IOException
{

	StartTag start = (StartTag) parser.read ();

	String namespace = null;
	String name = null;
        int size = v.size ();
        boolean dynamic = true;

	Attribute attr = start.getAttribute (classMap.enc, "arrayType");
	if (attr != null) {
	    String type = attr.getValue ();

	    int cut0 = type.indexOf (':');
	    int cut1 = type.indexOf ("[", cut0);
	    name = type.substring (cut0 + 1, cut1);
	    String prefix = cut0 == -1 ? "" : type.substring (0, cut0);
	    namespace = start.getPrefixMap ().getNamespace (prefix);

	    size = getIndex(attr, cut1, -1);
	    if (size != -1) { 
		v.setSize (size);
		dynamic = false;
	    }
	}

	if (elementType == null)
	    elementType = ElementType.OBJECT_TYPE;

	parser.skip ();

        int position = getIndex 
	    (start.getAttribute (classMap.enc, "offset"), 0, 0);

	while (parser.peek ().getType () != Xml.END_TAG) {
            // handle position

	    position = getIndex (parser.peek ().getAttribute 
				 (classMap.enc, "position"), 0,
position);

            if (dynamic && position >= size) {
		size = position + 1;
		v.setSize (size);
            }

            // implicit handling of position exceeding specified size
	    v.setElementAt 
		(read (v, position, namespace, name, elementType),
		 position);
	    
            position++;
	    parser.skip ();
	}

	parser.read (Xml.END_TAG, start.getNamespace (), start.getName
());
    }
}






-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf
Of Mike Jones
Sent: Friday, August 01, 2003 10:18 PM
To: [email protected]
Subject: RE: Ksoap: Fixes to SoapParser



If only one file is affected, a simple option would be to send a diff
file to 
this list?

Ok, I can do that. Does anyone know how to make a diff on Windows? As
stupid as it sounds, I have never done it.

Mike



_______________________________________________
Ksoap mailing list
[email protected]
http://www.enhydra.org/mailman/listinfo.cgi/ksoap