KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Copyright 2001-2005 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 java.io.IOException JavaDoc;
20 import java.lang.reflect.Array JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import javax.xml.namespace.QName JavaDoc;
24 import org.w3c.dom.Element JavaDoc;
25 import org.xml.sax.Attributes JavaDoc;
26 import org.xml.sax.helpers.AttributesImpl JavaDoc;
27 import org.apache.axis.AxisEngine;
28 import org.apache.axis.Constants;
29 import org.apache.axis.MessageContext;
30 import org.apache.axis.components.logger.LogFactory;
31 import org.apache.axis.constants.Use;
32 import org.apache.axis.encoding.SerializationContext;
33 import org.apache.axis.encoding.Serializer;
34 import org.apache.axis.encoding.SerializerFactory;
35 import org.apache.axis.encoding.TypeMapping;
36 import org.apache.axis.schema.SchemaVersion;
37 import org.apache.axis.soap.SOAPConstants;
38 import org.apache.axis.utils.JavaUtils;
39 import org.apache.axis.utils.Messages;
40 import org.apache.axis.wsdl.fromJava.Types;
41 import org.apache.commons.logging.Log;
42
43 /**
44  * An ArraySerializer handles serializing of arrays.
45  *
46  * Some code borrowed from ApacheSOAP - thanks to Matt Duftler!
47  *
48  * @author Glen Daniels (gdaniels@apache.org)
49  *
50  * Multi-reference stuff:
51  * @author Rich Scheuerle (scheu@us.ibm.com)
52  */

53 public class ArraySerializer implements Serializer
54 {
55     QName JavaDoc xmlType = null;
56     Class JavaDoc javaType = null;
57     QName JavaDoc componentType = null;
58     QName JavaDoc componentQName = null;
59
60     /**
61      * Constructor
62      *
63      */

64     public ArraySerializer(Class JavaDoc javaType, QName JavaDoc xmlType) {
65         this.javaType = javaType;
66         this.xmlType = xmlType;
67     }
68
69     /**
70      * Constructor
71      * Special constructor that takes the component type of the array.
72      */

73     public ArraySerializer(Class JavaDoc javaType, QName JavaDoc xmlType, QName JavaDoc componentType) {
74         this(javaType, xmlType);
75         this.componentType = componentType;
76     }
77
78     /**
79      * Constructor
80      * Special constructor that takes the component type and QName of the array.
81      */

82     public ArraySerializer(Class JavaDoc javaType, QName JavaDoc xmlType, QName JavaDoc componentType, QName JavaDoc componentQName) {
83         this(javaType, xmlType, componentType);
84         this.componentQName = componentQName;
85     }
86
87     protected static Log log =
88         LogFactory.getLog(ArraySerializer.class.getName());
89
90     /**
91      * Serialize an element that is an array.
92      * @param name is the element name
93      * @param attributes are the attributes...serialize is free to add more.
94      * @param value is the value
95      * @param context is the SerializationContext
96      */

97     public void serialize(QName JavaDoc name, Attributes JavaDoc attributes,
98                           Object JavaDoc value, SerializationContext context)
99         throws IOException JavaDoc
100     {
101         if (value == null)
102             throw new IOException JavaDoc(Messages.getMessage("cantDoNullArray00"));
103
104         MessageContext msgContext = context.getMessageContext();
105         SchemaVersion schema = SchemaVersion.SCHEMA_2001;
106         SOAPConstants soap = SOAPConstants.SOAP11_CONSTANTS;
107         boolean encoded = context.isEncoded();
108         
109         if (msgContext != null) {
110             schema = msgContext.getSchemaVersion();
111             soap = msgContext.getSOAPConstants();
112         }
113
114         Class JavaDoc cls = value.getClass();
115         Collection JavaDoc list = null;
116
117         if (!cls.isArray()) {
118             if (!(value instanceof Collection JavaDoc)) {
119                 throw new IOException JavaDoc(
120                         Messages.getMessage("cantSerialize00", cls.getName()));
121             }
122             list = (Collection JavaDoc)value;
123         }
124
125         // Get the componentType of the array/list
126
Class JavaDoc componentClass;
127         if (list == null) {
128             componentClass = cls.getComponentType();
129         } else {
130             componentClass = Object JavaDoc.class;
131         }
132
133         // Get the QName of the componentType
134
// if it wasn't passed in from the constructor
135
QName JavaDoc componentTypeQName = this.componentType;
136
137         // Check to see if componentType is also an array.
138
// If so, set the componentType to the most nested non-array
139
// componentType. Increase the dims string by "[]"
140
// each time through the loop.
141
// Note from Rich Scheuerle:
142
// This won't handle Lists of Lists or
143
// arrays of Lists....only arrays of arrays.
144
String JavaDoc dims = "";
145         
146         if (componentTypeQName != null) {
147             // if we have a Type QName at this point,
148
// this is because ArraySerializer has been instanciated with it
149
TypeMapping tm = context.getTypeMapping();
150             SerializerFactory factory = (SerializerFactory) tm.getSerializer(
151                     componentClass, componentTypeQName);
152             while (componentClass.isArray()
153                     && factory instanceof ArraySerializerFactory) {
154                 ArraySerializerFactory asf = (ArraySerializerFactory) factory;
155                 componentClass = componentClass.getComponentType();
156                 QName JavaDoc componentType = null;
157                 if (asf.getComponentType() != null) {
158                     componentType = asf.getComponentType();
159                     if(encoded) {
160                         componentTypeQName = componentType;
161                     }
162                 }
163                 // update factory with the new values
164
factory = (SerializerFactory) tm.getSerializer(componentClass,
165                         componentType);
166                 if (soap == SOAPConstants.SOAP12_CONSTANTS)
167                     dims += "* ";
168                 else
169                     dims += "[]";
170             }
171         } else {
172             // compatibility mode
173
while (componentClass.isArray()) {
174                 componentClass = componentClass.getComponentType();
175                 if (soap == SOAPConstants.SOAP12_CONSTANTS)
176                     dims += "* ";
177                 else
178                     dims += "[]";
179             }
180         }
181
182         // Try the current XML type from the context
183
if (componentTypeQName == null) {
184             componentTypeQName = context.getCurrentXMLType();
185             if (componentTypeQName != null) {
186                 if ((componentTypeQName.equals(xmlType) ||
187                         componentTypeQName.equals(Constants.XSD_ANYTYPE) ||
188                         componentTypeQName.equals(soap.getArrayType()))) {
189                     componentTypeQName = null;
190                 }
191             }
192         }
193
194         if (componentTypeQName == null) {
195             componentTypeQName = context.getItemType();
196         }
197
198         // Then check the type mapping for the class
199
if (componentTypeQName == null) {
200             componentTypeQName = context.getQNameForClass(componentClass);
201         }
202
203         // If still not found, look at the super classes
204
if (componentTypeQName == null) {
205             Class JavaDoc searchCls = componentClass;
206             while(searchCls != null && componentTypeQName == null) {
207                 searchCls = searchCls.getSuperclass();
208                 componentTypeQName = context.getQNameForClass(searchCls);
209             }
210             if (componentTypeQName != null) {
211                 componentClass = searchCls;
212             }
213         }
214
215         // Still can't find it? Throw an error.
216
if (componentTypeQName == null) {
217             throw new IOException JavaDoc(
218                     Messages.getMessage("noType00", componentClass.getName()));
219         }
220
221         int len = (list == null) ? Array.getLength(value) : list.size();
222         String JavaDoc arrayType = "";
223         int dim2Len = -1;
224         if (encoded) {
225             if (soap == SOAPConstants.SOAP12_CONSTANTS) {
226                 arrayType = dims + len;
227             } else {
228                 arrayType = dims + "[" + len + "]";
229             }
230
231             // Discover whether array can be serialized directly as a two-dimensional
232
// array (i.e. arrayType=int[2,3]) versus an array of arrays.
233
// Benefits:
234
// - Less text passed on the wire.
235
// - Easier to read wire format
236
// - Tests the deserialization of multi-dimensional arrays.
237
// Drawbacks:
238
// - Is not safe! It is possible that the arrays are multiply
239
// referenced. Transforming into a 2-dim array will cause the
240
// multi-referenced information to be lost. Plus there is no
241
// way to determine whether the arrays are multi-referenced.
242
// - .NET currently (Dec 2002) does not support 2D SOAP-encoded arrays
243
//
244
// OLD Comment as to why this was ENABLED:
245
// It is necessary for
246
// interoperability (echo2DStringArray). It is 'safe' for now
247
// because Axis treats arrays as non multi-ref (see the note
248
// in SerializationContext.isPrimitive(...) )
249
// More complicated processing is necessary for 3-dim arrays, etc.
250
//
251
// Axis 1.1 - December 2002
252
// Turned this OFF because Microsoft .NET can not deserialize
253
// multi-dimensional SOAP-encoded arrays, and this interopability
254
// is pretty high visibility. Make it a global configuration parameter:
255
// <parameter name="enable2DArrayEncoding" value="true"/> (tomj)
256
//
257

258             // Check the message context to see if we should turn 2D processing ON
259
// Default is OFF
260
boolean enable2Dim = false;
261         
262             // Vidyanand : added this check
263
if( msgContext != null ) {
264                enable2Dim = JavaUtils.isTrueExplicitly(msgContext.getProperty(
265                        AxisEngine.PROP_TWOD_ARRAY_ENCODING));
266             }
267
268             if (enable2Dim && !dims.equals("")) {
269                 if (cls.isArray() && len > 0) {
270                     boolean okay = true;
271                     // Make sure all of the component arrays are the same size
272
for (int i=0; i < len && okay; i++) {
273
274                         Object JavaDoc elementValue = Array.get(value, i);
275                         if (elementValue == null)
276                             okay = false;
277                         else if (dim2Len < 0) {
278                             dim2Len = Array.getLength(elementValue);
279                             if (dim2Len <= 0) {
280                                 okay = false;
281                             }
282                         } else if (dim2Len != Array.getLength(elementValue)) {
283                             okay = false;
284                         }
285                     }
286                     // Update the arrayType to use mult-dim array encoding
287
if (okay) {
288                         dims = dims.substring(0, dims.length()-2);
289                         if (soap == SOAPConstants.SOAP12_CONSTANTS)
290                             arrayType = dims + len + " " + dim2Len;
291                         else
292                             arrayType = dims + "[" + len + "," + dim2Len + "]";
293                     } else {
294                         dim2Len = -1;
295                     }
296                 }
297             }
298         }
299
300         // Need to distinguish if this is array processing for an
301
// actual schema array or for a maxOccurs usage.
302
// For the maxOccurs case, the currentXMLType of the context is
303
// the same as the componentTypeQName.
304
QName JavaDoc itemQName = context.getItemQName();
305         boolean maxOccursUsage = !encoded && itemQName == null &&
306                 componentTypeQName.equals(context.getCurrentXMLType());
307
308         if (encoded) {
309             AttributesImpl JavaDoc attrs;
310             if (attributes == null) {
311                 attrs = new AttributesImpl JavaDoc();
312             } else if (attributes instanceof AttributesImpl JavaDoc) {
313                 attrs = (AttributesImpl JavaDoc)attributes;
314             } else {
315                 attrs = new AttributesImpl JavaDoc(attributes);
316             }
317
318             String JavaDoc compType = context.attributeQName2String(componentTypeQName);
319
320             if (attrs.getIndex(soap.getEncodingURI(), soap.getAttrItemType()) == -1) {
321                 String JavaDoc encprefix =
322                        context.getPrefixForURI(soap.getEncodingURI());
323
324                 if (soap != SOAPConstants.SOAP12_CONSTANTS) {
325                     compType = compType + arrayType;
326                     
327                     attrs.addAttribute(soap.getEncodingURI(),
328                                        soap.getAttrItemType(),
329                                        encprefix + ":arrayType",
330                                        "CDATA",
331                                        compType);
332
333                 } else {
334                     attrs.addAttribute(soap.getEncodingURI(),
335                                        soap.getAttrItemType(),
336                                        encprefix + ":itemType",
337                                        "CDATA",
338                                        compType);
339
340                     attrs.addAttribute(soap.getEncodingURI(),
341                                        "arraySize",
342                                        encprefix + ":arraySize",
343                                        "CDATA",
344                                    arrayType);
345                 }
346             }
347
348             // Force type to be SOAP_ARRAY for all array serialization.
349
//
350
// There are two choices here:
351
// Force the type to type=SOAP_ARRAY
352
// Pros: More interop test successes.
353
// Cons: Since we have specific type information it
354
// is more correct to use it. Plus the specific
355
// type information may be important on the
356
// server side to disambiguate overloaded operations.
357
// Use the specific type information:
358
// Pros: The specific type information is more correct
359
// and may be useful for operation overloading.
360
// Cons: More interop test failures (as of 2/6/2002).
361
//
362
String JavaDoc qname =
363                     context.getPrefixForURI(schema.getXsiURI(),
364                                             "xsi") + ":type";
365             QName JavaDoc soapArray;
366             if (soap == SOAPConstants.SOAP12_CONSTANTS) {
367                 soapArray = Constants.SOAP_ARRAY12;
368             } else {
369                 soapArray = Constants.SOAP_ARRAY;
370             }
371
372             int typeI = attrs.getIndex(schema.getXsiURI(),
373                                        "type");
374             if (typeI != -1) {
375                 attrs.setAttribute(typeI,
376                                    schema.getXsiURI(),
377                                    "type",
378                                    qname,
379                                    "CDATA",
380                                    context.qName2String(soapArray));
381             } else {
382                 attrs.addAttribute(schema.getXsiURI(),
383                                    "type",
384                                    qname,
385                                    "CDATA",
386                                    context.qName2String(soapArray));
387             }
388
389             attributes = attrs;
390         }
391
392         // For the maxOccurs case, each item is named with the QName
393
// we got in the arguments. For normal array case, we write an element with
394
// that QName, and then serialize each item as <item>
395
QName JavaDoc elementName = name;
396         Attributes JavaDoc serializeAttr = attributes;
397         if (!maxOccursUsage) {
398             serializeAttr = null; // since we are putting them here
399
context.startElement(name, attributes);
400             if (itemQName != null)
401                 elementName = itemQName;
402             else if(componentQName != null)
403                 elementName = componentQName;
404         }
405
406
407         if (dim2Len < 0) {
408             // Normal case, serialize each array element
409
if (list == null) {
410                 for (int index = 0; index < len; index++) {
411                     Object JavaDoc aValue = Array.get(value, index);
412
413                     // Serialize the element.
414
context.serialize(elementName,
415                             (serializeAttr == null ?
416                             serializeAttr : new AttributesImpl JavaDoc(serializeAttr)),
417                             aValue,
418                             componentTypeQName); // prefered type QName
419
}
420             } else {
421                 for (Iterator JavaDoc iterator = list.iterator(); iterator.hasNext();) {
422                     Object JavaDoc aValue = iterator.next();
423
424                     // Serialize the element.
425
context.serialize(elementName,
426                             (serializeAttr == null ?
427                             serializeAttr : new AttributesImpl JavaDoc(serializeAttr)),
428                             aValue,
429                             componentTypeQName); // prefered type QName
430
}
431             }
432         } else {
433             // Serialize as a 2 dimensional array
434
for (int index = 0; index < len; index++) {
435                 for (int index2 = 0; index2 < dim2Len; index2++) {
436                     Object JavaDoc aValue = Array.get(Array.get(value, index), index2);
437                     context.serialize(elementName, null, aValue, componentTypeQName);
438                 }
439             }
440         }
441
442         if (!maxOccursUsage)
443             context.endElement();
444     }
445
446     public String JavaDoc getMechanismType() { return Constants.AXIS_SAX; }
447
448     private static boolean isArray(Class JavaDoc clazz)
449     {
450         return clazz.isArray() || java.util.Collection JavaDoc.class.isAssignableFrom(clazz);
451     }
452
453     private static Class JavaDoc getComponentType(Class JavaDoc clazz)
454     {
455         if (clazz.isArray())
456         {
457             return clazz.getComponentType();
458         }
459         else if (java.util.Collection JavaDoc.class.isAssignableFrom(clazz))
460         {
461             return Object JavaDoc.class;
462         }
463         else
464         {
465             return null;
466         }
467     }
468
469
470     /**
471      * Return XML schema for the specified type, suitable for insertion into
472      * the &lt;types&gt; element of a WSDL document, or underneath an
473      * &lt;element&gt; or &lt;attribute&gt; declaration.
474      *
475      * @param javaType the Java Class we're writing out schema for
476      * @param types the Java2WSDL Types object which holds the context
477      * for the WSDL being generated.
478      * @return a type element containing a schema simpleType/complexType
479      * @see org.apache.axis.wsdl.fromJava.Types
480      */

481     public Element JavaDoc writeSchema(Class JavaDoc javaType, Types types) throws Exception JavaDoc {
482         boolean encoded = true;
483         MessageContext mc = MessageContext.getCurrentContext();
484         if (mc != null) {
485             encoded = mc.isEncoded();
486         } else {
487             encoded = types.getServiceDesc().getUse() == Use.ENCODED;
488         }
489         
490         if (!encoded) {
491             Class JavaDoc cType = Object JavaDoc.class;
492             if (javaType.isArray()) {
493                 cType = javaType.getComponentType();
494             }
495
496             String JavaDoc typeName = types.writeType(cType);
497             return types.createLiteralArrayElement(typeName, null);
498         }
499         
500         // If an array the component type should be processed first
501
String JavaDoc componentTypeName = null;
502         Class JavaDoc componentType = null;
503         if (isArray(javaType)) {
504             String JavaDoc dimString = "[]";
505             componentType = getComponentType(javaType);
506             while (isArray(componentType)) {
507                 dimString += "[]";
508                 componentType = getComponentType(componentType);
509             }
510             types.writeType(componentType,null);
511
512             componentTypeName =
513                     types.getQNameString(types.getTypeQName(componentType)) +
514                     dimString;
515         }
516
517         // Use Types helper method to actually create the complexType
518
return types.createArrayElement(componentTypeName);
519     }
520 }
521
Popular Tags