001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.impl.xs.opti;
019:
020: import java.io.IOException;
021:
022: import org.apache.xerces.impl.Constants;
023: import org.apache.xerces.impl.XMLErrorReporter;
024: import org.apache.xerces.impl.xs.SchemaSymbols;
025: import org.apache.xerces.impl.xs.XSMessageFormatter;
026: import org.apache.xerces.util.XMLAttributesImpl;
027: import org.apache.xerces.util.XMLChar;
028: import org.apache.xerces.xni.Augmentations;
029: import org.apache.xerces.xni.NamespaceContext;
030: import org.apache.xerces.xni.QName;
031: import org.apache.xerces.xni.XMLAttributes;
032: import org.apache.xerces.xni.XMLLocator;
033: import org.apache.xerces.xni.XMLString;
034: import org.apache.xerces.xni.XNIException;
035: import org.apache.xerces.xni.parser.XMLEntityResolver;
036: import org.apache.xerces.xni.parser.XMLInputSource;
037: import org.apache.xerces.xni.parser.XMLParserConfiguration;
038: import org.w3c.dom.Document;
039:
040: /**
041: * @xerces.internal
042: *
043: * @author Rahul Srivastava, Sun Microsystems Inc.
044: * @author Sandy Gao, IBM
045: *
046: * @version $Id: SchemaDOMParser.java 446728 2006-09-15 20:43:46Z mrglavas $
047: */
048: public class SchemaDOMParser extends DefaultXMLDocumentHandler {
049:
050: //
051: // Data
052: //
053:
054: /** Property identifier: error reporter. */
055: public static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX
056: + Constants.ERROR_REPORTER_PROPERTY;
057:
058: /** Feature identifier: generate synthetic annotations. */
059: public static final String GENERATE_SYNTHETIC_ANNOTATION = Constants.XERCES_FEATURE_PREFIX
060: + Constants.GENERATE_SYNTHETIC_ANNOTATIONS_FEATURE;
061:
062: // the locator containing line/column information
063: protected XMLLocator fLocator;
064:
065: // namespace context, needed for producing
066: // representations of annotations
067: protected NamespaceContext fNamespaceContext = null;
068:
069: SchemaDOM schemaDOM;
070:
071: XMLParserConfiguration config;
072:
073: //
074: // Constructors
075: //
076:
077: /** Default constructor. */
078: public SchemaDOMParser(XMLParserConfiguration config) {
079: this .config = config;
080: }
081:
082: // Reference to the current annotation element.
083: private ElementImpl fCurrentAnnotationElement;
084: // where an annotation element itself begins
085: // -1 means not in an annotation's scope
086: private int fAnnotationDepth = -1;
087: // Where xs:appinfo or xs:documentation starts;
088: // -1 means not in the scope of either of the two elements.
089: private int fInnerAnnotationDepth = -1;
090: // The current element depth
091: private int fDepth = -1;
092: // Use to report the error when characters are not allowed.
093: XMLErrorReporter fErrorReporter;
094:
095: // fields for generate-synthetic annotations feature
096: private boolean fGenerateSyntheticAnnotation = false;
097: private BooleanStack fHasNonSchemaAttributes = new BooleanStack();
098: private BooleanStack fSawAnnotation = new BooleanStack();
099: private XMLAttributes fEmptyAttr = new XMLAttributesImpl();
100:
101: //
102: // XMLDocumentHandler methods
103: //
104:
105: public void startDocument(XMLLocator locator, String encoding,
106: NamespaceContext namespaceContext, Augmentations augs)
107: throws XNIException {
108: fErrorReporter = (XMLErrorReporter) config
109: .getProperty(ERROR_REPORTER);
110: fGenerateSyntheticAnnotation = config
111: .getFeature(GENERATE_SYNTHETIC_ANNOTATION);
112: fHasNonSchemaAttributes.clear();
113: fSawAnnotation.clear();
114: schemaDOM = new SchemaDOM();
115: fCurrentAnnotationElement = null;
116: fAnnotationDepth = -1;
117: fInnerAnnotationDepth = -1;
118: fDepth = -1;
119: fLocator = locator;
120: fNamespaceContext = namespaceContext;
121: schemaDOM.setDocumentURI(locator.getExpandedSystemId());
122: } // startDocument(XMLLocator,String,NamespaceContext, Augmentations)
123:
124: /**
125: * The end of the document.
126: * @param augs Additional information that may include infoset augmentations
127: *
128: * @throws XNIException Thrown by handler to signal an error.
129: */
130: public void endDocument(Augmentations augs) throws XNIException {
131: // To debug the DOM created uncomment the line below
132: // schemaDOM.printDOM();
133: } // endDocument()
134:
135: /**
136: * A comment.
137: *
138: * @param text The text in the comment.
139: * @param augs Additional information that may include infoset augmentations
140: *
141: * @exception XNIException
142: * Thrown by application to signal an error.
143: */
144: public void comment(XMLString text, Augmentations augs)
145: throws XNIException {
146: if (fAnnotationDepth > -1) {
147: schemaDOM.comment(text);
148: }
149: }
150:
151: /**
152: * A processing instruction. Processing instructions consist of a
153: * target name and, optionally, text data. The data is only meaningful
154: * to the application.
155: * <p>
156: * Typically, a processing instruction's data will contain a series
157: * of pseudo-attributes. These pseudo-attributes follow the form of
158: * element attributes but are <strong>not</strong> parsed or presented
159: * to the application as anything other than text. The application is
160: * responsible for parsing the data.
161: *
162: * @param target The target.
163: * @param data The data or null if none specified.
164: * @param augs Additional information that may include infoset augmentations
165: *
166: * @exception XNIException
167: * Thrown by handler to signal an error.
168: */
169: public void processingInstruction(String target, XMLString data,
170: Augmentations augs) throws XNIException {
171: if (fAnnotationDepth > -1) {
172: schemaDOM.processingInstruction(target, data);
173: }
174: }
175:
176: /**
177: * Character content.
178: *
179: * @param text The content.
180: * @param augs Additional information that may include infoset augmentations
181: *
182: * @exception XNIException
183: * Thrown by handler to signal an error.
184: */
185: public void characters(XMLString text, Augmentations augs)
186: throws XNIException {
187: // when it's not within xs:appinfo or xs:documentation
188: if (fInnerAnnotationDepth == -1) {
189: for (int i = text.offset; i < text.offset + text.length; i++) {
190: // and there is a non-whitespace character
191: if (!XMLChar.isSpace(text.ch[i])) {
192: // the string we saw: starting from the first non-whitespace character.
193: String txt = new String(text.ch, i, text.length
194: + text.offset - i);
195: // report an error
196: fErrorReporter.reportError(
197: XSMessageFormatter.SCHEMA_DOMAIN,
198: "s4s-elt-character", new Object[] { txt },
199: XMLErrorReporter.SEVERITY_ERROR);
200: break;
201: }
202: }
203: // don't call super.characters() when it's not within one of the 2
204: // annotation elements: the traversers ignore them anyway. We can
205: // save time/memory creating the text nodes.
206: }
207: // when it's within either of the 2 elements, characters are allowed
208: // and we need to store them.
209: else {
210: schemaDOM.characters(text);
211: }
212:
213: }
214:
215: /**
216: * The start of an element.
217: *
218: * @param element The name of the element.
219: * @param attributes The element attributes.
220: * @param augs Additional information that may include infoset augmentations
221: *
222: * @exception XNIException
223: * Thrown by handler to signal an error.
224: */
225: public void startElement(QName element, XMLAttributes attributes,
226: Augmentations augs) throws XNIException {
227:
228: fDepth++;
229: // while it is true that non-whitespace character data
230: // may only occur in appInfo or documentation
231: // elements, it's certainly legal for comments and PI's to
232: // occur as children of annotation; we need
233: // to account for these here.
234: if (fAnnotationDepth == -1) {
235: if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
236: && element.localpart == SchemaSymbols.ELT_ANNOTATION) {
237: if (fGenerateSyntheticAnnotation) {
238: if (fSawAnnotation.size() > 0) {
239: fSawAnnotation.pop();
240: }
241: fSawAnnotation.push(true);
242: }
243: fAnnotationDepth = fDepth;
244: schemaDOM.startAnnotation(element, attributes,
245: fNamespaceContext);
246: fCurrentAnnotationElement = schemaDOM.startElement(
247: element, attributes, fLocator.getLineNumber(),
248: fLocator.getColumnNumber(), fLocator
249: .getCharacterOffset());
250: return;
251: } else if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
252: && fGenerateSyntheticAnnotation) {
253: fSawAnnotation.push(false);
254: fHasNonSchemaAttributes.push(hasNonSchemaAttributes(
255: element, attributes));
256: }
257: } else if (fDepth == fAnnotationDepth + 1) {
258: fInnerAnnotationDepth = fDepth;
259: schemaDOM.startAnnotationElement(element, attributes);
260: } else {
261: schemaDOM.startAnnotationElement(element, attributes);
262: // avoid falling through; don't call startElement in this case
263: return;
264: }
265: schemaDOM.startElement(element, attributes, fLocator
266: .getLineNumber(), fLocator.getColumnNumber(), fLocator
267: .getCharacterOffset());
268:
269: }
270:
271: /**
272: * An empty element.
273: *
274: * @param element The name of the element.
275: * @param attributes The element attributes.
276: * @param augs Additional information that may include infoset augmentations
277: *
278: * @exception XNIException
279: * Thrown by handler to signal an error.
280: */
281: public void emptyElement(QName element, XMLAttributes attributes,
282: Augmentations augs) throws XNIException {
283:
284: if (fGenerateSyntheticAnnotation && fAnnotationDepth == -1
285: && element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
286: && element.localpart != SchemaSymbols.ELT_ANNOTATION
287: && hasNonSchemaAttributes(element, attributes)) {
288:
289: schemaDOM.startElement(element, attributes, fLocator
290: .getLineNumber(), fLocator.getColumnNumber(),
291: fLocator.getCharacterOffset());
292:
293: attributes.removeAllAttributes();
294: String schemaPrefix = fNamespaceContext
295: .getPrefix(SchemaSymbols.URI_SCHEMAFORSCHEMA);
296: QName annQName = new QName(schemaPrefix,
297: SchemaSymbols.ELT_ANNOTATION, schemaPrefix
298: + (schemaPrefix.length() == 0 ? "" : ":")
299: + SchemaSymbols.ELT_ANNOTATION,
300: SchemaSymbols.URI_SCHEMAFORSCHEMA);
301: schemaDOM.startAnnotation(annQName, attributes,
302: fNamespaceContext);
303: QName elemQName = new QName(schemaPrefix,
304: SchemaSymbols.ELT_DOCUMENTATION, schemaPrefix
305: + (schemaPrefix.length() == 0 ? "" : ":")
306: + SchemaSymbols.ELT_DOCUMENTATION,
307: SchemaSymbols.URI_SCHEMAFORSCHEMA);
308: schemaDOM.startAnnotationElement(elemQName, attributes);
309: schemaDOM.characters(new XMLString("SYNTHETIC_ANNOTATION"
310: .toCharArray(), 0, 20));
311: schemaDOM.endSyntheticAnnotationElement(elemQName, false);
312: schemaDOM.endSyntheticAnnotationElement(annQName, true);
313:
314: schemaDOM.endElement();
315:
316: return;
317: }
318: // the order of events that occurs here is:
319: // schemaDOM.startAnnotation/startAnnotationElement (if applicable)
320: // schemaDOM.emptyElement (basically the same as startElement then endElement)
321: // schemaDOM.endAnnotationElement (if applicable)
322: // the order of events that would occur if this was <element></element>:
323: // schemaDOM.startAnnotation/startAnnotationElement (if applicable)
324: // schemaDOM.startElement
325: // schemaDOM.endAnnotationElement (if applicable)
326: // schemaDOM.endElementElement
327: // Thus, we can see that the order of events isn't the same. However, it doesn't
328: // seem to matter. -- PJM
329: if (fAnnotationDepth == -1) {
330: // this is messed up, but a case to consider:
331: if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
332: && element.localpart == SchemaSymbols.ELT_ANNOTATION) {
333: schemaDOM.startAnnotation(element, attributes,
334: fNamespaceContext);
335: }
336: } else {
337: schemaDOM.startAnnotationElement(element, attributes);
338: }
339:
340: ElementImpl newElem = schemaDOM.emptyElement(element,
341: attributes, fLocator.getLineNumber(), fLocator
342: .getColumnNumber(), fLocator
343: .getCharacterOffset());
344:
345: if (fAnnotationDepth == -1) {
346: // this is messed up, but a case to consider:
347: if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
348: && element.localpart == SchemaSymbols.ELT_ANNOTATION) {
349: schemaDOM.endAnnotation(element, newElem);
350: }
351: } else {
352: schemaDOM.endAnnotationElement(element);
353: }
354: }
355:
356: /**
357: * The end of an element.
358: *
359: * @param element The name of the element.
360: * @param augs Additional information that may include infoset augmentations
361: *
362: * @exception XNIException
363: * Thrown by handler to signal an error.
364: */
365: public void endElement(QName element, Augmentations augs)
366: throws XNIException {
367:
368: // when we reach the endElement of xs:appinfo or xs:documentation,
369: // change fInnerAnnotationDepth to -1
370: if (fAnnotationDepth > -1) {
371: if (fInnerAnnotationDepth == fDepth) {
372: fInnerAnnotationDepth = -1;
373: schemaDOM.endAnnotationElement(element);
374: schemaDOM.endElement();
375: } else if (fAnnotationDepth == fDepth) {
376: fAnnotationDepth = -1;
377: schemaDOM.endAnnotation(element,
378: fCurrentAnnotationElement);
379: schemaDOM.endElement();
380: } else { // inside a child of annotation
381: schemaDOM.endAnnotationElement(element);
382: }
383: } else { // not in an annotation at all
384: if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA
385: && fGenerateSyntheticAnnotation) {
386: boolean value = fHasNonSchemaAttributes.pop();
387: boolean sawann = fSawAnnotation.pop();
388: if (value && !sawann) {
389: String schemaPrefix = fNamespaceContext
390: .getPrefix(SchemaSymbols.URI_SCHEMAFORSCHEMA);
391: QName annQName = new QName(schemaPrefix,
392: SchemaSymbols.ELT_ANNOTATION, schemaPrefix
393: + (schemaPrefix.length() == 0 ? ""
394: : ":")
395: + SchemaSymbols.ELT_ANNOTATION,
396: SchemaSymbols.URI_SCHEMAFORSCHEMA);
397: schemaDOM.startAnnotation(annQName, fEmptyAttr,
398: fNamespaceContext);
399: QName elemQName = new QName(schemaPrefix,
400: SchemaSymbols.ELT_DOCUMENTATION,
401: schemaPrefix
402: + (schemaPrefix.length() == 0 ? ""
403: : ":")
404: + SchemaSymbols.ELT_DOCUMENTATION,
405: SchemaSymbols.URI_SCHEMAFORSCHEMA);
406: schemaDOM.startAnnotationElement(elemQName,
407: fEmptyAttr);
408: schemaDOM
409: .characters(new XMLString(
410: "SYNTHETIC_ANNOTATION"
411: .toCharArray(), 0, 20));
412: schemaDOM.endSyntheticAnnotationElement(elemQName,
413: false);
414: schemaDOM.endSyntheticAnnotationElement(annQName,
415: true);
416: }
417: }
418: schemaDOM.endElement();
419: }
420: fDepth--;
421:
422: }
423:
424: /**
425: * @param attributes
426: * @return
427: */
428: private boolean hasNonSchemaAttributes(QName element,
429: XMLAttributes attributes) {
430: final int length = attributes.getLength();
431: for (int i = 0; i < length; ++i) {
432: String uri = attributes.getURI(i);
433: if (uri != null
434: && uri != SchemaSymbols.URI_SCHEMAFORSCHEMA
435: && uri != NamespaceContext.XMLNS_URI
436: && !(uri == NamespaceContext.XML_URI
437: && attributes.getQName(i) == SchemaSymbols.ATT_XML_LANG && element.localpart == SchemaSymbols.ELT_SCHEMA)) {
438: return true;
439: }
440: }
441: return false;
442: }
443:
444: /**
445: * Ignorable whitespace. For this method to be called, the document
446: * source must have some way of determining that the text containing
447: * only whitespace characters should be considered ignorable. For
448: * example, the validator can determine if a length of whitespace
449: * characters in the document are ignorable based on the element
450: * content model.
451: *
452: * @param text The ignorable whitespace.
453: * @param augs Additional information that may include infoset augmentations
454: *
455: * @exception XNIException
456: * Thrown by handler to signal an error.
457: */
458: public void ignorableWhitespace(XMLString text, Augmentations augs)
459: throws XNIException {
460: // unlikely to be called, but you never know...
461: if (fAnnotationDepth != -1) {
462: schemaDOM.characters(text);
463: }
464: }
465:
466: /**
467: * The start of a CDATA section.
468: *
469: * @param augs Additional information that may include infoset augmentations
470: *
471: * @exception XNIException
472: * Thrown by handler to signal an error.
473: */
474: public void startCDATA(Augmentations augs) throws XNIException {
475: // only deal with CDATA boundaries within an annotation.
476: if (fAnnotationDepth != -1) {
477: schemaDOM.startAnnotationCDATA();
478: }
479: }
480:
481: /**
482: * The end of a CDATA section.
483: *
484: * @param augs Additional information that may include infoset augmentations
485: *
486: * @exception XNIException
487: * Thrown by handler to signal an error.
488: */
489: public void endCDATA(Augmentations augs) throws XNIException {
490: // only deal with CDATA boundaries within an annotation.
491: if (fAnnotationDepth != -1) {
492: schemaDOM.endAnnotationCDATA();
493: }
494: }
495:
496: //
497: // other methods
498: //
499:
500: /**
501: * Returns the DOM document object.
502: */
503: public Document getDocument() {
504: return schemaDOM;
505: }
506:
507: /**
508: * Delegates to SchemaParsingConfig.setFeature
509: * @param featureId
510: * @param state
511: */
512: public void setFeature(String featureId, boolean state) {
513: config.setFeature(featureId, state);
514: }
515:
516: /**
517: * Delegates to SchemaParsingConfig.getFeature
518: * @param featureId
519: * @return boolean
520: */
521: public boolean getFeature(String featureId) {
522: return config.getFeature(featureId);
523: }
524:
525: /**
526: * Delegates to SchemaParsingConfig.setProperty.
527: * @param propertyId
528: * @param value
529: */
530: public void setProperty(String propertyId, Object value) {
531: config.setProperty(propertyId, value);
532: }
533:
534: /**
535: * Delegates to SchemaParsingConfig.getProperty.
536: * @param propertyId
537: * @return Object
538: */
539: public Object getProperty(String propertyId) {
540: return config.getProperty(propertyId);
541: }
542:
543: /**
544: * Delegates to SchemaParsingConfig.setEntityResolver.
545: * @param er XMLEntityResolver
546: */
547: public void setEntityResolver(XMLEntityResolver er) {
548: config.setEntityResolver(er);
549: }
550:
551: /**
552: * Delegates parsing to SchemaParsingConfig
553: *
554: * @param inputSource
555: * @throws IOException
556: */
557: public void parse(XMLInputSource inputSource) throws IOException {
558: config.parse(inputSource);
559: }
560:
561: /**
562: * Gets the document from SchemaParsingConfig
563: * @return Document
564: */
565: public Document getDocument2() {
566: return ((SchemaParsingConfig) config).getDocument();
567: }
568:
569: /**
570: * Reset SchemaParsingConfig
571: */
572: public void reset() {
573: ((SchemaParsingConfig) config).reset();
574: }
575:
576: /**
577: * ResetNodePool on SchemaParsingConfig
578: */
579: public void resetNodePool() {
580: ((SchemaParsingConfig) config).resetNodePool();
581: }
582:
583: /**
584: * A simple boolean based stack.
585: *
586: * @xerces.internal
587: */
588: private static final class BooleanStack {
589:
590: //
591: // Data
592: //
593:
594: /** Stack depth. */
595: private int fDepth;
596:
597: /** Stack data. */
598: private boolean[] fData;
599:
600: //
601: // Constructor
602: //
603:
604: public BooleanStack() {
605: }
606:
607: //
608: // Public methods
609: //
610:
611: /** Returns the size of the stack. */
612: public int size() {
613: return fDepth;
614: }
615:
616: /** Pushes a value onto the stack. */
617: public void push(boolean value) {
618: ensureCapacity(fDepth + 1);
619: fData[fDepth++] = value;
620: }
621:
622: /** Pops a value off of the stack. */
623: public boolean pop() {
624: return fData[--fDepth];
625: }
626:
627: /** Clears the stack. */
628: public void clear() {
629: fDepth = 0;
630: }
631:
632: //
633: // Private methods
634: //
635:
636: /** Ensures capacity. */
637: private void ensureCapacity(int size) {
638: if (fData == null) {
639: fData = new boolean[32];
640: } else if (fData.length <= size) {
641: boolean[] newdata = new boolean[fData.length * 2];
642: System.arraycopy(fData, 0, newdata, 0, fData.length);
643: fData = newdata;
644: }
645: }
646: }
647: }
|