Source code: org/apache/axis/wsdl/symbolTable/SymbolTable.java
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 package org.apache.axis.wsdl.symbolTable;
17
18 import org.apache.axis.Constants;
19 import org.apache.axis.constants.Style;
20 import org.apache.axis.constants.Use;
21 import org.apache.axis.utils.Messages;
22 import org.apache.axis.utils.URLHashSet;
23 import org.apache.axis.utils.XMLUtils;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.NamedNodeMap;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28 import org.xml.sax.SAXException;
29
30 import javax.wsdl.Binding;
31 import javax.wsdl.BindingFault;
32 import javax.wsdl.BindingInput;
33 import javax.wsdl.BindingOperation;
34 import javax.wsdl.BindingOutput;
35 import javax.wsdl.Definition;
36 import javax.wsdl.Fault;
37 import javax.wsdl.Import;
38 import javax.wsdl.Input;
39 import javax.wsdl.Message;
40 import javax.wsdl.Operation;
41 import javax.wsdl.Output;
42 import javax.wsdl.Part;
43 import javax.wsdl.Port;
44 import javax.wsdl.PortType;
45 import javax.wsdl.Service;
46 import javax.wsdl.WSDLException;
47 import javax.wsdl.extensions.ExtensibilityElement;
48 import javax.wsdl.extensions.UnknownExtensibilityElement;
49 import javax.wsdl.extensions.http.HTTPBinding;
50 import javax.wsdl.extensions.mime.MIMEContent;
51 import javax.wsdl.extensions.mime.MIMEMultipartRelated;
52 import javax.wsdl.extensions.mime.MIMEPart;
53 import javax.wsdl.extensions.soap.SOAPBinding;
54 import javax.wsdl.extensions.soap.SOAPBody;
55 import javax.wsdl.extensions.soap.SOAPFault;
56 import javax.wsdl.extensions.soap.SOAPHeader;
57 import javax.wsdl.extensions.soap.SOAPHeaderFault;
58 import javax.wsdl.factory.WSDLFactory;
59 import javax.wsdl.xml.WSDLReader;
60 import javax.xml.namespace.QName;
61 import javax.xml.parsers.ParserConfigurationException;
62 import javax.xml.rpc.holders.BooleanHolder;
63 import javax.xml.rpc.holders.IntHolder;
64 import javax.xml.rpc.holders.QNameHolder;
65 import java.io.File;
66 import java.io.IOException;
67 import java.net.MalformedURLException;
68 import java.net.URL;
69 import java.util.ArrayList;
70 import java.util.Collection;
71 import java.util.Collections;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Set;
78 import java.util.Vector;
79
80 /**
81 * This class represents a table of all of the top-level symbols from a set of WSDL Definitions and
82 * DOM Documents: XML types; WSDL messages, portTypes, bindings, and services.
83 * <p/>
84 * This symbolTable contains entries of the form <key, value> where key is of type QName and value is
85 * of type Vector. The Vector's elements are all of the objects that have the given QName. This is
86 * necessary since names aren't unique among the WSDL types. message, portType, binding, service,
87 * could all have the same QName and are differentiated merely by type. SymbolTable contains
88 * type-specific getters to bypass the Vector layer:
89 * public PortTypeEntry getPortTypeEntry(QName name), etc.
90 */
91 public class SymbolTable {
92
93 // used to cache dervied types
94 protected HashMap derivedTypes = new HashMap();
95
96 // Should the contents of imported files be added to the symbol table?
97
98 /** Field addImports */
99 private boolean addImports;
100
101 // The actual symbol table. This symbolTable contains entries of the form
102 // <key, value> where key is of type QName and value is of type Vector. The
103 // Vector's elements are all of the objects that have the given QName. This
104 // is necessary since names aren't unique among the WSDL types. message,
105 // portType, binding, service, could all have the same QName and are
106 // differentiated merely by type. SymbolTable contains type-specific
107 // getters to bypass the Vector layer:
108 // public PortTypeEntry getPortTypeEntry(QName name), etc.
109
110 /** Field symbolTable */
111 private HashMap symbolTable = new HashMap();
112
113 // a map of qnames -> Elements in the symbol table
114
115 /** Field elementTypeEntries */
116 private final Map elementTypeEntries = new HashMap();
117
118 // an unmodifiable wrapper so that we can share the index with others, safely
119
120 /** Field elementIndex */
121 private final Map elementIndex =
122 Collections.unmodifiableMap(elementTypeEntries);
123
124 // a map of qnames -> Types in the symbol table
125
126 /** Field typeTypeEntries */
127 private final Map typeTypeEntries = new HashMap();
128
129 // an unmodifiable wrapper so that we can share the index with others, safely
130
131 /** Field typeIndex */
132 private final Map typeIndex = Collections.unmodifiableMap(typeTypeEntries);
133
134 /**
135 * cache of nodes -> base types for complexTypes. The cache is
136 * built on nodes because multiple TypeEntry objects may use the
137 * same node.
138 */
139 protected final Map node2ExtensionBase =
140 new HashMap(); // allow friendly access
141
142 /** Field verbose */
143 private boolean verbose;
144
145 /** Field quiet */
146 protected boolean quiet;
147
148 /** Field btm */
149 private BaseTypeMapping btm = null;
150
151 // should we attempt to treat document/literal WSDL as "rpc-style"
152
153 /** Field nowrap */
154 private boolean nowrap;
155
156 // Did we encounter wraped mode WSDL
157
158 /** Field wrapped */
159 private boolean wrapped = false;
160
161 /** Field ANON_TOKEN */
162 public static final String ANON_TOKEN = ">";
163
164 /** Field def */
165 private Definition def = null;
166
167 /** Field wsdlURI */
168 private String wsdlURI = null;
169
170 /** If this is false, we will "unwrap" literal arrays, generating a plan "String[]" instead
171 * of "ArrayOfString" when encountering an element containing a single maxOccurs="unbounded"
172 * inner element.
173 */
174 private boolean wrapArrays;
175
176 Set arrayTypeQNames = new HashSet();
177
178 /** Field elementFormDefaults */
179 private final Map elementFormDefaults = new HashMap();
180 /**
181 * Construct a symbol table with the given Namespaces.
182 *
183 * @param btm
184 * @param addImports
185 * @param verbose
186 * @param nowrap
187 */
188 public SymbolTable(BaseTypeMapping btm, boolean addImports,
189 boolean verbose, boolean nowrap) {
190
191 this.btm = btm;
192 this.addImports = addImports;
193 this.verbose = verbose;
194 this.nowrap = nowrap;
195 } // ctor
196
197 /**
198 * Method isQuiet
199 *
200 * @return
201 */
202 public boolean isQuiet() {
203 return quiet;
204 }
205
206 /**
207 * Method setQuiet
208 *
209 * @param quiet
210 */
211 public void setQuiet(boolean quiet) {
212 this.quiet = quiet;
213 }
214
215 /**
216 * Get the raw symbol table HashMap.
217 *
218 * @return
219 */
220 public HashMap getHashMap() {
221 return symbolTable;
222 } // getHashMap
223
224 /**
225 * Get the list of entries with the given QName. Since symbols can share QNames, this list is
226 * necessary. This list will not contain any more than one element of any given SymTabEntry.
227 *
228 * @param qname
229 * @return
230 */
231 public Vector getSymbols(QName qname) {
232 return (Vector) symbolTable.get(qname);
233 } // get
234
235 /**
236 * Get the entry with the given QName of the given class. If it does not exist, return null.
237 *
238 * @param qname
239 * @param cls
240 * @return
241 */
242 public SymTabEntry get(QName qname, Class cls) {
243
244 Vector v = (Vector) symbolTable.get(qname);
245
246 if (v == null) {
247 return null;
248 } else {
249 for (int i = 0; i < v.size(); ++i) {
250 SymTabEntry entry = (SymTabEntry) v.elementAt(i);
251
252 if (cls.isInstance(entry)) {
253 return entry;
254 }
255 }
256
257 return null;
258 }
259 } // get
260
261 /**
262 * Get the type entry for the given qname.
263 *
264 * @param qname
265 * @param wantElementType boolean that indicates type or element (for type= or ref=)
266 * @return
267 */
268 public TypeEntry getTypeEntry(QName qname, boolean wantElementType) {
269
270 if (wantElementType) {
271 return getElement(qname);
272 } else {
273 return getType(qname);
274 }
275 } // getTypeEntry
276
277 /**
278 * Get the Type TypeEntry with the given QName. If it doesn't
279 * exist, return null.
280 *
281 * @param qname
282 * @return
283 */
284 public Type getType(QName qname) {
285 return (Type) typeTypeEntries.get(qname);
286 } // getType
287
288 /**
289 * Get the Element TypeEntry with the given QName. If it doesn't
290 * exist, return null.
291 *
292 * @param qname
293 * @return
294 */
295 public Element getElement(QName qname) {
296 return (Element) elementTypeEntries.get(qname);
297 } // getElement
298
299 /**
300 * Get the MessageEntry with the given QName. If it doesn't exist, return null.
301 *
302 * @param qname
303 * @return
304 */
305 public MessageEntry getMessageEntry(QName qname) {
306 return (MessageEntry) get(qname, MessageEntry.class);
307 } // getMessageEntry
308
309 /**
310 * Get the PortTypeEntry with the given QName. If it doesn't exist, return null.
311 *
312 * @param qname
313 * @return
314 */
315 public PortTypeEntry getPortTypeEntry(QName qname) {
316 return (PortTypeEntry) get(qname, PortTypeEntry.class);
317 } // getPortTypeEntry
318
319 /**
320 * Get the BindingEntry with the given QName. If it doesn't exist, return null.
321 *
322 * @param qname
323 * @return
324 */
325 public BindingEntry getBindingEntry(QName qname) {
326 return (BindingEntry) get(qname, BindingEntry.class);
327 } // getBindingEntry
328
329 /**
330 * Get the ServiceEntry with the given QName. If it doesn't exist, return null.
331 *
332 * @param qname
333 * @return
334 */
335 public ServiceEntry getServiceEntry(QName qname) {
336 return (ServiceEntry) get(qname, ServiceEntry.class);
337 } // getServiceEntry
338
339 /**
340 * Get the list of all the XML schema types in the symbol table. In other words, all entries
341 * that are instances of TypeEntry.
342 *
343 * @return
344 * @deprecated use specialized get{Element,Type}Index() methods instead
345 */
346 public Vector getTypes() {
347
348 Vector v = new Vector();
349
350 v.addAll(elementTypeEntries.values());
351 v.addAll(typeTypeEntries.values());
352
353 return v;
354 } // getTypes
355
356 /**
357 * Return an unmodifiable map of qnames -> Elements in the symbol
358 * table.
359 *
360 * @return an unmodifiable <code>Map</code> value
361 */
362 public Map getElementIndex() {
363 return elementIndex;
364 }
365
366 /**
367 * Return an unmodifiable map of qnames -> Elements in the symbol
368 * table.
369 *
370 * @return an unmodifiable <code>Map</code> value
371 */
372 public Map getTypeIndex() {
373 return typeIndex;
374 }
375
376 /**
377 * Return the count of TypeEntries in the symbol table.
378 *
379 * @return an <code>int</code> value
380 */
381 public int getTypeEntryCount() {
382 return elementTypeEntries.size() + typeTypeEntries.size();
383 }
384
385 /**
386 * Get the Definition. The definition is null until
387 * populate is called.
388 *
389 * @return
390 */
391 public Definition getDefinition() {
392 return def;
393 } // getDefinition
394
395 /**
396 * Get the WSDL URI. The WSDL URI is null until populate
397 * is called, and ONLY if a WSDL URI is provided.
398 *
399 * @return
400 */
401 public String getWSDLURI() {
402 return wsdlURI;
403 } // getWSDLURI
404
405 /**
406 * Are we wrapping literal soap body elements.
407 *
408 * @return
409 */
410 public boolean isWrapped() {
411 return wrapped;
412 }
413
414 /**
415 * Turn on/off element wrapping for literal soap body's.
416 *
417 * @param wrapped
418 */
419 public void setWrapped(boolean wrapped) {
420 this.wrapped = wrapped;
421 }
422
423 /**
424 * Dump the contents of the symbol table. For debugging purposes only.
425 *
426 * @param out
427 */
428 public void dump(java.io.PrintStream out) {
429
430 out.println();
431 out.println(Messages.getMessage("symbolTable00"));
432 out.println("-----------------------");
433
434 Iterator it = symbolTable.values().iterator();
435
436 while (it.hasNext()) {
437 Vector v = (Vector) it.next();
438
439 for (int i = 0; i < v.size(); ++i) {
440 out.println(v.elementAt(i).getClass().getName());
441 out.println(v.elementAt(i));
442 }
443 }
444
445 out.println("-----------------------");
446 } // dump
447
448 /**
449 * Call this method if you have a uri for the WSDL document
450 *
451 * @param uri wsdlURI the location of the WSDL file.
452 * @throws IOException
453 * @throws WSDLException
454 * @throws SAXException
455 * @throws ParserConfigurationException
456 */
457 public void populate(String uri)
458 throws IOException, WSDLException, SAXException,
459 ParserConfigurationException {
460 populate(uri, null, null);
461 } // populate
462
463 /**
464 * Method populate
465 *
466 * @param uri
467 * @param username
468 * @param password
469 * @throws IOException
470 * @throws WSDLException
471 * @throws SAXException
472 * @throws ParserConfigurationException
473 */
474 public void populate(String uri, String username, String password)
475 throws IOException, WSDLException, SAXException,
476 ParserConfigurationException {
477
478 if (verbose) {
479 System.out.println(Messages.getMessage("parsing00", uri));
480 }
481
482 Document doc = XMLUtils.newDocument(uri, username, password);
483
484 this.wsdlURI = uri;
485
486 try {
487 File f = new File(uri);
488
489 if (f.exists()) {
490 uri = f.toURL().toString();
491 }
492 } catch (Exception e) {
493 }
494
495 populate(uri, doc);
496 } // populate
497
498 /**
499 * Call this method if your WSDL document has already been parsed as an XML DOM document.
500 *
501 * @param context context This is directory context for the Document. If the Document were from file "/x/y/z.wsdl" then the context could be "/x/y" (even "/x/y/z.wsdl" would work). If context is null, then the context becomes the current directory.
502 * @param doc doc This is the XML Document containing the WSDL.
503 * @throws IOException
504 * @throws SAXException
505 * @throws WSDLException
506 * @throws ParserConfigurationException
507 */
508 public void populate(String context, Document doc)
509 throws IOException, SAXException, WSDLException,
510 ParserConfigurationException {
511
512 WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
513
514 reader.setFeature("javax.wsdl.verbose", verbose);
515
516 this.def = reader.readWSDL(context, doc);
517
518 add(context, def, doc);
519 } // populate
520
521 /**
522 * Add the given Definition and Document information to the symbol table (including imported
523 * symbols), populating it with SymTabEntries for each of the top-level symbols. When the
524 * symbol table has been populated, iterate through it, setting the isReferenced flag
525 * appropriately for each entry.
526 *
527 * @param context
528 * @param def
529 * @param doc
530 * @throws IOException
531 * @throws SAXException
532 * @throws WSDLException
533 * @throws ParserConfigurationException
534 */
535 protected void add(String context, Definition def, Document doc)
536 throws IOException, SAXException, WSDLException,
537 ParserConfigurationException {
538
539 URL contextURL = (context == null)
540 ? null
541 : getURL(null, context);
542
543 populate(contextURL, def, doc, null);
544 processTypes();
545 checkForUndefined();
546 populateParameters();
547 setReferences(def, doc); // uses wrapped flag set in populateParameters
548 } // add
549
550 /**
551 * Scan the Definition for undefined objects and throw an error.
552 *
553 * @param def
554 * @param filename
555 * @throws IOException
556 */
557 private void checkForUndefined(Definition def, String filename)
558 throws IOException {
559
560 if (def != null) {
561
562 // Bindings
563 Iterator ib = def.getBindings().values().iterator();
564
565 while (ib.hasNext()) {
566 Binding binding = (Binding) ib.next();
567
568 if (binding.isUndefined()) {
569 if (filename == null) {
570 throw new IOException(
571 Messages.getMessage(
572 "emitFailtUndefinedBinding01",
573 binding.getQName().getLocalPart()));
574 } else {
575 throw new IOException(
576 Messages.getMessage(
577 "emitFailtUndefinedBinding02",
578 binding.getQName().getLocalPart(), filename));
579 }
580 }
581 }
582
583 // portTypes
584 Iterator ip = def.getPortTypes().values().iterator();
585
586 while (ip.hasNext()) {
587 PortType portType = (PortType) ip.next();
588
589 if (portType.isUndefined()) {
590 if (filename == null) {
591 throw new IOException(
592 Messages.getMessage(
593 "emitFailtUndefinedPort01",
594 portType.getQName().getLocalPart()));
595 } else {
596 throw new IOException(
597 Messages.getMessage(
598 "emitFailtUndefinedPort02",
599 portType.getQName().getLocalPart(), filename));
600 }
601 }
602 }
603
604 /*
605 * tomj: This is a bad idea, faults seem to be undefined
606 * / RJB reply: this MUST be done for those systems that do something with
607 * / messages. Perhaps we have to do an extra step for faults? I'll leave
608 * / this commented for now, until someone uses this generator for something
609 * / other than WSDL2Java.
610 * // Messages
611 * Iterator i = def.getMessages().values().iterator();
612 * while (i.hasNext()) {
613 * Message message = (Message) i.next();
614 * if (message.isUndefined()) {
615 * throw new IOException(
616 * Messages.getMessage("emitFailtUndefinedMessage01",
617 * message.getQName().getLocalPart()));
618 * }
619 * }
620 */
621 }
622 }
623
624 /**
625 * Scan the symbol table for undefined types and throw an exception.
626 *
627 * @throws IOException
628 */
629 private void checkForUndefined() throws IOException {
630
631 Iterator it = symbolTable.values().iterator();
632
633 while (it.hasNext()) {
634 Vector v = (Vector) it.next();
635
636 for (int i = 0; i < v.size(); ++i) {
637 SymTabEntry entry = (SymTabEntry) v.get(i);
638
639 // Report undefined types
640 if (entry instanceof UndefinedType) {
641 QName qn = entry.getQName();
642
643 // Special case dateTime/timeInstant that changed
644 // from version to version.
645 if ((qn.getLocalPart().equals(
646 "dateTime") && !qn.getNamespaceURI().equals(
647 Constants.URI_2001_SCHEMA_XSD)) || (qn.getLocalPart().equals(
648 "timeInstant") && qn.getNamespaceURI().equals(
649 Constants.URI_2001_SCHEMA_XSD))) {
650 throw new IOException(
651 Messages.getMessage(
652 "wrongNamespace00", qn.getLocalPart(),
653 qn.getNamespaceURI()));
654 }
655
656 // Check for a undefined XSD Schema Type and throw
657 // an unsupported message instead of undefined
658 if (SchemaUtils.isSimpleSchemaType(qn)) {
659 throw new IOException(
660 Messages.getMessage(
661 "unsupportedSchemaType00", qn.getLocalPart()));
662 }
663
664 // last case, its some other undefined thing
665 throw new IOException(
666 Messages.getMessage(
667 "undefined00", qn.toString()));
668 } // if undefined
669 else if (entry instanceof UndefinedElement) {
670 throw new IOException(
671 Messages.getMessage(
672 "undefinedElem00", entry.getQName().toString()));
673 }
674 }
675 }
676 } // checkForUndefined
677
678 /**
679 * Add the given Definition and Document information to the symbol table (including imported
680 * symbols), populating it with SymTabEntries for each of the top-level symbols.
681 * NOTE: filename is used only by checkForUndefined so that it can report which WSDL file
682 * has the problem. If we're on the primary WSDL file, then we don't know the name and
683 * filename will be null. But we know the names of all imported files.
684 */
685 private URLHashSet importedFiles = new URLHashSet();
686
687 /**
688 * Method populate
689 *
690 * @param context
691 * @param def
692 * @param doc
693 * @param filename
694 * @throws IOException
695 * @throws ParserConfigurationException
696 * @throws SAXException
697 * @throws WSDLException
698 */
699 private void populate(
700 URL context, Definition def, Document doc, String filename)
701 throws IOException, ParserConfigurationException, SAXException,
702 WSDLException {
703
704 if (doc != null) {
705 populateTypes(context, doc);
706
707 if (addImports) {
708
709 // Add the symbols from any xsd:import'ed documents.
710 lookForImports(context, doc);
711 }
712 }
713
714 if (def != null) {
715 checkForUndefined(def, filename);
716
717 if (addImports) {
718
719 // Add the symbols from the wsdl:import'ed WSDL documents
720 Map imports = def.getImports();
721 Object[] importKeys = imports.keySet().toArray();
722
723 for (int i = 0; i < importKeys.length; ++i) {
724 Vector v = (Vector) imports.get(importKeys[i]);
725
726 for (int j = 0; j < v.size(); ++j) {
727 Import imp = (Import) v.get(j);
728
729 if (!importedFiles.contains(imp.getLocationURI())) {
730 importedFiles.add(imp.getLocationURI());
731
732 URL url = getURL(context, imp.getLocationURI());
733
734 populate(url, imp.getDefinition(),
735 XMLUtils.newDocument(url.toString()),
736 url.toString());
737 }
738 }
739 }
740 }
741
742 populateMessages(def);
743 populatePortTypes(def);
744 populateBindings(def);
745 populateServices(def);
746 }
747 } // populate
748
749 /**
750 * This is essentially a call to "new URL(contextURL, spec)" with extra handling in case spec is
751 * a file.
752 *
753 * @param contextURL
754 * @param spec
755 * @return
756 * @throws IOException
757 */
758 private static URL getURL(URL contextURL, String spec) throws IOException {
759
760 // First, fix the slashes as windows filenames may have backslashes
761 // in them, but the URL class wont do the right thing when we later
762 // process this URL as the contextURL.
763 String path = spec.replace('\\', '/');
764
765 // See if we have a good URL.
766 URL url = null;
767
768 try {
769
770 // first, try to treat spec as a full URL
771 url = new URL(contextURL, path);
772
773 // if we are deail with files in both cases, create a url
774 // by using the directory of the context URL.
775 if ((contextURL != null) && url.getProtocol().equals("file")
776 && contextURL.getProtocol().equals("file")) {
777 url = getFileURL(contextURL, path);
778 }
779 } catch (MalformedURLException me) {
780
781 // try treating is as a file pathname
782 url = getFileURL(contextURL, path);
783 }
784
785 // Everything is OK with this URL, although a file url constructed
786 // above may not exist. This will be caught later when the URL is
787 // accessed.
788 return url;
789 } // getURL
790
791 /**
792 * Method getFileURL
793 *
794 * @param contextURL
795 * @param path
796 * @return
797 * @throws IOException
798 */
799 private static URL getFileURL(URL contextURL, String path)
800 throws IOException {
801
802 if (contextURL != null) {
803
804 // get the parent directory of the contextURL, and append
805 // the spec string to the end.
806 String contextFileName = contextURL.getFile();
807 URL parent = null;
808 File parentFile = new File(contextFileName).getParentFile();
809 if ( parentFile != null ) {
810 parent = parentFile.toURL();
811 }
812 if (parent != null) {
813 return new URL(parent, path);
814 }
815 }
816
817 return new URL("file", "", path);
818 } // getFileURL
819
820 /**
821 * Recursively find all xsd:import'ed objects and call populate for each one.
822 *
823 * @param context
824 * @param node
825 * @throws IOException
826 * @throws ParserConfigurationException
827 * @throws SAXException
828 * @throws WSDLException
829 */
830 private void lookForImports(URL context, Node node)
831 throws IOException, ParserConfigurationException, SAXException,
832 WSDLException {
833
834 NodeList children = node.getChildNodes();
835
836 for (int i = 0; i < children.getLength(); i++) {
837 Node child = children.item(i);
838
839 if ("import".equals(child.getLocalName())) {
840 NamedNodeMap attributes = child.getAttributes();
841 Node namespace = attributes.getNamedItem("namespace");
842
843 // skip XSD import of soap encoding
844 if ((namespace != null)
845 && isKnownNamespace(namespace.getNodeValue())) {
846 continue;
847 }
848
849 Node importFile = attributes.getNamedItem("schemaLocation");
850
851 if (importFile != null) {
852 URL url = getURL(context, importFile.getNodeValue());
853
854 if (!importedFiles.contains(url)) {
855 importedFiles.add(url);
856
857 String filename = url.toString();
858
859 populate(url, null, XMLUtils.newDocument(filename),
860 filename);
861 }
862 }
863 }
864
865 lookForImports(context, child);
866 }
867 } // lookForImports
868
869 /**
870 * Check if this is a known namespace (soap-enc or schema xsd or schema xsi or xml)
871 *
872 * @param namespace
873 * @return true if this is a know namespace.
874 */
875 public boolean isKnownNamespace(String namespace) {
876
877 if (Constants.isSOAP_ENC(namespace)) {
878 return true;
879 }
880
881 if (Constants.isSchemaXSD(namespace)) {
882 return true;
883 }
884
885 if (Constants.isSchemaXSI(namespace)) {
886 return true;
887 }
888
889 if (namespace.equals(Constants.NS_URI_XML)) {
890 return true;
891 }
892
893 return false;
894 }
895
896 /**
897 * Populate the symbol table with all of the Types from the Document.
898 *
899 * @param context
900 * @param doc
901 * @throws IOException
902 * @throws SAXException
903 * @throws WSDLException
904 * @throws ParserConfigurationException
905 */
906 public void populateTypes(URL context, Document doc)
907 throws IOException, SAXException, WSDLException,
908 ParserConfigurationException {
909 addTypes(context, doc, ABOVE_SCHEMA_LEVEL);
910 } // populateTypes
911
912 /**
913 * Utility method which walks the Document and creates Type objects for
914 * each complexType, simpleType, attributeGroup or element referenced or defined.
915 * <p/>
916 * What goes into the symbol table? In general, only the top-level types
917 * (ie., those just below
918 * the schema tag). But base types and references can
919 * appear below the top level. So anything
920 * at the top level is added to the symbol table,
921 * plus non-Element types (ie, base and refd)
922 * that appear deep within other types.
923 */
924 private static final int ABOVE_SCHEMA_LEVEL = -1;
925
926 /** Field SCHEMA_LEVEL */
927 private static final int SCHEMA_LEVEL = 0;
928
929 /**
930 * Method addTypes
931 *
932 * @param context
933 * @param node
934 * @param level
935 * @throws IOException
936 * @throws ParserConfigurationException
937 * @throws WSDLException
938 * @throws SAXException
939 */
940 private void addTypes(URL context, Node node, int level)
941 throws IOException, ParserConfigurationException, WSDLException,
942 SAXException {
943
944 if (node == null) {
945 return;
946 }
947
948 // Get the kind of node (complexType, wsdl:part, etc.)
949 String localPart = node.getLocalName();
950
951 if (localPart != null) {
952 boolean isXSD =
953 Constants.isSchemaXSD(node.getNamespaceURI());
954
955 if (((isXSD && localPart.equals("complexType"))
956 || localPart.equals("simpleType"))) {
957
958 // If an extension or restriction is present,
959 // create a type for the reference
960 Node re = SchemaUtils.getRestrictionOrExtensionNode(node);
961
962 if ((re != null) && (Utils.getAttribute(re, "base") != null)) {
963 createTypeFromRef(re);
964 }
965
966 Node list = SchemaUtils.getListNode(node);
967 if (list != null && Utils.getAttribute(list,"itemType") != null) {
968 createTypeFromRef(list);
969 }
970
971 Node union = SchemaUtils.getUnionNode(node);
972 if (union != null) {
973 QName [] memberTypes = Utils.getMemberTypeQNames(union);
974 if (memberTypes != null) {
975 for (int i=0;i<memberTypes.length;i++) {
976 if (SchemaUtils.isSimpleSchemaType(memberTypes[i]) &&
977 getType(memberTypes[i]) == null) {
978 symbolTablePut(new BaseType(memberTypes[i]));
979 }
980 }
981 }
982 }
983
984 // This is a definition of a complex type.
985 // Create a Type.
986 createTypeFromDef(node, false, false);
987 } else if (isXSD && localPart.equals("element")) {
988
989 // Create a type entry for the referenced type
990 createTypeFromRef(node);
991
992 // If an extension or restriction is present,
993 // create a type for the reference
994 Node re = SchemaUtils.getRestrictionOrExtensionNode(node);
995
996 if ((re != null) && (Utils.getAttribute(re, "base") != null)) {
997 createTypeFromRef(re);
998 }
999
1000 // Create a type representing an element. (This may
1001 // seem like overkill, but is necessary to support ref=
1002 // and element=.
1003 createTypeFromDef(node, true, level > SCHEMA_LEVEL);
1004 } else if (isXSD && localPart.equals("attributeGroup")) {
1005
1006 // bug 23145: support attributeGroup (Brook Richan)
1007 // Create a type entry for the referenced type
1008 createTypeFromRef(node);
1009
1010 // Create a type representing an attributeGroup.
1011 createTypeFromDef(node, false, level > SCHEMA_LEVEL);
1012 } else if (isXSD && localPart.equals("group")) {
1013 // Create a type entry for the referenced type
1014 createTypeFromRef(node);
1015 // Create a type representing an group
1016 createTypeFromDef(node, false, level > SCHEMA_LEVEL);
1017 } else if (isXSD && localPart.equals("attribute")) {
1018
1019 // Create a type entry for the referenced type
1020 BooleanHolder forElement = new BooleanHolder();
1021 QName refQName = Utils.getTypeQName(node, forElement,
1022 false);
1023
1024 if ((refQName != null) && !forElement.value) {
1025 createTypeFromRef(node);
1026
1027 // Get the symbol table entry and make sure it is a simple
1028 // type
1029 if (refQName != null) {
1030 TypeEntry refType = getTypeEntry(refQName, false);
1031
1032 if ((refType != null)
1033 && (refType instanceof Undefined)) {
1034
1035 // Don't know what the type is.
1036 // It better be simple so set it as simple
1037 refType.setSimpleType(true);
1038 } else if ((refType == null)
1039 || (!(refType instanceof BaseType)
1040 && !refType.isSimpleType())) {
1041
1042 // Problem if not simple
1043 throw new IOException(
1044 Messages.getMessage(
1045 "AttrNotSimpleType01",
1046 refQName.toString()));
1047 }
1048 }
1049 }
1050 createTypeFromDef(node, true, level > SCHEMA_LEVEL);
1051 } else if (isXSD && localPart.equals("any")) {
1052
1053 // Map xsd:any element to special xsd:any "type"
1054 if (getType(Constants.XSD_ANY) == null) {
1055 Type type = new BaseType(Constants.XSD_ANY);
1056
1057 symbolTablePut(type);
1058 }
1059 } else if (localPart.equals("part")
1060 && Constants.isWSDL(node.getNamespaceURI())) {
1061
1062 // This is a wsdl part. Create an TypeEntry representing the reference
1063 createTypeFromRef(node);
1064 } else if (isXSD && localPart.equals("include")) {
1065 String includeName = Utils.getAttribute(node, "schemaLocation");
1066
1067 if (includeName != null) {
1068 URL url = getURL(context, includeName);
1069 Document includeDoc = XMLUtils.newDocument(url.toString());
1070
1071 // Vidyanand : Fix for Bug #15124
1072 org.w3c.dom.Element schemaEl =
1073 includeDoc.getDocumentElement();
1074
1075 if (!schemaEl.hasAttribute("targetNamespace")) {
1076 org.w3c.dom.Element parentSchemaEl =
1077 (org.w3c.dom.Element) node.getParentNode();
1078
1079 if (parentSchemaEl.hasAttribute("targetNamespace")) {
1080
1081 // we need to set two things in here
1082 // 1. targetNamespace
1083 // 2. setup the xmlns=<targetNamespace> attribute
1084 String tns =
1085 parentSchemaEl.getAttribute("targetNamespace");
1086
1087 schemaEl.setAttribute("targetNamespace", tns);
1088 schemaEl.setAttribute("xmlns", tns);
1089 }
1090 }
1091
1092 populate(url, null, includeDoc, url.toString());
1093 }
1094 }
1095 }
1096
1097 if (level == ABOVE_SCHEMA_LEVEL) {
1098 if ((localPart != null)
1099 && localPart.equals("schema")) {
1100 level = SCHEMA_LEVEL;
1101 String targetNamespace = ((org.w3c.dom.Element) node).getAttribute("targetNamespace");
1102 String elementFormDefault = ((org.w3c.dom.Element) node).getAttribute("elementFormDefault");
1103 if (targetNamespace != null && targetNamespace.length() > 0) {
1104 elementFormDefault = (elementFormDefault == null || elementFormDefault.length() == 0) ?
1105 "unqualified" : elementFormDefault;
1106 if(elementFormDefaults.get(targetNamespace)==null) {
1107 elementFormDefaults.put(targetNamespace, elementFormDefault);
1108 }
1109 }
1110 }
1111 } else {
1112 ++level;
1113 }
1114
1115 // Recurse through children nodes
1116 NodeList children = node.getChildNodes();
1117
1118 for (int i = 0; i < children.getLength(); i++) {
1119 addTypes(context, children.item(i), level);
1120 }
1121 } // addTypes
1122
1123 /**
1124 * Create a TypeEntry from the indicated node, which defines a type
1125 * that represents a complexType, simpleType or element (for ref=).
1126 *
1127 * @param node
1128 * @param isElement
1129 * @param belowSchemaLevel
1130 * @throws IOException
1131 */
1132 private void createTypeFromDef(
1133 Node node, boolean isElement, boolean belowSchemaLevel)
1134 throws IOException {
1135
1136 // Get the QName of the node's name attribute value
1137 QName qName = Utils.getNodeNameQName(node);
1138
1139 if (qName != null) {
1140
1141 // If the qname is already registered as a base type,
1142 // don't create a defining type/element.
1143 if (!isElement && (btm.getBaseName(qName) != null)) {
1144 return;
1145 }
1146
1147 // If the node has a type or ref attribute, get the
1148 // qname representing the type
1149 BooleanHolder forElement = new BooleanHolder();
1150 QName refQName = Utils.getTypeQName(node, forElement,
1151 false);
1152
1153 if (refQName != null) {
1154
1155 // Error check - bug 12362
1156 if (qName.getLocalPart().length() == 0) {
1157 String name = Utils.getAttribute(node, "name");
1158
1159 if (name == null) {
1160 name = "unknown";
1161 }
1162
1163 throw new IOException(Messages.getMessage("emptyref00",
1164 name));
1165 }
1166
1167 // Now get the TypeEntry
1168 TypeEntry refType = getTypeEntry(refQName, forElement.value);
1169
1170 if (!belowSchemaLevel) {
1171 if (refType == null) {
1172 throw new IOException(
1173 Messages.getMessage(
1174 "absentRef00", refQName.toString(),
1175 qName.toString()));
1176 }
1177
1178 symbolTablePut(new DefinedElement(qName, refType, node,
1179 ""));
1180 }
1181 } else {
1182
1183 // Flow to here indicates no type= or ref= attribute.
1184 // See if this is an array or simple type definition.
1185 IntHolder numDims = new IntHolder();
1186 BooleanHolder underlTypeNillable = new BooleanHolder();
1187
1188 // If we're supposed to unwrap arrays, supply someplace to put the "inner" QName
1189 // so we can propagate it into the appropriate metadata container.
1190 QNameHolder itemQName = wrapArrays ? null : new QNameHolder();
1191
1192 numDims.value = 0;
1193
1194 QName arrayEQName =
1195 SchemaUtils.getArrayComponentQName(node,
1196 numDims,
1197 underlTypeNillable,
1198 itemQName,
1199 this);
1200
1201 if (arrayEQName != null) {
1202
1203 // Get the TypeEntry for the array element type
1204 refQName = arrayEQName;
1205
1206 TypeEntry refType = getTypeEntry(refQName, false);
1207
1208 if (refType == null) {
1209// arrayTypeQNames.add(refQName);
1210
1211 // Not defined yet, add one
1212 String baseName = btm.getBaseName(refQName);
1213
1214 if (baseName != null) {
1215 refType = new BaseType(refQName);
1216 } else {
1217 refType = new UndefinedType(refQName);
1218 }
1219
1220 symbolTablePut(refType);
1221 }
1222
1223 // Create a defined type or element that references refType
1224 String dims = "";
1225
1226 while (numDims.value > 0) {
1227 dims += "[]";
1228
1229 numDims.value--;
1230 }
1231
1232 TypeEntry defType = null;
1233
1234 if (isElement) {
1235 if (!belowSchemaLevel) {
1236 defType =
1237 new DefinedElement(qName, refType, node, dims);
1238 // Save component type for ArraySerializer
1239 defType.setComponentType(arrayEQName);
1240 if (itemQName != null)
1241 defType.setItemQName(itemQName.value);
1242 }
1243 } else {
1244 defType = new DefinedType(qName, refType, node, dims);
1245 // Save component type for ArraySerializer
1246 defType.setComponentType(arrayEQName);
1247 defType.setUnderlTypeNillable(underlTypeNillable.value);
1248 if (itemQName != null)
1249 defType.setItemQName(itemQName.value);
1250 }
1251
1252 if (defType != null) {
1253 symbolTablePut(defType);
1254 }
1255 } else {
1256
1257 // Create a TypeEntry representing this type/element
1258 String baseName = btm.getBaseName(qName);
1259
1260 if (baseName != null) {
1261 symbolTablePut(new BaseType(qName));
1262 } else {
1263
1264 // Create a type entry, set whether it should
1265 // be mapped as a simple type, and put it in the
1266 // symbol table.
1267 TypeEntry te = null;
1268 TypeEntry parentType = null;
1269
1270 if (!isElement) {
1271 te = new DefinedType(qName, node);
1272
1273 // check if we are an anonymous type underneath
1274 // an element. If so, we point the refType of the
1275 // element to us (the real type).
1276 if (qName.getLocalPart().indexOf(ANON_TOKEN) >= 0) {
1277 Node parent = node.getParentNode();
1278 QName parentQName =
1279 Utils.getNodeNameQName(parent);
1280 parentType = getElement(parentQName);
1281 }
1282 } else {
1283 if (!belowSchemaLevel) {
1284 te = new DefinedElement(qName, node);
1285 }
1286 }
1287
1288 if (te != null) {
1289 if (SchemaUtils.isSimpleTypeOrSimpleContent(node)) {
1290 te.setSimpleType(true);
1291 }
1292 te = (TypeEntry)symbolTablePut(te);
1293
1294 if (parentType != null) {
1295 parentType.setRefType(te);
1296 }
1297 }
1298 }
1299 }
1300 }
1301 }
1302 } // createTypeFromDef
1303
1304 /**
1305 * Node may contain a reference (via type=, ref=, or element= attributes) to
1306 * another type. Create a Type object representing this referenced type.
1307 *
1308 * @param node
1309 * @throws IOException
1310 */
1311 private void createTypeFromRef(Node node) throws IOException {
1312
1313 // Get the QName of the node's type attribute value
1314 BooleanHolder forElement = new BooleanHolder();
1315 QName qName = Utils.getTypeQName(node, forElement, false);
1316
1317 if (qName == null || (Constants.isSchemaXSD(qName.getNamespaceURI()) &&
1318 qName.getLocalPart().equals("simpleRestrictionModel"))) {
1319 return;
1320 }
1321
1322 // Error check - bug 12362
1323 if (qName.getLocalPart().length() == 0) {
1324 String name = Utils.getAttribute(node, "name");
1325
1326 if (name == null) {
1327 name = "unknown";
1328 }
1329
1330 throw new IOException(Messages.getMessage("emptyref00", name));
1331 }
1332
1333 // Get Type or Element depending on whether type attr was used.
1334 TypeEntry type = getTypeEntry(qName, forElement.value);
1335
1336 // A symbol table entry is created if the TypeEntry is not found
1337 if (type == null) {
1338
1339 // See if this is a special QName for collections
1340 if (qName.getLocalPart().indexOf("[") > 0) {
1341 QName containedQName = Utils.getTypeQName(node,
1342 forElement, true);
1343 TypeEntry containedTE = getTypeEntry(containedQName,
1344 forElement.value);
1345
1346 if (!forElement.value) {
1347
1348 // Case of type and maxOccurs
1349 if (containedTE == null) {
1350
1351 // Collection Element Type not defined yet, add one.
1352 String baseName = btm.getBaseName(containedQName);
1353
1354 if (baseName != null) {
1355 containedTE = new BaseType(containedQName);
1356 } else {
1357 containedTE = new UndefinedType(containedQName);
1358 }
1359
1360 symbolTablePut(containedTE);
1361 }
1362 symbolTablePut(new CollectionType(qName, containedTE,
1363 node, "[]"));
1364 } else {
1365
1366 // Case of ref and maxOccurs
1367 if (containedTE == null) {
1368 containedTE = new UndefinedElement(containedQName);
1369
1370 symbolTablePut(containedTE);
1371 }
1372
1373 symbolTablePut(new CollectionElement(qName,
1374 containedTE, node,
1375 "[]"));
1376 }
1377 } else {
1378
1379 // Add a BaseType or Undefined Type/Element
1380 String baseName = btm.getBaseName(qName);
1381
1382 if (baseName != null) {
1383 symbolTablePut(new BaseType(qName));
1384
1385 // bugzilla 23145: handle attribute groups
1386 // soap/encoding is treated as a "known" schema
1387 // so now let's act like we know it
1388 } else if (qName.equals(Constants.SOAP_COMMON_ATTRS11)) {
1389 symbolTablePut(new BaseType(qName));
1390
1391 // the 1.1 commonAttributes type contains two attributes
1392 // make sure those attributes' types are in the symbol table
1393 // attribute name = "id" type = "xsd:ID"
1394 if (getTypeEntry(Constants.XSD_ID, false) == null) {
1395 symbolTablePut(new BaseType(Constants.XSD_ID));
1396 }
1397
1398 // attribute name = "href" type = "xsd:anyURI"
1399 if (getTypeEntry(Constants.XSD_ANYURI, false) == null) {
1400 symbolTablePut(new BaseType(Constants.XSD_ANYURI));
1401 }
1402 } else if (qName.equals(Constants.SOAP_COMMON_ATTRS12)) {
1403 symbolTablePut(new BaseType(qName));
1404
1405 // the 1.2 commonAttributes type contains one attribute
1406 // make sure the attribute's type is in the symbol table
1407 // attribute name = "id" type = "xsd:ID"
1408 if (getTypeEntry(Constants.XSD_ID, false) == null) {
1409 symbolTablePut(new BaseType(Constants.XSD_ID));
1410 }
1411 } else if (qName.equals(Constants.SOAP_ARRAY_ATTRS11)) {
1412 symbolTablePut(new BaseType(qName));
1413
1414 // the 1.1 arrayAttributes type contains two attributes
1415 // make sure the attributes' types are in the symbol table
1416 // attribute name = "arrayType" type = "xsd:string"
1417 if (getTypeEntry(Constants.XSD_STRING, false) == null) {
1418 symbolTablePut(new BaseType(Constants.XSD_STRING));
1419 }
1420
1421 // attribute name = "offset" type = "soapenc:arrayCoordinate"
1422 // which is really an xsd:string
1423 } else if (qName.equals(Constants.SOAP_ARRAY_ATTRS12)) {
1424 symbolTablePut(new BaseType(qName));
1425
1426 // the 1.2 arrayAttributes type contains two attributes
1427 // make sure the attributes' types are in the symbol table
1428 // attribute name = "arraySize" type = "2003soapenc:arraySize"
1429 // which is really a hairy beast that is not
1430 // supported, yet; so let's just use string
1431 if (getTypeEntry(Constants.XSD_STRING, false) == null) {
1432 symbolTablePut(new BaseType(Constants.XSD_STRING));
1433 }
1434
1435 // attribute name = "itemType" type = "xsd:QName"
1436 if (getTypeEntry(Constants.XSD_QNAME, false) == null) {
1437 symbolTablePut(new BaseType(Constants.XSD_QNAME));
1438 }
1439 } else if (forElement.value == false) {
1440 symbolTablePut(new UndefinedType(qName));
1441 } else {
1442 symbolTablePut(new UndefinedElement(qName));
1443 }
1444 }
1445 }
1446 } // createTypeFromRef
1447
1448 /**
1449 * Populate the symbol table with all of the MessageEntry's from the Definition.
1450 *
1451 * @param def
1452 * @throws IOException
1453 */
1454 private void populateMessages(Definition def) throws IOException {
1455
1456 Iterator i = def.getMessages().values().iterator();
1457
1458 while (i.hasNext()) {
1459 Message message = (Message) i.next();
1460 MessageEntry mEntry = new MessageEntry(message);
1461
1462 symbolTablePut(mEntry);
1463 }
1464 } // populateMessages
1465
1466 /**
1467 * ensures that a message in a <code><input></code>, <code><output></code>,
1468 * or <code><fault></fault> element in an <code><operation></code>
1469 * element is valid. In particular, ensures that
1470 * <ol>
1471 * <li>an attribute <code>message</code> is present (according to the
1472 * XML Schema for WSDL 1.1 <code>message</code> is <strong>required</strong>
1473 * <p/>
1474 * <li>the value of attribute <code>message</code> (a QName) refers to
1475 * an already defined message
1476 * </ol>
1477 * <p/>
1478 * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code> rather than
1479 * a <code>java.io.IOException</code>
1480 *
1481 * @param message the message object
1482 * @throws IOException thrown, if the message is not valid
1483 */
1484 protected void ensureOperationMessageValid(Message message)
1485 throws IOException {
1486
1487 // make sure the message is not null (i.e. there is an
1488 // attribute 'message ')
1489 //
1490 if (message == null) {
1491 throw new IOException(
1492 "<input>,<output>, or <fault> in <operation ..> without attribute 'message' found. Attribute 'message' is required.");
1493 }
1494
1495 // make sure the value of the attribute refers to an
1496 // already defined message
1497 //
1498 if (message.isUndefined()) {
1499 throw new IOException(
1500 "<input ..>, <output ..> or <fault ..> in <portType> with undefined message found. message name is '"
1501 + message.getQName().toString() + "'");
1502 }
1503 }
1504
1505 /**
1506 * ensures that an an element <code><operation></code> within
1507 * an element <code><portType><code> is valid. Throws an exception
1508 * if the operation is not valid.
1509 * <p/>
1510 * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code>
1511 * rather than a <code>java.io.IOException</code>
1512 *
1513 * @param operation the operation element
1514 * @throws IOException thrown, if the element is not valid.
1515 * @throws IllegalArgumentException thrown, if operation is null
1516 */
1517 protected void ensureOperationValid(Operation operation)
1518 throws IOException {
1519
1520 if (operation == null) {
1521 throw new IllegalArgumentException(
1522 "parameter 'operation' must not be null");
1523 }
1524
1525 Input input = operation.getInput();
1526 Message message;
1527
1528 if (input != null) {
1529 message = input.getMessage();
1530 if (message == null) {
1531 throw new IOException(
1532 "No 'message' attribute in <input> for operation '" +
1533 operation.getName() + "'");
1534 }
1535 ensureOperationMessageValid(message);
1536 }
1537
1538 Output output = operation.getOutput();
1539
1540 if (output != null) {
1541 message = output.getMessage();
1542 if (message == null) {
1543 throw new IOException(
1544 "No 'message' attribute in <output> for operation '" +
1545 operation.getName() + "'");
1546 }
1547 ensureOperationMessageValid(output.getMessage());
1548 }
1549
1550 Map faults = operation.getFaults();
1551
1552 if (faults != null) {
1553 Iterator it = faults.values().iterator();
1554
1555 while (it.hasNext()) {
1556 Fault fault = (Fault)it.next();
1557 message = fault.getMessage();
1558 if (message == null) {
1559 throw new IOException(
1560 "No 'message' attribute in <fault> named '" +
1561 fault.getName() + "' for operation '" +
1562 operation.getName() + "'");
1563 }
1564 ensureOperationMessageValid(message);
1565 }
1566 }
1567 }
1568
1569 /**
1570 * ensures that an an element <code><portType></code>
1571 * is valid. Throws an exception if the portType is not valid.
1572 * <p/>
1573 * <strong>Note</strong>: this method should throw a <code>javax.wsdl.WSDLException</code>
1574 * rather than a <code>java.io.IOException</code>
1575 *
1576 * @param portType the portType element
1577 * @throws IOException thrown, if the element is not valid.
1578 * @throws IllegalArgumentException thrown, if operation is null
1579 */
1580 protected void ensureOperationsOfPortTypeValid(PortType portType)
1581 throws IOException {
1582
1583 if (portType == null) {
1584 throw new IllegalArgumentException(
1585 "parameter 'portType' must not be null");
1586 }
1587
1588 List operations = portType.getOperations();
1589
1590 // no operations defined ? -> valid according to the WSDL 1.1 schema
1591 //
1592 if ((operations == null) || (operations.size() == 0)) {
1593 return;
1594 }
1595
1596 // check operations defined in this portType
1597 //
1598 Iterator it = operations.iterator();
1599
1600 while (it.hasNext()) {
1601 Operation operation = (Operation) it.next();
1602
1603 ensureOperationValid(operation);
1604 }
1605 }
1606
1607 /**
1608 * Populate the symbol table with all of the PortTypeEntry's from the Definition.
1609 *
1610 * @param def
1611 * @throws IOException
1612 */
1613 private void populatePortTypes(Definition def) throws IOException {
1614
1615 Iterator i = def.getPortTypes().values().iterator();
1616
1617 while (i.hasNext()) {
1618 PortType portType = (PortType) i.next();
1619
1620 // If the portType is undefined, then we're parsing a Definition
1621 // that didn't contain a portType, merely a binding that referred
1622 // to a non-existent port type. Don't bother with it.
1623 if (!portType.isUndefined()) {
1624 ensureOperationsOfPortTypeValid(portType);
1625
1626 PortTypeEntry ptEntry = new PortTypeEntry(portType);
1627
1628 symbolTablePut(ptEntry);
1629 }
1630 }
1631 } // populatePortTypes
1632
1633 /**
1634 * Create the parameters and store them in the bindingEntry.
1635 *
1636 * @throws IOException
1637 */
1638 private void populateParameters() throws IOException {
1639
1640 Iterator it = symbolTable.values().iterator();
1641
1642 while (it.hasNext()) {
1643 Vector v = (Vector) it.next();
1644
1645 for (int i = 0; i < v.size(); ++i) {
1646 if (v.get(i) instanceof BindingEntry) {
1647 BindingEntry bEntry = (BindingEntry) v.get(i);
1648
1649 // Skip non-soap bindings
1650 if (bEntry.getBindingType() != BindingEntry.TYPE_SOAP) {
1651 continue;
1652 }
1653
1654 Binding binding = bEntry.getBinding();
1655 Collection bindOperations = bEntry.getOperations();
1656 PortType portType = binding.getPortType();
1657 HashMap parameters = new HashMap();
1658 Iterator operations =
1659 portType.getOperations().iterator();
1660
1661 // get parameters
1662 while (operations.hasNext()) {
1663 Operation operation = (Operation) operations.next();
1664
1665 // See if the PortType operation has a corresponding
1666 // Binding operation and report an error if it doesn't.
1667 if (!bindOperations.contains(operation)) {
1668 throw new IOException(
1669 Messages.getMessage(
1670 "emitFailNoMatchingBindOperation01",
1671 operation.getName(),
1672 portType.getQName().getLocalPart()));
1673 }
1674
1675 String namespace =
1676 portType.getQName().getNamespaceURI();
1677 Parameters parms = getOperationParameters(operation,
1678 namespace, bEntry);
1679 parameters.put(operation, parms);
1680 }
1681
1682 bEntry.setParameters(parameters);
1683 }
1684 }
1685 }
1686 } // populateParameters
1687
1688 /**
1689 * For the given operation, this method returns the parameter info conveniently collated.
1690 * There is a bit of processing that is needed to write the interface, stub, and skeleton.
1691 * Rather than do that processing 3 times, it is done once, here, and stored in the
1692 * Parameters object.
1693 *
1694 * @param operation
1695 * @param namespace
1696 * @param bindingEntry
1697 * @return
1698 * @throws IOException
1699 */
1700 public Parameters getOperationParameters(
1701 Operation operation, String namespace, BindingEntry bindingEntry)
1702 throws IOException {
1703
1704 Parameters parameters = new Parameters();
1705
1706 // The input and output Vectors of Parameters
1707 Vector inputs = new Vector();
1708 Vector outputs = new Vector();
1709 List parameterOrder = operation.getParameterOrdering();
1710
1711 // Handle parameterOrder="", which is techinically illegal
1712 if ((parameterOrder != null) && parameterOrder.isEmpty()) {
1713 parameterOrder = null;
1714 }
1715
1716 Input input = operation.getInput();
1717 Output output = operation.getOutput();
1718
1719 parameters.mep = operation.getStyle();
1720
1721 // All input parts MUST be in the parameterOrder list. It is an error otherwise.
1722 if (parameterOrder != null && !wrapped) {
1723 if (input != null) {
1724 Message inputMsg = input.getMessage();
1725 Map allInputs = inputMsg.getParts();
1726 Collection orderedInputs =
1727 inputMsg.getOrderedParts(parameterOrder);
1728
1729 if (allInputs.size() != orderedInputs.size()) {
1730 throw new IOException(
1731 Messages.getMessage("emitFail00", operation.getName()));
1732 }
1733 }
1734 }
1735
1736 boolean literalInput = false;
1737 boolean literalOutput = false;
1738
1739 if (bindingEntry != null) {
1740 literalInput = (bindingEntry.getInputBodyType(operation)
1741 == Use.LITERAL);
1742 literalOutput = (bindingEntry.getOutputBodyType(operation)
1743 == Use.LITERAL);
1744 }
1745
1746 // Collect all the input parameters
1747 if ((input != null) && (input.getMessage() != null)) {
1748 getParametersFromParts(inputs,
1749 input.getMessage().getOrderedParts(null),
1750 literalInput, operation.getName(),
1751 bindingEntry);
1752 }
1753
1754 // Collect all the output parameters
1755 if ((output != null) && (output.getMessage() != null)) {
1756 getParametersFromParts(outputs,
1757 output.getMessage().getOrderedParts(null),
1758 literalOutput, operation.getName(),
1759 bindingEntry);
1760 }
1761
1762 if (parameterOrder != null && !wrapped) {
1763
1764 // Construct a list of the parameters in the parameterOrder list, determining the
1765 // mode of each parameter and preserving the parameterOrder list.
1766 for (int i = 0; i < parameterOrder.size(); ++i) {
1767 String name = (String) parameterOrder.get(i);
1768
1769 // index in the inputs Vector of the given name, -1 if it doesn't exist.
1770 int index = getPartIndex(name, inputs);
1771
1772 // index in the outputs Vector of the given name, -1 if it doesn't exist.
1773 int outdex = getPartIndex(name, outputs);
1774
1775 if (index >= 0) {
1776
1777 // The mode of this parameter is either in or inout
1778 addInishParm(inputs, outputs, index, outdex, parameters,
1779 true);
1780 } else if (outdex >= 0) {
1781 addOutParm(outputs, outdex, parameters, true);
1782 } else {
1783 System.err.println(Messages.getMessage("noPart00", name));
1784 }
1785 }
1786 }
1787
1788 // Some special case logic for JAX-RPC, but also to make things
1789 // nicer for the user.
1790 // If we have a single input and output with the same name
1791 // instead of: void echo(StringHolder inout)
1792 // Do this: string echo(string in)
1793 if (wrapped && (inputs.size() == 1) && (outputs.size() == 1)
1794 &&
1795 Utils.getLastLocalPart(((Parameter) inputs.get(0)).getName()).equals(
1796 Utils.getLastLocalPart(((Parameter) outputs.get(0)).getName()))
1797 ) {
1798
1799 // add the input and make sure its a IN not an INOUT
1800 addInishParm(inputs, null, 0, -1, parameters, false);
1801 } else {
1802
1803 // Get the mode info about those parts that aren't in the
1804 // parameterOrder list. Since they're not in the parameterOrder list,
1805 // the order is, first all in (and inout) parameters, then all out
1806 // parameters, in the order they appear in the messages.
1807 for (int i = 0; i < inputs.size(); i++) {
1808 Parameter p = (Parameter) inputs.get(i);
1809 int outdex = getPartIndex(p.getName(), outputs);
1810
1811 addInishParm(inputs, outputs, i, outdex, parameters, false);
1812 }
1813 }
1814
1815 // Now that the remaining in and inout parameters are collected,
1816 // determine the status of outputs. If there is only 1, then it
1817 // is the return value. If there are more than 1, then they are
1818 // out parameters.
1819 if (outputs.size() == 1) {
1820 parameters.returnParam = (Parameter) outputs.get(0);
1821
1822 parameters.returnParam.setMode(Parameter.OUT);
1823
1824 if (parameters.returnParam.getType() instanceof DefinedElement) {
1825 parameters.returnParam.setQName(
1826 parameters.returnParam.getType().getQName());
1827 }
1828
1829 ++parameters.outputs;
1830 } else {
1831 for (int i = 0; i < outputs.size(); i++) {
1832 addOutParm(outputs, i, parameters, false);
1833 }
1834 }
1835
1836 parameters.faults = operation.getFaults();
1837
1838 // before we return the paramters,
1839 // make sure we dont have a duplicate name
1840 Vector used = new Vector(parameters.list.size());
1841 Iterator i = parameters.list.iterator();
1842
1843 while (i.hasNext()) {
1844 Parameter parameter = (Parameter) i.next();
1845 int count = 2;
1846
1847 while (used.contains(parameter.getName())) {
1848
1849 // duplicate, add a suffix and try again
1850 parameter.setName(parameter.getName()
1851 + Integer.toString(count++));
1852 }
1853
1854 used.add(parameter.getName());
1855 }
1856
1857 return parameters;
1858 } // parameters
1859
1860 /**
1861 * Return the index of the given name in the given Vector, -1 if it doesn't exist.
1862 *
1863 * @param name
1864 * @param v
1865 * @return
1866 */
1867 private int getPartIndex(String name, Vector v) {
1868 name = Utils.getLastLocalPart(name);
1869 for (int i = 0; i < v.size(); i++) {
1870 String paramName = ((Parameter) v.get(i)).getName();
1871 paramName = Utils.getLastLocalPart(paramName);
1872 if (name.equals(paramName)) {
1873 return i;
1874 }
1875 }
1876
1877 return -1;
1878 } // getPartIndex
1879
1880 /**
1881 * Add an in or inout parameter to the parameters object.
1882 *
1883 * @param inputs
1884 * @param outputs
1885 * @param index
1886 * @param outdex
1887 * @param parameters
1888 * @param trimInput
1889 */
1890 private void addInishParm(Vector inputs, Vector outputs, int index,
1891 int outdex, Parameters parameters,
1892 boolean trimInput) {
1893
1894 Parameter p = (Parameter) inputs.get(index);
1895
1896 // If this is an element, we want the XML to reflect the element name
1897 // not the part name. Same check is made in addOutParam below.
1898 if (p.getType() instanceof DefinedElement) {
1899 DefinedElement de = (DefinedElement) p.getType();
1900
1901 p.setQName(de.getQName());
1902 }
1903
1904 // If this is a collection we want the XML to reflect the type in
1905 // the collection, not foo[unbounded].
1906 // Same check is made in addOutParam below.
1907 if (p.getType() instanceof CollectionElement) {
1908 p.setQName(p.getType().getRefType().getQName());
1909 }
1910
1911 // Should we remove the given parameter type/name entries from the Vector?
1912 if (trimInput) {
1913 inputs.remove(index);
1914 }
1915
1916 // At this point we know the name and type of the parameter, and that it's at least an
1917 // in parameter. Now check to see whether it's also in the outputs Vector. If it is,
1918 // then it's an inout parameter.
1919 if (outdex >= 0) {
1920 Parameter outParam = (Parameter) outputs.get(outdex);
1921
1922 TypeEntry paramEntry = p.getType();
1923 TypeEntry outParamEntry = outParam.getType();
1924// String paramLastLocalPart = Utils.getLastLocalPart(paramEntry.getQName().getLocalPart());
1925// String outParamLastLocalPart = Utils.getLastLocalPart(outParamEntry.getQName().getLocalPart());
1926 if (paramEntry.equals(outParamEntry)) {
1927 outputs.remove(outdex);
1928 p.setMode(Parameter.INOUT);
1929
1930 ++parameters.inouts;
1931 }
1932 /*
1933 else if (paramLastLocalPart.equals(outParamLastLocalPart)) {
1934 outputs.remove(outdex);
1935 p.setMode(Parameter.INOUT);
1936
1937 ++parameters.inouts;
1938 if (paramEntry.isBaseType()) {
1939 if (paramEntry.getBaseType().equals(outParamEntry.getBaseType())) {
1940 outputs.remove(outdex);
1941 p.setMode(Parameter.INOUT);
1942
1943 ++parameters.inouts;
1944 }
1945 }
1946 else if (paramEntry.getRefType() != null) {
1947 if (paramEntry.getRefType().equals(outParamEntry.getRefType())) {
1948 outputs.remove(outdex);
1949 p.setMode(Parameter.INOUT);
1950
1951 ++parameters.inouts;
1952 }
1953 }
1954 else {
1955 ++parameters.inputs;
1956 }
1957 }
1958 */
1959 else {
1960
1961 // If we're here, we have both an input and an output
1962 // part with the same name but different types.... guess
1963 // it's not really an inout....
1964 //
1965 // throw new IOException(Messages.getMessage("differentTypes00",
1966 // new String[] { p.getName(),
1967 // p.getType().getQName().toString(),
1968 // outParam.getType().getQName().toString()
1969 // }
1970 // ));
1971 // There is some controversy about this, and the specs are
1972 // a bit vague about what should happen if the types don't
1973 // agree. Throwing an error is not correct with document/lit
1974 // operations, as part names get resused (i.e. "body").
1975 // See WSDL 1.1 section 2.4.6,
1976 // WSDL 1.2 working draft 9 July 2002 section 2.3.1
1977 ++parameters.inputs;
1978 }
1979 } else {
1980 ++parameters.inputs;
1981 }
1982
1983 parameters.list.add(p);
1984 } // addInishParm
1985
1986 /**
1987 * Add an output parameter to the parameters object.
1988 *
1989 * @param outputs
1990 * @param outdex
1991 * @param parameters
1992 * @param trim
1993 */
1994 private void addOutParm(Vector outputs, int outdex, Parameters parameters,
1995 boolean trim) {
1996
1997 Parameter p = (Parameter) outputs.get(outdex);
1998
1999 // If this is an element, we want the XML to reflect the element name
2000 // not the part name. Same check is made in addInishParam above.
2001 if (p.getType() instanceof DefinedElement) {
2002 DefinedElement de = (DefinedElement) p.getType();
2003
2004 p.setQName(de.getQName());
2005 }
2006
2007 // If this is a collection we want the XML to reflect the type in
2008 // the collection, not foo[unbounded].
2009 // Same check is made in addInishParam above.
2010 if (p.getType() instanceof CollectionElement) {
2011 p.setQName(p.getType().getRefType().getQName());
2012 }
2013
2014 if (trim) {
2015 outputs.remove(outdex);
2016 }
2017
2018 p.setMode(Parameter.OUT);
2019
2020 ++parameters.outputs;
2021
2022 parameters.list.add(p);
2023 } // addOutParm
2024
2025 /**
2026 * This method returns a vector containing Parameters which represent
2027 * each Part (shouldn't we call these "Parts" or something?)
2028 *
2029 * This routine does the wrapped doc/lit processing.
2030 * It is also used for generating Faults, and this really confuses things
2031 * but we need to do the same processing for the fault messages.
2032 *
2033 * This whole method is waaaay too complex.
2034 * It needs rewriting (for instance, we sometimes new up
2035 * a Parameter, then ignore it in favor of another we new up.)
2036 *
2037 * @param v The output vector of parameters
2038 * @param parts The parts of the message
2039 * @param literal Are we in a literal operation (or fault)?
2040 * @param opName The operation (or fault) name
2041 * @param bindingEntry The binding for this operation - can be NULL if we are looking at a fault
2042 * @throws IOException when encountering an error in the WSDL
2043 */
2044 public void getParametersFromParts(Vector v,
2045 Collection parts,
2046 boolean literal,
2047 String opName,
2048 BindingEntry bindingEntry)
2049 throws IOException {
2050
2051 // Determine if there's only one element. For wrapped
2052 // style, we normally only have 1 part which is an
2053 // element. But with MIME we could have any number of
2054 // types along with that single element. As long as
2055 // there's only ONE element, and it's the same name as
2056 // the operation, we can unwrap it.
2057 int numberOfElements = 0;
2058 boolean possiblyWrapped = false;
2059 Iterator i = parts.iterator();
2060
2061 while (i.hasNext()) {
2062 Part part = (Part) i.next();
2063
2064 if (part.getElementName() != null) {
2065 ++numberOfElements;
2066
2067 if (part.getElementName().getLocalPart().equals(opName)) {
2068 possiblyWrapped = true;
2069 }
2070 }
2071 }
2072
2073 // Try to sense "wrapped" document literal mode
2074 // if we haven't been told not to.
2075 // Criteria:
2076 // - If there is a single element part,
2077 // - That part is an element
2078 // - That element has the same name as the operation
2079 // - That element has no attributes (check done below)
2080
2081 if (!nowrap && literal && (numberOfElements == 1) && possiblyWrapped) {
2082 wrapped = true;
2083 }
2084
2085 i = parts.iterator();
2086
2087 while (i.hasNext()) {
2088 Parameter param = new Parameter();
2089 Part part = (Part) i.next();
2090 QName elementName = part.getElementName();
2091 QName typeName = part.getTypeName();
2092 String partName = part.getName();
2093
2094 // if we are either:
2095 // 1. encoded
2096 // 2. literal & not wrapped.
2097 if (!literal || !wrapped || (elementName == null)) {
2098 param.setName(partName);
2099
2100 // Add this type or element name
2101 if (typeName != null) {
2102 param.setType(getType(typeName));
2103 } else if (elementName != null) {
2104
2105 // Just an FYI: The WSDL spec says that for use=encoded
2106 // that parts reference an abstract type using the type attr
2107 // but we kinda do the right thing here, so let it go.
2108 // if (!literal)
2109 // error...
2110 param.setType(getElement(elementName));
2111 } else {
2112
2113 // no type or element
2114 throw new IOException(
2115 Messages.getMessage(
2116 "noTypeOrElement00", new String[]{partName,
2117 opName}));
2118 }
2119
2120 fillParamInfo(param, bindingEntry, opName, partName);
2121 v.add(param);
2122
2123 continue; // next part
2124 }
2125
2126 // flow to here means wrapped literal !
2127 // See if we can map all the XML types to java(?) types
2128 // if we can, we use these as the types
2129 Node node = null;
2130 TypeEntry typeEntry = null;
2131
2132 if ((typeName != null)
2133 && (bindingEntry == null || bindingEntry.getMIMETypes().size() == 0)) {
2134
2135 // Since we can't (yet?) make the Axis engine generate the right
2136 // XML for literal parts that specify the type attribute,
2137 // (unless they're MIME types) abort processing with an
2138 // error if we encounter this case
2139 //
2140 // node = getTypeEntry(typeName, false).getNode();
2141 String bindingName = (bindingEntry == null)
2142 ? "unknown"
2143 : bindingEntry.getBinding().getQName().toString();
2144
2145 throw new IOException(Messages.getMessage("literalTypePart00",
2146 new String[]{
2147 partName,
2148 opName,
2149 bindingName}));
2150 }
2151
2152 // Get the node which corresponds to the type entry for this
2153 // element. i.e.:
2154 // <part name="part" element="foo:bar"/>
2155 // ...
2156 // <schema targetNamespace="foo">
2157 // <element name="bar"...> <--- This one
2158 typeEntry = getTypeEntry(elementName, true);
2159 node = typeEntry.getNode();
2160
2161 // Check if this element is of the form:
2162 // <element name="foo" type="tns:foo_type"/>
2163 BooleanHolder forElement = new BooleanHolder();
2164 QName type = Utils.getTypeQName(node, forElement,
2165 false);
2166 if ((type != null) && !forElement.value) {
2167
2168 // If in fact we have such a type, go get the node that
2169 // corresponds to THAT definition.
2170 typeEntry = getTypeEntry(type, false);
2171 node = typeEntry.getNode();
2172 }
2173
2174 Vector vTypes = null;
2175
2176 // If we have nothing at this point, we're in trouble.
2177 if (node == null) {
2178 // If node is null, that means the element in question has no type declaration,
2179 // therefore is not a wrapper element.
2180 wrapped = false;
2181 } else {
2182
2183 // check for attributes
2184 if (typeEntry.getContainedAttributes() != null) {
2185 // can't do wrapped mode
2186 wrapped = false;
2187 }
2188
2189 if (!SchemaUtils.isWrappedType(node)) {
2190 // mark the type entry as not just literal referenced
2191 // This doesn't work, but it may help in the future.
2192 // The problem is "wrapped" is a symbol table wide flag,
2193 // which means if one operation breaks the rules
2194 // implemented in isWrappedType(), then everything goes bad
2195 // For example, see bug Axis-1900.
2196 typeEntry.setOnlyLiteralReference(false);
2197 wrapped = false;
2198 }
2199
2200 // Get the nested type entries.
2201 // TODO - If we are unable to represent any of the types in the
2202 // element, we need to use SOAPElement/SOAPBodyElement.
2203 // I don't believe getContainedElementDecl does the right thing yet.
2204 vTypes = typeEntry.getContainedElements();
2205 }
2206
2207 // IF we got the type entries and we didn't find attributes
2208 // THEN use the things in this element as the parameters
2209 if ((vTypes != null) && wrapped) {
2210
2211 // add the elements in this list
2212 for (int j = 0; j < vTypes.size(); j++) {
2213 ElementDecl elem = (ElementDecl) vTypes.elementAt(j);
2214 Parameter p = new Parameter();
2215
2216 p.setQName(elem.getQName());
2217 // If the parameter is a anonymous complex type, the parameter
2218 // name should just be the name of the element, not >foo>element
2219 String paramName = p.getName();
2220 final int gt = paramName.lastIndexOf(ANON_TOKEN);
2221 if (gt != 1) {
2222 paramName = paramName.substring(gt+1);
2223 }
2224 p.setName(paramName);
2225 p.setType(elem.getType());
2226 p.setOmittable(elem.getMinOccursIs0());
2227 fillParamInfo(p, bindingEntry, opName, partName);
2228 v.add(p);
2229 }
2230 } else {
2231
2232 // - we were unable to get the types OR
2233 // - we found attributes
2234 // so we can't use wrapped mode.
2235 param.setName(partName);
2236
2237 if (typeName != null) {
2238 param.setType(getType(typeName));
2239 } else if (elementName != null) {
2240 param.setType(getElement(elementName));
2241 }
2242
2243 fillParamInfo(param, bindingEntry, opName, partName);
2244 v.add(param);
2245 }
2246 } // while
2247 } // getParametersFromParts
2248
2249 /**
2250 * Set the header information for this paramter
2251 *
2252 * @param param Parameter to modify
2253 * @param bindingEntry Binding info for this operation/parameter
2254 * @param opName the operation we are processing
2255 * @param partName the part we are processing
2256 */
2257 private void fillParamInfo(Parameter param, BindingEntry bindingEntry,
2258 String opName, String partName) {
2259
2260 // If we don't have a binding, can't do anything
2261 if (bindingEntry == null)
2262 return;
2263
2264 setMIMEInfo(param, bindingEntry.getMIMEInfo(opName, partName));
2265
2266 boolean isHeader = false;
2267
2268 // Is this parameter in an Input header?
2269 if (bindingEntry.isInHeaderPart(opName, partName)) {
2270 isHeader = true;
2271 param.setInHeader(true);
2272 }
2273
2274 // Is this parameter in an Output header?
2275 if (bindingEntry.isOutHeaderPart(opName, partName)) {
2276 isHeader = true;
2277 param.setOutHeader(true);
2278 }
2279
2280 // If this parameter is part of a header, find the binding operation
2281 // that we are processing and get the QName of the parameter.
2282 if (isHeader && (bindingEntry.getBinding() != null)) {
2283 List list = bindingEntry.getBinding().getBindingOperations();
2284
2285 for (int i = 0; (list != null) && (i < list.size()); i++) {
2286 BindingOperation operation = (BindingOperation) list.get(i);
2287
2288 if (operation.getName().equals(opName)) {
2289 if (param.isInHeader()) {
2290 QName qName = getBindedParameterName(
2291 operation.getBindingInput().getExtensibilityElements(),
2292 param);
2293
2294 if(qName!= null) {
2295 param.setQName(qName);
2296 }
2297 } else if (param.isOutHeader()) {
2298 QName qName = getBindedParameterName(
2299 operation.getBindingOutput().getExtensibilityElements(),
2300 param);
2301
2302 if(qName!= null) {
2303 param.setQName(qName);
2304 }
2305 }
2306 }
2307 }
2308 }
2309 }
2310
2311 /**
2312 * Method getBindedParameterName
2313 *
2314 * @param elements
2315 * @param p
2316 * @return
2317 */
2318 private QName getBindedParameterName(List elements, Parameter p) {
2319
2320 // If the parameter can either be in the message header or in the
2321 // message body.
2322 // When it is in the header, there may be a SOAPHeader (soap:header)
2323 // with its part name. The namespace used is the one of the soap:header.
2324 // When it is in the body, if there is a SOAPBody with its part name,
2325 // the namespace used is the one of this soap:body.
2326 //
2327 // If the parameter is in the body and there is a soap:body with no parts,
2328 // its namespace is used for the parameter.
2329 QName paramName = null;
2330 String defaultNamespace = null;
2331 String parameterPartName = p.getName();
2332
2333 for (Iterator k = elements.iterator(); k.hasNext();) {
2334 ExtensibilityElement element = (ExtensibilityElement) k.next();
2335
2336 if (element instanceof SOAPBody) {
2337 SOAPBody bodyElement = (SOAPBody) element;
2338 List parts = bodyElement.getParts();
2339
2340 if ((parts == null) || (parts.size() == 0)) {
2341 defaultNamespace = bodyElement.getNamespaceURI();
2342 } else {
2343 boolean found = false;
2344
2345 for (Iterator l = parts.iterator(); l.hasNext();) {
2346 Object o = l.next();
2347
2348 if(o instanceof String) {
2349 if (parameterPartName.equals((String)o)) {
2350 paramName =
2351 new QName(bodyElement.getNamespaceURI(),
2352 parameterPartName);
2353 found = true;
2354 break;
2355 }
2356 }
2357 }
2358
2359 if (found) {
2360 break;
2361 }
2362 }
2363 } else if (element instanceof SOAPHeader) {
2364 SOAPHeader headerElement = (SOAPHeader) element;
2365 String part = headerElement.getPart();
2366
2367 if (parameterPartName.equals(part)) {
2368 paramName = new QName(headerElement.getNamespaceURI(),
2369 parameterPartName);
2370 break;
2371 }
2372 }
2373 }
2374
2375 if ((paramName == null) && (!p.isInHeader()) && (!p.isOutHeader())) {
2376 if (defaultNamespace != null) {
2377 paramName = new QName(defaultNamespace, parameterPartName);
2378 } else {
2379 paramName = p.getQName();
2380 }
2381 }
2382
2383 return paramName;
2384 }
2385
2386 /**
2387 * Set the MIME type. This can be determine in one of two ways:
2388 * 1. From WSDL 1.1 MIME constructs on the binding (passed in);
2389 * 2. From AXIS-specific xml MIME types.
2390 *
2391 * @param p
2392 * @param mimeInfo
2393 */
2394 private void setMIMEInfo(Parameter p, MimeInfo mimeInfo) {
2395
2396 // If there is no binding MIME construct (ie., the mimeType parameter is
2397 // null), then get the MIME type from the AXIS-specific xml MIME type.
2398 if (mimeInfo == null && p.getType() != null) {
2399 QName mimeQName = p.getType().getQName();
2400
2401 if (mimeQName.getNamespaceURI().equals(Constants.NS_URI_XMLSOAP)) {
2402 if (Constants.MIME_IMAGE.equals(mimeQName)) {
2403 mimeInfo = new MimeInfo("image/jpeg", "");
2404 } else if (Constants.MIME_PLAINTEXT.equals(mimeQName)) {
2405 mimeInfo = new MimeInfo("text/plain", "");
2406 } else if (Constants.MIME_MULTIPART.equals(mimeQName)) {
2407 mimeInfo = new MimeInfo("multipart/related", "");
2408 } else if (Constants.MIME_SOURCE.equals(mimeQName)) {
2409 mimeInfo = new MimeInfo("text/xml", "");
2410 } else if (Constants.MIME_OCTETSTREAM.equals(mimeQName)) {
2411 mimeInfo = new MimeInfo("application/octet-stream", "");
2412 }
2413 }
2414 }
2415
2416 p.setMIMEInfo(mimeInfo);
2417 } // setMIMEType
2418
2419 /**
2420 * Populate the symbol table with all of the BindingEntry's from the Definition.
2421 *
2422 * @param def
2423 * @throws IOException
2424 */
2425 private void populateBindings(Definition def) throws IOException {
2426
2427 Iterator i = def.getBindings().values().iterator();
2428
2429 while (i.hasNext()) {
2430 Binding binding = (Binding) i.next();
2431 BindingEntry bEntry = new BindingEntry(binding);
2432
2433 symbolTablePut(bEntry);
2434
2435 Iterator extensibilityElementsIterator =
2436 binding.getExtensibilityElements().iterator();
2437
2438 while (extensibilityElementsIterator.hasNext()) {
2439 Object obj = extensibilityElementsIterator.next();
2440
2441 if (obj instanceof SOAPBinding) {
2442 bEntry.setBindingType(BindingEntry.TYPE_SOAP);
2443
2444 SOAPBinding sb = (SOAPBinding) obj;
2445 String style = sb.getStyle();
2446
2447 if ("rpc".equalsIgnoreCase(style)) {
2448 bEntry.setBindingStyle(Style.RPC);
2449 }
2450 } else if (obj instanceof HTTPBinding) {
2451 HTTPBinding hb = (HTTPBinding) obj;
2452
2453 if (hb.getVerb().equalsIgnoreCase("post")) {
2454 bEntry.setBindingType(BindingEntry.TYPE_HTTP_POST);
2455 } else {
2456 bEntry.setBindingType(BindingEntry.TYPE_HTTP_GET);
2457 }
2458 } else if (obj instanceof UnknownExtensibilityElement) {
2459
2460 // TODO: After WSDL4J supports soap12, change this code
2461 UnknownExtensibilityElement unkElement =
2462 (UnknownExtensibilityElement) obj;
2463 QName name =
2464 unkElement.getElementType();
2465
2466 if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP)
2467 && name.getLocalPart().equals("binding")) {
2468 bEntry.setBindingType(BindingEntry.TYPE_SOAP);
2469
2470 String style =
2471 unkElement.getElement().getAttribute("style");
2472
2473 if ("rpc".equalsIgnoreCase(style)) {
2474 bEntry.setBindingStyle(Style.RPC);
2475 }
2476 }
2477 }
2478 }
2479
2480 // Step through the binding operations, setting the following as appropriate:
2481 // - hasLiteral
2482 // - body types
2483 // - mimeTypes
2484 // - headers
2485 HashMap attributes = new HashMap();
2486 List bindList = binding.getBindingOperations();
2487 HashMap faultMap = new HashMap(); // name to SOAPFault from WSDL4J
2488
2489 for (Iterator opIterator = bindList.iterator();
2490 opIterator.hasNext();) {
2491 BindingOperation bindOp =
2492 (BindingOperation) opIterator.next();
2493 Operation operation = bindOp.getOperation();
2494 BindingInput bindingInput = bindOp.getBindingInput();
2495 BindingOutput bindingOutput = bindOp.getBindingOutput();
2496 String opName = bindOp.getName();
2497
2498 // First, make sure the binding operation matches a portType operation
2499 String inputName = (bindingInput == null)
2500 ? null
2501 : bindingInput.getName();
2502 String outputName = (bindingOutput == null)
2503 ? null
2504 : bindingOutput.getName();
2505
2506 if (binding.getPortType().getOperation(
2507 opName, inputName, outputName) == null) {
2508 throw new IOException(Messages.getMessage("unmatchedOp",
2509 new String[]{
2510 opName,
2511 inputName,
2512 outputName}));
2513 }
2514
2515 ArrayList faults = new ArrayList();
2516
2517 // input
2518 if (bindingInput != null) {
2519 if (bindingInput.getExtensibilityElements() != null) {
2520 Iterator inIter =
2521 bindingInput.getExtensibilityElements().iterator();
2522
2523 fillInBindingInfo(bEntry, operation, inIter, faults,
2524 true);
2525 }
2526 }
2527
2528 // output
2529 if (bindingOutput != null) {
2530 if (bindingOutput.getExtensibilityElements() != null) {
2531 Iterator outIter =
2532 bindingOutput.getExtensibilityElements().iterator();
2533
2534 fillInBindingInfo(bEntry, operation, outIter, faults,
2535 false);
2536 }
2537 }
2538
2539 // faults
2540 faultsFromSOAPFault(binding, bindOp, operation, faults);
2541
2542 // Add this fault name and info to the map
2543 faultMap.put(bindOp, faults);
2544
2545 Use inputBodyType = bEntry.getInputBodyType(operation);
2546 Use outputBodyType = bEntry.getOutputBodyType(operation);
2547
2548 // Associate the portType operation that goes with this binding
2549 // with the body types.
2550 attributes.put(bindOp.getOperation(),
2551 new BindingEntry.OperationAttr(inputBodyType,
2552 outputBodyType, faultMap));
2553
2554 // If the input or output body uses literal, flag the binding as using literal.
2555 // NOTE: should I include faultBodyType in this check?
2556 if ((inputBodyType == Use.LITERAL)
2557 || (outputBodyType == Use.LITERAL)) {
2558 bEntry.setHasLiteral(true);
2559 }
2560
2561 bEntry.setFaultBodyTypeMap(operation, faultMap);
2562 } // binding operations
2563
2564 bEntry.setFaults(faultMap);
2565 }
2566 } // populateBindings
2567
2568 /**
2569 * Fill in some binding information: bodyType, mimeType, header info.
2570 *
2571 * @param bEntry
2572 * @param operation
2573 * @param it
2574 * @param faults
2575 * @param input
2576 * @throws IOException
2577 */
2578 private void fillInBindingInfo(
2579 BindingEntry bEntry, Operation operation, Iterator it, ArrayList faults, boolean input)
2580 throws IOException {
2581
2582 for (; it.hasNext();) {
2583 Object obj = it.next();
2584
2585 if (obj instanceof SOAPBody) {
2586 setBodyType(((SOAPBody) obj).getUse(), bEntry, operation,
2587 input);
2588 } else if (obj instanceof SOAPHeader) {
2589 SOAPHeader header = (SOAPHeader) obj;
2590
2591 setBodyType(header.getUse(), bEntry, operation, input);
2592
2593 // Note, this only works for explicit headers - those whose
2594 // parts come from messages used in the portType's operation
2595 // input/output clauses - it does not work for implicit
2596 // headers - those whose parts come from messages not used in
2597 // the portType's operation's input/output clauses. I don't
2598 // know what we're supposed to emit for implicit headers.
2599 bEntry.setHeaderPart(operation.getName(), header.getPart(),
2600 input
2601 ? BindingEntry.IN_HEADER
2602 : BindingEntry.OUT_HEADER);
2603
2604 // Add any soap:headerFault info to the faults array
2605 Iterator headerFaults = header.getSOAPHeaderFaults().iterator();
2606
2607 while (headerFaults.hasNext()) {
2608 SOAPHeaderFault headerFault =
2609 (SOAPHeaderFault) headerFaults.next();
2610
2611 faults.add(new FaultInfo(headerFault, this));
2612 }
2613 } else if (obj instanceof MIMEMultipartRelated) {
2614 bEntry.setBodyType(
2615 operation,
2616 addMIMETypes(
2617 bEntry, (MIMEMultipartRelated) obj, operation), input);
2618 } else if (obj instanceof UnknownExtensibilityElement) {
2619 UnknownExtensibilityElement unkElement =
2620 (UnknownExtensibilityElement) obj;
2621 QName name =
2622 unkElement.getElementType();
2623
2624 if (name.getNamespaceURI().equals(Constants.URI_DIME_WSDL)
2625 && name.getLocalPart().equals("message")) {
2626 fillInDIMEInformation(unkElement, input, operation, bEntry);
2627 }
2628
2629 // TODO: After WSDL4J supports soap12, change this code
2630 if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP)
2631 && name.getLocalPart().equals("body")) {
2632 setBodyType(unkElement.getElement().getAttribute("use"),
2633 bEntry, operation, input);
2634 }
2635
2636 // TODO: After WSDL4J supports soap12, change this code
2637 if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP)
2638 && name.getLocalPart().equals("header")) {
2639 setBodyType(unkElement.getElement().getAttribute("use"),
2640 bEntry, operation, input);
2641
2642 // Note, this only works for explicit headers - those whose
2643 // parts come from messages used in the portType's operation
2644 // input/output clauses - it does not work for implicit
2645 // headers - those whose parts come from messages not used in
2646 // the portType's operation's input/output clauses. I don't
2647 // know what we're supposed to emit for implicit headers.
2648 bEntry.setHeaderPart(
2649 operation.getName(),
2650 unkElement.getElement().getAttribute("part"), input
2651 ? BindingEntry.IN_HEADER
2652 : BindingEntry.OUT_HEADER);
2653
2654 // Add any soap12:headerFault info to the faults array
2655 NodeList headerFaults =
2656 unkElement.getElement().getChildNodes();
2657
2658 for (int i = 0; i < headerFaults.getLength(); i++) {
2659 String faultMessage =
2660 unkElement.getElement().getAttribute("message");
2661 String faultPart =
2662 unkElement.getElement().getAttribute("part");
2663 String faultUse =
2664 unkElement.getElement().getAttribute("use");
2665 String faultNamespaceURI =
2666 unkElement.getElement().getAttribute("namespace");
2667 QName faultMessageQName = null;
2668 int sep = faultMessage.indexOf(':');
2669
2670 if (sep == -1) {
2671 faultMessageQName = new QName(faultMessage);
2672 } else {
2673 faultMessageQName =
2674 new QName(faultMessage.substring(0, sep),
2675 faultMessage.substring(sep + 1));
2676 }
2677
2678 faults.add(new FaultInfo(faultMessageQName, faultPart,
2679 faultUse, faultNamespaceURI,
2680 this));
2681 }
2682 }
2683 }
2684 }
2685 } // fillInBindingInfo
2686
2687 /**
2688 * Fill in DIME information
2689 *
2690 * @param unkElement
2691 * @param input
2692 * @param operation
2693 * @param bEntry
2694 */
2695 private void fillInDIMEInformation(UnknownExtensibilityElement unkElement,
2696 boolean input, Operation operation,
2697 BindingEntry bEntry) {
2698
2699 String layout = unkElement.getElement().getAttribute("layout");
2700
2701 // TODO: what to do with layout info?
2702 if (layout.equals(Constants.URI_DIME_CLOSED_LAYOUT)) {
2703 } else if (layout.equals(Constants.URI_DIME_OPEN_LAYOUT)) {
2704 }
2705
2706 Map parts = null;
2707
2708 if (input) {
2709 parts = operation.getInput().getMessage().getParts();
2710 } else {
2711 parts = operation.getOutput().getMessage().getParts();
2712 }
2713
2714 if (parts != null) {
2715 Iterator iterator = parts.values().iterator();
2716
2717 while (iterator.hasNext()) {
2718 Part part = (Part) iterator.next();
2719
2720 if (part != null) {
2721 String dims = "";
2722 org.w3c.dom.Element element = null;
2723
2724 if (part.getTypeName() != null) {
2725 TypeEntry partType = getType(part.getTypeName());
2726
2727 if (partType.getDimensions().length() > 0) {
2728 dims = partType.getDimensions();
2729 partType = partType.getRefType();
2730 }
2731
2732 element = (org.w3c.dom.Element) partType.getNode();
2733 } else if (part.getElementName() != null) {
2734 TypeEntry partElement =
2735 getElement(part.getElementName()).getRefType();
2736
2737 element = (org.w3c.dom.Element) partElement.getNode();
2738
2739 QName name = getInnerCollectionComponentQName(element);
2740
2741 if (name != null) {
2742 dims += "[]";
2743 partElement = getType(name);
2744 element =
2745 (org.w3c.dom.Element) partElement.getNode();
2746 } else {
2747 name = getInnerTypeQName(element);
2748
2749 if (name != null) {
2750 partElement = getType(name);
2751 element =
2752 (org.w3c.dom.Element) partElement.getNode();
2753 }
2754 }
2755 }
2756
2757 if (element != null) {
2758 org.w3c.dom.Element e =
2759 (org.w3c.dom.Element) XMLUtils.findNode(
2760 element,
2761 new QName(
2762 Constants.URI_DIME_CONTENT, "mediaType"));
2763
2764 if (e != null) {
2765 String value = e.getAttribute("value");
2766
2767 bEntry.setOperationDIME(operation.getName());
2768 bEntry.setMIMEInfo(operation.getName(),
2769 part.getName(), value, dims);
2770 }
2771 }
2772 }
2773 }
2774 }
2775 }
2776
2777 /**
2778 * Get the faults from the soap:fault clause.
2779 *
2780 * @param binding
2781 * @param bindOp
2782 * @param operation
2783 * @param faults
2784 * @throws IOException
2785 */
2786 private void faultsFromSOAPFault(
2787 Binding binding, BindingOperation bindOp, Operation operation, ArrayList faults)
2788 throws IOException {
2789
2790 Iterator faultMapIter = bindOp.getBindingFaults().values().iterator();
2791
2792 for (; faultMapIter.hasNext();) {
2793 BindingFault bFault = (BindingFault) faultMapIter.next();
2794
2795 // Set default entry for this fault
2796 String faultName = bFault.getName();
2797
2798 // Check to make sure this fault is named
2799 if ((faultName == null) || (faultName.length() == 0)) {
2800 throw new IOException(
2801 Messages.getMessage(
2802 "unNamedFault00", bindOp.getName(),
2803 binding.getQName().toString()));
2804 }
2805
2806 boolean foundSOAPFault = false;
2807 String soapFaultUse = "";
2808 String soapFaultNamespace = "";
2809 Iterator faultIter =
2810 bFault.getExtensibilityElements().iterator();
2811
2812 for (; faultIter.hasNext();) {
2813 Object obj = faultIter.next();
2814
2815 if (obj instanceof SOAPFault) {
2816 foundSOAPFault = true;
2817 soapFaultUse = ((SOAPFault) obj).getUse();
2818 soapFaultNamespace = ((SOAPFault) obj).getNamespaceURI();
2819
2820 break;
2821 } else if (obj instanceof UnknownExtensibilityElement) {
2822
2823 // TODO: After WSDL4J supports soap12, change this code
2824 UnknownExtensibilityElement unkElement =
2825 (UnknownExtensibilityElement) obj;
2826 QName name =
2827 unkElement.getElementType();
2828
2829 if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP)
2830 && name.getLocalPart().equals("fault")) {
2831 if (unkElement.getElement().getAttribute("use")
2832 != null) {
2833 soapFaultUse =
2834 unkElement.getElement().getAttribute("use");
2835 }
2836
2837 if (unkElement.getElement().getAttribute("namespace")
2838 != null) {
2839 soapFaultNamespace =
2840 unkElement.getElement().getAttribute(
2841 "namespace");
2842 }
2843 }
2844 }
2845 }
2846
2847 // Check to make sure we have a soap:fault element
2848 if (!foundSOAPFault) {
2849 throw new IOException(
2850 Messages.getMessage(
2851 "missingSoapFault00", faultName, bindOp.getName(),
2852 binding.getQName().toString()));
2853 }
2854
2855 // TODO error checking:
2856 // if use=literal, no use of namespace on the soap:fault
2857 // if use=encoded, no use of element on the part
2858 // Check this fault to make sure it matches the one
2859 // in the matching portType Operation
2860 Fault opFault = operation.getFault(bFault.getName());
2861
2862 if (opFault == null) {
2863 throw new IOException(Messages.getMessage("noPortTypeFault",
2864 new String[]{
2865 bFault.getName(),
2866 bindOp.getName(),
2867 binding.getQName().toString()}));
2868 }
2869
2870 // put the updated entry back in the map
2871 faults.add(new FaultInfo(opFault, Use.getUse(soapFaultUse),
2872 soapFaultNamespace, this));
2873 }
2874 } // faultsFromSOAPFault
2875
2876 /**
2877 * Set the body type.
2878 *
2879 * @param use
2880 * @param bEntry
2881 * @param operation
2882 * @param input
2883 */
2884 private void setBodyType(String use,
2885 BindingEntry bEntry,
2886 Operation operation,
2887 boolean input)
2888 {
2889
2890 if (use == null) {
2891 // Deprecated
2892 // throw new IOException(Messages.getMessage("noUse",
2893 // operation.getName()));
2894 // for WS-I BP 1.0 R2707.
2895 // Set default of use to literal.
2896 use = "literal";
2897 }
2898
2899 if (use.equalsIgnoreCase("literal")) {
2900 bEntry.setBodyType(operation, Use.LITERAL, input);
2901 }
2902 } // setBodyType
2903
2904 /**
2905 * Add the parts that are really MIME types as MIME types.
2906 * A side effect is to return the body Type of the given
2907 * MIMEMultipartRelated object.
2908 *
2909 * @param bEntry
2910 * @param mpr
2911 * @param op
2912 * @return
2913 * @throws IOException
2914 */
2915 private Use addMIMETypes(
2916 BindingEntry bEntry, MIMEMultipartRelated mpr, Operation op)
2917 throws IOException {
2918
2919 Use bodyType = Use.ENCODED;
2920 List parts = mpr.getMIMEParts();
2921 Iterator i = parts.iterator();
2922
2923 while (i.hasNext()) {
2924 MIMEPart part = (MIMEPart) i.next();
2925 List elems = part.getExtensibilityElements();
2926 Iterator j = elems.iterator();
2927
2928 while (j.hasNext()) {
2929 Object obj = j.next();
2930
2931 if (obj instanceof MIMEContent) {
2932 MIMEContent content = (MIMEContent) obj;
2933 TypeEntry typeEntry = findPart(op, content.getPart());
2934 if (typeEntry == null) {
2935 throw new RuntimeException(Messages.getMessage("cannotFindPartForOperation00", content.getPart(),
2936 op.getName(), content.getType()));
2937 }
2938 String dims = typeEntry.getDimensions();
2939
2940 if ((dims.length() <= 0)
2941 && (typeEntry.getRefType() != null)) {
2942 Node node = typeEntry.getRefType().getNode();
2943
2944 if (getInnerCollectionComponentQName(node) != null) {
2945 dims += "[]";
2946 }
2947 }
2948
2949 String type = content.getType();
2950
2951 if ((type == null) || (type.length() == 0)) {
2952 type = "text/plain";
2953 }
2954
2955 bEntry.setMIMEInfo(op.getName(), content.getPart(), type,
2956 dims);
2957 } else if (obj instanceof SOAPBody) {
2958 String use = ((SOAPBody) obj).getUse();
2959
2960 if (use == null) {
2961 throw new IOException(
2962 Messages.getMessage("noUse", op.getName()));
2963 }
2964
2965 if (use.equalsIgnoreCase("literal")) {
2966 bodyType = Use.LITERAL;
2967 }
2968 } else if (obj instanceof UnknownExtensibilityElement) {
2969
2970 // TODO: After WSDL4J supports soap12, change this code
2971 UnknownExtensibilityElement unkElement =
2972 (UnknownExtensibilityElement) obj;
2973 QName name =
2974 unkElement.getElementType();
2975
2976 if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP)
2977 && name.getLocalPart().equals("body")) {
2978 String use =
2979 unkElement.getElement().getAttribute("use");
2980
2981 if (use == null) {
2982 throw new IOException(
2983 Messages.getMessage("noUse", op.getName()));
2984 }
2985
2986 if (use.equalsIgnoreCase("literal")) {
2987 bodyType = Use.LITERAL;
2988 }
2989 }
2990 }
2991 }
2992 }
2993
2994 return bodyType;
2995 } // addMIMETypes
2996
2997 /**
2998 * Method findPart
2999 *
3000 * @param operation
3001 * @param partName
3002 * @return
3003 */
3004 private TypeEntry findPart(Operation operation, String partName) {
3005
3006 Map parts = operation.getInput().getMessage().getParts();
3007 Iterator iterator = parts.values().iterator();
3008 TypeEntry part = findPart(iterator, partName);
3009
3010 if (part == null) {
3011 parts = operation.getOutput().getMessage().getParts();
3012 iterator = parts.values().iterator();
3013 part = findPart(iterator, partName);
3014 }
3015
3016 return part;
3017 }
3018
3019 /**
3020 * Method findPart
3021 *
3022 * @param iterator
3023 * @param partName
3024 * @return
3025 */
3026 private TypeEntry findPart(Iterator iterator, String partName) {
3027
3028 while (iterator.hasNext()) {
3029 Part part = (Part) iterator.next();
3030
3031 if (part != null) {
3032 String typeName = part.getName();
3033
3034 if (partName.equals(typeName)) {
3035 if (part.getTypeName() != null) {
3036 return getType(part.getTypeName());
3037 } else if (part.getElementName() != null) {
3038 return getElement(part.getElementName());
3039 }
3040 }
3041 }
3042 }
3043
3044 return null;
3045 }
3046
3047 /**
3048 * Populate the symbol table with all of the ServiceEntry's from the Definition.
3049 *
3050 * @param def
3051 * @throws IOException
3052 */
3053 private void populateServices(Definition def) throws IOException {
3054
3055 Iterator i = def.getServices().values().iterator();
3056
3057 while (i.hasNext()) {
3058 Service service = (Service) i.next();
3059
3060 // do a bit of name validation
3061 if ((service.getQName() == null)
3062 || (service.getQName().getLocalPart() == null)
3063 || service.getQName().getLocalPart().equals("")) {
3064 throw new IOException(Messages.getMessage("BadServiceName00"));
3065 }
3066
3067 ServiceEntry sEntry = new ServiceEntry(service);
3068
3069 symbolTablePut(sEntry);
3070 populatePorts(service.getPorts());
3071 }
3072 } // populateServices
3073
3074 /**
3075 * populates the symbol table with port elements defined within a <service>
3076 * element.
3077 *
3078 * @param ports a map of name->port pairs (i.e. what is returned by service.getPorts()
3079 * @throws IOException thrown, if an IO or WSDL error is detected
3080 * @see javax.wsdl.Service#getPorts()
3081 * @see javax.wsdl.Port
3082 */
3083 private void populatePorts(Map ports) throws IOException {
3084
3085 if (ports == null) {
3086 return;
3087 }
3088
3089 Iterator it = ports.values().iterator();
3090
3091 while (it.hasNext()) {
3092 Port port = (Port) it.next();
3093 String portName = port.getName();
3094 Binding portBinding = port.getBinding();
3095
3096 // make sure there is a port name. The 'name' attribute for WSDL ports is
3097 // mandatory
3098 //
3099 if (portName == null) {
3100
3101 // REMIND: should rather be a javax.wsdl.WSDLException ?
3102 throw new IOException(
3103 Messages.getMessage("missingPortNameException"));
3104 }
3105
3106 // make sure there is a binding for the port. The 'binding' attribute for
3107 // WSDL ports is mandatory
3108 //
3109 if (portBinding == null) {
3110
3111 // REMIND: should rather be a javax.wsdl.WSDLException ?
3112 throw new IOException(
3113 Messages.getMessage("missingBindingException"));
3114 }
3115
3116 // make sure the port name is unique among all port names defined in this
3117 // WSDL document.
3118 //
3119 // NOTE: there's a flaw in com.ibm.wsdl.xml.WSDLReaderImpl#parsePort() and
3120 // com.ibm.wsdl.xml.WSDLReaderImpl#addPort(). These methods do not enforce
3121 // the port name exists and is unique. Actually, if two port definitions with
3122 // the same name exist within the same service element, only *one* port
3123 // element is present after parsing and the following exception is not thrown.
3124 //
3125 // If two ports with the same name exist in different service elements,
3126 // the exception below is thrown. This is conformant to the WSDL 1.1 spec (sec 2.6)
3127 // , which states: "The name attribute provides a unique name among all ports
3128 // defined within in the enclosing WSDL document."
3129 //
3130 //
3131 if (existsPortWithName(new QName(portName))) {
3132
3133 // REMIND: should rather be a javax.wsdl.WSDLException ?
3134 throw new IOException(
3135 Messages.getMessage("twoPortsWithSameName", portName));
3136 }
3137
3138 PortEntry portEntry = new PortEntry(port);
3139
3140 symbolTablePut(portEntry);
3141 }
3142 }
3143
3144 /**
3145 * Set each SymTabEntry's isReferenced flag. The default is false. If no other symbol
3146 * references this symbol, then leave it false, otherwise set it to true.
3147 * (An exception to the rule is that derived types are set as referenced if
3148 * their base type is referenced. This is necessary to support generation and
3149 * registration of derived types.)
3150 *
3151 * @param def
3152 * @param doc
3153 */
3154 private void setReferences(Definition def, Document doc) {
3155
3156 Map stuff = def.getServices();
3157
3158 if (stuff.isEmpty()) {
3159 stuff = def.getBindings();
3160
3161 if (stuff.isEmpty()) {
3162 stuff = def.getPortTypes();
3163
3164 if (stuff.isEmpty()) {
3165 stuff = def.getMessages();
3166
3167 if (stuff.isEmpty()) {
3168 for (Iterator i = elementTypeEntries.values().iterator();
3169 i.hasNext();) {
3170 setTypeReferences((TypeEntry) i.next(), doc, false);
3171 }
3172
3173 for (Iterator i = typeTypeEntries.values().iterator();
3174 i.hasNext();) {
3175 setTypeReferences((TypeEntry) i.next(), doc, false);
3176 }
3177 } else {
3178 Iterator i = stuff.values().iterator();
3179
3180 while (i.hasNext()) {
3181 Message message = (Message) i.next();
3182 MessageEntry mEntry =
3183 getMessageEntry(message.getQName());
3184
3185 setMessageReferences(mEntry, def, doc, false);
3186 }
3187 }
3188 } else {
3189 Iterator i = stuff.values().iterator();
3190
3191 while (i.hasNext()) {
3192 PortType portType = (PortType) i.next();
3193 PortTypeEntry ptEntry =
3194 getPortTypeEntry(portType.getQName());
3195
3196 setPortTypeReferences(ptEntry, null, def, doc);
3197 }
3198 }
3199 } else {
3200 Iterator i = stuff.values().iterator();
3201
3202 while (i.hasNext()) {
3203 Binding binding = (Binding) i.next();
3204 BindingEntry bEntry = getBindingEntry(binding.getQName());
3205
3206 setBindingReferences(bEntry, def, doc);
3207 }
3208 }
3209 } else {
3210 Iterator i = stuff.values().iterator();
3211
3212 while (i.hasNext()) {
3213 Service service = (Service) i.next();
3214 ServiceEntry sEntry = getServiceEntry(service.getQName());
3215
3216 setServiceReferences(sEntry, def, doc);
3217 }
3218 }
3219 } // setReferences
3220
3221 /**
3222 * Set the isReferenced flag to true on the given TypeEntry and all
3223 * SymTabEntries that it refers to.
3224 *
3225 * @param entry
3226 * @param doc
3227 * @param literal
3228 */
3229 private void setTypeReferences(TypeEntry entry, Document doc,
3230 boolean literal) {
3231
3232 // Check to see if already processed.
3233 if ((entry.isReferenced() && !literal)
3234 || (entry.isOnlyLiteralReferenced() && literal)) {
3235 return;
3236 }
3237
3238 if (wrapped) {
3239
3240 // If this type is ONLY referenced from a literal usage in a binding,
3241 // then isOnlyLiteralReferenced should return true.
3242 if (!entry.isReferenced() && literal) {
3243 entry.setOnlyLiteralReference(true);
3244 }
3245
3246 // If this type was previously only referenced as a literal type,
3247 // but now it is referenced in a non-literal manner, turn off the
3248 // onlyLiteralReference flag.
3249 else if (entry.isOnlyLiteralReferenced() && !literal) {
3250 entry.setOnlyLiteralReference(false);
3251 }
3252 }
3253
3254 // If we don't want to emit stuff from imported files, only set the
3255 // isReferenced flag if this entry exists in the immediate WSDL file.
3256 Node node = entry.getNode();
3257
3258 if (addImports || (node == null) || (node.getOwnerDocument() == doc)) {
3259 entry.setIsReferenced(true);
3260
3261 if (entry instanceof DefinedElement) {
3262 BooleanHolder forElement = new BooleanHolder();
3263 QName referentName = Utils.getTypeQName(node,
3264 forElement, false);
3265
3266 if (referentName != null) {
3267 TypeEntry referent = getTypeEntry(referentName,
3268 forElement.value);
3269
3270 if (referent != null) {
3271 setTypeReferences(referent, doc, literal);
3272 }
3273 }
3274
3275 // If the Defined Element has an anonymous type,
3276 // process it with the current literal flag setting.
3277 QName anonQName =
3278 SchemaUtils.getElementAnonQName(entry.getNode());
3279
3280 if (anonQName != null) {
3281 TypeEntry anonType = getType(anonQName);
3282
3283 if (anonType != null) {
3284 setTypeReferences(anonType, doc, literal);
3285
3286 return;
3287 }
3288 }
3289 }
3290 }
3291
3292 HashSet nestedTypes = entry.getNestedTypes(this, true);
3293 Iterator it = nestedTypes.iterator();
3294
3295 while (it.hasNext()) {
3296 TypeEntry nestedType = (TypeEntry) it.next();
3297 TypeEntry refType = entry.getRefType();
3298
3299 if (nestedType == null) {
3300 continue;
3301 }
3302
3303 // If this entry has a referenced type that isn't
3304 // the same as the nested type
3305 // AND the OnlyLiteral reference switch is on
3306 // THEN turn the OnlyLiteral reference switch off.
3307 // If only someone had put a comment here saying why we do this...
3308 if ((refType != null)
3309 && !refType.equals(nestedType)
3310 && nestedType.isOnlyLiteralReferenced()) {
3311 nestedType.setOnlyLiteralReference(false);
3312 }
3313
3314 if (!nestedType.isReferenced()) {
3315
3316 // setTypeReferences(nestedType, doc, literal);
3317 if (nestedType != entry) {
3318 setTypeReferences(nestedType, doc, false);
3319 }
3320 }
3321 }
3322 } // setTypeReferences
3323
3324 /**
3325 * Set the isReferenced flag to true on the given MessageEntry and all
3326 * SymTabEntries that it refers to.
3327 *
3328 * @param entry
3329 * @param def
3330 * @param doc
3331 * @param literal
3332 */
3333 private void setMessageReferences(MessageEntry entry, Definition def,
3334 Document doc, boolean literal) {
3335
3336 // If we don't want to emit stuff from imported files, only set the
3337 // isReferenced flag if this entry exists in the immediate WSDL file.
3338 Message message = entry.getMessage();
3339
3340 if (addImports) {
3341 entry.setIsReferenced(true);
3342 } else {
3343
3344 // NOTE: I thought I could have simply done:
3345 // if (def.getMessage(message.getQName()) != null)
3346 // but that method traces through all imported messages.
3347 Map messages = def.getMessages();
3348
3349 if (messages.containsValue(message)) {
3350 entry.setIsReferenced(true);
3351 }
3352 }
3353
3354 // Set all the message's types
3355 Iterator parts = message.getParts().values().iterator();
3356
3357 while (parts.hasNext()) {
3358 Part part = (Part) parts.next();
3359 TypeEntry type = getType(part.getTypeName());
3360
3361 if (type != null) {
3362 setTypeReferences(type, doc, literal);
3363 }
3364
3365 type = getElement(part.getElementName());
3366
3367 if (type != null) {
3368 setTypeReferences(type, doc, literal);
3369
3370 TypeEntry refType = type.getRefType();
3371
3372 if (refType != null) {
3373 setTypeReferences(refType, doc, literal);
3374 }
3375 }
3376 }
3377 } // setMessageReference
3378
3379 /**
3380 * Set the isReferenced flag to true on the given PortTypeEntry and all
3381 * SymTabEntries that it refers to.
3382 *
3383 * @param entry
3384 * @param bEntry
3385 * @param def
3386 * @param doc
3387 */
3388 private void setPortTypeReferences(PortTypeEntry entry,
3389 BindingEntry bEntry, Definition def,
3390 Document doc) {
3391
3392 // If we don't want to emit stuff from imported files, only set the
3393 // isReferenced flag if this entry exists in the immediate WSDL file.
3394 PortType portType = entry.getPortType();
3395
3396 if (addImports) {
3397 entry.setIsReferenced(true);
3398 } else {
3399
3400 // NOTE: I thought I could have simply done:
3401 // if (def.getPortType(portType.getQName()) != null)
3402 // but that method traces through all imported portTypes.
3403 Map portTypes = def.getPortTypes();
3404
3405 if (portTypes.containsValue(portType)) {
3406 entry.setIsReferenced(true);
3407 }
3408 }
3409
3410 // Set all the portType's messages
3411 Iterator operations = portType.getOperations().iterator();
3412
3413 // For each operation, query its input, output, and fault messages
3414 while (operations.hasNext()) {
3415 Operation operation = (Operation) operations.next();
3416 Input input = operation.getInput();
3417 Output output = operation.getOutput();
3418
3419 // Find out if this reference is a literal reference or not.
3420 boolean literalInput = false;
3421 boolean literalOutput = false;
3422
3423 if (bEntry != null) {
3424 literalInput = bEntry.getInputBodyType(operation)
3425 == Use.LITERAL;
3426 literalOutput = bEntry.getOutputBodyType(operation)
3427 == Use.LITERAL;
3428 }
3429
3430 // Query the input message
3431 if (input != null) {
3432 Message message = input.getMessage();
3433
3434 if (message != null) {
3435 MessageEntry mEntry = getMessageEntry(message.getQName());
3436
3437 if (mEntry != null) {
3438 setMessageReferences(mEntry, def, doc, literalInput);
3439 }
3440 }
3441 }
3442
3443 // Query the output message
3444 if (output != null) {
3445 Message message = output.getMessage();
3446
3447 if (message != null) {
3448 MessageEntry mEntry = getMessageEntry(message.getQName());
3449
3450 if (mEntry != null) {
3451 setMessageReferences(mEntry, def, doc, literalOutput);
3452 }
3453 }
3454 }
3455
3456 // Query the fault messages
3457 Iterator faults = operation.getFaults().values().iterator();
3458
3459 while (faults.hasNext()) {
3460 Message message = ((Fault) faults.next()).getMessage();
3461
3462 if (message != null) {
3463 MessageEntry mEntry = getMessageEntry(message.getQName());
3464
3465 if (mEntry != null) {
3466 setMessageReferences(mEntry, def, doc, false);
3467 }
3468 }
3469 }
3470 }
3471 } // setPortTypeReferences
3472
3473 /**
3474 * Set the isReferenced flag to true on the given BindingEntry and all
3475 * SymTabEntries that it refers to ONLY if this binding is a SOAP binding.
3476 *
3477 * @param entry
3478 * @param def
3479 * @param doc
3480 */
3481 private void setBindingReferences(BindingEntry entry, Definition def,
3482 Document doc) {
3483
3484 if (entry.getBindingType() == BindingEntry.TYPE_SOAP) {
3485
3486 // If we don't want to emit stuff from imported files, only set the
3487 // isReferenced flag if this entry exists in the immediate WSDL file.
3488 Binding binding = entry.getBinding();
3489
3490 if (addImports) {
3491 entry.setIsReferenced(true);
3492 } else {
3493
3494 // NOTE: I thought I could have simply done:
3495 // if (def.getBindng(binding.getQName()) != null)
3496 // but that method traces through all imported bindings.
3497 Map bindings = def.getBindings();
3498
3499 if (bindings.containsValue(binding)) {
3500 entry.setIsReferenced(true);
3501 }
3502 }
3503
3504 // Set all the binding's portTypes
3505 PortType portType = binding.getPortType();
3506 PortTypeEntry ptEntry = getPortTypeEntry(portType.getQName());
3507
3508 if (ptEntry != null) {
3509 setPortTypeReferences(ptEntry, entry, def, doc);
3510 }
3511 }
3512 } // setBindingReferences
3513
3514 /**
3515 * Set the isReferenced flag to true on the given ServiceEntry and all
3516 * SymTabEntries that it refers to.
3517 *
3518 * @param entry
3519 * @param def
3520 * @param doc
3521 */
3522 private void setServiceReferences(ServiceEntry entry, Definition def,
3523 Document doc) {
3524
3525 // If we don't want to emit stuff from imported files, only set the
3526 // isReferenced flag if this entry exists in the immediate WSDL file.
3527 Service service = entry.getService();
3528
3529 if (addImports) {
3530 entry.setIsReferenced(true);
3531 } else {
3532
3533 // NOTE: I thought I could have simply done:
3534 // if (def.getService(service.getQName()) != null)
3535 // but that method traces through all imported services.
3536 Map services = def.getServices();
3537
3538 if (services.containsValue(service)) {
3539 entry.setIsReferenced(true);
3540 }
3541 }
3542
3543 // Set all the service's bindings
3544 Iterator ports = service.getPorts().values().iterator();
3545
3546 while (ports.hasNext()) {
3547 Port port = (Port) ports.next();
3548 Binding binding = port.getBinding();
3549
3550 if (binding != null) {
3551 BindingEntry bEntry = getBindingEntry(binding.getQName());
3552
3553 if (bEntry != null) {
3554 setBindingReferences(bEntry, def, doc);
3555 }
3556 }
3557 }
3558 } // setServiceReferences
3559
3560 /**
3561 * Put the given SymTabEntry into the symbol table, if appropriate.
3562 *
3563 * @param entry
3564 * @throws IOException
3565 */
3566 private SymTabEntry symbolTablePut(SymTabEntry entry) throws IOException {
3567
3568 QName name = entry.getQName();
3569
3570 SymTabEntry e = get(name, entry.getClass());
3571
3572 if (e == null) {
3573 e = entry;
3574
3575 // An entry of the given qname of the given type doesn't exist yet.
3576 if ((entry instanceof Type)
3577 && (get(name, UndefinedType.class) != null)) {
3578
3579 // A undefined type exists in the symbol table, which means
3580 // that the type is used, but we don't yet have a definition for
3581 // the type. Now we DO have a definition for the type, so
3582 // replace the existing undefined type with the real type.
3583 if (((TypeEntry) get(name, UndefinedType.class)).isSimpleType()
3584 && !((TypeEntry) entry).isSimpleType()) {
3585
3586 // Problem if the undefined type was used in a
3587 // simple type context.
3588 throw new IOException(
3589 Messages.getMessage(
3590 "AttrNotSimpleType01", name.toString()));
3591 }
3592
3593 Vector v = (Vector) symbolTable.get(name);
3594
3595 for (int i = 0; i < v.size(); ++i) {
3596 Object oldEntry = v.elementAt(i);
3597
3598 if (oldEntry instanceof UndefinedType) {
3599
3600 // Replace it in the symbol table
3601 v.setElementAt(entry, i);
3602
3603 // Replace it in the types index
3604 typeTypeEntries.put(name, entry);
3605
3606 // Update all of the entries that refer to the unknown type
3607 ((UndefinedType) oldEntry).update((Type) entry);
3608 }
3609 }
3610 } else if ((entry instanceof Element)
3611 && (get(name, UndefinedElement.class) != null)) {
3612
3613 // A undefined element exists in the symbol table, which means
3614 // that the element is used, but we don't yet have a definition for
3615 // the element. Now we DO have a definition for the element, so
3616 // replace the existing undefined element with the real element.
3617 Vector v = (Vector) symbolTable.get(name);
3618
3619 for (int i = 0; i < v.size(); ++i) {
3620 Object oldEntry = v.elementAt(i);
3621
3622 if (oldEntry instanceof UndefinedElement) {
3623
3624 // Replace it in the symbol table
3625 v.setElementAt(entry, i);
3626
3627 // Replace it in the elements index
3628 elementTypeEntries.put(name, entry);
3629
3630 // Update all of the entries that refer to the unknown type
3631 ((Undefined) oldEntry).update((Element) entry);
3632 }
3633 }
3634 } else {
3635
3636 // Add this entry to the symbol table
3637 Vector v = (Vector) symbolTable.get(name);
3638
3639 if (v == null) {
3640 v = new Vector();
3641
3642 symbolTable.put(name, v);
3643 }
3644
3645 v.add(entry);
3646
3647 // add TypeEntries to specialized indices for
3648 // fast lookups during reference resolution.
3649 if (entry instanceof Element) {
3650 elementTypeEntries.put(name, entry);
3651 } else if (entry instanceof Type) {
3652 typeTypeEntries.put(name, entry);
3653 }
3654 }
3655 } else {
3656 if (!quiet) {
3657 System.out.println(Messages.getMessage("alreadyExists00",
3658 "" + name));
3659 }
3660 }
3661
3662 return e;
3663 } // symbolTablePut
3664
3665 /**
3666 * checks whether there exists a WSDL port with a given name in the current
3667 * symbol table
3668 *
3669 * @param name the QName of the port. Note: only the local part of the qname is relevant,
3670 * since port names are not qualified with a namespace. They are of type nmtoken in WSDL 1.1
3671 * and of type ncname in WSDL 1.2
3672 * @return true, if there is a port element with the specified name; false, otherwise
3673 */
3674 protected boolean existsPortWithName(QName name) {
3675
3676 Vector v = (Vector) symbolTable.get(name);
3677
3678 if (v == null) {
3679 return false;
3680 }
3681
3682 Iterator it = v.iterator();
3683
3684 while (it.hasNext()) {
3685 Object o = it.next();
3686
3687 if (o instanceof PortEntry) {
3688 return true;
3689 }
3690 }
3691
3692 return false;
3693 }
3694
3695 /**
3696 * Method getInnerCollectionComponentQName
3697 *
3698 * @param node
3699 * @return
3700 */
3701 private static QName getInnerCollectionComponentQName(Node node) {
3702
3703 if (node == null) {
3704 return null;
3705 }
3706
3707 QName name = SchemaUtils.getCollectionComponentQName(node, new QNameHolder());
3708
3709 if (name != null) {
3710 return name;
3711 }
3712
3713 // Dive into the node if necessary
3714 NodeList children = node.getChildNodes();
3715
3716 for (int i = 0; i < children.getLength(); i++) {
3717 name = getInnerCollectionComponentQName(children.item(i));
3718
3719 if (name != null) {
3720 return name;
3721 }
3722 }
3723
3724 return null;
3725 }
3726
3727 /**
3728 * Method getInnerTypeQName
3729 *
3730 * @param node
3731 * @return
3732 */
3733 private static QName getInnerTypeQName(Node node) {
3734
3735 if (node == null) {
3736 return null;
3737 }
3738
3739 BooleanHolder forElement = new BooleanHolder();
3740 QName name = Utils.getTypeQName(node, forElement, true);
3741
3742 if (name != null) {
3743 return name;
3744 }
3745
3746 // Dive into the node if necessary
3747 NodeList children = node.getChildNodes();
3748
3749 for (int i = 0; i < children.getLength(); i++) {
3750 name = getInnerTypeQName(children.item(i));
3751
3752 if (name != null) {
3753 return name;
3754 }
3755 }
3756
3757 return null;
3758 }
3759
3760 protected void processTypes() {
3761 for (Iterator i = typeTypeEntries.values().iterator(); i.hasNext(); ) {
3762 Type type = (Type) i.next();
3763 Node node = type.getNode();
3764
3765 // Process the attributes
3766 Vector attributes =
3767 SchemaUtils.getContainedAttributeTypes(node, this);
3768
3769 if (attributes != null) {
3770 type.setContainedAttributes(attributes);
3771 }
3772
3773 // Process the elements
3774 Vector elements =
3775 SchemaUtils.getContainedElementDeclarations(node, this);
3776
3777 if (elements != null) {
3778 type.setContainedElements(elements);
3779 }
3780 }
3781 }
3782
3783 public List getMessageEntries() {
3784 List messageEntries = new ArrayList();
3785 Iterator iter = symbolTable.values().iterator();
3786 while (iter.hasNext()) {
3787 Vector v = (Vector)iter.next();
3788 for (int i = 0; i < v.size(); ++i) {
3789 SymTabEntry entry = (SymTabEntry)v.elementAt(i);
3790 if (entry instanceof MessageEntry) {
3791 messageEntries.add(entry);
3792 }
3793 }
3794 }
3795
3796 return messageEntries;
3797 }
3798
3799 public void setWrapArrays(boolean wrapArrays) {
3800 this.wrapArrays = wrapArrays;
3801 }
3802
3803 public Map getElementFormDefaults() {
3804 return elementFormDefaults;
3805 }
3806}