001: /*
002: * SchemaParser.java
003: *
004: * Created on October 9, 2006, 4:56 PM
005: *
006: * To change this template, choose Tools | Template Manager
007: * and open the template in the editor.
008: */
009:
010: package org.netbeans.modules.e2e.schema;
011:
012: import java.io.FileNotFoundException;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.net.ConnectException;
016: import java.net.URI;
017: import java.net.URISyntaxException;
018: import java.util.ArrayList;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Stack;
024: import javax.xml.namespace.QName;
025: import javax.xml.parsers.ParserConfigurationException;
026: import javax.xml.parsers.SAXParser;
027: import javax.xml.parsers.SAXParserFactory;
028: import org.netbeans.modules.e2e.api.schema.Element;
029: import org.netbeans.modules.e2e.api.schema.RepeatableSchemaConstruct;
030: import org.netbeans.modules.e2e.api.schema.SchemaConstruct;
031: import org.netbeans.modules.e2e.api.schema.SchemaException;
032: import org.netbeans.modules.e2e.api.schema.SchemaHolder;
033: import org.netbeans.modules.e2e.api.schema.Type;
034: import org.netbeans.modules.e2e.api.wsdl.wsdl2java.WSDL2Java;
035: import org.netbeans.modules.e2e.wsdl.WSDLConstants;
036: import org.xml.sax.Attributes;
037: import org.xml.sax.Locator;
038: import org.xml.sax.SAXException;
039: import org.xml.sax.helpers.DefaultHandler;
040:
041: /**
042: *
043: * @author Michal Skvor
044: */
045: public class SchemaParser extends DefaultHandler {
046:
047: private Stack<String> state = new Stack<String>();
048: private Stack<SchemaConstruct> schemaConstructs = new Stack<SchemaConstruct>();
049:
050: private SchemaHolder schemaHolder;
051:
052: private String targetNamespace;
053: private boolean elementFormDefault = false;
054: private Stack<String> targetNamespaceStack = new Stack<String>();
055: private Map<String, String> prefixMapping = new HashMap<String, String>();
056: private Locator locator;
057:
058: private String schemaURI;
059:
060: /* SCHEMA constants */
061: private static final String SCHEMA = "schema";
062:
063: private static final String ELEMENT = "element";
064: private static final String COMPLEX_TYPE = "complexType";
065: private static final String COMPLEX_CONTENT = "complexContent";
066: private static final String EXTENSION = "extension";
067: private static final String SEQUENCE = "sequence";
068: private static final String SIMPLE_TYPE = "simpleType";
069: private static final String RESTRICTION = "restriction";
070: private static final String ENUMERATION = "enumeration";
071:
072: private boolean parseWSDLTags = true;
073:
074: private List<WSDL2Java.ValidationResult> validationResults;
075:
076: public SchemaParser() {
077: schemaHolder = new SchemaHolderImpl();
078: validationResults = new ArrayList<WSDL2Java.ValidationResult>();
079:
080: addPrimitiveTypes();
081: }
082:
083: public SchemaParser(boolean parseWSDLTags) {
084: this ();
085: this .parseWSDLTags = parseWSDLTags;
086:
087: state.push(WSDLConstants.TYPES.getLocalPart());
088: }
089:
090: private void addPrimitiveTypes() {
091: Type string_type = new Type(SchemaConstants.TYPE_STRING,
092: Type.FLAVOR_PRIMITIVE);
093: string_type.setJavaTypeName("String");
094: schemaHolder.addSchemaType(string_type);
095: Type int_type = new Type(SchemaConstants.TYPE_INT,
096: Type.FLAVOR_PRIMITIVE);
097: int_type.setJavaTypeName("int");
098: schemaHolder.addSchemaType(int_type);
099: Type short_type = new Type(SchemaConstants.TYPE_SHORT,
100: Type.FLAVOR_PRIMITIVE);
101: short_type.setJavaTypeName("short");
102: schemaHolder.addSchemaType(short_type);
103: Type long_type = new Type(SchemaConstants.TYPE_LONG,
104: Type.FLAVOR_PRIMITIVE);
105: long_type.setJavaTypeName("long");
106: schemaHolder.addSchemaType(long_type);
107: Type boolean_type = new Type(SchemaConstants.TYPE_BOOLEAN,
108: Type.FLAVOR_PRIMITIVE);
109: boolean_type.setJavaTypeName("boolean");
110: schemaHolder.addSchemaType(boolean_type);
111: Type float_type = new Type(SchemaConstants.TYPE_FLOAT,
112: Type.FLAVOR_PRIMITIVE);
113: float_type.setJavaTypeName("float");
114: schemaHolder.addSchemaType(float_type);
115: Type double_type = new Type(SchemaConstants.TYPE_DOUBLE,
116: Type.FLAVOR_PRIMITIVE);
117: double_type.setJavaTypeName("double");
118: schemaHolder.addSchemaType(double_type);
119: Type byte_type = new Type(SchemaConstants.TYPE_BYTE,
120: Type.FLAVOR_PRIMITIVE);
121: byte_type.setJavaTypeName("byte");
122: schemaHolder.addSchemaType(byte_type);
123:
124: Type base64binary_type = new Type(
125: SchemaConstants.TYPE_BASE64_BINARY,
126: Type.FLAVOR_PRIMITIVE);
127: base64binary_type.setJavaTypeName("byte[]");
128: schemaHolder.addSchemaType(base64binary_type);
129: Type hexBinary_type = new Type(SchemaConstants.TYPE_HEX_BINARY,
130: Type.FLAVOR_PRIMITIVE);
131: hexBinary_type.setJavaTypeName("byte[]");
132: schemaHolder.addSchemaType(hexBinary_type);
133: Type qname_type = new Type(SchemaConstants.TYPE_QNAME,
134: Type.FLAVOR_PRIMITIVE);
135: qname_type.setJavaTypeName("javax.xml.namespace.QName");
136: schemaHolder.addSchemaType(qname_type);
137: }
138:
139: public SchemaHolder getSchemaHolder() {
140: return schemaHolder;
141: }
142:
143: public List<WSDL2Java.ValidationResult> getValidationResults() {
144: return Collections.unmodifiableList(validationResults);
145: }
146:
147: public void parse(InputStream is) throws SchemaException {
148: SAXParserFactory spf = SAXParserFactory.newInstance();
149: try {
150: spf.setNamespaceAware(true);
151: spf.setValidating(false);
152:
153: SAXParser parser = spf.newSAXParser();
154:
155: parser.parse(is, this );
156: } catch (SAXException e) {
157: if (e.getException() instanceof SchemaException) {
158: throw new SchemaException(e.getCause());
159: }
160: } catch (ParserConfigurationException e) {
161: e.printStackTrace();
162: } catch (IOException e) {
163: e.printStackTrace();
164: }
165: }
166:
167: public void parseLocation(String uri) throws SchemaException {
168: SAXParserFactory spf = SAXParserFactory.newInstance();
169: try {
170: spf.setNamespaceAware(true);
171: spf.setValidating(false);
172:
173: SAXParser parser = spf.newSAXParser();
174:
175: schemaURI = uri;
176: parser.parse(uri, this );
177: } catch (SAXException e) {
178: if (e.getException() instanceof SchemaException) {
179: validationResults.add(new WSDL2Java.ValidationResult(
180: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
181: "Error during parsing of the schema file."));
182: // throw new SchemaException( e.getCause());
183: }
184: } catch (ParserConfigurationException e) {
185: e.printStackTrace();
186: } catch (FileNotFoundException e) {
187: validationResults.add(new WSDL2Java.ValidationResult(
188: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
189: "Schema " + uri + " cannot be located."));
190: // throw new SchemaException( "");
191: } catch (ConnectException e) {
192: validationResults.add(new WSDL2Java.ValidationResult(
193: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
194: "Connection problem. Cannot download schema from "
195: + uri + " location."));
196: } catch (IOException e) {
197: validationResults.add(new WSDL2Java.ValidationResult(
198: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
199: "Connection problem. Cannot download schema from "
200: + uri + " location."));
201: } catch (IllegalArgumentException e) {
202: validationResults.add(new WSDL2Java.ValidationResult(
203: WSDL2Java.ValidationResult.ErrorLevel.FATAL, e
204: .getLocalizedMessage()));
205: } catch (Exception e) {
206: validationResults.add(new WSDL2Java.ValidationResult(
207: WSDL2Java.ValidationResult.ErrorLevel.FATAL, e
208: .getLocalizedMessage()));
209: }
210: }
211:
212: @Override
213: public void setDocumentLocator(Locator locator) {
214: this .locator = locator;
215: }
216:
217: @Override
218: public void startPrefixMapping(String prefix, String uri)
219: throws SAXException {
220: prefixMapping.put(prefix, uri);
221: }
222:
223: @Override
224: public void startElement(String uri, String localName,
225: String qName, Attributes attributes) throws SAXException {
226: QName qname = new QName(uri, localName);
227:
228: if (uri.equals(WSDLConstants.WSDL_URI)) {
229: if (WSDLConstants.DEFINITIONS.getLocalPart().equals(
230: localName)) {
231: state.push(WSDLConstants.DEFINITIONS.getLocalPart());
232: return;
233: }
234: if (WSDLConstants.TYPES.getLocalPart().equals(localName)) {
235: state.push(WSDLConstants.TYPES.getLocalPart());
236: return;
237: }
238: }
239:
240: if (uri.equals(SchemaConstants.SCHEMA_URI) && state.size() > 0) {
241: //System.err.println("<schema:" + localName + ">" );
242:
243: if (localName.equals("import")) {
244: String namespace = attributes.getValue("namespace");
245: String schemaLocation = attributes
246: .getValue("schemaLocation");
247: // System.err.println("<import namespace='" + namespace + "' schemaLocation='" + schemaLocation + "'/>" );
248: if (schemaLocation == null)
249: return;
250: SchemaParser sp = new SchemaParser(false);
251:
252: try {
253: URI u = new URI(schemaURI);
254: URI sl = u.resolve(schemaLocation);
255: sp.parseLocation(sl.toString());
256: } catch (SchemaException ex) {
257: ex.printStackTrace();
258: } catch (URISyntaxException ex) {
259: ex.printStackTrace();
260: }
261: schemaHolder.importSchema(sp.getSchemaHolder());
262: validationResults.addAll(sp.getValidationResults());
263: }
264:
265: // schema
266: if (localName.equalsIgnoreCase(SchemaConstants.SCHEMA
267: .getLocalPart())) {
268: state.push(SCHEMA);
269: // System.err.println("<schema>");
270: targetNamespace = targetNamespaceStack.push(attributes
271: .getValue("targetNamespace"));
272: if ("qualified".equals(attributes
273: .getValue("elementFormDefault"))) {
274: elementFormDefault = true;
275: }
276: return;
277: }
278:
279: // element
280: if (localName.equalsIgnoreCase(SchemaConstants.ELEMENT
281: .getLocalPart())) {
282: state.push(ELEMENT);
283: // TODO: check name
284: QName qn = null;
285: // Check for ref attribute
286: if (attributes.getValue("ref") != null) {
287: validationResults
288: .add(new WSDL2Java.ValidationResult(
289: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
290: "Reference in element is not supported by this version of stub compiler."));
291: } else if (schemaConstructs.isEmpty()
292: || elementFormDefault) {
293: qn = new QName(targetNamespace, attributes
294: .getValue("name"));
295: } else {
296: qn = new QName("", attributes.getValue("name"));
297: }
298: Element e = new Element(qn);
299: if (schemaConstructs.isEmpty() || elementFormDefault) {
300: e.setTargetNamespace(targetNamespace);
301: }
302: parseSchemaConstruct(e, attributes);
303: String nillable = attributes.getValue("nillable");
304: if (nillable != null && "true".equals(nillable)) {
305: e.setNillable(true);
306: }
307: String typeName = attributes.getValue("type");
308: if (typeName != null) {
309: QName typeQName = parseQName(typeName);
310: Type type = schemaHolder.getSchemaType(typeQName);
311: if (type == null) {
312: type = new Type(typeQName);
313: schemaHolder.addSchemaType(type);
314: }
315: e.setType(type);
316: }
317: if (!schemaConstructs.isEmpty()) {
318: SchemaConstruct.ConstructType t = schemaConstructs
319: .peek().getConstructType();
320: if (SchemaConstruct.ConstructType.TYPE
321: .equals(schemaConstructs.peek()
322: .getConstructType())) {
323: ((Type) schemaConstructs.peek())
324: .addSubconstruct(e);
325: }
326: }
327:
328: schemaHolder.addSchemaElement(e);
329: schemaConstructs.push(e);
330: return;
331: }
332: // complexType
333: if (localName.equalsIgnoreCase(SchemaConstants.COMPLEX_TYPE
334: .getLocalPart())) {
335: state.push(COMPLEX_TYPE);
336: String name = attributes.getValue("name");
337: QName qn = null;
338: Type type = null;
339: if (name != null) {
340: qn = new QName(targetNamespace, name);
341: type = schemaHolder.getSchemaType(qn);
342: if (type == null) {
343: type = new Type(qn);
344: schemaHolder.addSchemaType(type);
345: } else {
346: type.setFlavor(Type.FLAVOR_SEQUENCE);
347: }
348: }
349: if (type == null) {
350: type = new Type();
351: type.setFlavor(Type.FLAVOR_SEQUENCE);
352: }
353: if (!schemaConstructs.isEmpty()
354: && SchemaConstruct.ConstructType.ELEMENT
355: .equals(schemaConstructs.peek()
356: .getConstructType())) {
357: Element e = (Element) schemaConstructs.peek();
358: e.setType(type);
359: } else {
360: schemaHolder.addSchemaType(type);
361: }
362: schemaConstructs.push(type);
363: return;
364: }
365: // Sequence
366: if (localName.equalsIgnoreCase(SchemaConstants.SEQUENCE
367: .getLocalPart())) {
368: state.push(SEQUENCE);
369: SchemaConstruct sc = schemaConstructs.peek();
370: if (sc instanceof Type) {
371: Type type = (Type) sc;
372: type.setFlavor(Type.FLAVOR_SEQUENCE);
373: }
374: return;
375: }
376: // // Complex content
377: if (localName
378: .equalsIgnoreCase(SchemaConstants.COMPLEX_CONTENT
379: .getLocalPart())
380: && state.peek().equals(COMPLEX_TYPE)) {
381: state.push(COMPLEX_CONTENT);
382: }
383: // Extension
384: if (localName.equalsIgnoreCase(SchemaConstants.EXTENSION
385: .getLocalPart())
386: && state.peek().equals(COMPLEX_CONTENT)) {
387: state.push(EXTENSION);
388: String name = attributes.getValue("base");
389: QName qn = null;
390: Type type = null;
391: if (name != null) {
392: qn = parseQName(name);
393: type = schemaHolder.getSchemaType(qn);
394: if (type == null) {
395: type = new Type(qn);
396: schemaHolder.addSchemaType(type);
397: }
398: SchemaConstruct sc = schemaConstructs.peek();
399: sc.setParent(type);
400: }
401: return;
402: }
403: // Simple Type
404: if (localName.equals(SchemaConstants.SIMPLE_TYPE
405: .getLocalPart())) {
406: // state.push( SIMPLE_TYPE );
407: // String name = attributes.getValue( "name" );
408: // QName qn = null;
409: // if( name != null ) {
410: // qn = new QName( targetNamespace, name );
411: // }
412: // // add as type
413: // type = new Type( qn );
414: // schemaHolder.addSchemaType( type );
415: String typeName = attributes.getValue("name");
416: if (typeName == null)
417: typeName = "";
418:
419: validationResults.add(new WSDL2Java.ValidationResult(
420: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
421: "Simple type is not supported by JSR-172 - "
422: + typeName));
423: // throw new SAXException( "", new SchemaException( "Invalid type 'simple type'" ));
424: }
425: // // Restriction
426: // if( localName.equals( SchemaConstants.RESTRICTION.getLocalPart())) {
427: // state.push( RESTRICTION );
428: // }
429: // if( localName.equals( SchemaConstants.ENUMERATION.getLocalPart())) {
430: // state.push( ENUMERATION );
431: // }
432: // Enumeration
433: /* Unsupported tags */
434: // if( localName.equals( SchemaConstants.QNAME_RESTRICTION.getLocalPart())) {
435: // throw new SAXException( "", new SchemaException( "restriction is not supported" ));
436: // }
437: // ALL
438: if (localName.equalsIgnoreCase(SchemaConstants.ALL
439: .getLocalPart())
440: && state.peek().equals(COMPLEX_TYPE)) {
441: validationResults
442: .add(new WSDL2Java.ValidationResult(
443: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
444: "'all' element in complex-type element is not supported by JSR-172."));
445: }
446: if (localName.equalsIgnoreCase(SchemaConstants.CHOICE
447: .getLocalPart())
448: && state.peek().equals(COMPLEX_TYPE)) {
449: validationResults
450: .add(new WSDL2Java.ValidationResult(
451: WSDL2Java.ValidationResult.ErrorLevel.FATAL,
452: "'choice' element in complex-type element is not supported by JSR-172."));
453: }
454: }
455: }
456:
457: @Override
458: public void endElement(String uri, String localName, String qName)
459: throws SAXException {
460:
461: if (uri.equals(WSDLConstants.WSDL_URI)) {
462: if (WSDLConstants.DEFINITIONS.getLocalPart().equals(
463: localName)) {
464: state.pop();
465: return;
466: }
467: if (WSDLConstants.TYPES.getLocalPart().equals(localName)) {
468: state.pop();
469: return;
470: }
471: }
472:
473: if (uri.equalsIgnoreCase(SchemaConstants.SCHEMA_URI)
474: && state.size() > 0) {
475: // element
476: if (SchemaConstants.ELEMENT.getLocalPart()
477: .equals(localName)) {
478: schemaConstructs.pop();
479: if (!ELEMENT.equals(state.pop())) {
480: throw new SAXException("", new SchemaException(
481: "Invalid end tag for 'element'."));
482: }
483: if (SCHEMA.equals(state.peek())) {
484: // System.err.println(" - top element ");
485: } else {
486: if (!SEQUENCE.equals(state.peek())) {
487: throw new SAXException("", new SchemaException(
488: "Invalid super tag for 'element'."));
489: }
490: }
491: }
492: // complexType
493: if (SchemaConstants.COMPLEX_TYPE.getLocalPart().equals(
494: localName)) {
495: schemaConstructs.pop();
496: if (!COMPLEX_TYPE.equals(state.pop())) {
497: throw new SAXException("", new SchemaException(
498: "Invalid end tag for 'complexType'."));
499: }
500:
501: }
502: if (SchemaConstants.SEQUENCE.getLocalPart().equals(
503: localName)) {
504: if (!SEQUENCE.equals(state.pop())) {
505: throw new SAXException("", new SchemaException(
506: "Invalid end tag for 'sequence'."));
507: }
508: if (!COMPLEX_TYPE.equals(state.peek())
509: && !EXTENSION.equals(state.peek())) {
510: throw new SAXException("", new SchemaException(
511: "Invalid 'sequence' tag position."));
512: }
513: }
514: if (SchemaConstants.EXTENSION.getLocalPart().equals(
515: localName)) {
516: state.pop();
517: }
518: if (SchemaConstants.COMPLEX_CONTENT.getLocalPart().equals(
519: localName)) {
520: state.pop();
521: }
522: }
523: }
524:
525: private void parseSchemaConstruct(RepeatableSchemaConstruct psc,
526: Attributes attributes) {
527: String minOccurs = attributes.getValue("minOccurs");
528: if (minOccurs != null) {
529: int value = Integer.parseInt(minOccurs);
530: psc.setMinOccurs(value);
531: } else {
532: psc.setMinOccurs(1);
533: }
534:
535: String maxOccurs = attributes.getValue("maxOccurs");
536: if (maxOccurs != null) {
537: if ("unbounded".equals(maxOccurs)) {
538: psc.setMaxOccurs(RepeatableSchemaConstruct.UNBOUNDED);
539: } else {
540: int value = Integer.parseInt(maxOccurs);
541: psc.setMaxOccurs(value);
542: }
543: } else {
544: psc.setMaxOccurs(1);
545: }
546: }
547:
548: private QName parseQName(String qName) {
549: if (qName == null)
550: return null;
551: int colonPos = qName.indexOf(':');
552: if (colonPos > 0) {
553: String prefix = qName.substring(0, colonPos);
554: String uri = prefixMapping.get(prefix);
555: return new QName(uri, qName.substring(colonPos + 1), prefix);
556: }
557: return new QName(targetNamespaceStack.peek(), qName);
558: }
559: }
|