Re: java.util.Calendar serializer patch contribution

Frank Cohen <[email protected]>
Newsgroups gmane.text.xml.soap.user
Message-ID <[email protected]>
Thanks Scott. Here's my contribution:

I made these changes to Apache SOAP 2.3.1 in response to a TestMaker 
user that hit an interoperability problem when working with 
java.util.Calendar types in a complex object. When the BeanSerializer 
operated on the complex object it emits an XML element:

<memoCalendar xlmns:ns3="http://java.util" 
xmi:type="ns3:Calendar">2004-03-16T-06:12:23:218Z</memoCalendar>

If this were .NET or Apache Axis the marshalled request element would 
look like this:

<memoCalendar 
xsi:type="xsd:dateTime">2004-03-16T-06:12:23:218Z</memoCalendar>

The customer's Web Service application responded with a SOAP exception 
saying it could not deserialize a java.util.Calendar type. The customer 
is not in control of the complex object (it came in a prepackaged 
signed jar file.)

Attached are three modified files:

DateSerializer.java - I modified this to accept java.util.Date and 
java.util.Calendar objects. Previously it only accepted 
java.util.Calendar.

CalendarSerializer.java - I modified this to accept java.util.Calendar 
and java.util.GregorianCalendar objects. Previously it only accepted 
java.util.GregorianCalendar.

SoapEngUtils.java - I modified this to look for java.util.Calendar 
types and if so it forces the use of a QName object with 
"http://www.w3.org/2001/XMLSchema" and a value of "dateTime".

These changes are a hack to get my customer's application to work. I 
would prefer to work with someone on the Apace SOAP team to design a 
better solution.

I don't like the idea of having branches so perhaps there is a way to 
work together to get this improvement redesigned and part of Apache 
SOAP. I'm open to your ideas.

-Frank



On Mar 12, 2004, at 6:36 PM, Scott Nichol wrote:

> We are always open to contributions.
>
> Scott Nichol
>
> Do not send e-mail directly to this e-mail address,
> because it is filtered to accept only mail from
> specific mail lists.
> ----- Original Message -----
> From: "Frank Cohen" <[email protected]>
> To: <[email protected]>
> Sent: Friday, March 12, 2004 8:35 PM
> Subject: java.util.Calendar serializer patch contribution
>
>
> My open-source framework and utility TestMaker bundles Apache SOAP. A
> user pointed out a problem where he was trying to serialized a
> java.util.Calendar object in a complex data type. The Apache SOAP code
> serializes GregorianCalendar but not Calendar. So, I modified the
> Calendar serializer class. I'm wondering if this is worthwhile to
> submit to the Apache SOAP project as a contribution? I intend to ship
> the modified library with TestMaker 4.0.9 and later.
>
> Please let me know if you want the patch and how to send it. Thanks.
>
> -Frank
>
>
>
> --
> Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
> Enterprise test automation solutions to check and monitor Web-enabled
> applications for functionality, scalability and reliability.
>
>
>
--
Frank Cohen, PushToTest, http://www.PushToTest.com, phone: 408 374 7426
Enterprise test automation solutions to check and monitor Web-enabled
applications for functionality, scalability and reliability.
DateSerializer.java (text/plain, 9.4 KB)
/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "SOAP" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2000, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.soap.encoding.soapenc;

import org.apache.soap.util.xml.*;
import org.apache.soap.util.*;
import org.apache.soap.rpc.SOAPContext;
import java.io.*;
import org.w3c.dom.*;
import java.io.*;
import java.util.*;
import java.text.*;

/**
*
* This class de/serializes instances of java.util.Date from/to
* the XML-Schema type 'timeInstant'. For details see the
* <A HREF="http://www.w3.org/TR/xmlschema-2/#timeInstant">XML-Schema specification</A>
*
* @author Phil Mork
* @author Glen Daniels ([email protected])
* @author Matthew J. Duftler ([email protected])
* @author Sam Ruby ([email protected])
* @see Serializer
* @see Deserializer
*/
public class DateSerializer implements Serializer, Deserializer
{
  SimpleDateFormat sdf;

  public DateSerializer()
  {
    sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
                            //  0123456789 0 123456789
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
  }

  public void marshall(String inScopeEncStyle, Class javaType, Object src,
                       Object context, Writer sink, NSStack nsStack,
                       XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                        throws IllegalArgumentException, IOException
  {
    
    /* [email protected] March 15, 2004: Changed to allow this serializer to
       marshal java.util.Calendar object types too. 
    */
    Date calDate = null;
      
    if( javaType.equals(java.util.Date.class))
    {
        calDate = new Date( ((Date)src).getTime() );
    }

    if( javaType.equals(java.util.Calendar.class))
    {
        calDate = ((Calendar)src).getTime();
    }
      
    if ( calDate == null )
      throw new IllegalArgumentException("Can only serialize java.util.Date or java.util.Calendar instances");
    
    nsStack.pushScope();
    if(src!=null)
    {
        
      /* [email protected] March 16,2004: Change the 2nd param below to use dateTime always. This
         is to force interoperability for Cricket Communications' Web Service application. I do not
         recommend this for anyone else.
      */
        
      SoapEncUtils.generateStructureHeader(inScopeEncStyle,
                                           javaType,
                                           context,
                                           sink,
                                           nsStack,
                                           xjmr,
                                           ctx);

      String fdate=null;

      synchronized(sdf)
      {
        fdate=sdf.format( calDate );
      }

      sink.write(fdate);
      sink.write("</" + context + '>');
    }
    else
    {
      SoapEncUtils.generateNullStructure(inScopeEncStyle,
                                           javaType,
                                           context,
                                           sink,
                                           nsStack,
                                           xjmr,
                                           ctx);
    }
    nsStack.popScope();
  }

  public Bean unmarshall(String inScopeEncStyle, QName elementType, Node src,
                         XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                          throws IllegalArgumentException
  {
    Date date=null;
    Element root = (Element)src;
    String value = DOMUtils.getChildCharacterData(root);
    if(value!=null && !((value=value.trim()).equals("")))
    {
      try
      {
        synchronized(sdf)
        {
          date=sdf.parse(value);
        }
      }
      catch (ParseException pe)
      {
        try
        {
          // validate fixed portion of format
          if (value.length() < 19) 
            throw new ParseException("",0);

          if (value.charAt(4) != '-' || value.charAt(7) != '-' ||
              value.charAt(10) != 'T')
            throw new ParseException("",0);

          if (value.charAt(13) != ':' || value.charAt(16) != ':')
            throw new ParseException("",0);

          // convert what we have validated so far
          try
          {
            synchronized(sdf)
            {
              date=sdf.parse(value.substring(0,19)+".000Z"); 
            }
          }
          catch (Exception e)
          {
            throw new ParseException("",0);
          }

          int pos = 19;

          // parse optional milliseconds
          if (pos < value.length() && value.charAt(pos)=='.') {
            int milliseconds = 0;
            int start = ++pos;
            while (pos<value.length() && Character.isDigit(value.charAt(pos)))
              pos++;

            String decimal=value.substring(start,pos);
            if (decimal.length()==3) {
              milliseconds=Integer.parseInt(decimal);
            } else if (decimal.length() < 3) {
              milliseconds=Integer.parseInt((decimal+"000").substring(0,3));
            } else {
              milliseconds=Integer.parseInt(decimal.substring(0,3));
              if (decimal.charAt(3)>='5') ++milliseconds;
            }

            // add milliseconds to the current result
            date.setTime(date.getTime()+milliseconds);
          }

          // parse optional timezone
          if (pos+5 < value.length() &&
               (value.charAt(pos)=='+' || (value.charAt(pos)=='-')))
          {
            if (!Character.isDigit(value.charAt(pos+1)) || 
                !Character.isDigit(value.charAt(pos+2)) || 
                value.charAt(pos+3) != ':'              || 
                !Character.isDigit(value.charAt(pos+4)) || 
                !Character.isDigit(value.charAt(pos+5)))
               throw new ParseException("",0);

            int hours = (value.charAt(pos+1)-'0')*10+value.charAt(pos+2)-'0';
            int mins  = (value.charAt(pos+4)-'0')*10+value.charAt(pos+5)-'0';
            int milliseconds = (hours*60+mins)*60*1000;

            // subtract milliseconds from the current date to obtain GMT
            if (value.charAt(pos)=='+') milliseconds=-milliseconds;
            date.setTime(date.getTime()+milliseconds);
            pos+=6;  
          }

          if (pos < value.length() && value.charAt(pos)=='Z') pos++;

          if (pos < value.length())
            throw new ParseException("",0);
        }
        catch (ParseException pe2)
        {
          synchronized(sdf)
          {
            throw new IllegalArgumentException("String represents no valid " +
                                             "Date for this Deserializer; " +
                                             "try " + sdf.toPattern() + ".");
          }
        }
      }
    }
    return new Bean(java.util.Date.class,date);
  }
}
CalendarSerializer.java (text/plain, 6.3 KB)
/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "SOAP" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2000, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
* Initial revision - based on code from Apache serializer classes
*/

package org.apache.soap.encoding.soapenc;

import org.apache.soap.util.xml.*;
import org.apache.soap.util.*;
import org.apache.soap.encoding.soapenc.SoapEncUtils;
import org.apache.soap.rpc.SOAPContext;
import java.io.*;
import org.w3c.dom.*;
import java.io.*;
import java.util.*;
import java.text.*;

/**
*
* This class de/serializes instances of java.util.GregorianCalendar from/to
* the XML-Schema type 'date'. For details see the
* <A HREF="http://www.w3.org/TR/xmlschema-2/#date">XML-Schema specification</A>
*
* @author Phil Mork
* @author Glen Daniels ([email protected])
* @see Serializer
* @see Deserializer
*/
public class CalendarSerializer implements Serializer, Deserializer
{
  SimpleDateFormat sdf;  

  public CalendarSerializer()
  {    
    sdf=new SimpleDateFormat("yyyy-MM-dd");
    //For now just use the default locale timezone  - could set to GMT if needed
    //sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
  }

  public void marshall(String inScopeEncStyle, Class javaType, Object src,
                       Object context, Writer sink, NSStack nsStack,
                       XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                        throws IllegalArgumentException, IOException
  {    
      /* [email protected], March 12, 2003: Changed to be able to handle java.util.Calendar objects */
    Date calDate = null;
      
    if( javaType.equals(java.util.GregorianCalendar.class))
    {
        calDate = ((GregorianCalendar)src).getTime();
    }

    if( javaType.equals(java.util.Calendar.class))
    {
        calDate = ((Calendar)src).getTime();
    }
      
    if ( calDate == null )
      throw new IllegalArgumentException("Can only serialize GregorianCalendar or Calendar instances");

    nsStack.pushScope();
    if(src!=null)
    {
      SoapEncUtils.generateStructureHeader(inScopeEncStyle,
                                           javaType,
                                           context,
                                           sink,
                                           nsStack,
                                           xjmr,
                                           ctx);
      
      String fdate = null;

      synchronized(sdf)
      {
        fdate=sdf.format(calDate);
      }

      sink.write(fdate);
      sink.write("</" + context + '>');
    }
    else
    {
      SoapEncUtils.generateNullStructure(inScopeEncStyle,
                                           javaType,
                                           context,
                                           sink,
                                           nsStack,
                                           xjmr,
                                           ctx);
    }
    nsStack.popScope();
  }

  public Bean unmarshall(String inScopeEncStyle, QName elementType, Node src,
                         XMLJavaMappingRegistry xjmr, SOAPContext ctx)
                          throws IllegalArgumentException
  {
    Date date=null;
    Calendar calDate = new GregorianCalendar();
    Element root = (Element)src;
    String value = DOMUtils.getChildCharacterData(root);
    if(value!=null && !((value=value.trim()).equals("")))
    {
      try
      {
        synchronized(sdf)
        {
          date=sdf.parse(value);
        }
        calDate.setTime(date);
      }
      catch (ParseException pe)
      {
        throw new IllegalArgumentException("String represents no valid Date for this Deserializer");
      }
    }
    return new Bean(java.util.GregorianCalendar.class, calDate);
  }
}
SoapEncUtils.java (text/plain, 17.8 KB)
/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "SOAP" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2000, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.soap.encoding.soapenc;

import java.io.*;
import org.w3c.dom.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.*;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.rpc.SOAPContext;

/**
 * These static methods can be used to do much of the repetitive and
 * mechanical work that is required when generating structures using
 * the <code>SOAP-ENC</code> encoding style.
 *
 * @author Matthew J. Duftler ([email protected])
 * @author Scott Nichol ([email protected])
 */
public class SoapEncUtils
{
  public static void generateNullStructure(String inScopeEncStyle,
                                           Class javaType, Object context,
                                           Writer sink, NSStack nsStack,
                                           XMLJavaMappingRegistry xjmr,
                                           SOAPContext ctx)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, ctx, null, null, true);
  }

  public static void generateNullArray(String inScopeEncStyle,
                                       Class javaType, Object context,
                                       Writer sink, NSStack nsStack,
                                       XMLJavaMappingRegistry xjmr,
                                       SOAPContext ctx,
                                       QName arrayElementType,
                                       String arrayLengthStr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, ctx, arrayElementType, arrayLengthStr,
                            true);
  }

  public static void generateArrayHeader(String inScopeEncStyle,
                                         Class javaType, Object context,
                                         Writer sink, NSStack nsStack,
                                         XMLJavaMappingRegistry xjmr,
                                         SOAPContext ctx,
                                         QName arrayElementType,
                                         String arrayLengthStr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, ctx, arrayElementType, arrayLengthStr,
                            false);
  }

  public static void generateStructureHeader(String inScopeEncStyle,
                                             Class javaType, Object context,
                                             Writer sink, NSStack nsStack,
                                             XMLJavaMappingRegistry xjmr,
                                             SOAPContext ctx)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, ctx, null, null, false);
  }

  private static void generateStructureHeader(String inScopeEncStyle,
                                              Class javaType, Object context,
                                              Writer sink, NSStack nsStack,
                                              XMLJavaMappingRegistry xjmr,
                                              SOAPContext ctx,
                                              QName arrayElementType,
                                              String arrayLengthStr,
                                              boolean isNull)
    throws IllegalArgumentException, IOException
  {
      
    /* [email protected] March 16, 2004: Hacking the QName for java.util.Calendar
     to make it interoperate with a Web Service that requires dateTime. I would
     not recommend this approach to anyone. */
    
    QName elementType = null;

    if ( javaType.equals(java.util.Calendar.class) )
    {
        elementType = new QName( "http://www.w3.org/2001/XMLSchema", "dateTime" );
    }
    else
    {
        elementType = xjmr.queryElementType(javaType, Constants.NS_URI_SOAP_ENC);
    }
    
    String namespaceDecl = "";

    if (context instanceof PrefixedName)
    {
      PrefixedName pname = (PrefixedName)context;
      QName qname = pname.getQName();

      if (qname != null)
      {
        String namespaceURI = qname.getNamespaceURI();

        if (namespaceURI != null && !namespaceURI.equals(""))
        {
          if (pname.getPrefix() == null)
          {
            String prefix = nsStack.getPrefixFromURI(namespaceURI);

            if (prefix == null)
            {
              prefix = nsStack.addNSDeclaration(namespaceURI);
              namespaceDecl = " xmlns:" + prefix + "=\"" + namespaceURI + '\"';
            }

            pname.setPrefix(prefix);
          }
        }
      }
    }

    sink.write('<');
    sink.write(context.toString());
    sink.write(namespaceDecl);

    // Write the id for a multiRef element
    if (context instanceof MultiRefContext) {
      sink.write(' '+ Constants.ATTR_ID + "=\"" +
                 Constants.ATTRVAL_MULTIREF_ID_PREFIX);
      sink.write(((MultiRefContext) context).getId() + "");
      sink.write( '\"');
    }

    // Get prefixes for the needed namespaces.
    String elementTypeNS = elementType.getNamespaceURI();
    String xsiNamespaceURI;

    if (elementTypeNS.startsWith("http://www.w3.org/")
        && elementTypeNS.endsWith("/XMLSchema")) {
      xsiNamespaceURI = elementTypeNS + "-instance";
    } else if (xjmr instanceof SOAPMappingRegistry) {
      xsiNamespaceURI = ((SOAPMappingRegistry) xjmr).getSchemaURI() + "-instance";
    } else {
      xsiNamespaceURI = Constants.NS_URI_CURRENT_SCHEMA_XSI;
    }

    String xsiNSPrefix = nsStack.getPrefixFromURI(xsiNamespaceURI, sink);
    String elementTypeNSPrefix = nsStack.getPrefixFromURI(elementTypeNS, sink);

    if (ctx == null || !ctx.getDocLitSerialization()) {
      sink.write(' ');
      sink.write(xsiNSPrefix);
      sink.write(':' + Constants.ATTR_TYPE + "=\"");
      sink.write(elementTypeNSPrefix);
      sink.write(':');
      sink.write(elementType.getLocalPart());
      sink.write('\"');
    }

    if (inScopeEncStyle == null
        || !inScopeEncStyle.equals(Constants.NS_URI_SOAP_ENC))
    {
      // Determine the prefix associated with the NS_URI_SOAP_ENV
      // namespace URI.
      String soapEnvNSPrefix = nsStack.getPrefixFromURI(
        Constants.NS_URI_SOAP_ENV, sink);

      sink.write(' ');
      sink.write(soapEnvNSPrefix);
      sink.write(':' +Constants.ATTR_ENCODING_STYLE + "=\"" +
                 Constants.NS_URI_SOAP_ENC + '\"');
    }

    if (arrayElementType != null)
    {
      String arrayElementTypeNSPrefix = nsStack.getPrefixFromURI(
        arrayElementType.getNamespaceURI(), sink);
      String arrayTypeValue = arrayElementTypeNSPrefix + ':' +
                              arrayElementType.getLocalPart() +
                              '[' + arrayLengthStr + ']';
      String soapEncNSPrefix = nsStack.getPrefixFromURI(
        Constants.NS_URI_SOAP_ENC, sink);

      sink.write(' ');
      sink.write(soapEncNSPrefix);
      sink.write(':' + Constants.ATTR_ARRAY_TYPE +"=\"");
      sink.write(arrayTypeValue);
      sink.write('\"');
    }

    if (isNull)
    {
      sink.write(' ');
      sink.write(xsiNSPrefix);
      sink.write(':');
      sink.write(nilName(xsiNamespaceURI));
      sink.write("=\"" + Constants.ATTRVAL_TRUE + "\"/");
    }

    sink.write('>');
  }

  public static void generateNullStructure(String inScopeEncStyle,
                                           Class javaType, Object context,
                                           Writer sink, NSStack nsStack,
                                           XMLJavaMappingRegistry xjmr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, null, null, null, true);
  }

  public static void generateNullArray(String inScopeEncStyle,
                                       Class javaType, Object context,
                                       Writer sink, NSStack nsStack,
                                       XMLJavaMappingRegistry xjmr,
                                       QName arrayElementType,
                                       String arrayLengthStr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, null, arrayElementType, arrayLengthStr,
                            true);
  }

  public static void generateArrayHeader(String inScopeEncStyle,
                                         Class javaType, Object context,
                                         Writer sink, NSStack nsStack,
                                         XMLJavaMappingRegistry xjmr,
                                         QName arrayElementType,
                                         String arrayLengthStr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, null, arrayElementType, arrayLengthStr,
                            false);
  }

  public static void generateStructureHeader(String inScopeEncStyle,
                                             Class javaType, Object context,
                                             Writer sink, NSStack nsStack,
                                             XMLJavaMappingRegistry xjmr)
    throws IllegalArgumentException, IOException
  {
    generateStructureHeader(inScopeEncStyle, javaType, context, sink,
                            nsStack, xjmr, null, null, null, false);
  }

  private static String nilName(String currentSchemaXSI)
  {
    return (currentSchemaXSI.equals(Constants.NS_URI_2001_SCHEMA_XSI))
           ? Constants.ATTR_NIL
           : Constants.ATTR_NULL;
  }

  public static boolean isNull(Element element)
  {
    String nullValue = DOMUtils.getAttributeNS(element,
                                               Constants.NS_URI_2001_SCHEMA_XSI,
                                               Constants.ATTR_NIL);

    if (nullValue == null)
    {
      nullValue = DOMUtils.getAttributeNS(element,
                                          Constants.NS_URI_2000_SCHEMA_XSI,
                                          Constants.ATTR_NULL);
    }

    if (nullValue == null)
    {
      nullValue = DOMUtils.getAttributeNS(element,
                                          Constants.NS_URI_1999_SCHEMA_XSI,
                                          Constants.ATTR_NULL);
    }

	// This case accepts the buggy output from Apache SOAP prior to 2.3.1,
	// specifically a xsi:null for 2001 schema (should be xsi:nil).
    if (nullValue == null)
    {
      nullValue = DOMUtils.getAttributeNS(element,
                                          Constants.NS_URI_2001_SCHEMA_XSI,
                                          Constants.ATTR_NULL);
    }

    return nullValue != null && decodeBooleanValue(nullValue);
  }

  public static boolean decodeBooleanValue(String value)
  {
    switch (value.charAt(0))
    {
      case '0': case 'f': case 'F':
        return false;

      case '1': case 't': case 'T':
        return true;

      default:
        throw new IllegalArgumentException("Invalid boolean value: " + value);
    }
  }

  public static QName getAttributeValue(Element el,
                                        String attrNameNamespaceURI,
                                        String attrNameLocalPart,
                                        String elDesc,
                                        boolean isRequired)
    throws IllegalArgumentException
  {
    String attrValue = DOMUtils.getAttributeNS(el,
                                               attrNameNamespaceURI,
                                               attrNameLocalPart);

    if (attrValue != null)
    {
      int index = attrValue.indexOf(':');

      if (index != -1)
      {
        String attrValuePrefix       = attrValue.substring(0, index);
        String attrValueLocalPart    = attrValue.substring(index + 1);
        String attrValueNamespaceURI =
          DOMUtils.getNamespaceURIFromPrefix(el, attrValuePrefix);

        if (attrValueNamespaceURI != null)
        {
          return new QName(attrValueNamespaceURI, attrValueLocalPart);
        }
        else
        {
          throw new IllegalArgumentException("Unable to resolve namespace " +
                                             "URI for '" + attrValuePrefix +
                                             "'.");
        }
      }
      else
      {
        throw new IllegalArgumentException("The value of the '" +
                                           attrNameNamespaceURI + ':' +
                                           attrNameLocalPart +
                                           "' attribute must be " +
                                           "namespace-qualified.");
      }
    }
    else if (isRequired)
    {
      throw new IllegalArgumentException("The '" +
                                         attrNameNamespaceURI + ':' +
                                         attrNameLocalPart +
                                         "' attribute must be " +
                                         "specified for every " +
                                         elDesc + '.');
    }
    else
    {
      return null;
    }
  }

  /**
   * Get the value of the xsi:type attribute, for varying values of
   * the xsi namespace.  In the absence of an xsi:type attribute,
   * determine whether the element QName implies a type, e.g. if it
   * has the SOAP-ENC namespaceURI.  For now, this is limited to
   * checking for SOAP-ENC:Array.
   */
  public static QName getTypeQName(Element el)
    throws IllegalArgumentException
  {
    // Try 2001
    QName typeQName = getAttributeValue(el, Constants.NS_URI_2001_SCHEMA_XSI,
                                        Constants.ATTR_TYPE, null, false);

    if (typeQName != null)
      return typeQName;

    // Try 2000
    typeQName = getAttributeValue(el, Constants.NS_URI_2000_SCHEMA_XSI,
                                  Constants.ATTR_TYPE, null, false);

    if (typeQName != null)
      return typeQName;

    // Try 1999
    typeQName = getAttributeValue(el, Constants.NS_URI_1999_SCHEMA_XSI,
                                  Constants.ATTR_TYPE, null, false);

    if (typeQName != null)
      return typeQName;

    // Check for SOAP-ENC namespace for element name
    String nsURI = el.getNamespaceURI();
    if (nsURI != null && nsURI.equals(Constants.NS_URI_SOAP_ENC)) {
      // For now, just check for Array
      String localName = el.getLocalName();
      if (localName.equals("Array"))
        typeQName = new QName(nsURI, localName);
    }

    return typeQName;
  }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.