1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2005 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.xs.opti;
  22 
  23 import java.io.IOException;
  24 
  25 import com.sun.org.apache.xerces.internal.impl.Constants;
  26 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  27 import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols;
  28 import com.sun.org.apache.xerces.internal.impl.xs.XSMessageFormatter;
  29 import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  30 import com.sun.org.apache.xerces.internal.util.XMLChar;
  31 import com.sun.org.apache.xerces.internal.xni.Augmentations;
  32 import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  33 import com.sun.org.apache.xerces.internal.xni.QName;
  34 import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  35 import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  36 import com.sun.org.apache.xerces.internal.xni.XMLString;
  37 import com.sun.org.apache.xerces.internal.xni.XNIException;
  38 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  39 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  40 import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
  41 import org.w3c.dom.Document;
  42 
  43 /**
  44  * @xerces.internal
  45  *
  46  * @author Rahul Srivastava, Sun Microsystems Inc.
  47  * @author Sandy Gao, IBM
  48  *
  49  * @version $Id: SchemaDOMParser.java,v 1.8 2010-11-01 04:40:01 joehw Exp $
  50  */
  51 public class SchemaDOMParser extends DefaultXMLDocumentHandler {
  52 
  53     //
  54     // Data
  55     //
  56 
  57     /** Property identifier: error reporter. */
  58     public static final String ERROR_REPORTER =
  59         Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
  60 
  61     /** Feature identifier: generate synthetic annotations. */
  62     public static final String GENERATE_SYNTHETIC_ANNOTATION =
  63         Constants.XERCES_FEATURE_PREFIX + Constants.GENERATE_SYNTHETIC_ANNOTATIONS_FEATURE;
  64 
  65     // the locator containing line/column information
  66     protected XMLLocator   fLocator;
  67 
  68     // namespace context, needed for producing
  69     // representations of annotations
  70     protected NamespaceContext fNamespaceContext = null;
  71 
  72     SchemaDOM schemaDOM;
  73 
  74     XMLParserConfiguration config;
  75 
  76     //
  77     // Constructors
  78     //
  79 
  80     /** Default constructor. */
  81     public SchemaDOMParser(XMLParserConfiguration config) {
  82         this.config = config;
  83         config.setDocumentHandler(this);
  84         config.setDTDHandler(this);
  85         config.setDTDContentModelHandler(this);
  86     }
  87 
  88     // Reference to the current annotation element.
  89     private ElementImpl fCurrentAnnotationElement;
  90     // where an annotation element itself begins
  91     // -1 means not in an annotation's scope
  92     private int fAnnotationDepth = -1;
  93     // Where xs:appinfo or xs:documentation starts;
  94     // -1 means not in the scope of either of the two elements.
  95     private int fInnerAnnotationDepth = -1;
  96     // The current element depth
  97     private int fDepth = -1;
  98     // Use to report the error when characters are not allowed.
  99     XMLErrorReporter fErrorReporter;
 100 
 101     // fields for generate-synthetic annotations feature
 102     private boolean fGenerateSyntheticAnnotation = false;
 103     private BooleanStack fHasNonSchemaAttributes = new BooleanStack();
 104     private BooleanStack fSawAnnotation = new BooleanStack();
 105     private XMLAttributes fEmptyAttr = new XMLAttributesImpl();
 106 
 107     //
 108     // XMLDocumentHandler methods
 109     //
 110 
 111     public void startDocument(XMLLocator locator, String encoding,
 112             NamespaceContext namespaceContext, Augmentations augs)
 113     throws XNIException {
 114         fErrorReporter = (XMLErrorReporter)config.getProperty(ERROR_REPORTER);
 115         fGenerateSyntheticAnnotation = config.getFeature(GENERATE_SYNTHETIC_ANNOTATION);
 116         fHasNonSchemaAttributes.clear();
 117         fSawAnnotation.clear();
 118         schemaDOM = new SchemaDOM();
 119         fCurrentAnnotationElement = null;
 120         fAnnotationDepth = -1;
 121         fInnerAnnotationDepth = -1;
 122         fDepth = -1;
 123         fLocator = locator;
 124         fNamespaceContext = namespaceContext;
 125         schemaDOM.setDocumentURI(locator.getExpandedSystemId());
 126     } // startDocument(XMLLocator,String,NamespaceContext, Augmentations)
 127 
 128     /**
 129      * The end of the document.
 130      * @param augs     Additional information that may include infoset augmentations
 131      *
 132      * @throws XNIException Thrown by handler to signal an error.
 133      */
 134     public void endDocument(Augmentations augs) throws XNIException {
 135         // To debug the DOM created uncomment the line below
 136         // schemaDOM.printDOM();
 137     } // endDocument()
 138 
 139 
 140     /**
 141      * A comment.
 142      *
 143      * @param text   The text in the comment.
 144      * @param augs   Additional information that may include infoset augmentations
 145      *
 146      * @exception XNIException
 147      *                   Thrown by application to signal an error.
 148      */
 149     public void comment(XMLString text, Augmentations augs) throws XNIException {
 150         if(fAnnotationDepth > -1) {
 151             schemaDOM.comment(text);
 152         }
 153     }
 154 
 155     /**
 156      * A processing instruction. Processing instructions consist of a
 157      * target name and, optionally, text data. The data is only meaningful
 158      * to the application.
 159      * <p>
 160      * Typically, a processing instruction's data will contain a series
 161      * of pseudo-attributes. These pseudo-attributes follow the form of
 162      * element attributes but are <strong>not</strong> parsed or presented
 163      * to the application as anything other than text. The application is
 164      * responsible for parsing the data.
 165      *
 166      * @param target The target.
 167      * @param data   The data or null if none specified.
 168      * @param augs   Additional information that may include infoset augmentations
 169      *
 170      * @exception XNIException
 171      *                   Thrown by handler to signal an error.
 172      */
 173     public void processingInstruction(String target, XMLString data, Augmentations augs)
 174     throws XNIException {
 175         if (fAnnotationDepth > -1) {
 176             schemaDOM.processingInstruction(target, data);
 177         }
 178     }
 179 
 180     /**
 181      * Character content.
 182      *
 183      * @param text   The content.
 184      * @param augs   Additional information that may include infoset augmentations
 185      *
 186      * @exception XNIException
 187      *                   Thrown by handler to signal an error.
 188      */
 189     public void characters(XMLString text, Augmentations augs) throws XNIException {
 190         // when it's not within xs:appinfo or xs:documentation
 191         if (fInnerAnnotationDepth == -1 ) {
 192             for (int i=text.offset; i<text.offset+text.length; i++) {
 193                 // and there is a non-whitespace character
 194                 if (!XMLChar.isSpace(text.ch[i])) {
 195                     // the string we saw: starting from the first non-whitespace character.
 196                     String txt = new String(text.ch, i, text.length+text.offset-i);
 197                     // report an error
 198                     fErrorReporter.reportError(fLocator,
 199                             XSMessageFormatter.SCHEMA_DOMAIN,
 200                             "s4s-elt-character",
 201                             new Object[]{txt},
 202                             XMLErrorReporter.SEVERITY_ERROR);
 203                     break;
 204                 }
 205             }
 206             // don't call super.characters() when it's not within one of the 2
 207             // annotation elements: the traversers ignore them anyway. We can
 208             // save time/memory creating the text nodes.
 209         }
 210         // when it's within either of the 2 elements, characters are allowed
 211         // and we need to store them.
 212         else {
 213             schemaDOM.characters(text);
 214         }
 215 
 216     }
 217 
 218 
 219     /**
 220      * The start of an element.
 221      *
 222      * @param element    The name of the element.
 223      * @param attributes The element attributes.
 224      * @param augs       Additional information that may include infoset augmentations
 225      *
 226      * @exception XNIException
 227      *                   Thrown by handler to signal an error.
 228      */
 229     public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
 230     throws XNIException {
 231 
 232         fDepth++;
 233         // while it is true that non-whitespace character data
 234         // may only occur in appInfo or documentation
 235         // elements, it's certainly legal for comments and PI's to
 236         // occur as children of annotation; we need
 237         // to account for these here.
 238         if (fAnnotationDepth == -1) {
 239             if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA &&
 240                     element.localpart == SchemaSymbols.ELT_ANNOTATION) {
 241                 if (fGenerateSyntheticAnnotation) {
 242                     if (fSawAnnotation.size() > 0) {
 243                         fSawAnnotation.pop();
 244                     }
 245                     fSawAnnotation.push(true);
 246                 }
 247                 fAnnotationDepth = fDepth;
 248                 schemaDOM.startAnnotation(element, attributes, fNamespaceContext);
 249                 fCurrentAnnotationElement = schemaDOM.startElement(element, attributes,
 250                         fLocator.getLineNumber(),
 251                         fLocator.getColumnNumber(),
 252                         fLocator.getCharacterOffset());
 253                 return;
 254             }
 255             else if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA && fGenerateSyntheticAnnotation) {
 256                 fSawAnnotation.push(false);
 257                 fHasNonSchemaAttributes.push(hasNonSchemaAttributes(element, attributes));
 258             }
 259         }
 260         else if (fDepth == fAnnotationDepth + 1) {
 261             fInnerAnnotationDepth = fDepth;
 262             schemaDOM.startAnnotationElement(element, attributes);
 263         }
 264         else {
 265             schemaDOM.startAnnotationElement(element, attributes);
 266             // avoid falling through; don't call startElement in this case
 267             return;
 268         }
 269         schemaDOM.startElement(element, attributes,
 270                 fLocator.getLineNumber(),
 271                 fLocator.getColumnNumber(),
 272                 fLocator.getCharacterOffset());
 273 
 274     }
 275 
 276 
 277     /**
 278      * An empty element.
 279      *
 280      * @param element    The name of the element.
 281      * @param attributes The element attributes.
 282      * @param augs       Additional information that may include infoset augmentations
 283      *
 284      * @exception XNIException
 285      *                   Thrown by handler to signal an error.
 286      */
 287     public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
 288     throws XNIException {
 289 
 290         if (fGenerateSyntheticAnnotation && fAnnotationDepth == -1 &&
 291                 element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA && element.localpart != SchemaSymbols.ELT_ANNOTATION && hasNonSchemaAttributes(element, attributes)) {
 292 
 293             schemaDOM.startElement(element, attributes,
 294                     fLocator.getLineNumber(),
 295                     fLocator.getColumnNumber(),
 296                     fLocator.getCharacterOffset());
 297 
 298             attributes.removeAllAttributes();
 299             String schemaPrefix = fNamespaceContext.getPrefix(SchemaSymbols.URI_SCHEMAFORSCHEMA);
 300             final String annRawName = (schemaPrefix.length() == 0) ? SchemaSymbols.ELT_ANNOTATION : (schemaPrefix + ':' + SchemaSymbols.ELT_ANNOTATION);
 301             schemaDOM.startAnnotation(annRawName, attributes, fNamespaceContext);
 302             final String elemRawName = (schemaPrefix.length() == 0) ? SchemaSymbols.ELT_DOCUMENTATION : (schemaPrefix + ':' + SchemaSymbols.ELT_DOCUMENTATION);
 303             schemaDOM.startAnnotationElement(elemRawName, attributes);
 304             schemaDOM.charactersRaw("SYNTHETIC_ANNOTATION");
 305             schemaDOM.endSyntheticAnnotationElement(elemRawName, false);
 306             schemaDOM.endSyntheticAnnotationElement(annRawName, true);
 307 
 308             schemaDOM.endElement();
 309 
 310             return;
 311         }
 312         // the order of events that occurs here is:
 313         //   schemaDOM.startAnnotation/startAnnotationElement (if applicable)
 314         //   schemaDOM.emptyElement  (basically the same as startElement then endElement)
 315         //   schemaDOM.endAnnotationElement (if applicable)
 316         // the order of events that would occur if this was <element></element>:
 317         //   schemaDOM.startAnnotation/startAnnotationElement (if applicable)
 318         //   schemaDOM.startElement
 319         //   schemaDOM.endAnnotationElement (if applicable)
 320         //   schemaDOM.endElementElement
 321         // Thus, we can see that the order of events isn't the same.  However, it doesn't
 322         // seem to matter.  -- PJM
 323         if (fAnnotationDepth == -1) {
 324             // this is messed up, but a case to consider:
 325             if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA &&
 326                     element.localpart == SchemaSymbols.ELT_ANNOTATION) {
 327                 schemaDOM.startAnnotation(element, attributes, fNamespaceContext);
 328             }
 329         }
 330         else {
 331             schemaDOM.startAnnotationElement(element, attributes);
 332         }
 333 
 334         ElementImpl newElem = schemaDOM.emptyElement(element, attributes,
 335                 fLocator.getLineNumber(),
 336                 fLocator.getColumnNumber(),
 337                 fLocator.getCharacterOffset());
 338 
 339         if (fAnnotationDepth == -1) {
 340             // this is messed up, but a case to consider:
 341             if (element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA &&
 342                     element.localpart == SchemaSymbols.ELT_ANNOTATION) {
 343                 schemaDOM.endAnnotation(element, newElem);
 344             }
 345         }
 346         else {
 347             schemaDOM.endAnnotationElement(element);
 348         }
 349     }
 350 
 351 
 352     /**
 353      * The end of an element.
 354      *
 355      * @param element The name of the element.
 356      * @param augs    Additional information that may include infoset augmentations
 357      *
 358      * @exception XNIException
 359      *                   Thrown by handler to signal an error.
 360      */
 361     public void endElement(QName element, Augmentations augs) throws XNIException {
 362 
 363         // when we reach the endElement of xs:appinfo or xs:documentation,
 364         // change fInnerAnnotationDepth to -1
 365         if(fAnnotationDepth > -1) {
 366             if (fInnerAnnotationDepth == fDepth) {
 367                 fInnerAnnotationDepth = -1;
 368                 schemaDOM.endAnnotationElement(element);
 369                 schemaDOM.endElement();
 370             } else if (fAnnotationDepth == fDepth) {
 371                 fAnnotationDepth = -1;
 372                 schemaDOM.endAnnotation(element, fCurrentAnnotationElement);
 373                 schemaDOM.endElement();
 374             } else { // inside a child of annotation
 375                 schemaDOM.endAnnotationElement(element);
 376             }
 377         } else { // not in an annotation at all
 378             if(element.uri == SchemaSymbols.URI_SCHEMAFORSCHEMA && fGenerateSyntheticAnnotation) {
 379                 boolean value = fHasNonSchemaAttributes.pop();
 380                 boolean sawann = fSawAnnotation.pop();
 381                 if (value && !sawann) {
 382                     String schemaPrefix = fNamespaceContext.getPrefix(SchemaSymbols.URI_SCHEMAFORSCHEMA);
 383                     final String annRawName = (schemaPrefix.length() == 0) ? SchemaSymbols.ELT_ANNOTATION : (schemaPrefix + ':' + SchemaSymbols.ELT_ANNOTATION);
 384                     schemaDOM.startAnnotation(annRawName, fEmptyAttr, fNamespaceContext);
 385                     final String elemRawName = (schemaPrefix.length() == 0) ? SchemaSymbols.ELT_DOCUMENTATION : (schemaPrefix + ':' + SchemaSymbols.ELT_DOCUMENTATION);
 386                     schemaDOM.startAnnotationElement(elemRawName, fEmptyAttr);
 387                     schemaDOM.charactersRaw("SYNTHETIC_ANNOTATION");
 388                     schemaDOM.endSyntheticAnnotationElement(elemRawName, false);
 389                     schemaDOM.endSyntheticAnnotationElement(annRawName, true);
 390                 }
 391             }
 392             schemaDOM.endElement();
 393         }
 394         fDepth--;
 395 
 396     }
 397 
 398     /**
 399      * @param attributes
 400      * @return
 401      */
 402     private boolean hasNonSchemaAttributes(QName element, XMLAttributes attributes) {
 403         final int length = attributes.getLength();
 404         for (int i = 0; i < length; ++i) {
 405             String uri = attributes.getURI(i);
 406             if (uri != null && uri != SchemaSymbols.URI_SCHEMAFORSCHEMA &&
 407                     uri != NamespaceContext.XMLNS_URI &&
 408                     !(uri == NamespaceContext.XML_URI &&
 409                             attributes.getQName(i) == SchemaSymbols.ATT_XML_LANG && element.localpart == SchemaSymbols.ELT_SCHEMA)) {
 410                 return true;
 411             }
 412         }
 413         return false;
 414     }
 415 
 416     /**
 417      * Ignorable whitespace. For this method to be called, the document
 418      * source must have some way of determining that the text containing
 419      * only whitespace characters should be considered ignorable. For
 420      * example, the validator can determine if a length of whitespace
 421      * characters in the document are ignorable based on the element
 422      * content model.
 423      *
 424      * @param text   The ignorable whitespace.
 425      * @param augs   Additional information that may include infoset augmentations
 426      *
 427      * @exception XNIException
 428      *                   Thrown by handler to signal an error.
 429      */
 430     public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
 431         // unlikely to be called, but you never know...
 432         if (fAnnotationDepth != -1 ) {
 433             schemaDOM.characters(text);
 434         }
 435     }
 436 
 437     /**
 438      * The start of a CDATA section.
 439      *
 440      * @param augs   Additional information that may include infoset augmentations
 441      *
 442      * @exception XNIException
 443      *                   Thrown by handler to signal an error.
 444      */
 445     public void startCDATA(Augmentations augs) throws XNIException {
 446         // only deal with CDATA boundaries within an annotation.
 447         if (fAnnotationDepth != -1) {
 448             schemaDOM.startAnnotationCDATA();
 449         }
 450     }
 451 
 452     /**
 453      * The end of a CDATA section.
 454      *
 455      * @param augs   Additional information that may include infoset augmentations
 456      *
 457      * @exception XNIException
 458      *                   Thrown by handler to signal an error.
 459      */
 460     public void endCDATA(Augmentations augs) throws XNIException {
 461         // only deal with CDATA boundaries within an annotation.
 462         if (fAnnotationDepth != -1) {
 463             schemaDOM.endAnnotationCDATA();
 464         }
 465     }
 466 
 467 
 468     //
 469     // other methods
 470     //
 471 
 472     /**
 473      * Returns the DOM document object.
 474      */
 475     public Document getDocument() {
 476         return schemaDOM;
 477     }
 478 
 479     /**
 480      * Delegates to SchemaParsingConfig.setFeature
 481      * @param featureId
 482      * @param state
 483      */
 484     public void setFeature(String featureId, boolean state){
 485         config.setFeature(featureId, state);
 486     }
 487 
 488     /**
 489      * Delegates to SchemaParsingConfig.getFeature
 490      * @param featureId
 491      * @return boolean
 492      */
 493     public boolean getFeature(String featureId){
 494         return config.getFeature(featureId);
 495     }
 496 
 497     /**
 498      * Delegates to SchemaParsingConfig.setProperty.
 499      * @param propertyId
 500      * @param value
 501      */
 502     public void setProperty(String propertyId, Object value){
 503         config.setProperty(propertyId, value);
 504     }
 505 
 506     /**
 507      * Delegates to SchemaParsingConfig.getProperty.
 508      * @param propertyId
 509      * @return Object
 510      */
 511     public Object getProperty(String propertyId){
 512         return config.getProperty(propertyId);
 513     }
 514 
 515     /**
 516      * Delegates to SchemaParsingConfig.setEntityResolver.
 517      * @param er XMLEntityResolver
 518      */
 519     public void setEntityResolver(XMLEntityResolver er) {
 520         config.setEntityResolver(er);
 521     }
 522 
 523     /**
 524      * Delegates parsing to SchemaParsingConfig
 525      *
 526      * @param inputSource
 527      * @throws IOException
 528      */
 529     public void parse(XMLInputSource inputSource) throws IOException {
 530         config.parse(inputSource);
 531     }
 532 
 533     /**
 534      * Reset SchemaParsingConfig
 535      */
 536     public void reset() {
 537         ((SchemaParsingConfig)config).reset();
 538     }
 539 
 540     /**
 541      * ResetNodePool on SchemaParsingConfig
 542      */
 543     public void resetNodePool() {
 544         ((SchemaParsingConfig)config).resetNodePool();
 545     }
 546 
 547     /**
 548      * A simple boolean based stack.
 549      *
 550      * @xerces.internal
 551      */
 552     private static final class BooleanStack {
 553 
 554         //
 555         // Data
 556         //
 557 
 558         /** Stack depth. */
 559         private int fDepth;
 560 
 561         /** Stack data. */
 562         private boolean[] fData;
 563 
 564         //
 565         // Constructor
 566         //
 567 
 568         public BooleanStack () {}
 569 
 570         //
 571         // Public methods
 572         //
 573 
 574         /** Returns the size of the stack. */
 575         public int size() {
 576             return fDepth;
 577         }
 578 
 579         /** Pushes a value onto the stack. */
 580         public void push(boolean value) {
 581             ensureCapacity(fDepth + 1);
 582             fData[fDepth++] = value;
 583         }
 584 
 585         /** Pops a value off of the stack. */
 586         public boolean pop() {
 587             return fData[--fDepth];
 588         }
 589 
 590         /** Clears the stack. */
 591         public void clear() {
 592             fDepth = 0;
 593         }
 594 
 595         //
 596         // Private methods
 597         //
 598 
 599         /** Ensures capacity. */
 600         private void ensureCapacity(int size) {
 601             if (fData == null) {
 602                 fData = new boolean[32];
 603             }
 604             else if (fData.length <= size) {
 605                 boolean[] newdata = new boolean[fData.length * 2];
 606                 System.arraycopy(fData, 0, newdata, 0, fData.length);
 607                 fData = newdata;
 608             }
 609         }
 610     }
 611 }