KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > axis > encoding > ser > BeanDeserializer


1 /*
2  * Copyright 2001-2002,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.Constants;
20 import org.apache.axis.components.logger.LogFactory;
21 import org.apache.axis.description.ElementDesc;
22 import org.apache.axis.description.FieldDesc;
23 import org.apache.axis.description.TypeDesc;
24 import org.apache.axis.encoding.ConstructorTarget;
25 import org.apache.axis.encoding.DeserializationContext;
26 import org.apache.axis.encoding.Deserializer;
27 import org.apache.axis.encoding.DeserializerImpl;
28 import org.apache.axis.encoding.Target;
29 import org.apache.axis.encoding.TypeMapping;
30 import org.apache.axis.message.MessageElement;
31 import org.apache.axis.message.SOAPHandler;
32 import org.apache.axis.soap.SOAPConstants;
33 import org.apache.axis.utils.BeanPropertyDescriptor;
34 import org.apache.axis.utils.Messages;
35 import org.apache.commons.logging.Log;
36 import org.xml.sax.Attributes JavaDoc;
37 import org.xml.sax.SAXException JavaDoc;
38
39 import javax.xml.namespace.QName JavaDoc;
40 import java.io.CharArrayWriter JavaDoc;
41 import java.io.Serializable JavaDoc;
42 import java.lang.reflect.Constructor JavaDoc;
43 import java.util.Map JavaDoc;
44
45 /**
46  * General purpose deserializer for an arbitrary java bean.
47  *
48  * @author Sam Ruby <rubys@us.ibm.com>
49  * @author Rich Scheuerle <scheu@us.ibm.com>
50  * @author Tom Jordahl <tomj@macromedia.com>
51  */

52 public class BeanDeserializer extends DeserializerImpl implements Serializable JavaDoc
53 {
54     protected static Log log =
55         LogFactory.getLog(BeanDeserializer.class.getName());
56
57     private final CharArrayWriter JavaDoc val = new CharArrayWriter JavaDoc();
58
59     QName JavaDoc xmlType;
60     Class JavaDoc javaType;
61     protected Map JavaDoc propertyMap = null;
62     protected QName JavaDoc prevQName;
63
64     /**
65      * Constructor if no default constructor
66      */

67     protected Constructor JavaDoc constructorToUse = null;
68
69     /**
70      * Constructor Target object to use (if constructorToUse != null)
71      */

72     protected Target constructorTarget = null;
73     
74     /** Type metadata about this class for XML deserialization */
75     protected TypeDesc typeDesc = null;
76
77     // This counter is updated to deal with deserialize collection properties
78
protected int collectionIndex = -1;
79
80     protected SimpleDeserializer cacheStringDSer = null;
81     protected QName JavaDoc cacheXMLType = null;
82
83     // Construct BeanSerializer for the indicated class/qname
84
public BeanDeserializer(Class JavaDoc javaType, QName JavaDoc xmlType) {
85         this(javaType, xmlType, TypeDesc.getTypeDescForClass(javaType));
86     }
87
88     // Construct BeanDeserializer for the indicated class/qname and meta Data
89
public BeanDeserializer(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc ) {
90         this(javaType, xmlType, typeDesc,
91              BeanDeserializerFactory.getProperties(javaType, typeDesc));
92     }
93
94     // Construct BeanDeserializer for the indicated class/qname and meta Data
95
public BeanDeserializer(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc,
96                             Map JavaDoc propertyMap ) {
97         this.xmlType = xmlType;
98         this.javaType = javaType;
99         this.typeDesc = typeDesc;
100         this.propertyMap = propertyMap;
101
102         // create a value
103
try {
104             value=javaType.newInstance();
105         } catch (Exception JavaDoc e) {
106             // Don't process the exception at this point.
107
// This is defered until the call to startElement
108
// which will throw the exception.
109
}
110     }
111
112     /**
113      * startElement
114      *
115      * The ONLY reason that this method is overridden is so that
116      * the object value can be set or a reasonable exception is thrown
117      * indicating that the object cannot be created. This is done
118      * at this point so that it occurs BEFORE href/id processing.
119      * @param namespace is the namespace of the element
120      * @param localName is the name of the element
121      * @param prefix is the prefix of the element
122      * @param attributes are the attributes on the element...used to get the
123      * type
124      * @param context is the DeserializationContext
125      */

126     public void startElement(String JavaDoc namespace, String JavaDoc localName,
127                              String JavaDoc prefix, Attributes JavaDoc attributes,
128                              DeserializationContext context)
129         throws SAXException JavaDoc
130     {
131         // Create the bean object if it was not already
132
// created in the constructor.
133
if (value == null) {
134             try {
135                 value=javaType.newInstance();
136             } catch (Exception JavaDoc e) {
137                 // Use first found constructor.
138
// Note : the right way is to use XML mapping information
139
// for example JSR 109's constructor-parameter-order
140
Constructor JavaDoc[] constructors = javaType.getConstructors();
141                 if (constructors.length > 0) {
142                     constructorToUse = constructors[0];
143                 }
144
145                 // Failed to create an object if no constructor
146
if (constructorToUse == null) {
147                     throw new SAXException JavaDoc(Messages.getMessage("cantCreateBean00",
148                                                             javaType.getName(),
149                                                             e.toString()));
150                 }
151             }
152         }
153         // Invoke super.startElement to do the href/id processing.
154
super.startElement(namespace, localName,
155                            prefix, attributes, context);
156     }
157
158     /**
159      * Deserializer interface called on each child element encountered in
160      * the XML stream.
161      * @param namespace is the namespace of the child element
162      * @param localName is the local name of the child element
163      * @param prefix is the prefix used on the name of the child element
164      * @param attributes are the attributes of the child element
165      * @param context is the deserialization context.
166      * @return is a Deserializer to use to deserialize a child (must be
167      * a derived class of SOAPHandler) or null if no deserialization should
168      * be performed.
169      */

170     public SOAPHandler onStartChild(String JavaDoc namespace,
171                                     String JavaDoc localName,
172                                     String JavaDoc prefix,
173                                     Attributes JavaDoc attributes,
174                                     DeserializationContext context)
175         throws SAXException JavaDoc
176     {
177         handleMixedContent();
178
179         BeanPropertyDescriptor propDesc = null;
180         FieldDesc fieldDesc = null;
181
182         SOAPConstants soapConstants = context.getSOAPConstants();
183         String JavaDoc encodingStyle = context.getEncodingStyle();
184         boolean isEncoded = Constants.isSOAP_ENC(encodingStyle);
185
186         QName JavaDoc elemQName = new QName JavaDoc(namespace, localName);
187         // The collectionIndex needs to be reset for Beans with multiple arrays
188
if ((prevQName == null) || (!prevQName.equals(elemQName))) {
189             collectionIndex = -1;
190         }
191
192         boolean isArray = false;
193         QName JavaDoc itemQName = null;
194         if (typeDesc != null) {
195             // Lookup the name appropriately (assuming an unqualified
196
// name for SOAP encoding, using the namespace otherwise)
197
String JavaDoc fieldName = typeDesc.getFieldNameForElement(elemQName,
198                                                                isEncoded);
199             propDesc = (BeanPropertyDescriptor)propertyMap.get(fieldName);
200             fieldDesc = typeDesc.getFieldByName(fieldName);
201
202             if (fieldDesc != null) {
203                ElementDesc element = (ElementDesc)fieldDesc;
204                isArray = element.isMaxOccursUnbounded();
205                itemQName = element.getItemQName();
206            }
207         }
208
209         if (propDesc == null) {
210             // look for a field by this name.
211
propDesc = (BeanPropertyDescriptor) propertyMap.get(localName);
212         }
213
214         // try and see if this is an xsd:any namespace="##any" element before
215
// reporting a problem
216
if (propDesc == null ||
217                 (((prevQName != null) && prevQName.equals(elemQName) &&
218                         !(propDesc.isIndexed()||isArray)
219                         && getAnyPropertyDesc() != null ))) {
220             // try to put unknown elements into a SOAPElement property, if
221
// appropriate
222
prevQName = elemQName;
223             propDesc = getAnyPropertyDesc();
224             if (propDesc != null) {
225                 try {
226                     MessageElement [] curElements = (MessageElement[])propDesc.get(value);
227                     int length = 0;
228                     if (curElements != null) {
229                         length = curElements.length;
230                     }
231                     MessageElement [] newElements = new MessageElement[length + 1];
232                     if (curElements != null) {
233                         System.arraycopy(curElements, 0,
234                                          newElements, 0, length);
235                     }
236                     MessageElement thisEl = context.getCurElement();
237
238                     newElements[length] = thisEl;
239                     propDesc.set(value, newElements);
240                     // if this is the first pass through the MessageContexts
241
// make sure that the correct any element is set,
242
// that is the child of the current MessageElement, however
243
// on the first pass this child has not been set yet, so
244
// defer it to the child SOAPHandler
245
if (!localName.equals(thisEl.getName())) {
246                         return new SOAPHandler(newElements, length);
247                     }
248                     return new SOAPHandler();
249                 } catch (Exception JavaDoc e) {
250                     throw new SAXException JavaDoc(e);
251                 }
252             }
253         }
254
255
256         if (propDesc == null) {
257             // No such field
258
throw new SAXException JavaDoc(
259                     Messages.getMessage("badElem00", javaType.getName(),
260                                          localName));
261         }
262
263         prevQName = elemQName;
264         // Get the child's xsi:type if available
265
QName JavaDoc childXMLType = context.getTypeFromAttributes(namespace,
266                                                             localName,
267                                                             attributes);
268
269         String JavaDoc href = attributes.getValue(soapConstants.getAttrHref());
270         Class JavaDoc fieldType = propDesc.getType();
271
272         // If no xsi:type or href, check the meta-data for the field
273
if (childXMLType == null && fieldDesc != null && href == null) {
274             childXMLType = fieldDesc.getXmlType();
275             if (itemQName != null) {
276                 // This is actually a wrapped literal array and should be
277
// deserialized with the ArrayDeserializer
278
childXMLType = Constants.SOAP_ARRAY;
279                 fieldType = propDesc.getActualType();
280             } else {
281                 childXMLType = fieldDesc.getXmlType();
282             }
283         }
284         
285         // Get Deserializer for child, default to using DeserializerImpl
286
Deserializer dSer = getDeserializer(childXMLType,
287                                             fieldType,
288                                             href,
289                                             context);
290
291         // It is an error if the dSer is not found - the only case where we
292
// wouldn't have a deserializer at this point is when we're trying
293
// to deserialize something we have no clue about (no good xsi:type,
294
// no good metadata).
295
if (dSer == null) {
296             dSer = context.getDeserializerForClass(propDesc.getType());
297         }
298
299         // Fastpath nil checks...
300
if (context.isNil(attributes)) {
301             if (propDesc != null && (propDesc.isIndexed()||isArray)) {
302                 if (!((dSer != null) && (dSer instanceof ArrayDeserializer))) {
303                     collectionIndex++;
304                     dSer.registerValueTarget(new BeanPropertyTarget(value,
305                             propDesc, collectionIndex));
306                     addChildDeserializer(dSer);
307                     return (SOAPHandler)dSer;
308                 }
309             }
310             return null;
311         }
312           
313         if (dSer == null) {
314             throw new SAXException JavaDoc(Messages.getMessage("noDeser00",
315                                                        childXMLType.toString()));
316         }
317
318         if (constructorToUse != null) {
319             if (constructorTarget == null) {
320                 constructorTarget = new ConstructorTarget(constructorToUse, this);
321             }
322             dSer.registerValueTarget(constructorTarget);
323         } else if (propDesc.isWriteable()) {
324             // If this is an indexed property, and the deserializer we found
325
// was NOT the ArrayDeserializer, this is a non-SOAP array:
326
// <bean>
327
// <field>value1</field>
328
// <field>value2</field>
329
// ...
330
// In this case, we want to use the collectionIndex and make sure
331
// the deserialized value for the child element goes into the
332
// right place in the collection.
333

334             // Register value target
335
if ((itemQName != null || propDesc.isIndexed() || isArray) && !(dSer instanceof ArrayDeserializer)) {
336                 collectionIndex++;
337                 dSer.registerValueTarget(new BeanPropertyTarget(value,
338                         propDesc, collectionIndex));
339             } else {
340                 // If we're here, the element maps to a single field value,
341
// whether that be a "basic" type or an array, so use the
342
// normal (non-indexed) BeanPropertyTarget form.
343
collectionIndex = -1;
344                 dSer.registerValueTarget(new BeanPropertyTarget(value,
345                                                                 propDesc));
346             }
347         }
348         
349         // Let the framework know that we need this deserializer to complete
350
// for the bean to complete.
351
addChildDeserializer(dSer);
352         
353         return (SOAPHandler)dSer;
354     }
355
356     /**
357      * Get a BeanPropertyDescriptor which indicates where we should
358      * put extensibility elements (i.e. XML which falls under the
359      * auspices of an &lt;xsd:any&gt; declaration in the schema)
360      *
361      * @return an appropriate BeanPropertyDescriptor, or null
362      */

363     public BeanPropertyDescriptor getAnyPropertyDesc() {
364         if (typeDesc == null)
365             return null;
366         
367        return typeDesc.getAnyDesc();
368     }
369
370     /**
371      * Set the bean properties that correspond to element attributes.
372      *
373      * This method is invoked after startElement when the element requires
374      * deserialization (i.e. the element is not an href and the value is not
375      * nil.)
376      * @param namespace is the namespace of the element
377      * @param localName is the name of the element
378      * @param prefix is the prefix of the element
379      * @param attributes are the attributes on the element...used to get the
380      * type
381      * @param context is the DeserializationContext
382      */

383     public void onStartElement(String JavaDoc namespace, String JavaDoc localName,
384                                String JavaDoc prefix, Attributes JavaDoc attributes,
385                                DeserializationContext context)
386             throws SAXException JavaDoc {
387
388         // The value should have been created or assigned already.
389
// This code may no longer be needed.
390
if (value == null && constructorToUse == null) {
391             // create a value
392
try {
393                 value=javaType.newInstance();
394             } catch (Exception JavaDoc e) {
395                 throw new SAXException JavaDoc(Messages.getMessage("cantCreateBean00",
396                                                             javaType.getName(),
397                                                             e.toString()));
398             }
399         }
400
401         // If no type description meta data, there are no attributes,
402
// so we are done.
403
if (typeDesc == null)
404             return;
405
406         // loop through the attributes and set bean properties that
407
// correspond to attributes
408
for (int i=0; i < attributes.getLength(); i++) {
409             QName JavaDoc attrQName = new QName JavaDoc(attributes.getURI(i),
410                                         attributes.getLocalName(i));
411             String JavaDoc fieldName = typeDesc.getFieldNameForAttribute(attrQName);
412             if (fieldName == null)
413                 continue;
414
415             FieldDesc fieldDesc = typeDesc.getFieldByName(fieldName);
416             
417             // look for the attribute property
418
BeanPropertyDescriptor bpd =
419                     (BeanPropertyDescriptor) propertyMap.get(fieldName);
420             if (bpd != null) {
421                 if (constructorToUse == null) {
422                     // check only if default constructor
423
if (!bpd.isWriteable() || bpd.isIndexed()) continue ;
424                 }
425                 
426                 // Get the Deserializer for the attribute
427
Deserializer dSer = getDeserializer(fieldDesc.getXmlType(),
428                                                     bpd.getType(),
429                                                     null,
430                                                     context);
431                 if (dSer == null) {
432                     dSer = context.getDeserializerForClass(bpd.getType());
433                 }
434                 if (dSer == null)
435                     throw new SAXException JavaDoc(
436                             Messages.getMessage("unregistered00",
437                                                  bpd.getType().toString()));
438                 
439                 if (! (dSer instanceof SimpleDeserializer))
440                     throw new SAXException JavaDoc(
441                             Messages.getMessage("AttrNotSimpleType00",
442                                                  bpd.getName(),
443                                                  bpd.getType().toString()));
444                 
445                 // Success! Create an object from the string and set
446
// it in the bean
447
try {
448                     dSer.onStartElement(namespace, localName, prefix,
449                                         attributes, context);
450                     Object JavaDoc val = ((SimpleDeserializer)dSer).
451                         makeValue(attributes.getValue(i));
452                     if (constructorToUse == null) {
453                         bpd.set(value, val);
454                     } else {
455                         // add value for our constructor
456
if (constructorTarget == null) {
457                             constructorTarget = new ConstructorTarget(constructorToUse, this);
458                         }
459                         constructorTarget.set(val);
460                     }
461                 } catch (Exception JavaDoc e) {
462                     throw new SAXException JavaDoc(e);
463                 }
464                 
465             } // if
466
} // attribute loop
467
}
468
469     /**
470      * Get the Deserializer for the attribute or child element.
471      * @param xmlType QName of the attribute/child element or null if not known.
472      * @param javaType Class of the corresponding property
473      * @param href String is the value of the href attribute, which is used
474      * to determine whether the child element is complete or an
475      * href to another element.
476      * @param context DeserializationContext
477      * @return Deserializer or null if not found.
478     */

479     protected Deserializer getDeserializer(QName JavaDoc xmlType,
480                                            Class JavaDoc javaType,
481                                            String JavaDoc href,
482                                            DeserializationContext context) {
483         if (javaType.isArray()) {
484             context.setDestinationClass(javaType);
485         }
486         // See if we have a cached deserializer
487
if (cacheStringDSer != null) {
488             if (String JavaDoc.class.equals(javaType) &&
489                 href == null &&
490                 (cacheXMLType == null && xmlType == null ||
491                  cacheXMLType != null && cacheXMLType.equals(xmlType))) {
492                 cacheStringDSer.reset();
493                 return cacheStringDSer;
494             }
495         }
496         
497         Deserializer dSer = null;
498
499         if (xmlType != null && href == null) {
500             // Use the xmlType to get the deserializer.
501
dSer = context.getDeserializerForType(xmlType);
502         } else {
503             // If the xmlType is not set, get a default xmlType
504
TypeMapping tm = context.getTypeMapping();
505             QName JavaDoc defaultXMLType = tm.getTypeQName(javaType);
506             // If there is not href, then get the deserializer
507
// using the javaType and default XMLType,
508
// If there is an href, the create the generic
509
// DeserializerImpl and set its default type (the
510
// default type is used if the href'd element does
511
// not have an xsi:type.
512
if (href == null) {
513                 dSer = context.getDeserializer(javaType, defaultXMLType);
514             } else {
515                 dSer = new DeserializerImpl();
516                 context.setDestinationClass(javaType);
517                 dSer.setDefaultType(defaultXMLType);
518             }
519         }
520         if (javaType.equals(String JavaDoc.class) &&
521             dSer instanceof SimpleDeserializer) {
522             cacheStringDSer = (SimpleDeserializer) dSer;
523             cacheXMLType = xmlType;
524         }
525         return dSer;
526     }
527
528     public void characters(char[] chars, int start, int end) throws SAXException JavaDoc {
529         val.write(chars, start, end);
530     }
531
532     public void onEndElement(String JavaDoc namespace, String JavaDoc localName,
533                              DeserializationContext context) throws SAXException JavaDoc {
534         handleMixedContent();
535     }
536
537     protected void handleMixedContent() throws SAXException JavaDoc {
538         BeanPropertyDescriptor propDesc = getAnyPropertyDesc();
539         if (propDesc == null || val.size() == 0) {
540             return;
541         }
542         String JavaDoc textValue = val.toString().trim();
543         val.reset();
544         if (textValue.length() == 0) {
545             return;
546         }
547         try {
548             MessageElement[] curElements = (MessageElement[]) propDesc.get(value);
549             int length = 0;
550             if (curElements != null) {
551                 length = curElements.length;
552             }
553             MessageElement[] newElements = new MessageElement[length + 1];
554             if (curElements != null) {
555                 System.arraycopy(curElements, 0,
556                         newElements, 0, length);
557             }
558             MessageElement thisEl = new MessageElement(new org.apache.axis.message.Text(textValue));
559             newElements[length] = thisEl;
560             propDesc.set(value, newElements);
561         } catch (Exception JavaDoc e) {
562             throw new SAXException JavaDoc(e);
563         }
564     }
565 }
566
Popular Tags