| SimpleDeserializer.java |
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 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 "Axis" 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 apache@apache.org.
*
* 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. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.jboss.axis.encoding.ser;
import org.jboss.axis.description.TypeDesc;
import org.jboss.axis.encoding.DeserializationContext;
import org.jboss.axis.encoding.Deserializer;
import org.jboss.axis.encoding.DeserializerImpl;
import org.jboss.axis.encoding.SimpleType;
import org.jboss.axis.encoding.TypeMapping;
import org.jboss.axis.message.SOAPHandler;
import org.jboss.axis.utils.BeanPropertyDescriptor;
import org.jboss.axis.utils.BeanUtils;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import java.io.CharArrayWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* A deserializer for any simple type with a (String) constructor. Note:
* this class is designed so that subclasses need only override the makeValue
* method in order to construct objects of their own type.
*
* @author Glen Daniels (gdaniels@macromedia.com)
* @author Sam Ruby (rubys@us.ibm.com)
* Modified for JAX-RPC @author Rich Scheuerle (scheu@us.ibm.com)
*/
public class SimpleDeserializer extends DeserializerImpl
{
private static Logger log = Logger.getLogger(SimpleDeserializer.class.getName());
//StringBuffer val = new StringBuffer(); -- FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11945
private final CharArrayWriter val = new CharArrayWriter();
private Constructor constructor = null;
private Map propertyMap = null;
private HashMap attributeMap = null;
public QName xmlType;
public Class javaType;
private TypeDesc typeDesc = null;
protected SimpleDeserializer cacheStringDSer = null;
protected QName cacheXMLType = null;
/**
* The Deserializer is constructed with the xmlType and
* javaType (which could be a java primitive like int.class)
*/
public SimpleDeserializer(Class javaType, QName xmlType)
{
this.xmlType = xmlType;
this.javaType = javaType;
init();
}
public SimpleDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc)
{
this.xmlType = xmlType;
this.javaType = javaType;
this.typeDesc = typeDesc;
init();
}
/**
* Initialize the typeDesc, property descriptors and propertyMap.
*/
private void init()
{
// The typeDesc and map array are only necessary
// if this class extends SimpleType.
if (SimpleType.class.isAssignableFrom(javaType))
{
// Set the typeDesc if not already set
if (typeDesc == null)
{
typeDesc = TypeDesc.getTypeDescForClass(javaType);
}
// Get the cached propertyDescriptor from the type or
// generate a fresh one.
if (typeDesc != null)
{
propertyMap = typeDesc.getPropertyDescriptorMap();
}
else
{
BeanPropertyDescriptor[] pd = BeanUtils.getPd(javaType, null);
propertyMap = new HashMap();
for (int i = 0; i < pd.length; i++)
{
BeanPropertyDescriptor descriptor = pd[i];
propertyMap.put(descriptor.getName(), descriptor);
}
}
}
}
/**
* Reset deserializer for re-use
*/
public void reset()
{
//val.setLength(0); // Reset string buffer back to zero -- FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11945
val.reset();
attributeMap = null; // Remove attribute map
isNil = false; // Don't know if nil
isEnded = false; // Indicate the end of element not yet called
}
/**
* Remove the Value Targets of the Deserializer.
* Simple deserializers may be re-used, so don't
* nullify the vector.
*/
public void removeValueTargets()
{
if (targets != null)
{
targets.clear();
// targets = null;
}
}
/**
* The Factory calls setConstructor.
*/
public void setConstructor(Constructor c)
{
constructor = c;
}
/**
* There should not be nested elements, so thow and exception if this occurs.
*/
public SOAPHandler onStartChild(String namespace,
String localName,
String prefix,
Attributes attributes,
DeserializationContext context)
throws SAXException
{
throw new SAXException(Messages.getMessage("cantHandle00", "SimpleDeserializer"));
}
/**
* Append any characters received to the value. This method is defined
* by Deserializer.
*/
public void characters(char[] chars, int start, int end)
throws SAXException
{
//val.append(chars, start, end); -- FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11945
val.write(chars, start, end);
}
/**
* Append any characters to the value. This method is defined by
* Deserializer.
*/
public void onEndElement(String namespace, String localName,
DeserializationContext context)
throws SAXException
{
//if (isNil || val == null) { -- FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11945
if (isNil)
{
value = null;
return;
}
try
{
value = makeValue(val.toString());
}
catch (InvocationTargetException ite)
{
Throwable realException = ite.getTargetException();
if (realException instanceof Exception)
throw new SAXException((Exception)realException);
else
throw new SAXException(ite.getMessage());
}
catch (Exception e)
{
throw new SAXException(e);
}
// If this is a SimpleType, set attributes we have stashed away
setSimpleTypeAttributes();
}
/**
* Convert the string that has been accumulated into an Object. Subclasses
* may override this. Note that if the javaType is a primitive, the returned
* object is a wrapper class.
*
* @param source the serialized value to be deserialized
* @throws Exception any exception thrown by this method will be wrapped
*/
public Object makeValue(String source) throws Exception
{
log.debug("Making value [" + source + "] for javaType: " + javaType);
// If the javaType is a boolean, except a number of different sources
if (javaType == boolean.class || javaType == Boolean.class)
{
// This is a pretty lame test, but it is what the previous code did.
switch (source.charAt(0))
{
case '0':
case 'f':
case 'F':
return Boolean.FALSE;
case '1':
case 't':
case 'T':
return Boolean.TRUE;
default:
throw new NumberFormatException(Messages.getMessage("badBool00"));
}
}
// If expecting a Float or a Double, need to accept some special cases.
if (javaType == float.class ||
javaType == java.lang.Float.class)
{
if (source.equals("NaN"))
{
return new Float(Float.NaN);
}
else if (source.equals("INF"))
{
return new Float(Float.POSITIVE_INFINITY);
}
else if (source.equals("-INF"))
{
return new Float(Float.NEGATIVE_INFINITY);
}
}
if (javaType == double.class ||
javaType == java.lang.Double.class)
{
if (source.equals("NaN"))
{
return new Double(Double.NaN);
}
else if (source.equals("INF"))
{
return new Double(Double.POSITIVE_INFINITY);
}
else if (source.equals("-INF"))
{
return new Double(Double.NEGATIVE_INFINITY);
}
}
return constructor.newInstance(new Object[]{source});
}
/**
* Set the bean properties that correspond to element attributes.
* <p/>
* This method is invoked after startElement when the element requires
* deserialization (i.e. the element is not an href and the value is not nil.)
*
* @param namespace is the namespace of the element
* @param localName is the name of the element
* @param prefix is the prefix of the element
* @param attributes are the attributes on the element...used to get the type
* @param context is the DeserializationContext
*/
public void onStartElement(String namespace, String localName,
String prefix, Attributes attributes,
DeserializationContext context)
throws SAXException
{
// If we have no metadata, we have no attributes. Q.E.D.
if (typeDesc == null)
return;
// loop through the attributes and set bean properties that
// correspond to attributes
for (int i = 0; i < attributes.getLength(); i++)
{
QName attrQName = new QName(attributes.getURI(i),
attributes.getLocalName(i));
String fieldName = typeDesc.getFieldNameForAttribute(attrQName);
if (fieldName == null)
continue;
// look for the attribute property
BeanPropertyDescriptor bpd =
(BeanPropertyDescriptor)propertyMap.get(fieldName);
if (bpd != null)
{
if (!bpd.isWriteable() || bpd.isIndexed()) continue;
// determine the QName for this child element
TypeMapping tm = context.getTypeMapping();
Class type = bpd.getType();
QName qn = tm.getTypeQName(type);
if (qn == null)
throw new SAXException(Messages.getMessage("unregistered00", type.toString()));
// get the deserializer
Deserializer dSer = context.getDeserializerForType(qn);
if (dSer == null)
throw new SAXException(Messages.getMessage("noDeser00", type.toString()));
if (!(dSer instanceof SimpleDeserializer))
throw new SAXException(Messages.getMessage("AttrNotSimpleType00",
bpd.getName(),
type.toString()));
// Success! Create an object from the string and save
// it in our attribute map for later.
if (attributeMap == null)
{
attributeMap = new HashMap();
}
try
{
Object val = ((SimpleDeserializer)dSer).
makeValue(attributes.getValue(i));
attributeMap.put(fieldName, val);
}
catch (Exception e)
{
throw new SAXException(e);
}
} // if
} // attribute loop
} // onStartElement
/**
* Process any attributes we may have encountered (in onStartElement)
*/
private void setSimpleTypeAttributes() throws SAXException
{
// if this isn't a simpleType bean, wont have attributes
if (!SimpleType.class.isAssignableFrom(javaType) ||
attributeMap == null)
return;
// loop through map
Set entries = attributeMap.entrySet();
for (Iterator iterator = entries.iterator(); iterator.hasNext();)
{
Map.Entry entry = (Map.Entry)iterator.next();
String name = (String)entry.getKey();
Object val = entry.getValue();
BeanPropertyDescriptor bpd =
(BeanPropertyDescriptor)propertyMap.get(name);
if (!bpd.isWriteable() || bpd.isIndexed()) continue;
try
{
bpd.set(value, val);
}
catch (Exception e)
{
throw new SAXException(e);
}
}
}
}
| SimpleDeserializer.java |