001: /* *****************************************************************************
002: * SchemaParser.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.remote.json.soap;
011:
012: import java.util.*;
013: import java.io.*;
014: import javax.xml.rpc.*;
015: import javax.xml.parsers.*;
016: import javax.xml.namespace.*;
017: import org.w3c.dom.*;
018: import org.xml.sax.*;
019: import org.apache.axis.Constants;
020:
021: import org.apache.log4j.Logger;
022:
023: import org.apache.axis.utils.*;
024:
025: public class SchemaParser {
026: public static Logger mLogger = Logger.getLogger(SchemaParser.class);
027:
028: static final int ARRAY = 0;
029: static final int REGULAR_OBJECT = 1;
030: static final int SEQUENCED_OBJECT = 2;
031:
032: /** don't support for now
033: static final int EXTENDED_OBJECT = 3; // object with <xsd:extention>
034: **/
035:
036: // Contains a list of complex types that extend from types not yet defined
037: // in the schema. Once base complex type is found, subclasses are assigned
038: // the complex type objects.
039: Map mBaseForwardTypeMap;
040: Map mArrayForwardTypeMap;
041:
042: Map mComplexTypeMap;
043:
044: // Values which are set when WSDLParser is passed in.
045: String mNamespaceURI_SOAP_ENC;
046: String mNamespaceURI_SCHEMA_XSD;
047: String mNamespaceURI_WSDL;
048: String mTargetNamespaceURI;
049:
050: QName mSOAPEncodingArray;
051: QName mSOAPEncodingArrayType;
052:
053: Element mSchema;
054:
055: /**
056: * @param wp parsed WSDLParser
057: * @param schema element representing schema element
058: */
059: public SchemaParser(WSDLParser wp, Element schema) {
060:
061: mNamespaceURI_SCHEMA_XSD = wp.mNamespaceURI_SCHEMA_XSD;
062: mNamespaceURI_SOAP_ENC = wp.mNamespaceURI_SOAP_ENC;
063: mNamespaceURI_WSDL = wp.mNamespaceURI_WSDL;
064:
065: mSOAPEncodingArray = new QName(mNamespaceURI_SOAP_ENC, "Array");
066: mSOAPEncodingArrayType = new QName(mNamespaceURI_SOAP_ENC,
067: "arrayType");
068:
069: // set the target element here.
070: mTargetNamespaceURI = schema.getAttribute("targetNamespace");
071:
072: mBaseForwardTypeMap = new HashMap();
073: mArrayForwardTypeMap = new HashMap();
074:
075: mSchema = schema;
076:
077: if (mLogger.isDebugEnabled()) {
078: mLogger.debug("mNamespaceURI_SCHEMA_XSD: "
079: + mNamespaceURI_SCHEMA_XSD);
080: mLogger.debug("mNamespaceURI_SOAP_ENC: "
081: + mNamespaceURI_SOAP_ENC);
082: mLogger.debug("mNamespaceURI_WSDL_SOAP: "
083: + mNamespaceURI_WSDL);
084: mLogger.debug("mSOAPEncodingArray: " + mSOAPEncodingArray);
085: mLogger.debug("mSOAPEncodingArrayType: "
086: + mSOAPEncodingArrayType);
087: mLogger
088: .debug("mTargetNamespaceURI: "
089: + mTargetNamespaceURI);
090: }
091: }
092:
093: /**
094: * @param complexTypeMap map where ComplexType objects will be stored.
095: */
096: public void parse(Map complexTypeMap) {
097:
098: // save it so we can use it for checkExtension
099: mComplexTypeMap = complexTypeMap;
100:
101: // just get the complex types
102: NodeList list = mSchema.getElementsByTagNameNS(
103: mNamespaceURI_SCHEMA_XSD, "complexType");
104:
105: for (int i = 0; i < list.getLength(); i++) {
106: try {
107: ComplexType so = getComplexType((Element) list.item(i));
108: if (so != null) {
109: fixBaseForwardRefs(so);
110: fixArrayForwardRefs(so);
111: complexTypeMap.put(so.getName(), so);
112: } else {
113: mLogger.warn("skipping: " + list.item(i));
114: }
115: } catch (Exception e) {
116: mLogger.error(
117: "skipping complexType: " + e.getMessage(), e);
118: }
119: }
120:
121: if (mBaseForwardTypeMap.size() != 0) {
122: mLogger
123: .warn("The following base classes were not defined:");
124: Iterator iter = mBaseForwardTypeMap.keySet().iterator();
125: while (iter.hasNext()) {
126: QName baseQName = (QName) iter.next();
127: mLogger.warn(" " + baseQName);
128: }
129: }
130: }
131:
132: /**
133: * Checks to see if object was referred by other types earlier in the
134: * schema. If so, set their base class to baseType.
135: */
136: void fixBaseForwardRefs(ComplexType baseType) {
137: QName baseQName = baseType.getName();
138: Vector list = (Vector) mBaseForwardTypeMap.get(baseQName);
139: if (list == null)
140: return;
141: for (int i = 0; i < list.size(); i++) {
142: ComplexType ct = (ComplexType) list.get(i);
143: ct.setBase(baseType);
144: }
145: mBaseForwardTypeMap.remove(baseQName);
146: }
147:
148: /**
149: * Checks to see if object was referred by other types earlier in the
150: * schema. If so, set their base class to baseType.
151: */
152: void fixArrayForwardRefs(ComplexType arrayItemType) {
153: QName arrayQName = arrayItemType.getName();
154: Vector list = (Vector) mArrayForwardTypeMap.get(arrayQName);
155: if (list == null)
156: return;
157: for (int i = 0; i < list.size(); i++) {
158: ComplexType ct = (ComplexType) list.get(i);
159: ct.setArrayItemType(arrayItemType);
160: }
161: mArrayForwardTypeMap.remove(arrayQName);
162: }
163:
164: /**
165: * Set the base type object for complex type, if it exists. Otherwise, mark
166: * base type forward ref.
167: * @param baseQName the qname for the base type.
168: * @param ct the complex type object that extends from baseQName.
169: */
170: void setBaseType(QName baseQName, ComplexType ct) {
171: ComplexType baseType = (ComplexType) mComplexTypeMap
172: .get(baseQName);
173: if (baseType == null) {
174: // place soap enc type in map. Set actual soap enc for arrays.
175: if (Constants.isSOAP_ENC(baseQName.getNamespaceURI())) {
176: baseType = new ComplexType(baseQName);
177: mComplexTypeMap.put(baseQName, baseType);
178: ct.setBase(baseType);
179: } else {
180: Vector list = (Vector) mBaseForwardTypeMap
181: .get(baseQName);
182: if (list == null) {
183: list = new Vector();
184: mBaseForwardTypeMap.put(baseQName, list);
185: }
186: list.add(ct);
187: }
188: } else {
189: ct.setBase(baseType);
190: }
191: }
192:
193: /**
194: * Set the aray type object for array type, if it exists. Otherwise, mark
195: * array type forward ref.
196: * @param arrayQName the qname for the array type.
197: * @param ct the complex type object that is an array.
198: */
199: void setArrayItemType(QName arrayQName, ComplexType ct) {
200: ComplexType arrayItemType = (ComplexType) mComplexTypeMap
201: .get(arrayQName);
202: if (arrayItemType == null) {
203: // place simple type in map
204: if (Constants.isSchemaXSD(arrayQName.getNamespaceURI())) {
205: arrayItemType = new ComplexType(arrayQName);
206: mComplexTypeMap.put(arrayQName, arrayItemType);
207: ct.setArrayItemType(arrayItemType);
208: } else {
209: // set forward ref
210: Vector list = (Vector) mArrayForwardTypeMap
211: .get(arrayQName);
212: if (list == null) {
213: list = new Vector();
214: mArrayForwardTypeMap.put(arrayQName, list);
215: }
216: list.add(ct);
217: }
218:
219: } else {
220: ct.setArrayItemType(arrayItemType);
221: }
222: }
223:
224: ComplexType getComplexType(Element ct) throws Exception {
225: String name = ct.getAttribute("name");
226:
227: NodeList list;
228: list = ct.getElementsByTagNameNS(mNamespaceURI_SCHEMA_XSD,
229: "complexContent");
230: if (foundOne(list)) {
231: // check for array in elements inside of <complexContent>
232: return checkComplexContent(name, (Element) list.item(0));
233: }
234:
235: list = ct.getElementsByTagNameNS(mNamespaceURI_SCHEMA_XSD,
236: "all");
237: if (foundOne(list)) {
238: // get values inside <all> element
239: return checkAllOrSequence(name, (Element) list.item(0));
240: }
241:
242: list = ct.getElementsByTagNameNS(mNamespaceURI_SCHEMA_XSD,
243: "sequence");
244: if (foundOne(list)) {
245: // get values inside <sequence> element
246: return checkAllOrSequence(name, (Element) list.item(0));
247: }
248:
249: mLogger
250: .warn("no <complexContent>, <all>, or <sequence> nodes found under <complexType>");
251: return null;
252: }
253:
254: /**
255: * Currently, just checks to see if complex content is an array. Anything
256: * else will throw an exception.
257: */
258: ComplexType checkComplexContent(String name, Element cc)
259: throws Exception {
260: if (mLogger.isDebugEnabled()) {
261: mLogger.debug("checkComplexContent: " + name + ", " + cc);
262: }
263:
264: NodeList list;
265: list = cc.getElementsByTagNameNS(mNamespaceURI_SCHEMA_XSD,
266: "restriction");
267: if (foundOne(list)) {
268: return checkRestriction(name, (Element) list.item(0));
269: }
270:
271: list = cc.getElementsByTagNameNS(mNamespaceURI_SCHEMA_XSD,
272: "extension");
273: if (foundOne(list)) {
274: return checkExtension(name, (Element) list.item(0));
275: }
276:
277: // <restriction> and <extension> ony supported in <complexContent>
278: mLogger
279: .warn("No <restriction> or <extension> tags were found inside of <complexContent>");
280: return null;
281: }
282:
283: /**
284: *
285: */
286: ComplexType checkExtension(String name, Element extension)
287: throws Exception {
288: NodeList list;
289: ComplexType ct = null;
290: list = extension.getElementsByTagNameNS(
291: mNamespaceURI_SCHEMA_XSD, "all");
292: if (foundOne(list)) {
293: // get values inside <all> element
294: ct = checkAllOrSequence(name, (Element) list.item(0));
295: }
296:
297: list = extension.getElementsByTagNameNS(
298: mNamespaceURI_SCHEMA_XSD, "sequence");
299: if (foundOne(list)) {
300: // get values inside <sequence> element
301: ct = checkAllOrSequence(name, (Element) list.item(0));
302: }
303:
304: if (ct == null) {
305: mLogger
306: .warn("no <all> or <sequence> nodes found under <extension>");
307: return null;
308: }
309:
310: String base = extension.getAttribute("base");
311: if (base == null || base.equals("")) {
312: throw new Exception(
313: "no base attribute found in <extension>");
314: }
315:
316: QName baseQName = XMLUtils.getQNameFromString(base, extension);
317: setBaseType(baseQName, ct);
318: return ct;
319: }
320:
321: /**
322: * Assume <restriction> in <complexContent> means array (for now).
323: */
324: ComplexType checkRestriction(String name, Element restriction)
325: throws Exception {
326: String base = restriction.getAttribute("base");
327: if (base == null || base.equals("")) {
328: throw new Exception(
329: "no base attribute found in <restriction>");
330: }
331:
332: QName baseQName = XMLUtils
333: .getQNameFromString(base, restriction);
334: if (!mSOAPEncodingArray.equals(baseQName)) {
335: throw new Exception(
336: "only arrays are supported in <restriction>, instead found "
337: + baseQName);
338: }
339:
340: // now try to get type of array from <attribute> element in
341: // <restriction>
342: NodeList list = restriction.getElementsByTagNameNS(
343: mNamespaceURI_SCHEMA_XSD, "attribute");
344: if (!foundOne(list)) {
345: // FIXME
346: // XXX this is not necessarily true...I think we can assume anyType
347: // array if attribute is missing. Fix this later.
348: throw new Exception(
349: "expecting only one attribute inside <restriction base=\"soapenc:Array\">");
350: }
351:
352: Element attributeElement = (Element) list.item(0);
353: String ref = attributeElement.getAttribute("ref");
354:
355: String arrayItemType = attributeElement.getAttributeNS(
356: mNamespaceURI_WSDL, "arrayType");
357:
358: if (ref.equals("")) {
359: throw new Exception(
360: "empty ref attribute in <attribute> inside of <restriction> for <complexType> named "
361: + name);
362: }
363:
364: if (arrayItemType.equals("")) {
365: throw new Exception(
366: "empty ref attribute in <attribute> inside of <restriction> for <complexType> named "
367: + name);
368: }
369:
370: QName refQName = XMLUtils.getQNameFromString(ref,
371: attributeElement);
372: if (!mSOAPEncodingArrayType.equals(refQName)) {
373: throw new Exception(
374: "ref attribute in <attribute> inside of <restriction> does not refer to SOAP-ENC array");
375: }
376:
377: String type = removeBrackets(arrayItemType);
378: QName typeQName = XMLUtils.getQNameFromString(type,
379: attributeElement);
380:
381: // array complex type
382: ComplexType ct = new ComplexType(new QName(mTargetNamespaceURI,
383: name), true);
384: setArrayItemType(typeQName, ct);
385: setBaseType(baseQName, ct);
386: return ct;
387: }
388:
389: /**
390: * Get arrayItemType without brackets to just get type.
391: */
392: String removeBrackets(String arrayItemType) {
393: int index = arrayItemType.indexOf('[');
394: if (index == -1) {
395: return arrayItemType;
396: }
397: return arrayItemType.substring(0, index);
398: }
399:
400: /**
401: * Check for elements inside <all> or sequence. All elements must have a type
402: * attribute. We don't support anonymous types.
403: */
404: ComplexType checkAllOrSequence(String name, Element node)
405: throws Exception {
406: if (mLogger.isDebugEnabled()) {
407: mLogger.debug("checkAllOrSequence: " + name + ", " + node);
408: }
409:
410: String tag = node.getTagName();
411: Map members = new HashMap();
412: NodeList list = node.getElementsByTagNameNS(
413: mNamespaceURI_SCHEMA_XSD, "element");
414: for (int i = 0; i < list.getLength(); i++) {
415: Element element = (Element) list.item(i);
416: String elRef = element.getAttribute("ref");
417: String elName = element.getAttribute("name");
418: String elType = element.getAttribute("type");
419: if (!elRef.equals("")) {
420: mLogger.warn("!!! skipping element #" + (i + 1)
421: + " !!! " + "references ignored");
422: continue;
423: } else {
424: if (elName.equals("")) {
425: mLogger.warn("!!! skipping element #" + (i + 1)
426: + " !!! "
427: + "name attribute missing in inside <"
428: + tag + ">: " + node);
429: continue;
430: }
431: if (elType.equals("")) {
432: mLogger.warn("!!! skipping element #" + (i + 1)
433: + " !!! "
434: + "type attribute missing inside <" + tag
435: + ">: " + node
436: + "(anonymous types not supported)");
437: continue;
438: }
439: }
440: QName elTypeQName = XMLUtils.getQNameFromString(elType,
441: element);
442: members.put(elName, elTypeQName);
443: }
444:
445: // struct complex type
446: return new ComplexType(new QName(mTargetNamespaceURI, name),
447: members);
448: }
449:
450: /**
451: * See if complexType element was found.
452: */
453: boolean foundOne(NodeList list) {
454: return list.getLength() == 1;
455: }
456: }
|