1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.jaxp;
  58. import java.io.IOException;
  59. import javax.xml.validation.TypeInfoProvider;
  60. import javax.xml.validation.ValidatorHandler;
  61. import com.sun.org.apache.xerces.internal.dom.DOMInputImpl;
  62. import com.sun.org.apache.xerces.internal.impl.Constants;
  63. import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  64. import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
  65. import com.sun.org.apache.xerces.internal.util.DraconianErrorHandler;
  66. import com.sun.org.apache.xerces.internal.util.ErrorHandlerProxy;
  67. import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper;
  68. import com.sun.org.apache.xerces.internal.util.SymbolTable;
  69. import com.sun.org.apache.xerces.internal.util.TeeXMLDocumentFilterImpl;
  70. import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
  71. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  72. import com.sun.org.apache.xerces.internal.xni.QName;
  73. import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  74. import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
  75. import com.sun.org.apache.xerces.internal.xni.XMLString;
  76. import com.sun.org.apache.xerces.internal.xni.XNIException;
  77. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  78. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  79. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  80. import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  81. import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  82. import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  83. import org.w3c.dom.TypeInfo;
  84. import org.w3c.dom.ls.LSInput;
  85. import org.w3c.dom.ls.LSResourceResolver;
  86. import org.xml.sax.Attributes;
  87. import org.xml.sax.SAXException;
  88. import org.xml.sax.helpers.DefaultHandler;
  89. /**
  90. * Runs events through a {@link javax.xml.validation.ValidatorHandler}
  91. * and performs validation/infoset-augmentation by an external validator.
  92. *
  93. * <p>
  94. * This component sets up the pipeline as follows:
  95. *
  96. * <!-- this picture may look teribble on your IDE but it is correct. -->
  97. * <pre>
  98. * __ __
  99. * / |==> XNI2SAX --> Validator --> SAX2XNI ==>|
  100. * / | |
  101. * ==>| Tee| | next
  102. * \ | | component
  103. * \ |============other XNI events============>|
  104. * ~~ ~~
  105. * </pre>
  106. * <p>
  107. * only those events that need to go through Validator will go the 1st route,
  108. * and other events go the 2nd direct route.
  109. *
  110. * @author
  111. * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
  112. */
  113. public class JAXPValidatorComponent extends TeeXMLDocumentFilterImpl implements XMLComponent {
  114. // pipeline parts
  115. private final ValidatorHandler validator;
  116. private final XNI2SAX xni2sax = new XNI2SAX();
  117. private final SAX2XNI sax2xni = new SAX2XNI();
  118. // never be null
  119. private final TypeInfoProvider typeInfoProvider;
  120. /**
  121. * Used to store the {@link Augmentations} associated with the
  122. * current event, so that we can pick it up again
  123. * when the event is forwarded by the {@link ValidatorHandler}.
  124. *
  125. * UGLY HACK.
  126. */
  127. private Augmentations fCurrentAug;
  128. /**
  129. * {@link XMLAttributes} version of {@link #fCurrentAug}.
  130. */
  131. private XMLAttributes fCurrentAttributes;
  132. // components obtained from a manager / property
  133. private SymbolTable fSymbolTable;
  134. private XMLErrorReporter fErrorReporter;
  135. private XMLEntityResolver fEntityResolver;
  136. /**
  137. * @param validatorHandler
  138. * may not be null.
  139. */
  140. public JAXPValidatorComponent( ValidatorHandler validatorHandler ) {
  141. this.validator = validatorHandler;
  142. TypeInfoProvider tip = validatorHandler.getTypeInfoProvider();;
  143. if(tip==null) tip = noInfoProvider;
  144. this.typeInfoProvider = tip;
  145. // configure wiring between internal components.
  146. xni2sax.setContentHandler(validator);
  147. validator.setContentHandler(sax2xni);
  148. this.setSide(xni2sax);
  149. // configure validator with proper EntityResolver/ErrorHandler.
  150. validator.setErrorHandler(new ErrorHandlerProxy() {
  151. protected XMLErrorHandler getErrorHandler() {
  152. XMLErrorHandler handler = fErrorReporter.getErrorHandler();
  153. if(handler!=null) return handler;
  154. return new ErrorHandlerWrapper(DraconianErrorHandler.theInstance);
  155. }
  156. });
  157. validator.setResourceResolver(new LSResourceResolver() {
  158. public LSInput resolveResource(String type,String ns, String publicId, String systemId, String baseUri) {
  159. if(fEntityResolver==null) return null;
  160. try {
  161. XMLInputSource is = fEntityResolver.resolveEntity(
  162. new XMLResourceIdentifierImpl(publicId,systemId,baseUri,systemId));
  163. if(is==null) return null;
  164. LSInput di = new DOMInputImpl();
  165. di.setBaseURI(is.getBaseSystemId());
  166. di.setByteStream(is.getByteStream());
  167. di.setCharacterStream(is.getCharacterStream());
  168. di.setEncoding(is.getEncoding());
  169. di.setPublicId(is.getPublicId());
  170. di.setSystemId(is.getSystemId());
  171. return di;
  172. } catch( IOException e ) {
  173. // erors thrown by the callback is not supposed to be
  174. // reported to users.
  175. throw new XNIException(e);
  176. }
  177. }
  178. });
  179. }
  180. public void startElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException {
  181. fCurrentAttributes = attributes;
  182. fCurrentAug = augs;
  183. xni2sax.startElement(element,attributes,null);
  184. fCurrentAttributes = null; // mostly to make it easy to find any bug.
  185. }
  186. public void endElement(QName element, Augmentations augs) throws XNIException {
  187. fCurrentAug = augs;
  188. xni2sax.endElement(element,null);
  189. }
  190. public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException {
  191. startElement(element,attributes,augs);
  192. endElement(element,augs);
  193. }
  194. public void characters(XMLString text, Augmentations augs) throws XNIException {
  195. // since a validator may change the contents,
  196. // let this one go through a validator
  197. fCurrentAug = augs;
  198. xni2sax.characters(text,null);
  199. }
  200. public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
  201. // since a validator may change the contents,
  202. // let this one go through a validator
  203. fCurrentAug = augs;
  204. xni2sax.ignorableWhitespace(text,null);
  205. }
  206. public void reset(XMLComponentManager componentManager) throws XMLConfigurationException {
  207. // obtain references from the manager
  208. fSymbolTable = (SymbolTable)componentManager.getProperty(
  209. Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY);
  210. fErrorReporter = (XMLErrorReporter)componentManager.getProperty(
  211. Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);
  212. }
  213. /**
  214. *
  215. * Uses {@link DefaultHandler} as a default implementation of
  216. * {@link ContentHandler}.
  217. *
  218. * <p>
  219. * We only forward certain events from a {@link ValidatorHandler}.
  220. * Other events should go "the 2nd direct route".
  221. */
  222. private final class SAX2XNI extends DefaultHandler {
  223. /**
  224. * {@link Augmentations} to send along with events.
  225. * We reuse one object for efficiency.
  226. */
  227. private final Augmentations fAugmentations = new AugmentationsImpl();
  228. /**
  229. * {@link QName} to send along events.
  230. * we reuse one QName for efficiency.
  231. */
  232. private final QName fQName = new QName();
  233. public void characters(char[] ch, int start, int len) throws SAXException {
  234. try {
  235. handler().characters(new XMLString(ch,start,len),aug());
  236. } catch( XNIException e ) {
  237. throw toSAXException(e);
  238. }
  239. }
  240. public void ignorableWhitespace(char[] ch, int start, int len) throws SAXException {
  241. try {
  242. handler().ignorableWhitespace(new XMLString(ch,start,len),aug());
  243. } catch( XNIException e ) {
  244. throw toSAXException(e);
  245. }
  246. }
  247. public void startElement(String uri, String localName, String qname, Attributes atts) throws SAXException {
  248. try {
  249. updateAttributes(atts);
  250. handler().startElement(toQName(uri,localName,qname), fCurrentAttributes, elementAug());
  251. } catch( XNIException e ) {
  252. throw toSAXException(e);
  253. }
  254. }
  255. public void endElement(String uri, String localName, String qname) throws SAXException {
  256. try {
  257. handler().endElement(toQName(uri,localName,qname),aug());
  258. } catch( XNIException e ) {
  259. throw toSAXException(e);
  260. }
  261. }
  262. private Augmentations elementAug() {
  263. Augmentations aug = aug();
  264. aug.putItem(Constants.TYPEINFO,typeInfoProvider.getElementTypeInfo());
  265. return aug;
  266. }
  267. /**
  268. * Gets the {@link Augmentations} that should be associated with
  269. * the current event.
  270. */
  271. private Augmentations aug() {
  272. if( fCurrentAug!=null ) {
  273. Augmentations r = fCurrentAug;
  274. fCurrentAug = null; // we "consumed" this augmentation.
  275. return r;
  276. }
  277. fAugmentations.removeAllItems();
  278. return fAugmentations;
  279. }
  280. /**
  281. * Get the handler to which we should send events.
  282. */
  283. private XMLDocumentHandler handler() {
  284. return JAXPValidatorComponent.this.getDocumentHandler();
  285. }
  286. /**
  287. * Converts the {@link XNIException} received from a downstream
  288. * component to a {@link SAXException}.
  289. */
  290. private SAXException toSAXException( XNIException xe ) {
  291. Exception e = xe.getException();
  292. if( e==null ) e = xe;
  293. if( e instanceof SAXException ) return (SAXException)e;
  294. return new SAXException(e);
  295. }
  296. /**
  297. * Creates a proper {@link QName} object from 3 parts.
  298. * <p>
  299. * This method does the symbolization.
  300. */
  301. private QName toQName( String uri, String localName, String qname ) {
  302. String prefix = null;
  303. int idx = qname.indexOf(':');
  304. if( idx>0 )
  305. prefix = symbolize(qname.substring(0,idx));
  306. localName = symbolize(localName);
  307. qname = symbolize(qname);
  308. uri = symbolize(uri);
  309. // notify handlers
  310. fQName.setValues(prefix, localName, qname, uri);
  311. return fQName;
  312. }
  313. }
  314. /**
  315. * Compares the given {@link Attributes} with {@link #fCurrentAttributes}
  316. * and update the latter accordingly.
  317. */
  318. private void updateAttributes( Attributes atts ) {
  319. int len = atts.getLength();
  320. for( int i=0; i<len; i++ ) {
  321. String aqn = atts.getQName(i);
  322. int j = fCurrentAttributes.getIndex(aqn);
  323. String av = atts.getValue(i);
  324. if(j==-1) {
  325. // newly added attribute. add to the current attribute list.
  326. String prefix;
  327. int idx = aqn.indexOf(':');
  328. if( idx<0 ) {
  329. prefix = null;
  330. } else {
  331. prefix = symbolize(aqn.substring(0,idx));
  332. }
  333. j = fCurrentAttributes.addAttribute(
  334. new QName(
  335. prefix,
  336. symbolize(atts.getLocalName(i)),
  337. symbolize(aqn),
  338. symbolize(atts.getURI(i))),
  339. atts.getType(i),av);
  340. } else {
  341. // the attribute is present.
  342. if( !av.equals(fCurrentAttributes.getValue(j)) ) {
  343. // but the value was changed.
  344. fCurrentAttributes.setValue(j,av);
  345. }
  346. }
  347. Augmentations augs = fCurrentAttributes.getAugmentations(j);
  348. augs.putItem( Constants.TYPEINFO,
  349. typeInfoProvider.getAttributeTypeInfo(i) );
  350. augs.putItem( Constants.ID_ATTRIBUTE,
  351. typeInfoProvider.isIdAttribute(i)?Boolean.TRUE:Boolean.FALSE );
  352. }
  353. // spec says attributes won't be removed.
  354. //
  355. // // we might remove attributes as we go through,
  356. // // so iterate in the reverse order.
  357. // for( int j=fCurrentAttributes.getLength()-1; j>=0; j-- ) {
  358. // String aqn = fCurrentAttributes.getQName(j);
  359. // int i = atts.getIndex(aqn);
  360. // if(i==-1)
  361. // // this attribute is removed.
  362. // fCurrentAttributes.removeAttributeAt(j);
  363. // }
  364. }
  365. private String symbolize( String s ) {
  366. return fSymbolTable.addSymbol(s);
  367. }
  368. /**
  369. * {@link TypeInfoProvider} that returns no info.
  370. */
  371. private static final TypeInfoProvider noInfoProvider = new TypeInfoProvider() {
  372. public TypeInfo getElementTypeInfo() {
  373. return null;
  374. }
  375. public TypeInfo getAttributeTypeInfo(int index) {
  376. return null;
  377. }
  378. public TypeInfo getAttributeTypeInfo(String attributeQName) {
  379. return null;
  380. }
  381. public TypeInfo getAttributeTypeInfo(String attributeUri, String attributeLocalName) {
  382. return null;
  383. }
  384. public boolean isIdAttribute(int index) {
  385. return false;
  386. }
  387. public boolean isSpecified(int index) {
  388. return false;
  389. }
  390. };
  391. //
  392. //
  393. // XMLComponent implementation.
  394. //
  395. //
  396. // no property/feature supported
  397. public String[] getRecognizedFeatures() {
  398. return null;
  399. }
  400. public void setFeature(String featureId, boolean state) throws XMLConfigurationException {
  401. }
  402. public String[] getRecognizedProperties() {
  403. return new String[]{Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY};
  404. }
  405. public void setProperty(String propertyId, Object value) throws XMLConfigurationException {
  406. if(propertyId.equals(Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY)) {
  407. fEntityResolver = (XMLEntityResolver)value;
  408. }
  409. }
  410. public Boolean getFeatureDefault(String featureId) {
  411. return null;
  412. }
  413. public Object getPropertyDefault(String propertyId) {
  414. return null;
  415. }
  416. }