1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xerces.internal.jaxp; 23 24 import java.io.IOException; 25 26 import javax.xml.validation.TypeInfoProvider; 27 import javax.xml.validation.ValidatorHandler; 28 29 import com.sun.org.apache.xerces.internal.dom.DOMInputImpl; 30 import com.sun.org.apache.xerces.internal.impl.Constants; 31 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter; 32 import com.sun.org.apache.xerces.internal.impl.xs.opti.DefaultXMLDocumentHandler; 33 import com.sun.org.apache.xerces.internal.util.AttributesProxy; 34 import com.sun.org.apache.xerces.internal.util.AugmentationsImpl; 35 import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy; 36 import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper; 37 import com.sun.org.apache.xerces.internal.util.LocatorProxy; 38 import com.sun.org.apache.xerces.internal.util.SymbolTable; 39 import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl; 40 import com.sun.org.apache.xerces.internal.xni.Augmentations; 41 import com.sun.org.apache.xerces.internal.xni.NamespaceContext; 42 import com.sun.org.apache.xerces.internal.xni.QName; 43 import com.sun.org.apache.xerces.internal.xni.XMLAttributes; 44 import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler; 45 import com.sun.org.apache.xerces.internal.xni.XMLLocator; 46 import com.sun.org.apache.xerces.internal.xni.XMLString; 47 import com.sun.org.apache.xerces.internal.xni.XNIException; 48 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent; 49 import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager; 50 import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException; 51 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver; 52 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler; 53 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource; 54 import org.w3c.dom.TypeInfo; 55 import org.w3c.dom.ls.LSInput; 56 import org.w3c.dom.ls.LSResourceResolver; 57 import org.xml.sax.Attributes; 58 import org.xml.sax.ContentHandler; 59 import org.xml.sax.ErrorHandler; 60 import org.xml.sax.SAXException; 61 import org.xml.sax.SAXParseException; 62 import org.xml.sax.helpers.DefaultHandler; 63 64 /** 65 * Runs events through a {@link javax.xml.validation.ValidatorHandler} 66 * and performs validation/infoset-augmentation by an external validator. 67 * 68 * <p> 69 * This component sets up the pipeline as follows: 70 * 71 * <!-- this picture may look teribble on your IDE but it is correct. --> 72 * <pre> 73 * __ __ 74 * / |==> XNI2SAX --> Validator --> SAX2XNI ==>| 75 * / | | 76 * ==>| Tee| | next 77 * \ | | component 78 * \ |============other XNI events============>| 79 * ~~ ~~ 80 * </pre> 81 * <p> 82 * only those events that need to go through Validator will go the 1st route, 83 * and other events go the 2nd direct route. 84 * 85 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 86 */ 87 final class JAXPValidatorComponent 88 extends TeeXMLDocumentFilterImpl implements XMLComponent { 89 90 /** Property identifier: entity manager. */ 91 private static final String ENTITY_MANAGER = 92 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_MANAGER_PROPERTY; 93 94 /** Property identifier: error reporter. */ 95 private static final String ERROR_REPORTER = 96 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; 97 98 /** Property identifier: symbol table. */ 99 private static final String SYMBOL_TABLE = 100 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 101 102 // pipeline parts 103 private final ValidatorHandler validator; 104 private final XNI2SAX xni2sax = new XNI2SAX(); 105 private final SAX2XNI sax2xni = new SAX2XNI(); 106 107 // never be null 108 private final TypeInfoProvider typeInfoProvider; 109 110 /** 111 * Used to store the {@link Augmentations} associated with the 112 * current event, so that we can pick it up again 113 * when the event is forwarded by the {@link ValidatorHandler}. 114 * 115 * UGLY HACK. 116 */ 117 private Augmentations fCurrentAug; 118 119 /** 120 * {@link XMLAttributes} version of {@link #fCurrentAug}. 121 */ 122 private XMLAttributes fCurrentAttributes; 123 124 // components obtained from a manager / property 125 126 private SymbolTable fSymbolTable; 127 private XMLErrorReporter fErrorReporter; 128 private XMLEntityResolver fEntityResolver; 129 130 /** 131 * @param validatorHandler may not be null. 132 */ 133 public JAXPValidatorComponent( ValidatorHandler validatorHandler ) { 134 this.validator = validatorHandler; 135 TypeInfoProvider tip = validatorHandler.getTypeInfoProvider(); 136 if(tip==null) tip = noInfoProvider; 137 this.typeInfoProvider = tip; 138 139 // configure wiring between internal components. 140 xni2sax.setContentHandler(validator); 141 validator.setContentHandler(sax2xni); 142 this.setSide(xni2sax); 143 144 // configure validator with proper EntityResolver/ErrorHandler. 145 validator.setErrorHandler(new ErrorHandlerProxy() { 146 protected XMLErrorHandler getErrorHandler() { 147 XMLErrorHandler handler = fErrorReporter.getErrorHandler(); 148 if(handler!=null) return handler; 149 return new ErrorHandlerWrapper(DraconianErrorHandler.getInstance()); 150 } 151 }); 152 validator.setResourceResolver(new LSResourceResolver() { 153 public LSInput resolveResource(String type,String ns, String publicId, String systemId, String baseUri) { 154 if(fEntityResolver==null) return null; 155 try { 156 XMLInputSource is = fEntityResolver.resolveEntity( 157 new XMLResourceIdentifierImpl(publicId,systemId,baseUri,null)); 158 if(is==null) return null; 159 160 LSInput di = new DOMInputImpl(); 161 di.setBaseURI(is.getBaseSystemId()); 162 di.setByteStream(is.getByteStream()); 163 di.setCharacterStream(is.getCharacterStream()); 164 di.setEncoding(is.getEncoding()); 165 di.setPublicId(is.getPublicId()); 166 di.setSystemId(is.getSystemId()); 167 168 return di; 169 } catch( IOException e ) { 170 // erors thrown by the callback is not supposed to be 171 // reported to users. 172 throw new XNIException(e); 173 } 174 } 175 }); 176 } 177 178 179 public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { 180 fCurrentAttributes = attributes; 181 fCurrentAug = augs; 182 xni2sax.startElement(element,attributes,null); 183 fCurrentAttributes = null; // mostly to make it easy to find any bug. 184 } 185 186 public void endElement(QName element, Augmentations augs) throws XNIException { 187 fCurrentAug = augs; 188 xni2sax.endElement(element,null); 189 } 190 191 public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { 192 startElement(element,attributes,augs); 193 endElement(element,augs); 194 } 195 196 197 public void characters(XMLString text, Augmentations augs) throws XNIException { 198 // since a validator may change the contents, 199 // let this one go through a validator 200 fCurrentAug = augs; 201 xni2sax.characters(text,null); 202 } 203 204 public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException { 205 // since a validator may change the contents, 206 // let this one go through a validator 207 fCurrentAug = augs; 208 xni2sax.ignorableWhitespace(text,null); 209 } 210 211 public void reset(XMLComponentManager componentManager) throws XMLConfigurationException { 212 // obtain references from the manager 213 fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE); 214 fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER); 215 try { 216 fEntityResolver = (XMLEntityResolver) componentManager.getProperty(ENTITY_MANAGER); 217 } 218 catch (XMLConfigurationException e) { 219 fEntityResolver = null; 220 } 221 } 222 223 /** 224 * 225 * Uses {@link DefaultHandler} as a default implementation of 226 * {@link ContentHandler}. 227 * 228 * <p> 229 * We only forward certain events from a {@link ValidatorHandler}. 230 * Other events should go "the 2nd direct route". 231 */ 232 private final class SAX2XNI extends DefaultHandler { 233 234 /** 235 * {@link Augmentations} to send along with events. 236 * We reuse one object for efficiency. 237 */ 238 private final Augmentations fAugmentations = new AugmentationsImpl(); 239 240 /** 241 * {@link QName} to send along events. 242 * we reuse one QName for efficiency. 243 */ 244 private final QName fQName = new QName(); 245 246 public void characters(char[] ch, int start, int len) throws SAXException { 247 try { 248 handler().characters(new XMLString(ch,start,len),aug()); 249 } catch( XNIException e ) { 250 throw toSAXException(e); 251 } 252 } 253 254 public void ignorableWhitespace(char[] ch, int start, int len) throws SAXException { 255 try { 256 handler().ignorableWhitespace(new XMLString(ch,start,len),aug()); 257 } catch( XNIException e ) { 258 throw toSAXException(e); 259 } 260 } 261 262 public void startElement(String uri, String localName, String qname, Attributes atts) throws SAXException { 263 try { 264 updateAttributes(atts); 265 handler().startElement(toQName(uri,localName,qname), fCurrentAttributes, elementAug()); 266 } catch( XNIException e ) { 267 throw toSAXException(e); 268 } 269 } 270 271 public void endElement(String uri, String localName, String qname) throws SAXException { 272 try { 273 handler().endElement(toQName(uri,localName,qname),aug()); 274 } catch( XNIException e ) { 275 throw toSAXException(e); 276 } 277 } 278 279 private Augmentations elementAug() { 280 Augmentations aug = aug(); 281 /** aug.putItem(Constants.TYPEINFO,typeInfoProvider.getElementTypeInfo()); **/ 282 return aug; 283 } 284 285 286 /** 287 * Gets the {@link Augmentations} that should be associated with 288 * the current event. 289 */ 290 private Augmentations aug() { 291 if( fCurrentAug!=null ) { 292 Augmentations r = fCurrentAug; 293 fCurrentAug = null; // we "consumed" this augmentation. 294 return r; 295 } 296 fAugmentations.removeAllItems(); 297 return fAugmentations; 298 } 299 300 /** 301 * Get the handler to which we should send events. 302 */ 303 private XMLDocumentHandler handler() { 304 return JAXPValidatorComponent.this.getDocumentHandler(); 305 } 306 307 /** 308 * Converts the {@link XNIException} received from a downstream 309 * component to a {@link SAXException}. 310 */ 311 private SAXException toSAXException( XNIException xe ) { 312 Exception e = xe.getException(); 313 if( e==null ) e = xe; 314 if( e instanceof SAXException ) return (SAXException)e; 315 return new SAXException(e); 316 } 317 318 /** 319 * Creates a proper {@link QName} object from 3 parts. 320 * <p> 321 * This method does the symbolization. 322 */ 323 private QName toQName( String uri, String localName, String qname ) { 324 String prefix = null; 325 int idx = qname.indexOf(':'); 326 if( idx>0 ) 327 prefix = symbolize(qname.substring(0,idx)); 328 329 localName = symbolize(localName); 330 qname = symbolize(qname); 331 uri = symbolize(uri); 332 333 // notify handlers 334 fQName.setValues(prefix, localName, qname, uri); 335 return fQName; 336 } 337 } 338 339 /** 340 * Converts {@link XNI} events to {@link ContentHandler} events. 341 * 342 * <p> 343 * Deriving from {@link DefaultXMLDocumentHandler} 344 * to reuse its default {@link com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler} 345 * implementation. 346 * 347 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com) 348 */ 349 private final class XNI2SAX extends DefaultXMLDocumentHandler { 350 351 private ContentHandler fContentHandler; 352 353 private String fVersion; 354 355 /** Namespace context */ 356 protected NamespaceContext fNamespaceContext; 357 358 /** 359 * For efficiency, we reuse one instance. 360 */ 361 private final AttributesProxy fAttributesProxy = new AttributesProxy(null); 362 363 public void setContentHandler( ContentHandler handler ) { 364 this.fContentHandler = handler; 365 } 366 367 public ContentHandler getContentHandler() { 368 return fContentHandler; 369 } 370 371 372 public void xmlDecl(String version, String encoding, String standalone, Augmentations augs) throws XNIException { 373 this.fVersion = version; 374 } 375 376 public void startDocument(XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException { 377 fNamespaceContext = namespaceContext; 378 fContentHandler.setDocumentLocator(new LocatorProxy(locator)); 379 try { 380 fContentHandler.startDocument(); 381 } catch (SAXException e) { 382 throw new XNIException(e); 383 } 384 } 385 386 public void endDocument(Augmentations augs) throws XNIException { 387 try { 388 fContentHandler.endDocument(); 389 } catch (SAXException e) { 390 throw new XNIException(e); 391 } 392 } 393 394 public void processingInstruction(String target, XMLString data, Augmentations augs) throws XNIException { 395 try { 396 fContentHandler.processingInstruction(target,data.toString()); 397 } catch (SAXException e) { 398 throw new XNIException(e); 399 } 400 } 401 402 public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { 403 try { 404 // start namespace prefix mappings 405 int count = fNamespaceContext.getDeclaredPrefixCount(); 406 if (count > 0) { 407 String prefix = null; 408 String uri = null; 409 for (int i = 0; i < count; i++) { 410 prefix = fNamespaceContext.getDeclaredPrefixAt(i); 411 uri = fNamespaceContext.getURI(prefix); 412 fContentHandler.startPrefixMapping(prefix, (uri == null)?"":uri); 413 } 414 } 415 416 String uri = element.uri != null ? element.uri : ""; 417 String localpart = element.localpart; 418 fAttributesProxy.setAttributes(attributes); 419 fContentHandler.startElement(uri, localpart, element.rawname, fAttributesProxy); 420 } catch( SAXException e ) { 421 throw new XNIException(e); 422 } 423 } 424 425 public void endElement(QName element, Augmentations augs) throws XNIException { 426 try { 427 String uri = element.uri != null ? element.uri : ""; 428 String localpart = element.localpart; 429 fContentHandler.endElement(uri, localpart, element.rawname); 430 431 // send endPrefixMapping events 432 int count = fNamespaceContext.getDeclaredPrefixCount(); 433 if (count > 0) { 434 for (int i = 0; i < count; i++) { 435 fContentHandler.endPrefixMapping(fNamespaceContext.getDeclaredPrefixAt(i)); 436 } 437 } 438 } catch( SAXException e ) { 439 throw new XNIException(e); 440 } 441 } 442 443 public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { 444 startElement(element,attributes,augs); 445 endElement(element,augs); 446 } 447 448 public void characters(XMLString text, Augmentations augs) throws XNIException { 449 try { 450 fContentHandler.characters(text.ch,text.offset,text.length); 451 } catch (SAXException e) { 452 throw new XNIException(e); 453 } 454 } 455 456 public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException { 457 try { 458 fContentHandler.ignorableWhitespace(text.ch,text.offset,text.length); 459 } catch (SAXException e) { 460 throw new XNIException(e); 461 } 462 } 463 } 464 465 private static final class DraconianErrorHandler implements ErrorHandler { 466 467 /** 468 * Singleton instance. 469 */ 470 private static final DraconianErrorHandler ERROR_HANDLER_INSTANCE 471 = new DraconianErrorHandler(); 472 473 private DraconianErrorHandler() {} 474 475 /** Returns the one and only instance of this error handler. */ 476 public static DraconianErrorHandler getInstance() { 477 return ERROR_HANDLER_INSTANCE; 478 } 479 480 /** Warning: Ignore. */ 481 public void warning(SAXParseException e) throws SAXException { 482 // noop 483 } 484 485 /** Error: Throws back SAXParseException. */ 486 public void error(SAXParseException e) throws SAXException { 487 throw e; 488 } 489 490 /** Fatal Error: Throws back SAXParseException. */ 491 public void fatalError(SAXParseException e) throws SAXException { 492 throw e; 493 } 494 495 } // DraconianErrorHandler 496 497 498 /** 499 * Compares the given {@link Attributes} with {@link #fCurrentAttributes} 500 * and update the latter accordingly. 501 */ 502 private void updateAttributes( Attributes atts ) { 503 int len = atts.getLength(); 504 for( int i=0; i<len; i++ ) { 505 String aqn = atts.getQName(i); 506 int j = fCurrentAttributes.getIndex(aqn); 507 String av = atts.getValue(i); 508 if(j==-1) { 509 // newly added attribute. add to the current attribute list. 510 511 String prefix; 512 int idx = aqn.indexOf(':'); 513 if( idx<0 ) { 514 prefix = null; 515 } else { 516 prefix = symbolize(aqn.substring(0,idx)); 517 } 518 519 j = fCurrentAttributes.addAttribute( 520 new QName( 521 prefix, 522 symbolize(atts.getLocalName(i)), 523 symbolize(aqn), 524 symbolize(atts.getURI(i))), 525 atts.getType(i),av); 526 } else { 527 // the attribute is present. 528 if( !av.equals(fCurrentAttributes.getValue(j)) ) { 529 // but the value was changed. 530 fCurrentAttributes.setValue(j,av); 531 } 532 } 533 534 /** Augmentations augs = fCurrentAttributes.getAugmentations(j); 535 augs.putItem( Constants.TYPEINFO, 536 typeInfoProvider.getAttributeTypeInfo(i) ); 537 augs.putItem( Constants.ID_ATTRIBUTE, 538 typeInfoProvider.isIdAttribute(i)?Boolean.TRUE:Boolean.FALSE ); **/ 539 } 540 } 541 542 private String symbolize( String s ) { 543 return fSymbolTable.addSymbol(s); 544 } 545 546 547 /** 548 * {@link TypeInfoProvider} that returns no info. 549 */ 550 private static final TypeInfoProvider noInfoProvider = new TypeInfoProvider() { 551 public TypeInfo getElementTypeInfo() { 552 return null; 553 } 554 public TypeInfo getAttributeTypeInfo(int index) { 555 return null; 556 } 557 public TypeInfo getAttributeTypeInfo(String attributeQName) { 558 return null; 559 } 560 public TypeInfo getAttributeTypeInfo(String attributeUri, String attributeLocalName) { 561 return null; 562 } 563 public boolean isIdAttribute(int index) { 564 return false; 565 } 566 public boolean isSpecified(int index) { 567 return false; 568 } 569 }; 570 571 // 572 // 573 // XMLComponent implementation. 574 // 575 // 576 577 // no property/feature supported 578 public String[] getRecognizedFeatures() { 579 return null; 580 } 581 582 public void setFeature(String featureId, boolean state) throws XMLConfigurationException { 583 } 584 585 public String[] getRecognizedProperties() { 586 return new String[]{ENTITY_MANAGER, ERROR_REPORTER, SYMBOL_TABLE}; 587 } 588 589 public void setProperty(String propertyId, Object value) throws XMLConfigurationException { 590 } 591 592 public Boolean getFeatureDefault(String featureId) { 593 return null; 594 } 595 596 public Object getPropertyDefault(String propertyId) { 597 return null; 598 } 599 600 }