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.validation;
  58. import javax.xml.validation.TypeInfoProvider;
  59. import javax.xml.validation.ValidatorHandler;
  60. import com.sun.org.apache.xerces.internal.impl.Constants;
  61. import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
  62. import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  63. import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType;
  64. import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
  65. import com.sun.org.apache.xerces.internal.util.DraconianErrorHandler;
  66. import com.sun.org.apache.xerces.internal.util.LocatorWrapper;
  67. import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
  68. import com.sun.org.apache.xerces.internal.util.SymbolTable;
  69. import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  70. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  71. import com.sun.org.apache.xerces.internal.xni.QName;
  72. import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  73. import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  74. import com.sun.org.apache.xerces.internal.xni.XMLString;
  75. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  76. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  77. import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
  78. import com.sun.org.apache.xerces.internal.xs.AttributePSVI;
  79. import com.sun.org.apache.xerces.internal.xs.ElementPSVI;
  80. import com.sun.org.apache.xerces.internal.xs.ItemPSVI;
  81. import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
  82. import org.w3c.dom.TypeInfo;
  83. import org.w3c.dom.ls.LSResourceResolver;
  84. import org.xml.sax.Attributes;
  85. import org.xml.sax.ContentHandler;
  86. import org.xml.sax.ErrorHandler;
  87. import org.xml.sax.Locator;
  88. import org.xml.sax.SAXException;
  89. import org.xml.sax.SAXNotRecognizedException;
  90. import org.xml.sax.SAXNotSupportedException;
  91. /**
  92. * {@link ValidatorHandler} implementation that wraps
  93. * {@link InsulatedValidatorComponent}.
  94. *
  95. * <p>
  96. * This class implements all the SAX {@link org.xml.sax.ContentHandler}
  97. * methods and turn SAX events into XNI events.
  98. *
  99. * <p>
  100. * This class also implements {@link XMLComponentManager}
  101. * to host a validator.
  102. *
  103. * @author
  104. * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
  105. */
  106. final class ValidatorHandlerImpl extends ValidatorHandler {
  107. // TODO: reuse SAX2XNI
  108. /**
  109. * The actual validator.
  110. */
  111. private final InsulatedValidatorComponent validator;
  112. /**
  113. * <code>validator.getValidator()</code>.
  114. */
  115. private final XMLDocumentFilter validatorFilter;
  116. /**
  117. * Used to adopt the output from a validtor to the input of
  118. * the user specified {@link ContentHandler}.
  119. */
  120. private final XNI2SAXEx xni2sax = new XNI2SAXEx();
  121. // XMLSchemaValidator needs various helper components to work.
  122. private final SymbolTable symbolTable = new SymbolTable();
  123. private final NamespaceSupport nsContext = new NamespaceSupport();
  124. private final ValidationManager validationManager = new ValidationManager();
  125. private final XMLEntityManager entityManager = new XMLEntityManager();
  126. /** error reporter is used to format error messages. */
  127. private final XMLErrorReporter errorReporter = new XMLErrorReporter();
  128. /** User-specified error handler. Maybe null. */
  129. private ErrorHandler errorHandler;
  130. /** This flag is set to true while we are processing the startElement event. */
  131. private boolean inStartElement;
  132. /** The value of the <tt>http://xml.org/sax/features/namespace-prefixes</tt> feature. */
  133. private boolean namespacePrefixesFeature = false;
  134. //private Hashtable fProperties;
  135. /**
  136. * Used by {@link XMLDTDValidator} to report errors.
  137. */
  138. private final ErrorHandlerAdaptor xercesErrorHandler = new ErrorHandlerAdaptor() {
  139. protected ErrorHandler getErrorHandler() {
  140. if(errorHandler==null )
  141. return DraconianErrorHandler.theInstance;
  142. else
  143. return errorHandler;
  144. }
  145. };
  146. /** User-specified entity resolver. Maybe null. */
  147. private LSResourceResolver resourceResolver;
  148. ValidatorHandlerImpl( InsulatedValidatorComponent validator ) {
  149. this.validator = validator;
  150. this.validatorFilter = validator.getValidator();
  151. }
  152. /**
  153. * Obtains the current augmentation.
  154. * <p>
  155. * used for {@link javax.xml.validation.TypeInfoProvider}.
  156. *
  157. * @return
  158. * may return null.
  159. */
  160. private final Augmentations getCurrentAugmentation() {
  161. return xni2sax.getCurrentAugmentation();
  162. }
  163. /**
  164. * Obtains the current attributes.
  165. *
  166. * @throws IllegalStateException
  167. */
  168. private final XMLAttributes getCurrentAttributes() {
  169. return xni2sax.getCurrentAttributes();
  170. }
  171. public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
  172. if( name.equals("http://xml.org/sax/features/namespace-prefixes") )
  173. return namespacePrefixesFeature;
  174. return super.getFeature(name);
  175. }
  176. public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
  177. if( name.equals("http://xml.org/sax/features/namespace-prefixes") ) {
  178. namespacePrefixesFeature = value;
  179. return;
  180. }
  181. super.setFeature(name, value);
  182. }
  183. //
  184. //
  185. // ValidaorHandler implementation
  186. //
  187. //
  188. public boolean isValidSoFar() {
  189. return !xercesErrorHandler.hadError();
  190. }
  191. public void setErrorHandler(ErrorHandler errorHandler) {
  192. this.errorHandler = errorHandler;
  193. }
  194. public ErrorHandler getErrorHandler() {
  195. return errorHandler;
  196. }
  197. public void setResourceResolver(LSResourceResolver entityResolver) {
  198. this.resourceResolver = entityResolver;
  199. }
  200. public LSResourceResolver getResourceResolver() {
  201. return resourceResolver;
  202. }
  203. public final void setContentHandler(ContentHandler result) {
  204. xni2sax.setContentHandler(result);
  205. if(result==null) validatorFilter.setDocumentHandler(null);
  206. else validatorFilter.setDocumentHandler(xni2sax);
  207. }
  208. public final ContentHandler getContentHandler() {
  209. return xni2sax.getContentHandler();
  210. }
  211. //
  212. //
  213. // XMLComponentManager implementation
  214. //
  215. //
  216. private final XMLComponentManager manager = new XMLComponentManager() {
  217. public Object getProperty( String propName ) {
  218. if( propName.equals(XercesConstants.SYMBOL_TABLE) )
  219. return symbolTable;
  220. if( propName.equals(XercesConstants.VALIDATION_MANAGER) )
  221. return validationManager;
  222. if( propName.equals(XercesConstants.ERROR_REPORTER) )
  223. return errorReporter;
  224. if( propName.equals(XercesConstants.ERROR_HANDLER) )
  225. return xercesErrorHandler;
  226. if( propName.equals(XercesConstants.ENTITY_MANAGER) )
  227. return entityManager;
  228. if( propName.equals(XercesConstants.ENTITY_RESOLVER) )
  229. return entityManager;
  230. throw new XMLConfigurationException(
  231. XMLConfigurationException.NOT_RECOGNIZED, propName );
  232. }
  233. public boolean getFeature( String propName ) {
  234. // if( propName.equals(XercesConstants.SCHEMA_VALIDATION) )
  235. // // this flag will turn on the validation
  236. // return true;
  237. if( propName.equals(XercesConstants.VALIDATION) )
  238. return true;//TODO:: Configure
  239. throw new XMLConfigurationException(
  240. XMLConfigurationException.NOT_RECOGNIZED, propName );
  241. }
  242. };
  243. //
  244. //
  245. // ContentHandler implementation
  246. //
  247. //
  248. public void startDocument() throws SAXException {
  249. try {
  250. resetComponents();
  251. XMLLocator xmlLocator = (locator==null)?null:new LocatorWrapper(locator);
  252. // set the locator to the error reporter
  253. errorReporter.setDocumentLocator(xmlLocator);
  254. validatorFilter.startDocument(
  255. xmlLocator,
  256. null,
  257. nsContext,
  258. null);
  259. } catch( WrappedSAXException e ) {
  260. throw e.exception;
  261. }
  262. }
  263. /**
  264. * Resets the components we use internally.
  265. */
  266. private void resetComponents() {
  267. // reset the error flag when we start a new validation.
  268. xercesErrorHandler.reset();
  269. nsContext.reset();
  270. errorReporter.reset(manager);
  271. validator.reset(manager);
  272. }
  273. public void endDocument() throws SAXException {
  274. try {
  275. validatorFilter.endDocument(null);
  276. } catch( WrappedSAXException e ) {
  277. throw e.exception;
  278. }
  279. }
  280. public void startElement( String uri, String local, String qname, Attributes att ) throws SAXException {
  281. try {
  282. inStartElement = true;
  283. validatorFilter.startElement(createQName(uri,local,qname),createAttributes(att),null);
  284. } catch( WrappedSAXException e ) {
  285. throw e.exception;
  286. } finally {
  287. inStartElement = false;
  288. }
  289. }
  290. public void endElement( String uri, String local, String qname ) throws SAXException {
  291. try {
  292. validatorFilter.endElement(createQName(uri,local,qname),null);
  293. } catch( WrappedSAXException e ) {
  294. throw e.exception;
  295. }
  296. }
  297. public void characters( char[] buf, int offset, int len ) throws SAXException {
  298. try {
  299. validatorFilter.characters(new XMLString(buf,offset,len),null);
  300. } catch( WrappedSAXException e ) {
  301. throw e.exception;
  302. }
  303. }
  304. public void ignorableWhitespace( char[] buf, int offset, int len ) throws SAXException {
  305. try {
  306. validatorFilter.ignorableWhitespace(new XMLString(buf,offset,len),null);
  307. } catch( WrappedSAXException e ) {
  308. throw e.exception;
  309. }
  310. }
  311. public void startPrefixMapping( String prefix, String uri ) {
  312. nsContext.pushContext();
  313. nsContext.declarePrefix(prefix,uri);
  314. }
  315. public void endPrefixMapping( String prefix ) {
  316. nsContext.popContext();
  317. }
  318. public void processingInstruction( String target, String data ) throws SAXException {
  319. try {
  320. validatorFilter.processingInstruction(
  321. symbolize(target),createXMLString(data),null);
  322. } catch( WrappedSAXException e ) {
  323. throw e.exception;
  324. }
  325. }
  326. public void skippedEntity( String name ) {
  327. // there seems to be no corresponding method on XMLDocumentFilter.
  328. // just pass it down to the output, if any.
  329. ContentHandler handler = getContentHandler();
  330. if( handler!=null )
  331. skippedEntity(name);
  332. }
  333. private Locator locator;
  334. public void setDocumentLocator( Locator _loc ) {
  335. this.locator = _loc;
  336. }
  337. public TypeInfoProvider getTypeInfoProvider() {
  338. return typeInfoProvider;
  339. }
  340. /**
  341. * {@link TypeInfoProvider} implementation.
  342. *
  343. * REVISIT: I'm not sure if this code should belong here.
  344. */
  345. private final TypeInfoProvider typeInfoProvider = new TypeInfoProvider() {
  346. /**
  347. * Throws a {@link IllegalStateException} if we are not in
  348. * the startElement callback. the JAXP API requires this
  349. * for most of the public methods.
  350. */
  351. private void checkState() {
  352. if( !inStartElement )
  353. throw new IllegalStateException();
  354. }
  355. public TypeInfo getAttributeTypeInfo(int index) {
  356. checkState();
  357. return getAttributeType(index);
  358. }
  359. private XSTypeDefinition getAttributeType( int index ) {
  360. checkState();
  361. XMLAttributes atts = getCurrentAttributes();
  362. if( index<0 || atts.getLength()<=index )
  363. throw new IndexOutOfBoundsException(Integer.toString(index));
  364. Augmentations augs = atts.getAugmentations(index);
  365. if(augs==null) return null;
  366. AttributePSVI psvi = (AttributePSVI)augs.getItem(Constants.ATTRIBUTE_PSVI);
  367. return getTypeInfoFromPSVI(psvi);
  368. }
  369. public TypeInfo getAttributeTypeInfo(String attributeUri, String attributeLocalName) {
  370. checkState();
  371. return getAttributeTypeInfo(getCurrentAttributes().getIndex(attributeUri,attributeLocalName));
  372. }
  373. public TypeInfo getAttributeTypeInfo(String attributeQName) {
  374. checkState();
  375. return getAttributeTypeInfo(getCurrentAttributes().getIndex(attributeQName));
  376. }
  377. public TypeInfo getElementTypeInfo() {
  378. checkState();
  379. Augmentations augs = getCurrentAugmentation();
  380. if(augs==null) return null;
  381. ElementPSVI psvi = (ElementPSVI)augs.getItem(Constants.ELEMENT_PSVI);
  382. return getTypeInfoFromPSVI(psvi);
  383. }
  384. private XSTypeDefinition getTypeInfoFromPSVI( ItemPSVI psvi ) {
  385. if(psvi==null) return null;
  386. // TODO: make sure if this is correct.
  387. // TODO: since the number of types in a schema is quite limited,
  388. // TypeInfoImpl should be pooled. Even better, it should be a part
  389. // of the element decl.
  390. if( psvi.getValidity()== ElementPSVI.VALIDITY_VALID ) {
  391. XSTypeDefinition t = psvi.getMemberTypeDefinition();
  392. if(t!=null) return t;
  393. }
  394. XSTypeDefinition t = psvi.getTypeDefinition();
  395. if(t!=null) return t; // TODO: can t be null?
  396. return null;
  397. }
  398. public boolean isIdAttribute(int index) {
  399. checkState();
  400. XSSimpleType type = (XSSimpleType)getAttributeType(index);
  401. if(type==null) return false;
  402. return type.isIDType();
  403. }
  404. public boolean isSpecified(int index) {
  405. checkState();
  406. return getCurrentAttributes().isSpecified(index);
  407. }
  408. };
  409. //
  410. //
  411. // helper methods
  412. //
  413. //
  414. /** Symbolizes the specified string. */
  415. private String symbolize(String s) {
  416. if (s == null)
  417. return null;
  418. else
  419. return symbolTable.addSymbol(s);
  420. }
  421. /** Creates a QName object. */
  422. private QName createQName(String uri, String local, String raw) {
  423. if( local.length()==0 ) {
  424. // if naemspace processing is turned off, local could be "".
  425. // in that case, treat everything to be in the no namespace.
  426. uri = "";
  427. local = raw;
  428. }
  429. int idx = raw.indexOf(':');
  430. String prefix;
  431. if (idx < 0)
  432. prefix = null;
  433. else
  434. prefix = raw.substring(0, idx);
  435. if (uri != null && uri.length() == 0)
  436. uri = null; // XNI uses null whereas SAX uses the empty string
  437. return new QName(symbolize(prefix), symbolize(local), symbolize(raw), symbolize(uri));
  438. }
  439. /** only one instance of XMLAttributes is used. */
  440. private final XMLAttributes xa = new XMLAttributesImpl();
  441. /** Creates an XMLAttributes object. */
  442. private XMLAttributes createAttributes(Attributes att) {
  443. xa.removeAllAttributes();
  444. int len = att.getLength();
  445. for (int i = 0; i < len; i++) {
  446. int idx = xa.addAttribute(
  447. createQName(att.getURI(i), att.getLocalName(i), att.getQName(i)),
  448. att.getType(i),
  449. att.getValue(i));
  450. // attributes present in the original SAX event streams
  451. // are considered as "specified".
  452. xa.setSpecified(idx,true);
  453. }
  454. return xa;
  455. }
  456. private XMLString createXMLString(String str) {
  457. // with my patch
  458. // return new XMLString(str);
  459. // for now
  460. return new XMLString(str.toCharArray(), 0, str.length());
  461. }
  462. /**
  463. * Resets this handler.
  464. * <p>
  465. * Meaning resets all the user-specified configurations to the
  466. * initial state, then also resets all the components.
  467. */
  468. public void reset() {
  469. resetComponents();
  470. errorHandler = null;
  471. resourceResolver = null;
  472. }
  473. }