Frames | No Frames |
1: /* XmlReader.java -- 2: Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package gnu.xml.aelfred2; 39: 40: import java.io.IOException; 41: import java.util.Locale; 42: 43: import org.xml.sax.*; 44: import org.xml.sax.ext.*; 45: 46: import gnu.xml.pipeline.EventFilter; 47: import gnu.xml.pipeline.ValidationConsumer; 48: 49: 50: /** 51: * This SAX2 parser optionally layers a validator over the Ælfred2 52: * SAX2 parser. While this will not evaluate every XML validity constraint, 53: * it does support all the validity constraints that are of any real utility 54: * outside the strict SGML-compatible world. See the documentation for the 55: * SAXDriver class for information about the SAX2 features and properties 56: * that are supported, and documentation for the ValidationConsumer for 57: * information about what validity constraints may not be supported. 58: * (Ælfred2 tests some of those, even in non-validating mode, to 59: * achieve better conformance.) 60: * 61: * <p> Note that due to its internal construction, you can't change most 62: * handlers until parse() returns. This diverges slightly from SAX, which 63: * expects later binding to be supported. Early binding involves less 64: * runtime overhead, which is an issue for event pipelines as used inside 65: * this parser. Rather than relying on the parser to handle late binding 66: * to your own handlers, do it yourself. 67: * 68: * @see SAXDriver 69: * @see gnu.xml.pipeline.ValidationConsumer 70: * 71: * @author David Brownell 72: */ 73: public final class XmlReader 74: implements XMLReader 75: { 76: 77: static class FatalErrorHandler 78: extends DefaultHandler2 79: { 80: 81: public void error(SAXParseException e) 82: throws SAXException 83: { 84: throw e; 85: } 86: 87: } 88: 89: private SAXDriver aelfred2 = new SAXDriver(); 90: private EventFilter filter = new EventFilter(); 91: private boolean isValidating; 92: private boolean active; 93: 94: /** 95: * Constructs a SAX Parser. 96: */ 97: public XmlReader() 98: { 99: } 100: 101: /** 102: * Constructs a SAX Parser, optionally treating validity errors 103: * as if they were fatal errors. 104: */ 105: public XmlReader(boolean invalidIsFatal) 106: { 107: if (invalidIsFatal) 108: { 109: setErrorHandler(new FatalErrorHandler()); 110: } 111: } 112: 113: /** 114: * <b>SAX2</b>: Returns the object used to report the logical 115: * content of an XML document. 116: */ 117: public ContentHandler getContentHandler() 118: { 119: return filter.getContentHandler(); 120: } 121: 122: /** 123: * <b>SAX2</b>: Assigns the object used to report the logical 124: * content of an XML document. 125: * @exception IllegalStateException if called mid-parse 126: */ 127: public void setContentHandler(ContentHandler handler) 128: { 129: if (active) 130: { 131: throw new IllegalStateException("already parsing"); 132: } 133: filter.setContentHandler(handler); 134: } 135: 136: /** 137: * <b>SAX2</b>: Returns the object used to process declarations related 138: * to notations and unparsed entities. 139: */ 140: public DTDHandler getDTDHandler() 141: { 142: return filter.getDTDHandler(); 143: } 144: 145: /** 146: * <b>SAX1</b> Assigns DTD handler 147: * @exception IllegalStateException if called mid-parse 148: */ 149: public void setDTDHandler(DTDHandler handler) 150: { 151: if (active) 152: { 153: throw new IllegalStateException("already parsing"); 154: } 155: filter.setDTDHandler(handler); 156: } 157: 158: /** 159: * <b>SAX2</b>: Returns the object used when resolving external 160: * entities during parsing (both general and parameter entities). 161: */ 162: public EntityResolver getEntityResolver() 163: { 164: return aelfred2.getEntityResolver(); 165: } 166: 167: /** 168: * <b>SAX1</b> Assigns parser's entity resolver 169: */ 170: public void setEntityResolver(EntityResolver handler) 171: { 172: aelfred2.setEntityResolver(handler); 173: } 174: 175: /** 176: * <b>SAX2</b>: Returns the object used to receive callbacks for XML 177: * errors of all levels (fatal, nonfatal, warning); this is never null; 178: */ 179: public ErrorHandler getErrorHandler() 180: { 181: return aelfred2.getErrorHandler(); 182: } 183: 184: /** 185: * <b>SAX1</b> Assigns error handler 186: * @exception IllegalStateException if called mid-parse 187: */ 188: public void setErrorHandler(ErrorHandler handler) 189: { 190: if (active) 191: { 192: throw new IllegalStateException("already parsing"); 193: } 194: aelfred2.setErrorHandler(handler); 195: } 196: 197: /** 198: * <b>SAX2</b>: Assigns the specified property. 199: * @exception IllegalStateException if called mid-parse 200: */ 201: public void setProperty(String propertyId, Object value) 202: throws SAXNotRecognizedException, SAXNotSupportedException 203: { 204: if (active) 205: { 206: throw new IllegalStateException("already parsing"); 207: } 208: if (getProperty(propertyId) != value) 209: { 210: filter.setProperty(propertyId, value); 211: } 212: } 213: 214: /** 215: * <b>SAX2</b>: Returns the specified property. 216: */ 217: public Object getProperty(String propertyId) 218: throws SAXNotRecognizedException 219: { 220: if ((SAXDriver.PROPERTY + "declaration-handler").equals(propertyId) 221: || (SAXDriver.PROPERTY + "lexical-handler").equals(propertyId)) 222: { 223: return filter.getProperty(propertyId); 224: } 225: throw new SAXNotRecognizedException(propertyId); 226: } 227: 228: private void forceValidating() 229: throws SAXNotRecognizedException, SAXNotSupportedException 230: { 231: aelfred2.setFeature(SAXDriver.FEATURE + "namespace-prefixes", 232: true); 233: aelfred2.setFeature(SAXDriver.FEATURE + "external-general-entities", 234: true); 235: aelfred2.setFeature(SAXDriver.FEATURE + "external-parameter-entities", 236: true); 237: } 238: 239: /** 240: * <b>SAX2</b>: Sets the state of features supported in this parser. 241: * Note that this parser requires reporting of namespace prefixes when 242: * validating. 243: */ 244: public void setFeature(String featureId, boolean state) 245: throws SAXNotRecognizedException, SAXNotSupportedException 246: { 247: boolean value = getFeature(featureId); 248: 249: if (state == value) 250: { 251: return; 252: } 253: 254: if ((SAXDriver.FEATURE + "validation").equals(featureId)) 255: { 256: if (active) 257: { 258: throw new SAXNotSupportedException("already parsing"); 259: } 260: if (state) 261: { 262: forceValidating(); 263: } 264: isValidating = state; 265: } 266: else 267: { 268: aelfred2.setFeature(featureId, state); 269: } 270: } 271: 272: /** 273: * <b>SAX2</b>: Tells whether this parser supports the specified feature. 274: * At this time, this directly parallels the underlying SAXDriver, 275: * except that validation is optionally supported. 276: * 277: * @see SAXDriver 278: */ 279: public boolean getFeature(String featureId) 280: throws SAXNotRecognizedException, SAXNotSupportedException 281: { 282: if ((SAXDriver.FEATURE + "validation").equals(featureId)) 283: { 284: return isValidating; 285: } 286: 287: return aelfred2.getFeature(featureId); 288: } 289: 290: /** 291: * <b>SAX1</b>: Sets the locale used for diagnostics; currently, 292: * only locales using the English language are supported. 293: * @param locale The locale for which diagnostics will be generated 294: */ 295: public void setLocale(Locale locale) 296: throws SAXException 297: { 298: aelfred2.setLocale(locale); 299: } 300: 301: /** 302: * <b>SAX1</b>: Preferred API to parse an XML document, using a 303: * system identifier (URI). 304: */ 305: public void parse(String systemId) 306: throws SAXException, IOException 307: { 308: parse(new InputSource(systemId)); 309: } 310: 311: /** 312: * <b>SAX1</b>: Underlying API to parse an XML document, used 313: * directly when no URI is available. When this is invoked, 314: * and the parser is set to validate, some features will be 315: * automatically reset to appropriate values: for reporting 316: * namespace prefixes, and incorporating external entities. 317: * 318: * @param source The XML input source. 319: * 320: * @exception IllegalStateException if called mid-parse 321: * @exception SAXException The handlers may throw any SAXException, 322: * and the parser normally throws SAXParseException objects. 323: * @exception IOException IOExceptions are normally through through 324: * the parser if there are problems reading the source document. 325: */ 326: public void parse(InputSource source) 327: throws SAXException, IOException 328: { 329: EventFilter next; 330: boolean nsdecls; 331: 332: synchronized (aelfred2) 333: { 334: if (active) 335: { 336: throw new IllegalStateException("already parsing"); 337: } 338: active = true; 339: } 340: 341: // set up the output pipeline 342: if (isValidating) 343: { 344: forceValidating(); 345: next = new ValidationConsumer(filter); 346: } 347: else 348: { 349: next = filter; 350: } 351: 352: // connect pipeline and error handler 353: // don't let _this_ call to bind() affect xmlns* attributes 354: nsdecls = aelfred2.getFeature(SAXDriver.FEATURE + "namespace-prefixes"); 355: EventFilter.bind(aelfred2, next); 356: if (!nsdecls) 357: { 358: aelfred2.setFeature(SAXDriver.FEATURE + "namespace-prefixes", 359: false); 360: } 361: 362: // parse, clean up 363: try 364: { 365: aelfred2.parse(source); 366: } 367: finally 368: { 369: active = false; 370: } 371: } 372: 373: }