KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > shark > asap > util > BeanDeserializerShark


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

49 public class BeanDeserializerShark extends DeserializerImpl implements Serializable JavaDoc
50 {
51     protected static Log log =
52         LogFactory.getLog(BeanDeserializerShark.class.getName());
53
54     private final CharArrayWriter JavaDoc val = new CharArrayWriter JavaDoc();
55
56     private BeanDeserializerShark[] additional = null;
57     private boolean alreadyFailed [] = null;
58     private String JavaDoc[] _addLocalNames = {
59         "observerPropertiesGroup",
60         "instancePropertiesGroup",
61         "factoryPropertiesGroup"
62     };
63
64     QName JavaDoc xmlType;
65     Class JavaDoc javaType;
66     protected Map JavaDoc propertyMap = null;
67     protected QName JavaDoc prevQName;
68
69     /** Type metadata about this class for XML deserialization */
70     protected TypeDesc typeDesc = null;
71
72     // This counter is updated to deal with deserialize collection properties
73
protected int collectionIndex = -1;
74
75     protected SimpleDeserializer cacheStringDSer = null;
76     protected QName JavaDoc cacheXMLType = null;
77
78     // Construct BeanSerializer for the indicated class/qname
79
public BeanDeserializerShark(Class JavaDoc javaType, QName JavaDoc xmlType) {
80         this(javaType, xmlType, TypeDesc.getTypeDescForClass(javaType));
81     }
82
83     // Construct BeanDeserializer for the indicated class/qname and meta Data
84
public BeanDeserializerShark(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc ) {
85         this(javaType, xmlType, typeDesc,
86              AltBeanDeserializerFactory.getProperties(javaType, typeDesc));
87     }
88
89     // Construct BeanDeserializer for the indicated class/qname and meta Data
90
public BeanDeserializerShark(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc,
91                             Map JavaDoc propertyMap ) {
92         this.xmlType = xmlType;
93         this.javaType = javaType;
94         this.typeDesc = typeDesc;
95         this.propertyMap = propertyMap;
96
97         // create a value
98
try {
99             value=javaType.newInstance();
100         } catch (Exception JavaDoc e) {
101             // Don't process the exception at this point.
102
// This is defered until the call to startElement
103
// which will throw the exception.
104
}
105     }
106
107     /**
108      * startElement
109      *
110      * The ONLY reason that this method is overridden is so that
111      * the object value can be set or a reasonable exception is thrown
112      * indicating that the object cannot be created. This is done
113      * at this point so that it occurs BEFORE href/id processing.
114      * @param namespace is the namespace of the element
115      * @param localName is the name of the element
116      * @param prefix is the prefix of the element
117      * @param attributes are the attributes on the element...used to get the
118      * type
119      * @param context is the DeserializationContext
120      */

121     public void startElement(String JavaDoc namespace, String JavaDoc localName,
122                              String JavaDoc prefix, Attributes JavaDoc attributes,
123                              DeserializationContext context)
124         throws SAXException JavaDoc
125     {
126         // Create the bean object if it was not already
127
// created in the constructor.
128
if (value == null) {
129             try {
130                 value=javaType.newInstance();
131             } catch (Exception JavaDoc e) {
132                 // Failed to create an object.
133
throw new SAXException JavaDoc(Messages.getMessage("cantCreateBean00",
134                                                             javaType.getName(),
135                                                             e.toString()));
136             }
137         }
138         // Invoke super.startElement to do the href/id processing.
139
super.startElement(namespace, localName,
140                            prefix, attributes, context);
141     }
142
143     /**
144      * Deserializer interface called on each child element encountered in
145      * the XML stream.
146      * @param namespace is the namespace of the child element
147      * @param localName is the local name of the child element
148      * @param prefix is the prefix used on the name of the child element
149      * @param attributes are the attributes of the child element
150      * @param context is the deserialization context.
151      * @return is a Deserializer to use to deserialize a child (must be
152      * a derived class of SOAPHandler) or null if no deserialization should
153      * be performed.
154      */

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

353     public BeanPropertyDescriptor getAnyPropertyDesc() {
354         if (typeDesc == null)
355             return null;
356
357        return typeDesc.getAnyDesc();
358     }
359
360     /**
361      * Set the bean properties that correspond to element attributes.
362      *
363      * This method is invoked after startElement when the element requires
364      * deserialization (i.e. the element is not an href and the value is not
365      * nil.)
366      * @param namespace is the namespace of the element
367      * @param localName is the name of the element
368      * @param prefix is the prefix of the element
369      * @param attributes are the attributes on the element...used to get the
370      * type
371      * @param context is the DeserializationContext
372      */

373     public void onStartElement(String JavaDoc namespace, String JavaDoc localName,
374                                String JavaDoc prefix, Attributes JavaDoc attributes,
375                                DeserializationContext context)
376             throws SAXException JavaDoc {
377
378         if (xmlType.toString().endsWith("GetPropertiesRs")
379            ||xmlType.toString().endsWith("SetPropertiesRs")) {
380             //new Throwable("onStartElement namespace:"+namespace
381
// + "\nonStartElement localName:"+localName
382
// + "\nonStartElement prefix :"+prefix).printStackTrace();
383
String JavaDoc pp = xmlType.toString().substring(0, xmlType.toString().length() - "GetPropertiesRs".length());
384             try {
385                 additional = new BeanDeserializerShark[3];
386                 additional[0] = new BeanDeserializerShark(Class.forName("org.enhydra.shark.asap.types.ObserverPropertiesGroup"),
387                                                      new QName JavaDoc(pp + _addLocalNames[0]));
388                 additional[1] = new BeanDeserializerShark(Class.forName("org.enhydra.shark.asap.types.InstancePropertiesGroup"),
389                                                      new QName JavaDoc(pp + _addLocalNames[1]));
390                 additional[2] = new BeanDeserializerShark(Class.forName("org.enhydra.shark.asap.types.FactoryPropertiesGroup"),
391                                                      new QName JavaDoc(pp + _addLocalNames[2]));
392             } catch (Throwable JavaDoc t) {
393                 t.printStackTrace();
394                 throw new SAXException JavaDoc(t.getMessage());
395             }
396             alreadyFailed = new boolean[3];
397             int failures = 0;
398             for (int n = 0; n < 3; ++n) {
399                 try {
400                     alreadyFailed[n] = false;
401                     additional[n].startElement(namespace, _addLocalNames[n], prefix, attributes, context);
402                 } catch (Throwable JavaDoc t) {
403                     t.printStackTrace();
404                     alreadyFailed[n] = true;
405                     ++failures;
406                 }
407             }
408             if (3 == failures)
409                 throw new SAXException JavaDoc(Messages.getMessage("cantCreateBean00",
410                                                            _addLocalNames[0],
411                                                            ""));
412         }
413
414         // The value should have been created or assigned already.
415
// This code may no longer be needed.
416
if (value == null) {
417             // create a value
418
try {
419                 value=javaType.newInstance();
420             } catch (Exception JavaDoc e) {
421                 throw new SAXException JavaDoc(Messages.getMessage("cantCreateBean00",
422                                                             javaType.getName(),
423                                                             e.toString()));
424             }
425         }
426
427         // If no type description meta data, there are no attributes,
428
// so we are done.
429
if (typeDesc == null)
430             return;
431
432         // loop through the attributes and set bean properties that
433
// correspond to attributes
434
for (int i=0; i < attributes.getLength(); i++) {
435             QName JavaDoc attrQName = new QName JavaDoc(attributes.getURI(i),
436                                         attributes.getLocalName(i));
437             String JavaDoc fieldName = typeDesc.getFieldNameForAttribute(attrQName);
438             if (fieldName == null)
439                 continue;
440
441             FieldDesc fieldDesc = typeDesc.getFieldByName(fieldName);
442
443             // look for the attribute property
444
BeanPropertyDescriptor bpd =
445                     (BeanPropertyDescriptor) propertyMap.get(fieldName);
446             if (bpd != null) {
447                 if (!bpd.isWriteable() || bpd.isIndexed() ) continue ;
448
449                 // Get the Deserializer for the attribute
450
Deserializer dSer = getDeserializer(fieldDesc.getXmlType(),
451                                                     bpd.getType(),
452                                                     null,
453                                                     context);
454                 if (dSer == null) {
455                     dSer = context.getDeserializerForClass(bpd.getType());
456                 }
457                 if (dSer == null)
458                     throw new SAXException JavaDoc(
459                             Messages.getMessage("unregistered00",
460                                                  bpd.getType().toString()));
461
462                 if (! (dSer instanceof SimpleDeserializer))
463                     throw new SAXException JavaDoc(
464                             Messages.getMessage("AttrNotSimpleType00",
465                                                  bpd.getName(),
466                                                  bpd.getType().toString()));
467
468                 // Success! Create an object from the string and set
469
// it in the bean
470
try {
471                     dSer.onStartElement(namespace, localName, prefix,
472                                         attributes, context);
473                     Object JavaDoc val = ((SimpleDeserializer)dSer).
474                         makeValue(attributes.getValue(i));
475                     bpd.set(value, val);
476                 } catch (Exception JavaDoc e) {
477                     throw new SAXException JavaDoc(e);
478                 }
479
480             } // if
481
} // attribute loop
482
}
483
484     /**
485      * Get the Deserializer for the attribute or child element.
486      * @param xmlType QName of the attribute/child element or null if not known.
487      * @param javaType Class of the corresponding property
488      * @param href String is the value of the href attribute, which is used
489      * to determine whether the child element is complete or an
490      * href to another element.
491      * @param context DeserializationContext
492      * @return Deserializer or null if not found.
493     */

494     protected Deserializer getDeserializer(QName JavaDoc xmlType,
495                                            Class JavaDoc javaType,
496                                            String JavaDoc href,
497                                            DeserializationContext context) {
498         if (javaType.isArray()) {
499             context.setDestinationClass(javaType);
500         }
501         // See if we have a cached deserializer
502
if (cacheStringDSer != null) {
503             if (String JavaDoc.class.equals(javaType) &&
504                 href == null &&
505                 (cacheXMLType == null && xmlType == null ||
506                  cacheXMLType != null && cacheXMLType.equals(xmlType))) {
507                 cacheStringDSer.reset();
508                 return cacheStringDSer;
509             }
510         }
511
512         Deserializer dSer = null;
513
514         if (xmlType != null && href == null) {
515             // Use the xmlType to get the deserializer.
516
dSer = context.getDeserializerForType(xmlType);
517         } else {
518             // If the xmlType is not set, get a default xmlType
519
TypeMapping tm = context.getTypeMapping();
520             QName JavaDoc defaultXMLType = tm.getTypeQName(javaType);
521             // If there is not href, then get the deserializer
522
// using the javaType and default XMLType,
523
// If there is an href, the create the generic
524
// DeserializerImpl and set its default type (the
525
// default type is used if the href'd element does
526
// not have an xsi:type.
527
if (href == null) {
528                 dSer = context.getDeserializer(javaType, defaultXMLType);
529             } else {
530                 dSer = new DeserializerImpl();
531                 context.setDestinationClass(javaType);
532                 dSer.setDefaultType(defaultXMLType);
533             }
534         }
535         if (javaType.equals(String JavaDoc.class) &&
536             dSer instanceof SimpleDeserializer) {
537             cacheStringDSer = (SimpleDeserializer) dSer;
538             cacheXMLType = xmlType;
539         }
540         return dSer;
541     }
542
543     public void characters(char[] chars, int start, int end) throws SAXException JavaDoc {
544         val.write(chars, start, end);
545     }
546
547     public void onEndElement(String JavaDoc namespace, String JavaDoc localName,
548                              DeserializationContext context) throws SAXException JavaDoc {
549
550         if (xmlType.toString().endsWith("GetPropertiesRs")
551            ||xmlType.toString().endsWith("SetPropertiesRs")) {
552             for (int n = 0; n < 3; ++n) {
553                 if (!alreadyFailed[n]) {
554                     BeanPropertyDescriptor propDesc = (BeanPropertyDescriptor) propertyMap.get(_addLocalNames[n]);
555                     System.err.println("localName:"+_addLocalNames[n]+", propDesc:"+propDesc);
556                     try {
557                         propDesc.set(value, additional[n].getValue());
558                     } catch (Throwable JavaDoc t) {
559                         t.printStackTrace();
560                         throw new SAXException JavaDoc(t.getMessage());
561                     }
562                 }
563             }
564
565         }
566
567         handleMixedContent();
568     }
569
570     protected void handleMixedContent() throws SAXException JavaDoc {
571         BeanPropertyDescriptor propDesc = getAnyPropertyDesc();
572         if (propDesc == null || val.size() == 0) {
573             return;
574         }
575         String JavaDoc textValue = val.toString().trim();
576         val.reset();
577         if (textValue.length() == 0) {
578             return;
579         }
580         try {
581             MessageElement[] curElements = (MessageElement[]) propDesc.get(value);
582             int length = 0;
583             if (curElements != null) {
584                 length = curElements.length;
585             }
586             MessageElement[] newElements = new MessageElement[length + 1];
587             if (curElements != null) {
588                 System.arraycopy(curElements, 0,
589                         newElements, 0, length);
590             }
591             MessageElement thisEl = new MessageElement(new org.apache.axis.message.Text(textValue));
592             newElements[length] = thisEl;
593             propDesc.set(value, newElements);
594         } catch (Exception JavaDoc e) {
595             throw new SAXException JavaDoc(e);
596         }
597     }
598 }
599
Popular Tags