Source code: org/apache/axis/encoding/ser/SimpleDeserializer.java
1 /*
2 * Copyright 2001-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.axis.encoding.ser;
18
19 import org.apache.axis.description.TypeDesc;
20 import org.apache.axis.encoding.DeserializationContext;
21 import org.apache.axis.encoding.Deserializer;
22 import org.apache.axis.encoding.DeserializerImpl;
23 import org.apache.axis.encoding.SimpleType;
24 import org.apache.axis.encoding.TypeMapping;
25 import org.apache.axis.message.SOAPHandler;
26 import org.apache.axis.utils.BeanPropertyDescriptor;
27 import org.apache.axis.utils.BeanUtils;
28 import org.apache.axis.utils.Messages;
29 import org.xml.sax.Attributes;
30 import org.xml.sax.SAXException;
31
32 import javax.xml.namespace.QName;
33 import java.io.CharArrayWriter;
34 import java.lang.reflect.Constructor;
35 import java.lang.reflect.InvocationTargetException;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.Map;
39 import java.util.Set;
40
41 /**
42 * A deserializer for any simple type with a (String) constructor. Note:
43 * this class is designed so that subclasses need only override the makeValue
44 * method in order to construct objects of their own type.
45 *
46 * @author Glen Daniels (gdaniels@apache.org)
47 * @author Sam Ruby (rubys@us.ibm.com)
48 * Modified for JAX-RPC @author Rich Scheuerle (scheu@us.ibm.com)
49 */
50 public class SimpleDeserializer extends DeserializerImpl {
51
52 private static final Class[] STRING_STRING_CLASS =
53 new Class [] {String.class, String.class};
54
55 public static final Class[] STRING_CLASS =
56 new Class [] {String.class};
57
58 private final CharArrayWriter val = new CharArrayWriter();
59 private Constructor constructor = null;
60 private Map propertyMap = null;
61 private HashMap attributeMap = null;
62
63 public QName xmlType;
64 public Class javaType;
65
66 private TypeDesc typeDesc = null;
67
68 protected DeserializationContext context = null;
69 protected SimpleDeserializer cacheStringDSer = null;
70 protected QName cacheXMLType = null;
71 /**
72 * The Deserializer is constructed with the xmlType and
73 * javaType (which could be a java primitive like int.class)
74 */
75 public SimpleDeserializer(Class javaType, QName xmlType) {
76 this.xmlType = xmlType;
77 this.javaType = javaType;
78
79 init();
80 }
81
82 public SimpleDeserializer(Class javaType, QName xmlType, TypeDesc typeDesc) {
83 this.xmlType = xmlType;
84 this.javaType = javaType;
85 this.typeDesc = typeDesc;
86
87 init();
88 }
89
90 /**
91 * Initialize the typeDesc, property descriptors and propertyMap.
92 */
93 private void init() {
94 // The typeDesc and map array are only necessary
95 // if this class extends SimpleType.
96 if (SimpleType.class.isAssignableFrom(javaType)) {
97 // Set the typeDesc if not already set
98 if (typeDesc == null) {
99 typeDesc = TypeDesc.getTypeDescForClass(javaType);
100 }
101 }
102
103 // Get the cached propertyDescriptor from the type or
104 // generate a fresh one.
105 if (typeDesc != null) {
106 propertyMap = typeDesc.getPropertyDescriptorMap();
107 } else {
108 BeanPropertyDescriptor[] pd = BeanUtils.getPd(javaType, null);
109 propertyMap = new HashMap();
110 for (int i = 0; i < pd.length; i++) {
111 BeanPropertyDescriptor descriptor = pd[i];
112 propertyMap.put(descriptor.getName(), descriptor);
113 }
114 }
115 }
116
117 /**
118 * Reset deserializer for re-use
119 */
120 public void reset() {
121 val.reset();
122 attributeMap = null; // Remove attribute map
123 isNil = false; // Don't know if nil
124 isEnded = false; // Indicate the end of element not yet called
125 }
126
127 /**
128 * The Factory calls setConstructor.
129 */
130 public void setConstructor(Constructor c)
131 {
132 constructor = c;
133 }
134
135 /**
136 * There should not be nested elements, so thow and exception if this occurs.
137 */
138 public SOAPHandler onStartChild(String namespace,
139 String localName,
140 String prefix,
141 Attributes attributes,
142 DeserializationContext context)
143 throws SAXException
144 {
145 throw new SAXException(
146 Messages.getMessage("cantHandle00", "SimpleDeserializer"));
147 }
148
149 /**
150 * Append any characters received to the value. This method is defined
151 * by Deserializer.
152 */
153 public void characters(char [] chars, int start, int end)
154 throws SAXException
155 {
156 val.write(chars,start,end);
157 }
158
159 /**
160 * Append any characters to the value. This method is defined by
161 * Deserializer.
162 */
163 public void onEndElement(String namespace, String localName,
164 DeserializationContext context)
165 throws SAXException
166 {
167 if (isNil) {
168 value = null;
169 return;
170 }
171 try {
172 value = makeValue(val.toString());
173 } catch (InvocationTargetException ite) {
174 Throwable realException = ite.getTargetException();
175 if (realException instanceof Exception)
176 throw new SAXException((Exception)realException);
177 else
178 throw new SAXException(ite.getMessage());
179 } catch (Exception e) {
180 throw new SAXException(e);
181 }
182
183 // If this is a SimpleType, set attributes we have stashed away
184 setSimpleTypeAttributes();
185 }
186
187 /**
188 * Convert the string that has been accumulated into an Object. Subclasses
189 * may override this. Note that if the javaType is a primitive, the returned
190 * object is a wrapper class.
191 * @param source the serialized value to be deserialized
192 * @throws Exception any exception thrown by this method will be wrapped
193 */
194 public Object makeValue(String source) throws Exception
195 {
196 if (javaType == java.lang.String.class) {
197 return source;
198 }
199
200 // Trim whitespace if non-String
201 source = source.trim();
202
203 if (source.length() == 0 && typeDesc == null) {
204 return null;
205 }
206
207 // if constructor is set skip all basic java type checks
208 if (this.constructor == null) {
209 Object value = makeBasicValue(source);
210 if (value != null) {
211 return value;
212 }
213 }
214
215 Object [] args = null;
216
217 boolean isQNameSubclass = QName.class.isAssignableFrom(javaType);
218
219 if (isQNameSubclass) {
220 int colon = source.lastIndexOf(":");
221 String namespace = colon < 0 ? "" :
222 context.getNamespaceURI(source.substring(0, colon));
223 String localPart = colon < 0 ? source : source.substring(colon + 1);
224 args = new Object [] {namespace, localPart};
225 }
226
227 if (constructor == null) {
228 try {
229 if (isQNameSubclass) {
230 constructor =
231 javaType.getDeclaredConstructor(STRING_STRING_CLASS);
232 } else {
233 constructor =
234 javaType.getDeclaredConstructor(STRING_CLASS);
235 }
236 } catch (Exception e) {
237 return null;
238 }
239 }
240
241 if(constructor.getParameterTypes().length==0){
242 try {
243 Object obj = constructor.newInstance(new Object[]{});
244 obj.getClass().getMethod("set_value", new Class[]{String.class})
245 .invoke(obj, new Object[]{source});
246 return obj;
247 } catch (Exception e){
248 //Ignore exception
249 }
250 }
251 if (args == null) {
252 args = new Object[]{source};
253 }
254 return constructor.newInstance(args);
255 }
256
257 private Object makeBasicValue(String source) throws Exception {
258 // If the javaType is a boolean, except a number of different sources
259 if (javaType == boolean.class ||
260 javaType == Boolean.class) {
261 // This is a pretty lame test, but it is what the previous code did.
262 switch (source.charAt(0)) {
263 case '0': case 'f': case 'F':
264 return Boolean.FALSE;
265
266 case '1': case 't': case 'T':
267 return Boolean.TRUE;
268
269 default:
270 throw new NumberFormatException(
271 Messages.getMessage("badBool00"));
272 }
273
274 }
275
276 // If expecting a Float or a Double, need to accept some special cases.
277 if (javaType == float.class ||
278 javaType == java.lang.Float.class) {
279 if (source.equals("NaN")) {
280 return new Float(Float.NaN);
281 } else if (source.equals("INF")) {
282 return new Float(Float.POSITIVE_INFINITY);
283 } else if (source.equals("-INF")) {
284 return new Float(Float.NEGATIVE_INFINITY);
285 } else {
286 return new Float(source);
287 }
288 }
289
290 if (javaType == double.class ||
291 javaType == java.lang.Double.class) {
292 if (source.equals("NaN")) {
293 return new Double(Double.NaN);
294 } else if (source.equals("INF")) {
295 return new Double(Double.POSITIVE_INFINITY);
296 } else if (source.equals("-INF")) {
297 return new Double(Double.NEGATIVE_INFINITY);
298 } else {
299 return new Double(source);
300 }
301 }
302
303 if (javaType == int.class ||
304 javaType == java.lang.Integer.class) {
305 return new Integer(source);
306 }
307
308 if (javaType == short.class ||
309 javaType == java.lang.Short.class) {
310 return new Short(source);
311 }
312
313 if (javaType == long.class ||
314 javaType == java.lang.Long.class) {
315 return new Long(source);
316 }
317
318 if (javaType == byte.class ||
319 javaType == java.lang.Byte.class) {
320 return new Byte(source);
321 }
322
323 if (javaType == org.apache.axis.types.URI.class) {
324 return new org.apache.axis.types.URI(source);
325 }
326
327 return null;
328 }
329
330 /**
331 * Set the bean properties that correspond to element attributes.
332 *
333 * This method is invoked after startElement when the element requires
334 * deserialization (i.e. the element is not an href and the value is not nil.)
335 * @param namespace is the namespace of the element
336 * @param localName is the name of the element
337 * @param prefix is the prefix of the element
338 * @param attributes are the attributes on the element...used to get the type
339 * @param context is the DeserializationContext
340 */
341 public void onStartElement(String namespace, String localName,
342 String prefix, Attributes attributes,
343 DeserializationContext context)
344 throws SAXException
345 {
346
347 this.context = context;
348
349 // loop through the attributes and set bean properties that
350 // correspond to attributes
351 for (int i=0; i < attributes.getLength(); i++) {
352 QName attrQName = new QName(attributes.getURI(i),
353 attributes.getLocalName(i));
354
355 String fieldName = attributes.getLocalName(i);
356
357 if(typeDesc != null) {
358 fieldName = typeDesc.getFieldNameForAttribute(attrQName);
359 if (fieldName == null)
360 continue;
361 }
362
363 if (propertyMap == null)
364 continue;
365
366 // look for the attribute property
367 BeanPropertyDescriptor bpd =
368 (BeanPropertyDescriptor) propertyMap.get(fieldName);
369 if (bpd != null) {
370 if (!bpd.isWriteable() || bpd.isIndexed() ) continue ;
371
372 // determine the QName for this child element
373 TypeMapping tm = context.getTypeMapping();
374 Class type = bpd.getType();
375 QName qn = tm.getTypeQName(type);
376 if (qn == null)
377 throw new SAXException(
378 Messages.getMessage("unregistered00", type.toString()));
379
380 // get the deserializer
381 Deserializer dSer = context.getDeserializerForType(qn);
382 if (dSer == null)
383 throw new SAXException(
384 Messages.getMessage("noDeser00", type.toString()));
385 if (! (dSer instanceof SimpleDeserializer))
386 throw new SAXException(
387 Messages.getMessage("AttrNotSimpleType00",
388 bpd.getName(),
389 type.toString()));
390
391 // Success! Create an object from the string and save
392 // it in our attribute map for later.
393 if (attributeMap == null) {
394 attributeMap = new HashMap();
395 }
396 try {
397 Object val = ((SimpleDeserializer)dSer).
398 makeValue(attributes.getValue(i));
399 attributeMap.put(fieldName, val);
400 } catch (Exception e) {
401 throw new SAXException(e);
402 }
403 } // if
404 } // attribute loop
405 } // onStartElement
406
407 /**
408 * Process any attributes we may have encountered (in onStartElement)
409 */
410 private void setSimpleTypeAttributes() throws SAXException {
411 if (attributeMap == null)
412 return;
413
414 // loop through map
415 Set entries = attributeMap.entrySet();
416 for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
417 Map.Entry entry = (Map.Entry) iterator.next();
418 String name = (String) entry.getKey();
419 Object val = entry.getValue();
420
421 BeanPropertyDescriptor bpd =
422 (BeanPropertyDescriptor) propertyMap.get(name);
423 if (!bpd.isWriteable() || bpd.isIndexed()) continue;
424 try {
425 bpd.set(value, val );
426 } catch (Exception e) {
427 throw new SAXException(e);
428 }
429 }
430 }
431
432 }