Source code: org/apache/axis/wsdl/fromJava/Types.java
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 package org.apache.axis.wsdl.fromJava;
17
18 import org.apache.axis.AxisFault;
19 import org.apache.axis.AxisProperties;
20 import org.apache.axis.Constants;
21 import org.apache.axis.InternalException;
22 import org.apache.axis.MessageContext;
23 import org.apache.axis.handlers.soap.SOAPService;
24 import org.apache.axis.components.logger.LogFactory;
25 import org.apache.axis.constants.Style;
26 import org.apache.axis.description.ServiceDesc;
27 import org.apache.axis.encoding.Serializer;
28 import org.apache.axis.encoding.SerializerFactory;
29 import org.apache.axis.encoding.SimpleType;
30 import org.apache.axis.encoding.TypeMapping;
31 import org.apache.axis.encoding.ser.BeanSerializerFactory;
32 import org.apache.axis.encoding.ser.EnumSerializerFactory;
33 import org.apache.axis.soap.SOAPConstants;
34 import org.apache.axis.utils.JavaUtils;
35 import org.apache.axis.utils.Messages;
36 import org.apache.axis.utils.XMLUtils;
37 import org.apache.axis.utils.StringUtils;
38 import org.apache.axis.wsdl.symbolTable.BaseTypeMapping;
39 import org.apache.axis.wsdl.symbolTable.SymbolTable;
40 import org.apache.axis.wsdl.symbolTable.TypeEntry;
41 import org.apache.commons.logging.Log;
42 import org.w3c.dom.Attr;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.NamedNodeMap;
46 import org.w3c.dom.Node;
47 import org.w3c.dom.NodeList;
48 import org.xml.sax.SAXException;
49
50 import javax.wsdl.Definition;
51 import javax.wsdl.WSDLException;
52 import javax.xml.namespace.QName;
53 import javax.xml.parsers.ParserConfigurationException;
54 import javax.xml.rpc.holders.Holder;
55 import java.io.IOException;
56 import java.lang.reflect.Field;
57 import java.lang.reflect.Modifier;
58 import java.net.URL;
59 import java.util.ArrayList;
60 import java.util.HashMap;
61 import java.util.HashSet;
62 import java.util.Iterator;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66
67 /**
68 * <p>Description: </p> This class is used to recursively serializes a Java Class into
69 * an XML Schema representation.
70 * <p/>
71 * It has utility methods to create a schema node, assosiate namespaces to the various types
72 *
73 * @author unascribed
74 */
75 public class Types {
76
77 /** Field log */
78 protected static Log log = LogFactory.getLog(Types.class.getName());
79
80 /** Field def */
81 Definition def;
82
83 /** Field namespaces */
84 Namespaces namespaces = null;
85
86 /** Field tm */
87 TypeMapping tm;
88
89 /** Field defaultTM */
90 TypeMapping defaultTM;
91
92 /** Field targetNamespace */
93 String targetNamespace;
94
95 /** Field wsdlTypesElem */
96 Element wsdlTypesElem = null;
97
98 /** Field schemaTypes */
99 HashMap schemaTypes = null;
100
101 /** Field schemaElementNames */
102 HashMap schemaElementNames = null;
103
104 /** Field schemaUniqueElementNames */
105 HashMap schemaUniqueElementNames = null;
106
107 /** Field wrapperMap */
108 HashMap wrapperMap = new HashMap();
109
110 /** Field stopClasses */
111 List stopClasses = null;
112
113 /** Field beanCompatErrs */
114 List beanCompatErrs = new ArrayList();
115
116 /** Field serviceDesc */
117 private ServiceDesc serviceDesc = null;
118
119 /** Keep track of the element QNames we've written to avoid dups */
120 private Set writtenElementQNames = new HashSet();
121
122 /** Which types have we already written? */
123 Class [] mappedTypes = null;
124
125 /** The java to wsdl emitter */
126 Emitter emitter = null;
127
128 public static boolean isArray(Class clazz)
129 {
130 return clazz.isArray() || java.util.Collection.class.isAssignableFrom(clazz);
131 }
132
133 private static Class getComponentType(Class clazz)
134 {
135 if (clazz.isArray())
136 {
137 return clazz.getComponentType();
138 }
139 else if (java.util.Collection.class.isAssignableFrom(clazz))
140 {
141 return Object.class;
142 }
143 else
144 {
145 return null;
146 }
147 }
148
149 /**
150 * This class serailizes a <code>Class</code> to XML Schema. The constructor
151 * provides the context for the streamed node within the WSDL document
152 *
153 * @param def WSDL Definition Element to declare namespaces
154 * @param tm TypeMappingRegistry to handle known types
155 * @param defaultTM default TM
156 * @param namespaces user defined or autogenerated namespace and prefix maps
157 * @param targetNamespace targetNamespace of the document
158 * @param stopClasses
159 * @param serviceDesc
160 */
161 public Types(Definition def, TypeMapping tm, TypeMapping defaultTM,
162 Namespaces namespaces, String targetNamespace,
163 List stopClasses, ServiceDesc serviceDesc) {
164
165 this.def = def;
166 this.serviceDesc = serviceDesc;
167
168 createDocumentFragment();
169
170 this.tm = tm;
171 this.defaultTM = defaultTM;
172
173 mappedTypes = tm.getAllClasses();
174
175 this.namespaces = namespaces;
176 this.targetNamespace = targetNamespace;
177 this.stopClasses = stopClasses;
178 schemaElementNames = new HashMap();
179 schemaUniqueElementNames = new HashMap();
180 schemaTypes = new HashMap();
181 }
182
183 /**
184 * This class serailizes a <code>Class</code> to XML Schema. The constructor
185 * provides the context for the streamed node within the WSDL document
186 *
187 * @param def WSDL Definition Element to declare namespaces
188 * @param tm TypeMappingRegistry to handle known types
189 * @param defaultTM default TM
190 * @param namespaces user defined or autogenerated namespace and prefix maps
191 * @param targetNamespace targetNamespace of the document
192 * @param stopClasses
193 * @param serviceDesc
194 * @param emitter Java2Wsdl emitter
195 */
196 public Types(Definition def, TypeMapping tm, TypeMapping defaultTM,
197 Namespaces namespaces, String targetNamespace,
198 List stopClasses, ServiceDesc serviceDesc, Emitter emitter) {
199 this(def, tm, defaultTM, namespaces, targetNamespace, stopClasses, serviceDesc);
200 this.emitter = emitter;
201 }
202
203 /**
204 * Return the namespaces object for the current context
205 *
206 * @return
207 */
208 public Namespaces getNamespaces() {
209 return namespaces;
210 }
211
212 /**
213 * Loads the types from the input schema file.
214 *
215 * @param inputSchema file or URL
216 * @throws IOException
217 * @throws WSDLException
218 * @throws SAXException
219 * @throws ParserConfigurationException
220 */
221 public void loadInputSchema(String inputSchema)
222 throws IOException, WSDLException, SAXException,
223 ParserConfigurationException {
224
225 // Read the input wsdl file into a Document
226 Document doc = XMLUtils.newDocument(inputSchema);
227
228 // Ensure that the root element is xsd:schema
229 Element root = doc.getDocumentElement();
230
231 if (root.getLocalName().equals("schema")
232 && Constants.isSchemaXSD(root.getNamespaceURI())) {
233 Node schema = docHolder.importNode(root, true);
234
235 if (null == wsdlTypesElem) {
236 writeWsdlTypesElement();
237 }
238
239 wsdlTypesElem.appendChild(schema);
240
241 // Create a symbol table and populate it with the input types
242 BaseTypeMapping btm = new BaseTypeMapping() {
243
244 public String getBaseName(QName qNameIn) {
245
246 QName qName = new QName(qNameIn.getNamespaceURI(),
247 qNameIn.getLocalPart());
248 Class cls = defaultTM.getClassForQName(qName);
249
250 if (cls == null) {
251 return null;
252 } else {
253 return JavaUtils.getTextClassName(cls.getName());
254 }
255 }
256 };
257 SymbolTable symbolTable = new SymbolTable(btm, true, false, false);
258
259 symbolTable.populateTypes(new URL(inputSchema), doc);
260 processSymTabEntries(symbolTable);
261 } else {
262
263 // If not, we'll just bail out... perhaps we should log a warning
264 // or throw an exception?
265 ;
266 }
267 }
268
269 /**
270 * Walk the type/element entries in the symbol table and
271 * add each one to the list of processed types. This prevents
272 * the types from being duplicated.
273 *
274 * @param symbolTable
275 */
276 private void processSymTabEntries(SymbolTable symbolTable) {
277
278 Iterator iterator = symbolTable.getElementIndex().entrySet().iterator();
279
280 while (iterator.hasNext()) {
281 Map.Entry me = (Map.Entry) iterator.next();
282 QName name = (QName) me.getKey();
283 TypeEntry te = (TypeEntry) me.getValue();
284 String prefix = XMLUtils.getPrefix(name.getNamespaceURI(),
285 te.getNode());
286
287 if (!((null == prefix) || "".equals(prefix))) {
288 namespaces.putPrefix(name.getNamespaceURI(), prefix);
289 def.addNamespace(prefix, name.getNamespaceURI());
290 }
291
292 addToElementsList(name);
293 }
294
295 iterator = symbolTable.getTypeIndex().entrySet().iterator();
296
297 while (iterator.hasNext()) {
298 Map.Entry me = (Map.Entry) iterator.next();
299 QName name = (QName) me.getKey();
300 TypeEntry te = (TypeEntry) me.getValue();
301 String prefix = XMLUtils.getPrefix(name.getNamespaceURI(),
302 te.getNode());
303
304 if (!((null == prefix) || "".equals(prefix))) {
305 namespaces.putPrefix(name.getNamespaceURI(), prefix);
306 def.addNamespace(prefix, name.getNamespaceURI());
307 }
308
309 addToTypesList(name);
310 }
311 }
312
313 /**
314 * Load the types from the input wsdl file.
315 *
316 * @param inputWSDL file or URL
317 * @throws IOException
318 * @throws WSDLException
319 * @throws SAXException
320 * @throws ParserConfigurationException
321 */
322 public void loadInputTypes(String inputWSDL)
323 throws IOException, WSDLException, SAXException,
324 ParserConfigurationException {
325
326 // Read the input wsdl file into a Document
327 Document doc = XMLUtils.newDocument(inputWSDL);
328
329 // Search for the 'types' element
330 NodeList elements = doc.getChildNodes();
331
332 if ((elements.getLength() > 0)
333 && elements.item(0).getLocalName().equals("definitions")) {
334 elements = elements.item(0).getChildNodes();
335
336 for (int i = 0;
337 (i < elements.getLength()) && (wsdlTypesElem == null);
338 i++) {
339 Node node = elements.item(i);
340
341 if ((node.getLocalName() != null)
342 && node.getLocalName().equals("types")) {
343 wsdlTypesElem = (Element) node;
344 }
345 }
346 }
347
348 // If types element not found, there is no need to continue.
349 if (wsdlTypesElem == null) {
350 return;
351 }
352
353 // Import the types element into the Types docHolder document
354 wsdlTypesElem = (Element) docHolder.importNode(wsdlTypesElem, true);
355
356 docHolder.appendChild(wsdlTypesElem);
357
358 // Create a symbol table and populate it with the input wsdl document
359 BaseTypeMapping btm = new BaseTypeMapping() {
360
361 public String getBaseName(QName qNameIn) {
362
363 QName qName = new QName(qNameIn.getNamespaceURI(),
364 qNameIn.getLocalPart());
365 Class cls = tm.getClassForQName(qName);
366
367 if (cls == null) {
368 return null;
369 } else {
370 return JavaUtils.getTextClassName(cls.getName());
371 }
372 }
373 };
374 SymbolTable symbolTable = new SymbolTable(btm, true, false, false);
375
376 symbolTable.populate(null, doc);
377 processSymTabEntries(symbolTable);
378 }
379
380 /**
381 * Write out a type referenced by a part type attribute.
382 *
383 * @param type <code>Class</code> to generate the XML Schema info for
384 * @param qname <code>QName</code> of the type. If null, qname is
385 * defaulted from the class.
386 * @return the QName of the generated Schema type, null if void,
387 * if the Class type cannot be converted to a schema type
388 * then xsd:anytype is returned.
389 * @throws AxisFault
390 */
391 public QName writeTypeForPart(Class type, QName qname) throws AxisFault {
392
393 // patch by costin to fix an NPE; commented out till we find out what the problem is
394 // if you get NullPointerExceptions in this class, turn it on and submit some
395 // replicable test data to the Axis team via bugzilla
396
397 /*
398 * if( type==null ) {
399 * return null;
400 * }
401 */
402 if (type.getName().equals("void")) {
403 return null;
404 }
405
406 if (Holder.class.isAssignableFrom(type)) {
407 type = JavaUtils.getHolderValueType(type);
408 }
409
410 // Get the qname
411 if ((qname == null)
412 || (Constants.isSOAP_ENC(qname.getNamespaceURI())
413 && "Array".equals(qname.getLocalPart()))) {
414 qname = getTypeQName(type);
415
416 if (qname == null) {
417 throw new AxisFault("Class:" + type.getName());
418 }
419 }
420
421 if (!makeTypeElement(type, qname, null)) {
422 qname = Constants.XSD_ANYTYPE;
423 }
424
425 return qname;
426 }
427
428 /**
429 * Write out a type (and its subtypes) referenced by a part type attribute.
430 *
431 * @param type <code>Class</code> to generate the XML Schema info for
432 * @param qname <code>QName</code> of the type. If null, qname is
433 * defaulted from the class.
434 * @return the QName of the generated Schema type, null if void,
435 * if the Class type cannot be converted to a schema type
436 * then xsd:anytype is returned.
437 */
438 public QName writeTypeAndSubTypeForPart(Class type, QName qname)
439 throws AxisFault {
440
441 // Write out type in parameter
442 QName qNameRet = writeTypeForPart(type, qname);
443
444 // If mappedTypesexists
445 // Will write subTypes of the type in parameters
446 if (mappedTypes != null) {
447 for (int i = 0; i < mappedTypes.length; i++) {
448 Class tempMappedType = mappedTypes[i];
449 QName name;
450
451 // If tempMappedType is subtype of the "type" parameter
452 // and type is not Object (Object superclass of all Java class...)
453 // write the subtype
454 if (tempMappedType != null &&
455 type != Object.class &&
456 tempMappedType != type &&
457 type.isAssignableFrom(tempMappedType)) {
458 name = tm.getTypeQName(tempMappedType);
459 if (!isAnonymousType(name)) {
460 writeTypeForPart(tempMappedType, name);
461 }
462
463 // Only do each one once. This is OK to do because each
464 // Types instance is for generating a single WSDL.
465 mappedTypes[i] = null;
466 }
467 }
468 } //if (mappedTyped != null) {
469
470 return qNameRet;
471 }
472
473 /**
474 * Write out an element referenced by a part element attribute.
475 *
476 * @param type <code>Class</code> to generate the XML Schema info for
477 * @param qname <code>QName</code> of the element. If null, qname is
478 * defaulted from the class.
479 * @return the QName of the generated Schema type, null if no element
480 * @throws AxisFault
481 */
482 public QName writeElementForPart(Class type, QName qname) throws AxisFault {
483
484 // patch by costin to fix an NPE; commented out till we find out what the problem is
485 // if you get NullPointerExceptions in this class, turn it on and submit some
486 // replicable test data to the Axis team via bugzilla
487
488 /*
489 * if( type==null ) {
490 * return null;
491 * }
492 */
493 if (type.getName().equals("void")) {
494 return null;
495 }
496
497 if (Holder.class.isAssignableFrom(type)) {
498 type = JavaUtils.getHolderValueType(type);
499 }
500
501 // Get the qname
502 if ((qname == null)
503 || (Constants.isSOAP_ENC(qname.getNamespaceURI())
504 && "Array".equals(qname.getLocalPart()))) {
505 qname = getTypeQName(type);
506
507 if (qname == null) {
508 throw new AxisFault("Class:" + type.getName());
509 }
510 }
511
512 // Return null it a simple type (not an element)
513 String nsURI = qname.getNamespaceURI();
514
515 if (Constants.isSchemaXSD(nsURI)
516 || (Constants.isSOAP_ENC(nsURI)
517 && !"Array".equals(qname.getLocalPart()))) {
518 return null;
519 }
520
521 // Make sure a types section is present
522 if (wsdlTypesElem == null) {
523 writeWsdlTypesElement();
524 }
525
526 // Write Element, if problems occur return null.
527 if (writeTypeAsElement(type, qname) == null) {
528 qname = null;
529 }
530
531 return qname;
532 }
533
534 /**
535 * Write the element definition for a WRAPPED operation. This will
536 * write out any necessary namespace/schema declarations, an an element
537 * definition with an internal (anonymous) complexType. The name of the
538 * element will be *foo*Request or *foo*Response depending on whether the
539 * request boolean is true. If the operation contains parameters, then
540 * we also generate a >sequence< node underneath the complexType,
541 * and return it for later use by writeWrappedParameter() below.
542 *
543 * @param qname the desired element QName
544 * @param request true if we're writing the request wrapper, false if
545 * writing the response.
546 * @param hasParams true if there are parameters, and thus a sequence
547 * node is needed
548 * @return a DOM Element for the sequence, inside which we'll write the
549 * parameters as elements, or null if there are no parameters
550 * @throws AxisFault
551 */
552 public Element writeWrapperElement(
553 QName qname, boolean request, boolean hasParams) throws AxisFault {
554
555 // Make sure a types section is present
556 if (wsdlTypesElem == null) {
557 writeWsdlTypesElement();
558 }
559
560 // Write the namespace definition for the wrapper
561 writeTypeNamespace(qname.getNamespaceURI());
562
563 // Create an <element> for the wrapper
564 Element wrapperElement = docHolder.createElement("element");
565
566 writeSchemaElementDecl(qname, wrapperElement);
567 wrapperElement.setAttribute("name", qname.getLocalPart());
568
569 // Create an anonymous <complexType> for the wrapper
570 Element complexType = docHolder.createElement("complexType");
571
572 wrapperElement.appendChild(complexType);
573
574 // If we have parameters in the operation, create a <sequence>
575 // under the complexType and return it.
576 if (hasParams) {
577 Element sequence = docHolder.createElement("sequence");
578
579 complexType.appendChild(sequence);
580
581 return sequence;
582 }
583
584 return null;
585 }
586
587
588 /**
589 * Write a parameter (a sub-element) into a sequence generated by
590 * writeWrapperElement() above.
591 *
592 * @param sequence the <sequence> in which we're writing
593 * @param name is the name of an element to add to the wrapper element.
594 * @param type is the QName of the type of the element.
595 * @param javaType
596 * @throws AxisFault
597 */
598 public void writeWrappedParameter(
599 Element sequence, String name, QName type, Class javaType)
600 throws AxisFault {
601
602 if (javaType == void.class) {
603 return;
604 }
605
606 // JAX-RPC 1.1 says that byte[] should always be a Base64Binary
607 // This (rather strange) hack will ensure that we don't map it
608 // in to an maxoccurs=unbounded array.
609 if (javaType.isArray() && !javaType.equals(byte[].class)) {
610 type = writeTypeForPart(javaType.getComponentType(), null);
611 } else {
612 type = writeTypeForPart(javaType, type);
613 }
614
615 if (type == null) {
616 // TODO: throw an Exception!!
617 return;
618 }
619
620 Element childElem;
621
622 if (isAnonymousType(type)) {
623 childElem = createElementWithAnonymousType(name, javaType, false,
624 docHolder);
625 } else {
626
627 // Create the child <element> and add it to the wrapper <sequence>
628 childElem = docHolder.createElement("element");
629
630 childElem.setAttribute("name", name);
631
632 String prefix =
633 namespaces.getCreatePrefix(type.getNamespaceURI());
634 String prefixedName = prefix + ":" + type.getLocalPart();
635
636 childElem.setAttribute("type", prefixedName);
637
638 // JAX-RPC 1.1 says that byte[] should always be a Base64Binary
639 // This (rather strange) hack will ensure that we don't map it
640 // in to an maxoccurs=unbounded array.
641 if (javaType.isArray() && !javaType.equals(byte[].class)) {
642 childElem.setAttribute("maxOccurs", "unbounded");
643 }
644 }
645
646 sequence.appendChild(childElem);
647 }
648
649 /**
650 * Method isAnonymousType
651 *
652 * @param type
653 * @return
654 */
655 private boolean isAnonymousType(QName type) {
656 return type.getLocalPart().indexOf(SymbolTable.ANON_TOKEN) != -1;
657 }
658
659 /**
660 * Create a schema element for the given type
661 *
662 * @param type the class type
663 * @param qName
664 * @return the QName of the generated Element or problems occur
665 * @throws AxisFault
666 */
667 private QName writeTypeAsElement(Class type, QName qName) throws AxisFault {
668
669 if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) {
670 qName = getTypeQName(type);
671 }
672
673 writeTypeNamespace(type, qName);
674 String elementType = writeType(type, qName);
675
676 if (elementType != null) {
677
678 // Element element = createElementDecl(qName.getLocalPart(), type, qName, isNullable(type), false);
679 // if (element != null)
680 // writeSchemaElement(typeQName,element);
681 return qName;
682 }
683
684 return null;
685 }
686
687 /**
688 * write out the namespace declaration and return the type QName for the
689 * given <code>Class</code>
690 *
691 * @param type input Class
692 * @param qName qname of the Class
693 * @return QName for the schema type representing the class
694 */
695 private QName writeTypeNamespace(Class type, QName qName) {
696
697 if (qName == null) {
698 qName = getTypeQName(type);
699 }
700
701 writeTypeNamespace(qName.getNamespaceURI());
702
703 return qName;
704 }
705
706 /**
707 * write out the namespace declaration.
708 *
709 * @param namespaceURI qname of the type
710 */
711 private void writeTypeNamespace(String namespaceURI) {
712
713 if ((namespaceURI != null) && !namespaceURI.equals("")) {
714 String pref = def.getPrefix(namespaceURI);
715
716 if (pref == null) {
717 def.addNamespace(namespaces.getCreatePrefix(namespaceURI),
718 namespaceURI);
719 }
720 }
721 }
722
723 /**
724 * Return the QName of the specified javaType
725 *
726 * @param javaType input javaType Class
727 * @return QName
728 */
729 public QName getTypeQName(Class javaType) {
730 QName qName = null;
731
732 // Use the typeMapping information to lookup the qName.
733 qName = tm.getTypeQName(javaType);
734
735 // If the javaType is an array and the qName is
736 // SOAP_ARRAY, construct the QName using the
737 // QName of the component type
738 if (isArray(javaType) &&
739 Constants.equals(Constants.SOAP_ARRAY, qName)) {
740 Class componentType = getComponentType(javaType);
741
742 // For WS-I BP compliance, we can't use "ArrayOf" as a type prefix
743 // instead use "MyArrayOf" (gag)
744 String arrayTypePrefix = "ArrayOf";
745
746 boolean isWSICompliant = JavaUtils.isTrue(
747 AxisProperties.getProperty(Constants.WSIBP11_COMPAT_PROPERTY));
748 if (isWSICompliant) {
749 arrayTypePrefix = "MyArrayOf";
750 }
751
752 // If component namespace uri == targetNamespace
753 // Construct ArrayOf<componentLocalPart>
754 // Else
755 // Construct ArrayOf_<componentPrefix>_<componentLocalPart>
756 QName cqName = getTypeQName(componentType);
757
758 if (targetNamespace.equals(cqName.getNamespaceURI())) {
759 qName = new QName(targetNamespace,
760 arrayTypePrefix + cqName.getLocalPart());
761 } else {
762 String pre =
763 namespaces.getCreatePrefix(cqName.getNamespaceURI());
764
765 qName = new QName(targetNamespace,
766 arrayTypePrefix + "_" + pre + "_"
767 + cqName.getLocalPart());
768 }
769
770 return qName;
771 }
772
773 // If a qName was not found construct one using the
774 // class name information.
775 if (qName == null) {
776 String pkg = getPackageNameFromFullName(javaType.getName());
777 String lcl = getLocalNameFromFullName(javaType.getName());
778 String ns = namespaces.getCreate(pkg);
779
780 namespaces.getCreatePrefix(ns);
781
782 String localPart = lcl.replace('$', '_');
783
784 qName = new QName(ns, localPart);
785 }
786
787 return qName;
788 }
789
790 /**
791 * Return a string suitable for representing a given QName in the context
792 * of this WSDL document. If the namespace of the QName is not yet
793 * registered, we will register it up in the Definitions.
794 *
795 * @param qname a QName (typically a type)
796 * @return a String containing a standard "ns:localPart" rep of the QName
797 */
798 public String getQNameString(QName qname) {
799
800 String prefix = namespaces.getCreatePrefix(qname.getNamespaceURI());
801
802 return prefix + ":" + qname.getLocalPart();
803 }
804
805 /**
806 * Utility method to get the package name from a fully qualified java class name
807 *
808 * @param full input class name
809 * @return package name
810 */
811 public static String getPackageNameFromFullName(String full) {
812
813 if (full.lastIndexOf('.') < 0) {
814 return "";
815 } else {
816 return full.substring(0, full.lastIndexOf('.'));
817 }
818 }
819
820 /**
821 * Utility method to get the local class name from a fully qualified java class name
822 *
823 * @param full input class name
824 * @return package name
825 */
826 public static String getLocalNameFromFullName(String full) {
827
828 String end = "";
829
830 if (full.startsWith("[L")) {
831 end = "[]";
832 full = full.substring(3, full.length() - 1);
833 }
834
835 if (full.lastIndexOf('.') < 0) {
836 return full + end;
837 } else {
838 return full.substring(full.lastIndexOf('.') + 1) + end;
839 }
840 }
841
842 /**
843 * Method writeSchemaTypeDecl
844 *
845 * @param qname
846 * @param element
847 * @throws AxisFault
848 */
849 public void writeSchemaTypeDecl(QName qname, Element element)
850 throws AxisFault {
851 writeSchemaElement(qname.getNamespaceURI(), element);
852 }
853
854 /**
855 * Method writeSchemaElementDecl
856 *
857 * @param qname
858 * @param element
859 * @throws AxisFault
860 */
861 public void writeSchemaElementDecl(QName qname, Element element)
862 throws AxisFault {
863
864 if (writtenElementQNames.contains(qname)) {
865 throw new AxisFault(
866 Constants.FAULT_SERVER_GENERAL,
867 Messages.getMessage(
868 "duplicateSchemaElement", qname.toString()), null, null);
869 }
870
871 writeSchemaElement(qname.getNamespaceURI(), element);
872 writtenElementQNames.add(qname);
873 }
874
875 /**
876 * @deprecated
877 * Please use writeSchemaElement(String namespaceURI, Element element)
878 *
879 * @param qName qName to get the namespace of the schema node
880 * @param element the Element to append to the Schema node
881 * @throws AxisFault
882 */
883 public void writeSchemaElement(QName qName, Element element)
884 throws AxisFault {
885 writeSchemaElement(qName.getNamespaceURI(), element);
886 }
887
888 /**
889 * Write out the given Element into the appropriate schema node.
890 * If need be create the schema node as well
891 *
892 * @param namespaceURI namespace this node should get dropped into
893 * @param element the Element to append to the Schema node
894 * @throws AxisFault
895 */
896 public void writeSchemaElement(String namespaceURI, Element element)
897 throws AxisFault {
898
899 if (wsdlTypesElem == null) {
900 try {
901 writeWsdlTypesElement();
902 } catch (Exception e) {
903 log.error(e);
904
905 return;
906 }
907 }
908
909 if ((namespaceURI == null) || namespaceURI.equals("")) {
910 throw new AxisFault(
911 Constants.FAULT_SERVER_GENERAL,
912 Messages.getMessage("noNamespace00", namespaceURI), null, null);
913 }
914
915 Element schemaElem = null;
916 NodeList nl = wsdlTypesElem.getChildNodes();
917
918 for (int i = 0; i < nl.getLength(); i++) {
919 NamedNodeMap attrs = nl.item(i).getAttributes();
920
921 if (attrs != null) {
922 for (int n = 0; n < attrs.getLength(); n++) {
923 Attr a = (Attr) attrs.item(n);
924
925 if (a.getName().equals("targetNamespace")
926 && a.getValue().equals(namespaceURI)) {
927 schemaElem = (Element) nl.item(i);
928 }
929 }
930 }
931 }
932
933 if (schemaElem == null) {
934 schemaElem = docHolder.createElement("schema");
935
936 wsdlTypesElem.appendChild(schemaElem);
937 schemaElem.setAttribute("xmlns", Constants.URI_DEFAULT_SCHEMA_XSD);
938 schemaElem.setAttribute("targetNamespace", namespaceURI);
939
940 // Add SOAP-ENC namespace import if necessary
941 if (serviceDesc.getStyle() == Style.RPC) {
942 Element importElem = docHolder.createElement("import");
943
944 schemaElem.appendChild(importElem);
945 importElem.setAttribute("namespace",
946 Constants.URI_DEFAULT_SOAP_ENC);
947 }
948
949 SOAPService service = null;
950 if(MessageContext.getCurrentContext() != null) {
951 service = MessageContext.getCurrentContext().getService();
952 }
953 if(service != null && isPresent((String) service.getOption("schemaQualified"), namespaceURI)){
954 schemaElem.setAttribute("elementFormDefault", "qualified");
955 } else if(service != null && isPresent((String) service.getOption("schemaUnqualified"), namespaceURI)){
956 // DO nothing..default is unqualified.
957 } else if ((serviceDesc.getStyle() == Style.DOCUMENT)
958 || (serviceDesc.getStyle() == Style.WRAPPED)) {
959 schemaElem.setAttribute("elementFormDefault", "qualified");
960 }
961
962 writeTypeNamespace(namespaceURI);
963 }
964
965 schemaElem.appendChild(element);
966 }
967
968 /**
969 * check if the namespace is present in the list.
970 * @param list
971 * @param namespace
972 * @return
973 */
974 private boolean isPresent(String list, String namespace) {
975 if(list == null || list.length()==0)
976 return false;
977 String[] array = StringUtils.split(list,',');
978 for(int i=0;i<array.length;i++){
979 if(array[i].equals(namespace))
980 return true;
981 }
982 return false;
983 }
984
985 /**
986 * Get the Types element for the WSDL document. If not present, create one
987 */
988 private void writeWsdlTypesElement() {
989
990 if (wsdlTypesElem == null) {
991
992 // Create a <wsdl:types> element corresponding to the wsdl namespaces.
993 wsdlTypesElem = docHolder.createElementNS(Constants.NS_URI_WSDL11,
994 "types");
995
996 wsdlTypesElem.setPrefix(Constants.NS_PREFIX_WSDL);
997 }
998 }
999
1000 /**
1001 * Write a schema representation for the given <code>Class</code>. Recurse
1002 * through all the public fields as well as fields represented by java
1003 * bean compliant accessor methods.
1004 * <p/>
1005 * Then return the qualified string representation of the generated type
1006 *
1007 * @param type Class for which to generate schema
1008 * @return a prefixed string for the schema type
1009 * @throws AxisFault
1010 */
1011 public String writeType(Class type) throws AxisFault {
1012 return writeType(type, null);
1013 }
1014
1015 /**
1016 * Write a schema representation for the given <code>Class</code>. Recurse
1017 * through all the public fields as well as fields represented by java
1018 * bean compliant accessor methods.
1019 * <p/>
1020 * Then return the qualified string representation of the generated type
1021 *
1022 * @param type Class for which to generate schema
1023 * @param qName of the type to write
1024 * @return a prefixed string for the schema type or null if problems occur
1025 * @throws AxisFault
1026 */
1027 public String writeType(Class type, QName qName) throws AxisFault {
1028
1029 // Get a corresponding QName if one is not provided
1030 if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) {
1031 qName = getTypeQName(type);
1032 }
1033
1034 if (!makeTypeElement(type, qName, null)) {
1035 return null;
1036 }
1037
1038 return getQNameString(qName);
1039 }
1040
1041 /**
1042 * Method createArrayElement
1043 *
1044 * @param componentTypeName
1045 * @return
1046 */
1047 public Element createArrayElement(String componentTypeName) {
1048
1049 SOAPConstants constants;
1050 MessageContext mc = MessageContext.getCurrentContext();
1051 if(mc==null||mc.getSOAPConstants()==null){
1052 constants = SOAPConstants.SOAP11_CONSTANTS;
1053 } else {
1054 constants = mc.getSOAPConstants();
1055 }
1056 String prefix = namespaces.getCreatePrefix(constants.getEncodingURI());
1057 // ComplexType representation of array
1058 Element complexType = docHolder.createElement("complexType");
1059 Element complexContent = docHolder.createElement("complexContent");
1060
1061 complexType.appendChild(complexContent);
1062
1063 Element restriction = docHolder.createElement("restriction");
1064
1065 complexContent.appendChild(restriction);
1066 restriction.setAttribute("base",
1067 prefix + ":Array");
1068
1069 Element attribute = docHolder.createElement("attribute");
1070
1071 restriction.appendChild(attribute);
1072
1073 attribute.setAttribute("ref",
1074 prefix + ":arrayType");
1075
1076 prefix = namespaces.getCreatePrefix(Constants.NS_URI_WSDL11);
1077 attribute.setAttribute(prefix + ":arrayType",
1078 componentTypeName);
1079
1080 return complexType;
1081 }
1082
1083 /**
1084 * Create an array which is a wrapper type for "item" elements
1085 * of a component type. This is basically the unencoded parallel to
1086 * a SOAP-encoded array.
1087 *
1088 * @param componentType
1089 * @param itemName the QName of the inner element (right now we only use the localPart)
1090 * @return
1091 */
1092 public Element createLiteralArrayElement(String componentType,
1093 QName itemName) {
1094 String itemLocalName = "item";
1095 if (itemName != null) {
1096 itemLocalName = itemName.getLocalPart();
1097 }
1098
1099 Element complexType = docHolder.createElement("complexType");
1100 Element sequence = docHolder.createElement("sequence");
1101
1102 complexType.appendChild(sequence);
1103
1104 Element elem = docHolder.createElement("element");
1105 elem.setAttribute("name", itemLocalName);
1106 elem.setAttribute("type", componentType);
1107 elem.setAttribute("minOccurs", "0");
1108 elem.setAttribute("maxOccurs", "unbounded");
1109
1110 sequence.appendChild(elem);
1111
1112 return complexType;
1113 }
1114
1115 /**
1116 * Returns true if indicated type matches the JAX-RPC enumeration class.
1117 * Note: supports JSR 101 version 0.6 Public Draft
1118 *
1119 * @param cls
1120 * @return
1121 */
1122 public static boolean isEnumClass(Class cls) {
1123
1124 try {
1125 java.lang.reflect.Method m = cls.getMethod("getValue", null);
1126 java.lang.reflect.Method m2 = cls.getMethod("toString", null);
1127
1128 if ((m != null) && (m2 != null)) {
1129 java.lang.reflect.Method m3 =
1130 cls.getDeclaredMethod("fromString",
1131 new Class[]{
1132 java.lang.String.class});
1133 java.lang.reflect.Method m4 = cls.getDeclaredMethod("fromValue",
1134 new Class[]{
1135 m.getReturnType()});
1136
1137 if ((m3 != null) && Modifier.isStatic(m3.getModifiers())
1138 && Modifier.isPublic(m3.getModifiers()) && (m4 != null)
1139 && Modifier.isStatic(m4.getModifiers())
1140 && Modifier.isPublic(m4.getModifiers())) {
1141
1142 // Return false if there is a setValue member method
1143 try {
1144 if (cls.getMethod("setValue", new Class[]{
1145 m.getReturnType()}) == null) {
1146 return true;
1147 }
1148
1149 return false;
1150 } catch (java.lang.NoSuchMethodException e) {
1151 return true;
1152 }
1153 }
1154 }
1155 } catch (java.lang.NoSuchMethodException e) {
1156 }
1157
1158 return false;
1159 }
1160
1161 /**
1162 * Write Enumeration Complex Type
1163 * (Only supports enumeration classes of string types)
1164 *
1165 * @param qName QName of type.
1166 * @param cls class of type
1167 * @return
1168 * @throws NoSuchMethodException
1169 * @throws IllegalAccessException
1170 * @throws AxisFault
1171 */
1172 public Element writeEnumType(QName qName, Class cls)
1173 throws NoSuchMethodException, IllegalAccessException, AxisFault {
1174
1175 if (!isEnumClass(cls)) {
1176 return null;
1177 }
1178
1179 // Get the base type of the enum class
1180 java.lang.reflect.Method m = cls.getMethod("getValue", null);
1181 Class base = m.getReturnType();
1182
1183 // Create simpleType, restriction elements
1184 Element simpleType = docHolder.createElement("simpleType");
1185
1186 simpleType.setAttribute("name", qName.getLocalPart());
1187
1188 Element restriction = docHolder.createElement("restriction");
1189
1190 simpleType.appendChild(restriction);
1191
1192 String baseType = writeType(base, null);
1193
1194 restriction.setAttribute("base", baseType);
1195
1196 // Create an enumeration using the field values
1197 Field[] fields = cls.getDeclaredFields();
1198
1199 for (int i = 0; i < fields.length; i++) {
1200 Field field = fields[i];
1201 int mod = field.getModifiers();
1202
1203 // Inspect each public static final field of the same type
1204 // as the base
1205 if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
1206 && Modifier.isFinal(mod) && (field.getType() == base)) {
1207
1208 // Create an enumeration using the value specified
1209 Element enumeration = docHolder.createElement("enumeration");
1210
1211 enumeration.setAttribute("value", field.get(null).toString());
1212 restriction.appendChild(enumeration);
1213 }
1214 }
1215
1216 return simpleType;
1217 }
1218
1219 /**
1220 * Create a top-level element declaration in our generated schema
1221 *
1222 * @param qname
1223 * @param javaType
1224 * @param typeQName
1225 * @param nillable nillable attribute of the element
1226 * @param itemQName
1227 * @throws AxisFault
1228 */
1229 public void writeElementDecl(QName qname,
1230 Class javaType,
1231 QName typeQName,
1232 boolean nillable,
1233 QName itemQName)
1234 throws AxisFault {
1235
1236 if (writtenElementQNames.contains(qname)) {
1237 return;
1238 }
1239
1240 String name = qname.getLocalPart();
1241
1242 Element element = docHolder.createElement("element");
1243
1244 // Generate an element name that matches the type.
1245 element.setAttribute("name", name);
1246
1247 if (nillable) {
1248 element.setAttribute("nillable", "true");
1249 }
1250
1251 /*
1252 * These are not legal on top-level elements!
1253 * (feel free to delete this block after say Oct 2005)
1254 if (omittable) {
1255 element.setAttribute("minOccurs", "0");
1256 element.setAttribute("maxOccurs", "1");
1257 }
1258
1259 if (javaType.isArray()) {
1260 element.setAttribute("maxOccurs", "unbounded");
1261 }
1262 */
1263
1264 if (javaType.isArray()) {
1265 // TODO : Should check to see if this array type is specifically mapped
1266 String componentType = writeType(javaType.getComponentType());
1267 Element complexType = createLiteralArrayElement(componentType,
1268 itemQName);
1269 element.appendChild(complexType);
1270 } else {
1271 // Write the type for this element, handling anonymous or named
1272 // types appropriately.
1273 makeTypeElement(javaType, typeQName, element);
1274 }
1275
1276 writeSchemaElementDecl(qname, element);
1277 }
1278
1279 /**
1280 * Create Element with a given name and type
1281 *
1282 * @param elementName the name of the created element
1283 * @param elementType schema type representation of the element
1284 * @param nullable nullable attribute of the element
1285 * @param omittable
1286 * @param docHolder
1287 * @return the created Element
1288 */
1289 public Element createElement(String elementName, String elementType,
1290 boolean nullable, boolean omittable,
1291 Document docHolder) {
1292
1293 Element element = docHolder.createElement("element");
1294
1295 element.setAttribute("name", elementName);
1296
1297 if (nullable) {
1298 element.setAttribute("nillable", "true");
1299 }
1300
1301 if (omittable) {
1302 element.setAttribute("minOccurs", "0");
1303 element.setAttribute("maxOccurs", "1");
1304 }
1305
1306 if (elementType != null) {
1307 element.setAttribute("type", elementType);
1308 }
1309
1310 return element;
1311 }
1312
1313 /**
1314 * Create Attribute Element with a given name and type
1315 *
1316 * @param elementName the name of the created element
1317 * @param javaType
1318 * @param xmlType
1319 * @param nullable nullable attribute of the element
1320 * @param docHolder
1321 * @return the created Element
1322 * @throws AxisFault
1323 */
1324 public Element createAttributeElement(
1325 String elementName, Class javaType, QName xmlType, boolean nullable, Document docHolder)
1326 throws AxisFault {
1327
1328 Element element = docHolder.createElement("attribute");
1329
1330 element.setAttribute("name", elementName);
1331
1332 if (nullable) {
1333 element.setAttribute("nillable", "true");
1334 }
1335
1336 makeTypeElement(javaType, xmlType, element);
1337
1338 return element;
1339 }
1340
1341 /**
1342 * Is the given class one of the simple types? In other words,
1343 * do we have a mapping for this type which is in the xsd or
1344 * soap-enc namespaces?
1345 *
1346 * @param type input Class
1347 * @return true if the type is a simple type
1348 */
1349 boolean isSimpleType(Class type) {
1350
1351 QName qname = tm.getTypeQName(type);
1352
1353 if (qname == null) {
1354 return false; // No mapping
1355 }
1356
1357 String nsURI = qname.getNamespaceURI();
1358
1359 return (Constants.isSchemaXSD(nsURI) || Constants.isSOAP_ENC(nsURI));
1360 }
1361
1362 /**
1363 * Is the given class acceptable as an attribute
1364 *
1365 * @param type input Class
1366 * @return true if the type is a simple, enum type or extends SimpleType
1367 */
1368 public boolean isAcceptableAsAttribute(Class type) {
1369 return isSimpleType(type) || isEnumClass(type)
1370 || implementsSimpleType(type);
1371 }
1372
1373 /**
1374 * Does the class implement SimpleType
1375 *
1376 * @param type input Class
1377 * @return true if the type implements SimpleType
1378 */
1379 boolean implementsSimpleType(Class type) {
1380
1381 Class[] impls = type.getInterfaces();
1382
1383 for (int i = 0; i < impls.length; i++) {
1384 if (impls[i] == SimpleType.class) {
1385 return true;
1386 }
1387 }
1388
1389 return false;
1390 }
1391
1392 /**
1393 * Generates a unique element name for a given namespace of the form
1394 * el0, el1 ....
1395 *
1396 * @param qName the namespace for the generated element
1397 * @return elementname
1398 */
1399
1400 // *** NOT USED? ***
1401 //
1402 // private String generateUniqueElementName(QName qName) {
1403 // Integer count = (Integer)schemaUniqueElementNames.get(qName.getNamespaceURI());
1404 // if (count == null)
1405 // count = new Integer(0);
1406 // else
1407 // count = new Integer(count.intValue() + 1);
1408 // schemaUniqueElementNames.put(qName.getNamespaceURI(), count);
1409 // return "el" + count.intValue();
1410 // }
1411
1412 /**
1413 * Add the type to an ArrayList and return true if the Schema node
1414 * needs to be generated
1415 * If the type already exists, just return false to indicate that the type is already
1416 * generated in a previous iteration
1417 *
1418 * @param qName of the type.
1419 * @return if the type is added returns true,
1420 * else if the type is already present returns false
1421 */
1422 private boolean addToTypesList(QName qName) {
1423
1424 boolean added = false;
1425 String namespaceURI = qName.getNamespaceURI();
1426 ArrayList types = (ArrayList) schemaTypes.get(namespaceURI);
1427
1428 // Quick return if schema type (will never add these ourselves)
1429 if (Constants.isSchemaXSD(namespaceURI)
1430 || (Constants.isSOAP_ENC(namespaceURI)
1431 && !"Array".equals(qName.getLocalPart()))) {
1432
1433 // Make sure we do have the namespace declared, though...
1434 writeTypeNamespace(namespaceURI);
1435
1436 return false;
1437 }
1438
1439 if (types == null) {
1440 types = new ArrayList();
1441
1442 types.add(qName.getLocalPart());
1443
1444 writeTypeNamespace(namespaceURI);
1445 schemaTypes.put(namespaceURI, types);
1446
1447 added = true;
1448 } else {
1449 if (!types.contains(qName.getLocalPart())) {
1450 types.add(qName.getLocalPart());
1451
1452 added = true;
1453 }
1454 }
1455
1456 // If addded, look at the namespace uri to see if the schema element should be
1457 // generated.
1458 if (added) {
1459 String prefix = namespaces.getCreatePrefix(namespaceURI);
1460
1461 if (prefix.equals(Constants.NS_PREFIX_SOAP_ENV)
1462 || prefix.equals(Constants.NS_PREFIX_SOAP_ENC)
1463 || prefix.equals(Constants.NS_PREFIX_SCHEMA_XSD)
1464 || prefix.equals(Constants.NS_PREFIX_WSDL)
1465 || prefix.equals(Constants.NS_PREFIX_WSDL_SOAP)) {
1466 return false;
1467 } else {
1468 return true;
1469 }
1470 }
1471
1472 return false;
1473 }
1474
1475 /**
1476 * Add the element to an ArrayList and return true if the Schema element
1477 * needs to be generated
1478 * If the element already exists, just return false to indicate that the type is already
1479 * generated in a previous iteration
1480 *
1481 * @param qName the name space of the element
1482 * @return if the type is added returns true, else if the type is already present returns false
1483 */
1484 private boolean addToElementsList(QName qName) {
1485
1486 if (qName == null) {
1487 return false;
1488 }
1489
1490 boolean added = false;
1491 ArrayList elements =
1492 (ArrayList) schemaElementNames.get(qName.getNamespaceURI());
1493
1494 if (elements == null) {
1495 elements = new ArrayList();
1496
1497 elements.add(qName.getLocalPart());
1498 schemaElementNames.put(qName.getNamespaceURI(), elements);
1499
1500 added = true;
1501 } else {
1502 if (!elements.contains(qName.getLocalPart())) {
1503 elements.add(qName.getLocalPart());
1504
1505 added = true;
1506 }
1507 }
1508
1509 return added;
1510 }
1511
1512 /**
1513 * Determines if the field is nullable. All non-primitives are nillable.
1514 *
1515 * @param type input Class
1516 * @return true if nullable
1517 */
1518 public static boolean isNullable(Class type) {
1519 if (type.isPrimitive()) {
1520 return false;
1521 } else {
1522 return true;
1523 }
1524 }
1525
1526 /**
1527 * todo ravi: Get rid of Doccument fragment and import node stuuf,
1528 * once I have a handle on the wsdl4j mechanism to get at types.
1529 * <p/>
1530 * Switch over notes: remove docHolder, docFragment in favor of wsdl4j Types
1531 * <p/>
1532 * DocumentFragment docFragment;
1533 * <p/>
1534 * DocumentFragment docFragment;
1535 * <p/>
1536 * DocumentFragment docFragment;
1537 * <p/>
1538 * DocumentFragment docFragment;
1539 */
1540
1541 // DocumentFragment docFragment;
1542 Document docHolder;
1543
1544 /**
1545 * Method createDocumentFragment
1546 */
1547 private void createDocumentFragment() {
1548
1549 try {
1550 this.docHolder = XMLUtils.newDocument();
1551 } catch (ParserConfigurationException e) {
1552
1553 // This should not occur
1554 throw new InternalException(e);
1555 }
1556 }
1557
1558 /**
1559 * Method updateNamespaces
1560 */
1561 public void updateNamespaces() {
1562
1563 Namespaces namespaces = getNamespaces();
1564 Iterator nspIterator = namespaces.getNamespaces();
1565
1566 while (nspIterator.hasNext()) {
1567 String nsp = (String) nspIterator.next();
1568 String pref = def.getPrefix(nsp);
1569
1570 if (pref == null) {
1571 def.addNamespace(namespaces.getCreatePrefix(nsp), nsp);
1572 }
1573 }
1574 }
1575
1576 /**
1577 * Inserts the type fragment into the given wsdl document and ensures
1578 * that definitions from each embedded schema are allowed to reference
1579 * schema components from the other sibling schemas.
1580 * @param doc
1581 */
1582 public void insertTypesFragment(Document doc) {
1583
1584 updateNamespaces();
1585
1586 if (wsdlTypesElem == null)
1587 return;
1588
1589 // Make sure that definitions from each embedded schema are allowed
1590 // to reference schema components from the other sibling schemas.
1591 Element schemaElem = null;
1592 String tns = null;
1593 NodeList nl = wsdlTypesElem.getChildNodes();
1594 for (int i = 0; i < nl.getLength(); i++) {
1595 NamedNodeMap attrs = nl.item(i).getAttributes();
1596 if (attrs == null) continue; // Should never happen.
1597 for (int n = 0; n < attrs.getLength(); n++) {
1598 Attr a = (Attr) attrs.item(n);
1599 if (a.getName().equals("targetNamespace")) {
1600 tns = a.getValue();
1601 schemaElem = (Element) nl.item(i);
1602 break;
1603 }
1604 }
1605
1606 // Ignore what appears to be a not namespace-qualified
1607 // schema definition.
1608 if (tns != null && !"".equals(tns.trim())) {
1609 // By now we know that an import element might be necessary
1610 // for some sibling schemas. However, in the absence of
1611 // a symbol table proper, the best we can do is add one
1612 // for each sibling schema.
1613 Iterator it = schemaTypes.keySet().iterator();
1614 String otherTns;
1615 Element importElem;
1616 while (it.hasNext()) {
1617 if (!tns.equals(otherTns = (String) it.next())) {
1618 importElem = docHolder.createElement("import");
1619 importElem.setAttribute("namespace", otherTns);
1620 schemaElem.insertBefore(importElem,
1621 schemaElem.getFirstChild());
1622 }
1623 }
1624 }
1625 schemaElem = null;
1626 tns = null;
1627 }
1628
1629 // Import the wsdlTypesElement into the doc.
1630 org.w3c.dom.Node node = doc.importNode(wsdlTypesElem, true);
1631 // Insert the imported element at the beginning of the document
1632 doc.getDocumentElement().
1633 insertBefore(node,
1634 doc.getDocumentElement().getFirstChild());
1635 }
1636
1637 /**
1638 * Return the list of classes that we should not emit WSDL for.
1639 *
1640 * @return
1641 */
1642 public List getStopClasses() {
1643 return stopClasses;
1644 }
1645
1646 /**
1647 * Create a DOM Element in this context
1648 *
1649 * @param elementName
1650 * @return
1651 */
1652 public Element createElement(String elementName) {
1653 return docHolder.createElement(elementName);
1654 }
1655
1656 /**
1657 * isBeanCompatible
1658 *
1659 * @param javaType Class
1660 * @param issueErrors if true, issue messages if not compatible
1661 * Returns true if it appears that this class is a bean and
1662 * can be mapped to a complexType
1663 * @return
1664 */
1665 protected boolean isBeanCompatible(Class javaType, boolean issueErrors) {
1666
1667 // Must be a non-primitive and non array
1668 if (javaType.isArray() || javaType.isPrimitive()) {
1669 if (issueErrors && !beanCompatErrs.contains(javaType)) {
1670 log.warn(Messages.getMessage("beanCompatType00",
1671 javaType.getName()));
1672 beanCompatErrs.add(javaType);
1673 }
1674
1675 return false;
1676 }
1677
1678 // Anything in the java or javax package that
1679 // does not have a defined mapping is excluded.
1680 if (javaType.getName().startsWith("java.")
1681 || javaType.getName().startsWith("javax.")) {
1682 if (issueErrors && !beanCompatErrs.contains(javaType)) {
1683 log.warn(Messages.getMessage("beanCompatPkg00",
1684 javaType.getName()));
1685 beanCompatErrs.add(javaType);
1686 }
1687
1688 return false;
1689 }
1690
1691 // Return true if appears to be an enum class
1692 if (JavaUtils.isEnumClass(javaType)) {
1693 return true;
1694 }
1695
1696 // Must have a default public constructor if not
1697 // Throwable
1698 if (!java.lang.Throwable.class.isAssignableFrom(javaType)) {
1699 try {
1700 javaType.getConstructor(new Class[]{
1701 });
1702 } catch (java.lang.NoSuchMethodException e) {
1703 if (issueErrors && !beanCompatErrs.contains(javaType)) {
1704 log.warn(Messages.getMessage("beanCompatConstructor00",
1705 javaType.getName()));
1706 beanCompatErrs.add(javaType);
1707 }
1708
1709 return false;
1710 }
1711 }
1712
1713 // Make sure superclass is compatible
1714 Class superClass = javaType.getSuperclass();
1715
1716 if ((superClass != null) && (superClass != java.lang.Object.class)
1717 && (superClass != java.lang.Exception.class)
1718 && (superClass != java.lang.Throwable.class)
1719 && (superClass != java.rmi.RemoteException.class)
1720 && (superClass != org.apache.axis.AxisFault.class)
1721 && ((stopClasses == null)
1722 || !(stopClasses.contains(superClass.getName())))) {
1723 if (!isBeanCompatible(superClass, false)) {
1724 if (issueErrors && !beanCompatErrs.contains(javaType)) {
1725 log.warn(Messages.getMessage("beanCompatExtends00",
1726 javaType.getName(),
1727 superClass.getName(),
1728 javaType.getName()));
1729 beanCompatErrs.add(javaType);
1730 }
1731
1732 return false;
1733 }
1734 }
1735
1736 return true;
1737 }
1738
1739 /**
1740 * Write an <element> with an anonymous internal ComplexType
1741 *
1742 * @param elementName
1743 * @param fieldType
1744 * @param omittable
1745 * @param ownerDocument
1746 * @return
1747 * @throws AxisFault
1748 */
1749 public Element createElementWithAnonymousType(String elementName,
1750 Class fieldType,
1751 boolean omittable,
1752 Document ownerDocument)
1753 throws AxisFault {
1754
1755 Element element = docHolder.createElement("element");
1756
1757 element.setAttribute("name", elementName);
1758
1759 if (isNullable(fieldType)) {
1760 element.setAttribute("nillable", "true");
1761 }
1762
1763 if (omittable) {
1764 element.setAttribute("minOccurs", "0");
1765 element.setAttribute("maxOccurs", "1");
1766 }
1767
1768 makeTypeElement(fieldType, null, element);
1769
1770 return element;
1771 }
1772
1773 /**
1774 * Create a schema type element (either simpleType or complexType) for
1775 * the particular type/qName combination. If the type is named, we
1776 * handle inserting the new type into the appropriate <schema>
1777 * in the WSDL types section. If the type is anonymous, we append the
1778 * definition underneath the Element which was passed as the container
1779 * (typically a field of a higher-level type or a parameter in a wrapped
1780 * operation).
1781 *
1782 * @param type Java type to write
1783 * @param qName the desired type QName
1784 * @param containingElement a schema element ("element" or "attribute")
1785 * which should either receive a type="" attribute decoration
1786 * (for named types) or a child element defining an anonymous
1787 * type
1788 * @return true if the type was already present or was added, false if there was a problem
1789 * @throws AxisFault
1790 */
1791 private boolean makeTypeElement(Class type,
1792 QName qName,
1793 Element containingElement)
1794 throws AxisFault {
1795
1796 // Get a corresponding QName if one is not provided
1797 if ((qName == null) || Constants.equals(Constants.SOAP_ARRAY, qName)) {
1798 qName = getTypeQName(type);
1799 }
1800
1801 boolean anonymous = isAnonymousType(qName);
1802
1803 // Can't have an anonymous type outside of a containing element
1804 if (anonymous && (containingElement == null)) {
1805 throw new AxisFault(
1806 Messages.getMessage(
1807 "noContainerForAnonymousType", qName.toString()));
1808 }
1809
1810 // If we've already got this type (because it's a native type or
1811 // because we've already written it), just add the type="" attribute
1812 // (if appropriate) and return.
1813 if (!addToTypesList(qName) && !anonymous) {
1814 if (containingElement != null) {
1815 containingElement.setAttribute("type", getQNameString(qName));
1816 }
1817
1818 return true;
1819 }
1820
1821 // look up the serializer in the TypeMappingRegistry
1822 SerializerFactory factory;
1823 factory = (SerializerFactory) tm.getSerializer(type, qName);
1824
1825 // If no factory is found, use the BeanSerializerFactory
1826 // if applicable, otherwise issue errors and treat as an anyType
1827 if (factory == null) {
1828 if (isEnumClass(type)) {
1829 factory = new EnumSerializerFactory(type, qName);
1830 } else if (isBeanCompatible(type, true)) {
1831 factory = new BeanSerializerFactory(type, qName);
1832 } else {
1833 return false;
1834 }
1835 }
1836
1837 // factory is not null
1838 Serializer ser = (Serializer) factory.getSerializerAs(Constants.AXIS_SAX);
1839
1840 // if we can't get a serializer, that is bad.
1841 if (ser == null) {
1842 throw new AxisFault(Messages.getMessage("NoSerializer00",
1843 type.getName()));
1844 }
1845
1846 Element typeEl;
1847
1848 try {
1849 typeEl = ser.writeSchema(type, this);
1850 } catch (Exception e) {
1851 throw AxisFault.makeFault(e);
1852 }
1853
1854 // If this is an anonymous type, just make the type element a child
1855 // of containingElement. If not, set the "type" attribute of
1856 // containingElement to the right QName, and make sure the type is
1857 // correctly written into the appropriate <schema> element.
1858 if (anonymous) {
1859 if (typeEl == null) {
1860 containingElement.setAttribute("type", getQNameString(getTypeQName(type)));
1861 } else {
1862 containingElement.appendChild(typeEl);
1863 }
1864 } else {
1865 if (typeEl != null) {
1866 typeEl.setAttribute("name", qName.getLocalPart());
1867
1868 // Write the type in the appropriate <schema>
1869 writeSchemaTypeDecl(qName, typeEl);
1870 }
1871
1872 if (containingElement != null) {
1873 containingElement.setAttribute("type", getQNameString(qName));
1874 }
1875 }
1876
1877 // store the mapping of type qname and its correspoding java type
1878 if (emitter != null) {
1879 emitter.getQName2ClassMap().put(qName, type);
1880 }
1881
1882 return true;
1883 }
1884
1885 /**
1886 * return the service description
1887 * @return
1888 */
1889 public ServiceDesc getServiceDesc() {
1890 return serviceDesc;
1891 }
1892}