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 }