| /* |
| * Copyright 2001-2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.axis.encoding.ser; |
| |
| import org.apache.axis.description.TypeDesc; |
| import org.apache.axis.encoding.DeserializationContext; |
| import org.apache.axis.encoding.Deserializer; |
| import org.apache.axis.encoding.DeserializerImpl; |
| import org.apache.axis.encoding.SimpleType; |
| import org.apache.axis.encoding.TypeMapping; |
| import org.apache.axis.message.SOAPHandler; |
| import org.apache.axis.utils.BeanPropertyDescriptor; |
| import org.apache.axis.utils.BeanUtils; |
| import org.apache.axis.utils.Messages; |
| 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@apache.org) |
| * @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 final Class[] STRING_STRING_CLASS = |
| new Class [] {String.class, String.class}; |
| |
| public static final Class[] STRING_CLASS = |
| new Class [] {String.class}; |
| |
| 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 DeserializationContext context = 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.reset(); |
| attributeMap = null; // Remove attribute map |
| isNil = false; // Don't know if nil |
| isEnded = false; // Indicate the end of element not yet called |
| } |
| |
| /** |
| * 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.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) { |
| 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 |
| { |
| if (javaType == java.lang.String.class) { |
| return source; |
| } |
| |
| // Trim whitespace if non-String |
| source = source.trim(); |
| |
| if (source.length() == 0 && typeDesc == null) { |
| return null; |
| } |
| |
| // if constructor is set skip all basic java type checks |
| if (this.constructor == null) { |
| Object value = makeBasicValue(source); |
| if (value != null) { |
| return value; |
| } |
| } |
| |
| Object [] args = null; |
| |
| boolean isQNameSubclass = QName.class.isAssignableFrom(javaType); |
| |
| if (isQNameSubclass) { |
| int colon = source.lastIndexOf(":"); |
| String namespace = colon < 0 ? "" : |
| context.getNamespaceURI(source.substring(0, colon)); |
| String localPart = colon < 0 ? source : source.substring(colon + 1); |
| args = new Object [] {namespace, localPart}; |
| } |
| |
| if (constructor == null) { |
| try { |
| if (isQNameSubclass) { |
| constructor = |
| javaType.getDeclaredConstructor(STRING_STRING_CLASS); |
| } else { |
| constructor = |
| javaType.getDeclaredConstructor(STRING_CLASS); |
| } |
| } catch (Exception e) { |
| return null; |
| } |
| } |
| |
| if(constructor.getParameterTypes().length==0){ |
| try { |
| Object obj = constructor.newInstance(new Object[]{}); |
| obj.getClass().getMethod("set_value", new Class[]{String.class}) |
| .invoke(obj, new Object[]{source}); |
| return obj; |
| } catch (Exception e){ |
| //Ignore exception |
| } |
| } |
| if (args == null) { |
| args = new Object[]{source}; |
| } |
| return constructor.newInstance(args); |
| } |
| |
| private Object makeBasicValue(String source) throws Exception { |
| // 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); |
| } else { |
| return new Float(source); |
| } |
| } |
| |
| 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); |
| } else { |
| return new Double(source); |
| } |
| } |
| |
| if (javaType == int.class || |
| javaType == java.lang.Integer.class) { |
| return new Integer(source); |
| } |
| |
| if (javaType == short.class || |
| javaType == java.lang.Short.class) { |
| return new Short(source); |
| } |
| |
| if (javaType == long.class || |
| javaType == java.lang.Long.class) { |
| return new Long(source); |
| } |
| |
| if (javaType == byte.class || |
| javaType == java.lang.Byte.class) { |
| return new Byte(source); |
| } |
| |
| if (javaType == org.apache.axis.types.URI.class) { |
| return new org.apache.axis.types.URI(source); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Set the bean properties that correspond to element attributes. |
| * |
| * 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 |
| { |
| |
| this.context = context; |
| |
| // 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 = attributes.getLocalName(i); |
| |
| if(typeDesc != null) { |
| fieldName = typeDesc.getFieldNameForAttribute(attrQName); |
| if (fieldName == null) |
| continue; |
| } |
| |
| if (propertyMap == 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 (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); |
| } |
| } |
| } |
| |
| } |