001: /**
002: *
003: * Licensed to the Apache Software Foundation (ASF) under one or more
004: * contributor license agreements. See the NOTICE file distributed with
005: * this work for additional information regarding copyright ownership.
006: * The ASF licenses this file to You under the Apache License, Version 2.0
007: * (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package org.apache.openejb.server.axis.assembler;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021: import org.apache.openejb.OpenEJBException;
022: import org.apache.ws.commons.schema.XmlSchema;
023: import org.apache.ws.commons.schema.XmlSchemaAttribute;
024: import org.apache.ws.commons.schema.XmlSchemaChoice;
025: import org.apache.ws.commons.schema.XmlSchemaCollection;
026: import org.apache.ws.commons.schema.XmlSchemaComplexContentRestriction;
027: import org.apache.ws.commons.schema.XmlSchemaComplexType;
028: import org.apache.ws.commons.schema.XmlSchemaContent;
029: import org.apache.ws.commons.schema.XmlSchemaElement;
030: import org.apache.ws.commons.schema.XmlSchemaEnumerationFacet;
031: import org.apache.ws.commons.schema.XmlSchemaGroupBase;
032: import org.apache.ws.commons.schema.XmlSchemaObject;
033: import org.apache.ws.commons.schema.XmlSchemaObjectCollection;
034: import org.apache.ws.commons.schema.XmlSchemaParticle;
035: import org.apache.ws.commons.schema.XmlSchemaSequence;
036: import org.apache.ws.commons.schema.XmlSchemaSimpleType;
037: import org.apache.ws.commons.schema.XmlSchemaSimpleTypeContent;
038: import org.apache.ws.commons.schema.XmlSchemaSimpleTypeList;
039: import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
040: import org.apache.ws.commons.schema.XmlSchemaType;
041: import org.apache.ws.commons.schema.XmlSchemaContentModel;
042: import org.w3c.dom.Attr;
043: import org.w3c.dom.Element;
044: import org.w3c.dom.NamedNodeMap;
045: import org.w3c.dom.Node;
046:
047: import javax.xml.namespace.QName;
048: import java.net.URI;
049: import java.util.ArrayList;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.jar.JarFile;
055:
056: public class CommonsSchemaInfoBuilder {
057: private static final Log log = LogFactory
058: .getLog(CommonsSchemaInfoBuilder.class);
059: private static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
060: private static final String XML_NS_NS = "http://www.w3.org/2000/xmlns/";
061: private static final String SOAP_ENCODING_NS = "http://schemas.xmlsoap.org/soap/encoding/";
062: private static final QName SOAP_ARRAY = new QName(SOAP_ENCODING_NS,
063: "Array");
064: private static final QName SOAP_ARRAY_TYPE = new QName(
065: SOAP_ENCODING_NS, "arrayType");
066: private static final QName WSDL_ARRAY_TYPE = new QName(
067: "http://schemas.xmlsoap.org/wsdl/", "arrayType");
068:
069: private final XmlSchemaCollection xmlSchemaCollection;
070:
071: private final Map<QName, XmlTypeInfo> xmlTypes = new HashMap<QName, XmlTypeInfo>();
072: private final Map<QName, XmlElementInfo> xmlElements = new HashMap<QName, XmlElementInfo>();
073:
074: public CommonsSchemaInfoBuilder(JarFile moduleFile, URI wsdlUri)
075: throws OpenEJBException {
076: if (moduleFile == null)
077: throw new NullPointerException("moduleFile is null");
078: if (wsdlUri == null)
079: throw new NullPointerException("wsdlUri is null");
080:
081: CommonsSchemaLoader schemaLoader = new CommonsSchemaLoader(
082: wsdlUri, moduleFile);
083: xmlSchemaCollection = schemaLoader.loadSchema();
084: }
085:
086: public CommonsSchemaInfoBuilder(
087: XmlSchemaCollection xmlSchemaCollection) {
088: if (xmlSchemaCollection == null)
089: throw new NullPointerException("schemaTypeSystem is null");
090: this .xmlSchemaCollection = xmlSchemaCollection;
091: }
092:
093: public XmlSchemaInfo createSchemaInfo() throws OpenEJBException {
094:
095: buildXmlTypeInfos();
096:
097: XmlSchemaInfo schemaInfo = new XmlSchemaInfo();
098: schemaInfo.types.putAll(xmlTypes);
099: schemaInfo.elements.putAll(xmlElements);
100: return schemaInfo;
101: }
102:
103: private void buildXmlTypeInfos() {
104: for (XmlSchema schema : xmlSchemaCollection.getXmlSchemas()) {
105: // Global Elements
106: for (Iterator iterator = schema.getElements().getValues(); iterator
107: .hasNext();) {
108: XmlSchemaElement globalElement = (XmlSchemaElement) iterator
109: .next();
110: addGlobalElement(globalElement);
111: }
112:
113: // Global Types
114: for (Iterator iterator = schema.getSchemaTypes()
115: .getValues(); iterator.hasNext();) {
116: XmlSchemaType globalType = (XmlSchemaType) iterator
117: .next();
118: addType(globalType.getQName(), globalType);
119: }
120: }
121: }
122:
123: private void addGlobalElement(XmlSchemaElement element) {
124: // Nested anonymous type
125: QName xmlType = element.getSchemaTypeName();
126: if (xmlType == null) {
127: // Rule 1.b: Anonymous type inside an element ">E"
128: xmlType = new QName(element.getQName().getNamespaceURI(),
129: ">" + element.getQName().getLocalPart());
130: addType(xmlType, element.getSchemaType());
131: }
132:
133: // create the XmlElementInfo
134: XmlElementInfo elementInfo = createXmlElementInfo(element
135: .getQName(), xmlType, element);
136: xmlElements.put(element.getQName(), elementInfo);
137:
138: }
139:
140: private static XmlElementInfo createXmlElementInfo(QName qname,
141: QName xmlType, XmlSchemaElement element) {
142: XmlElementInfo elementInfo = new XmlElementInfo();
143:
144: elementInfo.qname = qname;
145: elementInfo.xmlType = xmlType;
146: elementInfo.minOccurs = element.getMinOccurs();
147: elementInfo.maxOccurs = element.getMaxOccurs();
148: elementInfo.nillable = element.isNillable();
149:
150: return elementInfo;
151: }
152:
153: private void addType(QName typeQName, XmlSchemaType type) {
154: // skip built in xml schema types
155: if (XML_SCHEMA_NS.equals(typeQName.getNamespaceURI())) {
156: return;
157: }
158:
159: XmlTypeInfo typeInfo = createXmlTypeInfo(typeQName, type);
160: xmlTypes.put(typeQName, typeInfo);
161:
162: if (type instanceof XmlSchemaComplexType) {
163: XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
164:
165: // process elements nested inside of this element
166: List<XmlSchemaElement> elements = getNestedElements(complexType);
167: for (XmlSchemaElement element : elements) {
168: addNestedElement(element, typeInfo);
169: }
170: }
171: }
172:
173: private void addNestedElement(XmlSchemaElement element,
174: XmlTypeInfo enclosingType) {
175: QName elementQName;
176: QName typeQName;
177: if (element.getRefName() == null) {
178: //
179: // Normal element in a type
180: //
181:
182: // Element Name with namespace
183: String elementNamespace = element.getQName()
184: .getNamespaceURI();
185: if (elementNamespace == null || elementNamespace.equals("")) {
186: elementNamespace = enclosingType.qname
187: .getNamespaceURI();
188: }
189: elementQName = new QName(elementNamespace, element
190: .getQName().getLocalPart());
191:
192: // Type name
193: if (element.getSchemaTypeName() != null) {
194: // Global type
195: typeQName = element.getSchemaTypeName();
196: } else {
197: // Anonymous type, so we need to declare it
198:
199: // Rule 2.b: Anonymous element absolute name ÒT>NÓ
200: String anonymoustName = enclosingType.qname
201: .getLocalPart()
202: + ">" + elementQName.getLocalPart();
203: QName anonymousQName = new QName(elementNamespace,
204: anonymoustName);
205:
206: // Rule 1.b: Anonymous type name ">E"
207: typeQName = new QName(elementNamespace, ">"
208: + anonymousQName.getLocalPart());
209: addType(typeQName, element.getSchemaType());
210: }
211: } else {
212: //
213: // Referenced global element
214: //
215:
216: // Local the referenced global element
217: XmlSchemaElement refElement = xmlSchemaCollection
218: .getElementByQName(element.getRefName());
219:
220: // The name and type of the nested element are determined by the referenced element
221: elementQName = refElement.getQName();
222: typeQName = refElement.getSchemaTypeName();
223: }
224:
225: // Add element to enclosing type
226: XmlElementInfo nestedElement = createXmlElementInfo(
227: elementQName, typeQName, element);
228: enclosingType.elements.put(nestedElement.qname, nestedElement);
229: }
230:
231: public static XmlTypeInfo createXmlTypeInfo(QName qname,
232: XmlSchemaType type) {
233: if (qname == null)
234: throw new NullPointerException("qname is null");
235: if (type == null)
236: throw new NullPointerException("type is null");
237:
238: XmlTypeInfo typeInfo = new XmlTypeInfo();
239: typeInfo.qname = qname;
240: typeInfo.anonymous = qname.getLocalPart().indexOf('>') >= 0;
241:
242: if (type instanceof XmlSchemaSimpleType) {
243: XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType) type;
244: XmlSchemaSimpleTypeContent content = simpleType
245: .getContent();
246: if (content instanceof XmlSchemaSimpleTypeList) {
247: XmlSchemaSimpleTypeList list = (XmlSchemaSimpleTypeList) content;
248: typeInfo.simpleBaseType = list.getItemType().getQName();
249:
250: // this is a list
251: typeInfo.listType = true;
252: } else if (content instanceof XmlSchemaSimpleTypeRestriction) {
253: XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) content;
254: typeInfo.simpleBaseType = restriction.getBaseTypeName();
255:
256: // is this an enumeration?
257: for (Iterator iterator = restriction.getFacets()
258: .getIterator(); iterator.hasNext();) {
259: if (iterator.next() instanceof XmlSchemaEnumerationFacet) {
260: typeInfo.enumType = true;
261: break;
262: }
263:
264: }
265: }
266: } else if (type instanceof XmlSchemaComplexType) {
267: XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
268:
269: // SOAP array component type
270: typeInfo.arrayComponentType = extractSoapArrayComponentType(complexType);
271:
272: // process attributes (skip soap arrays which have non-mappable attributes)
273: if (!isSoapArray(complexType)) {
274: XmlSchemaObjectCollection attributes = complexType
275: .getAttributes();
276: for (Iterator iterator = attributes.getIterator(); iterator
277: .hasNext();) {
278: Object item = iterator.next();
279: if (item instanceof XmlSchemaAttribute) {
280: XmlSchemaAttribute attribute = (XmlSchemaAttribute) item;
281: Object old = typeInfo.attributes.put(attribute
282: .getQName().getLocalPart(), attribute
283: .getSchemaTypeName());
284: if (old != null) {
285: throw new IllegalArgumentException(
286: "Complain to your expert group member, spec does not support attributes with the same local name and differing namespaces: original: "
287: + old
288: + ", duplicate local name: "
289: + attribute);
290: }
291: }
292: }
293: }
294: } else {
295: log.warn("Unknown schema type class "
296: + typeInfo.getClass().getName());
297: }
298:
299: return typeInfo;
300: }
301:
302: private static boolean isSoapArray(XmlSchemaComplexType complexType) {
303: // Soap arrays are based on complex content restriction
304: XmlSchemaContentModel contentModel = complexType
305: .getContentModel();
306: if (contentModel == null) {
307: return false;
308: }
309: XmlSchemaContent content = contentModel.getContent();
310: if (!(content instanceof XmlSchemaComplexContentRestriction)) {
311: return false;
312: }
313:
314: XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction) content;
315: return SOAP_ARRAY.equals(restriction.getBaseTypeName());
316: }
317:
318: /**
319: * Extract the nested component type of an Array from the XML Schema Type.
320: * @return the QName of the nested component type or null if the schema type can not be determined
321: * @throws org.apache.openejb.OpenEJBException if the XML Schema Type can not represent an Array @param complexType
322: */
323: private static QName extractSoapArrayComponentType(
324: XmlSchemaComplexType complexType) {
325: // Soap arrays are based on complex content restriction
326: if (!isSoapArray(complexType)) {
327: return null;
328: }
329:
330: XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction) complexType
331: .getContentModel().getContent();
332:
333: //First, handle case that looks like this:
334: // <complexType name="ArrayOfstring">
335: // <complexContent>
336: // <restriction base="soapenc:Array">
337: // <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:string[]"/>
338: // </restriction>
339: // </complexContent>
340: // </complexType>
341: XmlSchemaObjectCollection attributes = restriction
342: .getAttributes();
343: for (Iterator iterator = attributes.getIterator(); iterator
344: .hasNext();) {
345: Object item = iterator.next();
346: if (item instanceof XmlSchemaAttribute) {
347: XmlSchemaAttribute attribute = (XmlSchemaAttribute) item;
348: if (attribute.getRefName().equals(SOAP_ARRAY_TYPE)) {
349: for (Attr attr : attribute.getUnhandledAttributes()) {
350: QName attQName = new QName(attr
351: .getNamespaceURI(), attr.getLocalName());
352: if (WSDL_ARRAY_TYPE.equals(attQName)) {
353: // value is a namespace prefixed xsd type
354: String value = attr.getValue();
355:
356: // extract local part
357: int pos = value.lastIndexOf(":");
358: QName componentType;
359: if (pos < 0) {
360: componentType = new QName("", value);
361: } else {
362: String localPart = value
363: .substring(pos + 1);
364:
365: // resolve the namespace prefix
366: String prefix = value.substring(0, pos);
367: String namespace = getNamespaceForPrefix(
368: prefix, attr.getOwnerElement());
369:
370: componentType = new QName(namespace,
371: localPart);
372: }
373: log
374: .debug("determined component type from element type");
375: return componentType;
376: }
377: }
378: }
379: }
380: }
381:
382: // If that didn't work, try to handle case like this:
383: // <complexType name="ArrayOfstring1">
384: // <complexContent>
385: // <restriction base="soapenc:Array">
386: // <sequence>
387: // <element name="string1" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
388: // </sequence>
389: // </restriction>
390: // </complexContent>
391: // </complexType>
392: XmlSchemaParticle particle = restriction.getParticle();
393: if (particle instanceof XmlSchemaSequence) {
394: XmlSchemaSequence sequence = (XmlSchemaSequence) particle;
395: if (sequence.getItems().getCount() != 1) {
396: throw new IllegalArgumentException(
397: "more than one element inside array definition: "
398: + complexType);
399: }
400: XmlSchemaObject item = sequence.getItems().getItem(0);
401: if (item instanceof XmlSchemaElement) {
402: XmlSchemaElement element = (XmlSchemaElement) item;
403: QName componentType = element.getSchemaTypeName();
404: log
405: .debug("determined component type from element type");
406: return componentType;
407: }
408: }
409:
410: return null;
411: }
412:
413: private static String getNamespaceForPrefix(String prefix,
414: Element element) {
415: NamedNodeMap attributes = element.getAttributes();
416: for (int i = 0; i < attributes.getLength(); i++) {
417: Node node = attributes.item(i);
418: if (node instanceof Attr) {
419: Attr attr = (Attr) node;
420: if (XML_NS_NS.equals(attr.getNamespaceURI())) {
421: // this is a namespace declaration, is it the one we are looking for?
422: if (attr.getLocalName().equals(prefix)) {
423: return attr.getValue();
424: }
425: }
426: }
427: }
428:
429: // try parent
430: if (element.getParentNode() instanceof Element) {
431: return getNamespaceForPrefix(prefix, (Element) element
432: .getParentNode());
433: }
434:
435: // didn't find it - just use prefix as the namespace
436: return prefix;
437: }
438:
439: private static List<XmlSchemaElement> getNestedElements(
440: XmlSchemaComplexType complexType) {
441: List<XmlSchemaElement> elements = new ArrayList<XmlSchemaElement>();
442: XmlSchemaParticle particle = complexType.getParticle();
443: if (particle instanceof XmlSchemaElement) {
444: XmlSchemaElement element = (XmlSchemaElement) particle;
445: elements.add(element);
446: } else if (particle instanceof XmlSchemaGroupBase
447: && !(particle instanceof XmlSchemaChoice)) {
448: XmlSchemaGroupBase groupBase = (XmlSchemaGroupBase) particle;
449: for (Iterator iterator = groupBase.getItems().getIterator(); iterator
450: .hasNext();) {
451: XmlSchemaParticle child = (XmlSchemaParticle) iterator
452: .next();
453: if (child instanceof XmlSchemaElement) {
454: XmlSchemaElement element = (XmlSchemaElement) child;
455: elements.add(element);
456: }
457: }
458: } else {
459: // ignore all other types... you can have these other types, but JAX-RPC doesn't support them
460: }
461: return elements;
462: }
463: }
|