KickJava   Java API By Example, From Geeks To Geeks.

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


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.AxisFault;
20 import org.apache.axis.Constants;
21 import org.apache.axis.components.logger.LogFactory;
22 import org.apache.axis.description.FieldDesc;
23 import org.apache.axis.description.TypeDesc;
24 import org.apache.axis.description.ElementDesc;
25 import org.apache.axis.encoding.SerializationContext;
26 import org.apache.axis.encoding.Serializer;
27 import org.apache.axis.message.MessageElement;
28 import org.apache.axis.utils.BeanPropertyDescriptor;
29 import org.apache.axis.utils.BeanUtils;
30 import org.apache.axis.utils.JavaUtils;
31 import org.apache.axis.utils.Messages;
32 import org.apache.axis.utils.FieldPropertyDescriptor;
33 import org.apache.axis.wsdl.fromJava.Types;
34 import org.apache.axis.wsdl.symbolTable.SchemaUtils;
35 import org.apache.commons.logging.Log;
36 import org.w3c.dom.Element JavaDoc;
37 import org.xml.sax.Attributes JavaDoc;
38 import org.xml.sax.helpers.AttributesImpl JavaDoc;
39
40 import javax.xml.namespace.QName JavaDoc;
41 import java.io.IOException JavaDoc;
42 import java.io.Serializable JavaDoc;
43 import java.lang.reflect.InvocationTargetException JavaDoc;
44 import java.lang.reflect.Modifier JavaDoc;
45 import java.lang.reflect.Constructor JavaDoc;
46 import java.util.List JavaDoc;
47
48 /**
49  * General purpose serializer/deserializerFactory for an arbitrary java bean.
50  *
51  * @author Sam Ruby <rubys@us.ibm.com>
52  * @author Rich Scheuerle <scheu@us.ibm.com>
53  * @author Tom Jordahl <tomj@macromedia.com>
54  */

55 public class BeanSerializer implements Serializer, Serializable JavaDoc {
56
57     protected static Log log =
58         LogFactory.getLog(BeanSerializer.class.getName());
59
60     private static final QName JavaDoc MUST_UNDERSTAND_QNAME =
61         new QName JavaDoc(Constants.URI_SOAP11_ENV, Constants.ATTR_MUST_UNDERSTAND);
62     private static final Object JavaDoc[] ZERO_ARGS =
63         new Object JavaDoc [] { "0" };
64
65     QName JavaDoc xmlType;
66     Class JavaDoc javaType;
67
68     protected BeanPropertyDescriptor[] propertyDescriptor = null;
69     protected TypeDesc typeDesc = null;
70
71     // Construct BeanSerializer for the indicated class/qname
72
public BeanSerializer(Class JavaDoc javaType, QName JavaDoc xmlType) {
73         this(javaType, xmlType, TypeDesc.getTypeDescForClass(javaType));
74     }
75
76     // Construct BeanSerializer for the indicated class/qname
77
public BeanSerializer(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc) {
78         this(javaType, xmlType, typeDesc, null);
79
80         if (typeDesc != null) {
81             propertyDescriptor = typeDesc.getPropertyDescriptors();
82         } else {
83             propertyDescriptor = BeanUtils.getPd(javaType, null);
84         }
85     }
86
87     // Construct BeanSerializer for the indicated class/qname/propertyDesc
88
public BeanSerializer(Class JavaDoc javaType, QName JavaDoc xmlType, TypeDesc typeDesc,
89                           BeanPropertyDescriptor[] propertyDescriptor) {
90         this.xmlType = xmlType;
91         this.javaType = javaType;
92         this.typeDesc = typeDesc;
93         this.propertyDescriptor = propertyDescriptor;
94     }
95
96     /**
97      * Serialize a bean. Done simply by serializing each bean property.
98      * @param name is the element name
99      * @param attributes are the attributes...serialize is free to add more.
100      * @param value is the value
101      * @param context is the SerializationContext
102      */

103     public void serialize(QName JavaDoc name, Attributes JavaDoc attributes,
104                           Object JavaDoc value, SerializationContext context)
105         throws IOException JavaDoc
106     {
107         // Check for meta-data in the bean that will tell us if any of the
108
// properties are actually attributes, add those to the element
109
// attribute list
110
Attributes JavaDoc beanAttrs = getObjectAttributes(value, attributes, context);
111
112         // Get the encoding style
113
boolean isEncoded = context.isEncoded();
114
115         // check whether we have and xsd:any namespace="##any" type
116
boolean suppressElement = !isEncoded &&
117                                   name.getNamespaceURI().equals("") &&
118                                   name.getLocalPart().equals("any");
119
120         if (!suppressElement)
121             context.startElement(name, beanAttrs);
122
123         // check whether the array is converted to ArrayOfT shema type
124
if (value.getClass().isArray()) {
125            Object JavaDoc newVal = JavaUtils.convert(value, javaType);
126            if (newVal != null && javaType.isAssignableFrom(newVal.getClass())) {
127                value = newVal;
128            }
129         }
130         try {
131             // Serialize each property
132
for (int i=0; i<propertyDescriptor.length; i++) {
133                 String JavaDoc propName = propertyDescriptor[i].getName();
134                 if (propName.equals("class"))
135                     continue;
136                 QName JavaDoc qname = null;
137                 QName JavaDoc xmlType = null;
138                 Class JavaDoc javaType = propertyDescriptor[i].getType();
139
140                 boolean isOmittable = false;
141                 // isNillable default value depends on the field type
142
boolean isNillable = Types.isNullable(javaType);
143                 // isArray
144
boolean isArray = false;
145                 QName JavaDoc itemQName = null;
146
147                 // If we have type metadata, check to see what we're doing
148
// with this field. If it's an attribute, skip it. If it's
149
// an element, use whatever qname is in there. If we can't
150
// find any of this info, use the default.
151
if (typeDesc != null) {
152                     FieldDesc field = typeDesc.getFieldByName(propName);
153                     if (field != null) {
154                         if (!field.isElement()) {
155                             continue;
156                         }
157
158                         ElementDesc element = (ElementDesc)field;
159
160                         // If we're SOAP encoded, just use the local part,
161
// not the namespace. Otherwise use the whole
162
// QName.
163
if (isEncoded) {
164                             qname = new QName JavaDoc(element.getXmlName().getLocalPart());
165                         } else {
166                             qname = element.getXmlName();
167                         }
168                         isOmittable = element.isMinOccursZero();
169                         isNillable = element.isNillable();
170                         isArray = element.isMaxOccursUnbounded();
171                         xmlType = element.getXmlType();
172                         itemQName = element.getItemQName();
173                         context.setItemQName(itemQName);
174                     }
175                 }
176
177                 if (qname == null) {
178                     qname = new QName JavaDoc(isEncoded ? "" : name.getNamespaceURI(),
179                                       propName);
180                 }
181
182                 if (xmlType == null) {
183                     // look up the type QName using the class
184
xmlType = context.getQNameForClass(javaType);
185                 }
186
187                 // Read the value from the property
188
if (propertyDescriptor[i].isReadable()) {
189                     if (itemQName != null ||
190                             (!propertyDescriptor[i].isIndexed() && !isArray)) {
191                         // Normal case: serialize the value
192
Object JavaDoc propValue =
193                             propertyDescriptor[i].get(value);
194
195
196                         if (propValue == null) {
197                             // an element cannot be null if nillable property is set to
198
// "false" and the element cannot be omitted
199
if (!isNillable && !isOmittable) {
200                                 if (Number JavaDoc.class.isAssignableFrom(javaType)) {
201                                     // If we have a null and it's a number, though,
202
// we might turn it into the appropriate kind of 0.
203
// TODO : Should be caching these constructors?
204
try {
205                                         Constructor JavaDoc constructor =
206                                                 javaType.getConstructor(
207                                                         SimpleDeserializer.STRING_CLASS);
208                                         propValue = constructor.newInstance(ZERO_ARGS);
209                                     } catch (Exception JavaDoc e) {
210                                         // If anything goes wrong here, oh well we tried.
211
}
212                                 }
213
214                                 if (propValue == null) {
215                                     throw new IOException JavaDoc(
216                                             Messages.getMessage(
217                                                     "nullNonNillableElement",
218                                                     propName));
219                                 }
220                             }
221
222                             // if meta data says minOccurs=0, then we can skip
223
// it if its value is null and we aren't doing SOAP
224
// encoding.
225
if (isOmittable && !isEncoded) {
226                                 continue;
227                             }
228                         }
229
230                         context.serialize(qname,
231                                           null,
232                                           propValue,
233                                           xmlType);
234                     } else {
235                         // Collection of properties: serialize each one
236
int j=0;
237                         while(j >= 0) {
238                             Object JavaDoc propValue = null;
239                             try {
240                                 propValue =
241                                     propertyDescriptor[i].get(value, j);
242                                 j++;
243                             } catch (Exception JavaDoc e) {
244                                 j = -1;
245                             }
246                             if (j >= 0) {
247                                 context.serialize(qname, null,
248                                                   propValue, xmlType);
249                             }
250                         }
251                     }
252                 }
253             }
254
255             BeanPropertyDescriptor anyDesc = typeDesc == null ? null :
256                     typeDesc.getAnyDesc();
257             if (anyDesc != null) {
258                 // If we have "extra" content here, it'll be an array
259
// of MessageElements. Serialize each one.
260
Object JavaDoc anyVal = anyDesc.get(value);
261                 if (anyVal != null && anyVal instanceof MessageElement[]) {
262                     MessageElement [] anyContent = (MessageElement[])anyVal;
263                     for (int i = 0; i < anyContent.length; i++) {
264                         MessageElement element = anyContent[i];
265                         element.output(context);
266                     }
267                 }
268             }
269         } catch (InvocationTargetException JavaDoc ite) {
270             Throwable JavaDoc target = ite.getTargetException();
271             log.error(Messages.getMessage("exception00"), target);
272             throw new IOException JavaDoc(target.toString());
273         } catch (Exception JavaDoc e) {
274             log.error(Messages.getMessage("exception00"), e);
275             throw new IOException JavaDoc(e.toString());
276         }
277
278         if (!suppressElement)
279             context.endElement();
280     }
281
282
283
284     public String JavaDoc getMechanismType() { return Constants.AXIS_SAX; }
285
286     /**
287      * Return XML schema for the specified type, suitable for insertion into
288      * the &lt;types&gt; element of a WSDL document, or underneath an
289      * &lt;element&gt; or &lt;attribute&gt; declaration.
290      *
291      * @param javaType the Java Class we're writing out schema for
292      * @param types the Java2WSDL Types object which holds the context
293      * for the WSDL being generated.
294      * @return a type element containing a schema simpleType/complexType
295      * @see org.apache.axis.wsdl.fromJava.Types
296      */

297     public Element writeSchema(Class JavaDoc javaType, Types types) throws Exception JavaDoc {
298
299         // ComplexType representation of bean class
300
Element complexType = types.createElement("complexType");
301
302         // See if there is a super class, stop if we hit a stop class
303
Element e = null;
304         Class JavaDoc superClass = javaType.getSuperclass();
305         BeanPropertyDescriptor[] superPd = null;
306         List JavaDoc stopClasses = types.getStopClasses();
307         if (superClass != null &&
308                 superClass != java.lang.Object JavaDoc.class &&
309                 superClass != java.lang.Exception JavaDoc.class &&
310                 superClass != java.lang.Throwable JavaDoc.class &&
311                 superClass != java.lang.RuntimeException JavaDoc.class &&
312                 superClass != java.rmi.RemoteException JavaDoc.class &&
313                 superClass != org.apache.axis.AxisFault.class &&
314                 (stopClasses == null ||
315                 !(stopClasses.contains(superClass.getName()))) ) {
316             // Write out the super class
317
String JavaDoc base = types.writeType(superClass);
318             Element complexContent = types.createElement("complexContent");
319             complexType.appendChild(complexContent);
320             Element extension = types.createElement("extension");
321             complexContent.appendChild(extension);
322             extension.setAttribute("base", base);
323             e = extension;
324             // Get the property descriptors for the super class
325
TypeDesc superTypeDesc = TypeDesc.getTypeDescForClass(superClass);
326             if (superTypeDesc != null) {
327                 superPd = superTypeDesc.getPropertyDescriptors();
328             } else {
329                 superPd = BeanUtils.getPd(superClass, null);
330             }
331         } else {
332             e = complexType;
333         }
334
335         // Add fields under sequence element.
336
// Note: In most situations it would be okay
337
// to put the fields under an all element.
338
// However it is illegal schema to put an
339
// element with minOccurs=0 or maxOccurs>1 underneath
340
// an all element. This is the reason why a sequence
341
// element is used.
342
Element all = types.createElement("sequence");
343         e.appendChild(all);
344
345         if (Modifier.isAbstract(javaType.getModifiers())) {
346             complexType.setAttribute("abstract", "true");
347         }
348
349         // Serialize each property
350
for (int i=0; i<propertyDescriptor.length; i++) {
351             String JavaDoc propName = propertyDescriptor[i].getName();
352
353             // Don't serializer properties named class
354
boolean writeProperty = true;
355             if (propName.equals("class")) {
356                 writeProperty = false;
357             }
358
359             // Don't serialize the property if it is present
360
// in the super class property list
361
if (superPd != null && writeProperty) {
362                 for (int j=0; j<superPd.length && writeProperty; j++) {
363                     if (propName.equals(superPd[j].getName())) {
364                         writeProperty = false;
365                     }
366                 }
367             }
368             if (!writeProperty) {
369                 continue;
370             }
371
372             // If we have type metadata, check to see what we're doing
373
// with this field. If it's an attribute, skip it. If it's
374
// an element, use whatever qname is in there. If we can't
375
// find any of this info, use the default.
376

377             if (typeDesc != null) {
378                 Class JavaDoc fieldType = propertyDescriptor[i].getType();
379                 FieldDesc field = typeDesc.getFieldByName(propName);
380
381                 if (field != null) {
382                     QName JavaDoc qname = field.getXmlName();
383                     QName JavaDoc fieldXmlType = field.getXmlType();
384                     boolean isAnonymous = fieldXmlType != null && fieldXmlType.getLocalPart().startsWith(">");
385
386                     if (qname != null) {
387                         // FIXME!
388
// Check to see if this is in the right namespace -
389
// if it's not, we need to use an <element ref="">
390
// to represent it!!!
391

392                         // Use the default...
393
propName = qname.getLocalPart();
394                     }
395                     if (!field.isElement()) {
396                         writeAttribute(types,
397                                        propName,
398                                        fieldType,
399                                        fieldXmlType,
400                                        complexType);
401                     } else {
402                         writeField(types,
403                                    propName,
404                                    fieldXmlType,
405                                    fieldType,
406                                    propertyDescriptor[i].isIndexed(),
407                                    field.isMinOccursZero(),
408                                    all, isAnonymous,
409                                    ((ElementDesc)field).getItemQName());
410                     }
411                 } else {
412                     writeField(types,
413                                propName,
414                                null,
415                                fieldType,
416                                propertyDescriptor[i].isIndexed(), false, all, false, null);
417                 }
418             } else {
419                 boolean done = false;
420                 if (propertyDescriptor[i] instanceof FieldPropertyDescriptor){
421                     FieldPropertyDescriptor fpd = (FieldPropertyDescriptor) propertyDescriptor[i];
422                     Class JavaDoc clazz = fpd.getField().getType();
423                     if(types.getTypeQName(clazz)!=null) {
424                         writeField(types,
425                                    propName,
426                                    null,
427                                    clazz,
428                                    false, false, all, false, null);
429                    
430                         done = true;
431                     }
432                 }
433                 if(!done) {
434                     writeField(types,
435                                propName,
436                                null,
437                                propertyDescriptor[i].getType(),
438                                propertyDescriptor[i].isIndexed(), false, all, false, null);
439                 }
440                 
441             }
442         }
443
444         // done
445
return complexType;
446     }
447
448     /**
449      * write a schema representation of the given Class field and append it to
450      * the where Node, recurse on complex types
451      * @param fieldName name of the field
452      * @param xmlType the schema type of the field
453      * @param fieldType type of the field
454      * @param isUnbounded causes maxOccurs="unbounded" if set
455      * @param where location for the generated schema node
456      * @param itemQName
457      * @throws Exception
458      */

459     protected void writeField(Types types,
460                               String JavaDoc fieldName,
461                               QName JavaDoc xmlType,
462                               Class JavaDoc fieldType,
463                               boolean isUnbounded,
464                               boolean isOmittable,
465                               Element where,
466                               boolean isAnonymous,
467                               QName JavaDoc itemQName) throws Exception JavaDoc {
468         Element elem;
469         String JavaDoc elementType = null;
470
471         if (isAnonymous) {
472             elem = types.
473                     createElementWithAnonymousType(fieldName,
474                                                    fieldType,
475                                                    isOmittable,
476                                                    where.getOwnerDocument());
477         } else {
478             if (!SchemaUtils.isSimpleSchemaType(xmlType) &&
479                     Types.isArray(fieldType)) {
480                 xmlType = null;
481             }
482
483             if (itemQName != null &&
484                     SchemaUtils.isSimpleSchemaType(xmlType) &&
485                     Types.isArray(fieldType)) {
486                 xmlType = null;
487             }
488
489             QName JavaDoc typeQName = types.writeTypeAndSubTypeForPart(fieldType, xmlType);
490             elementType = types.getQNameString(typeQName);
491
492             if (elementType == null) {
493                 // If writeType returns null, then emit an anytype.
494
QName JavaDoc anyQN = Constants.XSD_ANYTYPE;
495                 String JavaDoc prefix = types.getNamespaces().
496                         getCreatePrefix(anyQN.getNamespaceURI());
497                 elementType = prefix + ":" + anyQN.getLocalPart();
498             }
499
500             // isNillable default value depends on the field type
501
boolean isNillable = Types.isNullable(fieldType);
502             if (typeDesc != null) {
503                 FieldDesc field = typeDesc.getFieldByName(fieldName);
504                 if (field != null && field.isElement()) {
505                     isNillable = ((ElementDesc)field).isNillable();
506                 }
507             }
508
509             elem = types.createElement(fieldName,
510                     elementType,
511                     isNillable,
512                     isOmittable,
513                     where.getOwnerDocument());
514         }
515
516         if (isUnbounded) {
517             elem.setAttribute("maxOccurs", "unbounded");
518         }
519
520         where.appendChild(elem);
521     }
522
523     /**
524      * write aa attribute element and append it to the 'where' Node
525      * @param fieldName name of the field
526      * @param fieldType type of the field
527      * @param where location for the generated schema node
528      * @throws Exception
529      */

530     protected void writeAttribute(Types types,
531                                 String JavaDoc fieldName,
532                                 Class JavaDoc fieldType,
533                                 QName JavaDoc fieldXmlType,
534                                 Element where) throws Exception JavaDoc {
535
536         // Attribute must be a simple type.
537
if (!types.isAcceptableAsAttribute(fieldType)) {
538             throw new AxisFault(Messages.getMessage("AttrNotSimpleType00",
539                                                      fieldName,
540                                                      fieldType.getName()));
541         }
542         Element elem = types.createAttributeElement(fieldName,
543                                            fieldType, fieldXmlType,
544                                            false,
545                                            where.getOwnerDocument());
546         where.appendChild(elem);
547     }
548
549     /**
550      * Check for meta-data in the bean that will tell us if any of the
551      * properties are actually attributes, add those to the element
552      * attribute list
553      *
554      * @param value the object we are serializing
555      * @return attributes for this element, null if none
556      */

557     protected Attributes JavaDoc getObjectAttributes(Object JavaDoc value,
558                                            Attributes JavaDoc attributes,
559                                            SerializationContext context) {
560
561         if (typeDesc == null || !typeDesc.hasAttributes())
562             return attributes;
563
564         AttributesImpl JavaDoc attrs;
565         if (attributes == null) {
566             attrs = new AttributesImpl JavaDoc();
567         } else if (attributes instanceof AttributesImpl JavaDoc) {
568             attrs = (AttributesImpl JavaDoc)attributes;
569         } else {
570             attrs = new AttributesImpl JavaDoc(attributes);
571         }
572
573         try {
574             // Find each property that is an attribute
575
// and add it to our attribute list
576
for (int i=0; i<propertyDescriptor.length; i++) {
577                 String JavaDoc propName = propertyDescriptor[i].getName();
578                 if (propName.equals("class"))
579                     continue;
580
581                 FieldDesc field = typeDesc.getFieldByName(propName);
582                 // skip it if its not an attribute
583
if (field == null || field.isElement())
584                     continue;
585
586                 QName JavaDoc qname = field.getXmlName();
587                 if (qname == null) {
588                     qname = new QName JavaDoc("", propName);
589                 }
590
591                 if (propertyDescriptor[i].isReadable() &&
592                     !propertyDescriptor[i].isIndexed()) {
593                     // add to our attributes
594
Object JavaDoc propValue = propertyDescriptor[i].get(value);
595                     // Convert true/false to 1/0 in case of soapenv:mustUnderstand
596
if (qname.equals(MUST_UNDERSTAND_QNAME)) {
597                         if (propValue.equals(Boolean.TRUE)) {
598                                 propValue = "1";
599                         } else if (propValue.equals(Boolean.FALSE)) {
600                             propValue = "0";
601                         }
602                     }
603                     // If the property value does not exist, don't serialize
604
// the attribute. In the future, the decision to serializer
605
// the attribute may be more sophisticated. For example, don't
606
// serialize if the attribute matches the default value.
607
if (propValue != null) {
608                         setAttributeProperty(propValue,
609                                              qname,
610                                              field.getXmlType(),
611                                              attrs,
612                                              context);
613                     }
614                 }
615             }
616         } catch (Exception JavaDoc e) {
617             // no attributes
618
return attrs;
619         }
620
621         return attrs;
622     }
623
624     private void setAttributeProperty(Object JavaDoc propValue,
625                                       QName JavaDoc qname,
626                                       QName JavaDoc xmlType, AttributesImpl JavaDoc attrs,
627                                       SerializationContext context) throws Exception JavaDoc {
628
629         String JavaDoc namespace = qname.getNamespaceURI();
630         String JavaDoc localName = qname.getLocalPart();
631
632         // org.xml.sax.helpers.AttributesImpl JavaDoc says: "For the
633
// sake of speed, this method does no checking to see if the
634
// attribute is already in the list: that is the
635
// responsibility of the application." check for the existence
636
// of the attribute to avoid adding it more than once.
637
if (attrs.getIndex(namespace, localName) != -1) {
638             return;
639         }
640
641         String JavaDoc propString = context.getValueAsString(propValue, xmlType);
642
643         attrs.addAttribute(namespace,
644                            localName,
645                            context.attributeQName2String(qname),
646                            "CDATA",
647                            propString);
648     }
649 }
650
Popular Tags