KickJava   Java API By Example, From Geeks To Geeks.

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

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

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

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

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

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