70 |
* |
* |
71 |
* @author David Brownell |
* @author David Brownell |
72 |
*/ |
*/ |
73 |
public final class XmlReader implements XMLReader |
public final class XmlReader |
74 |
|
implements XMLReader |
75 |
{ |
{ |
|
private SAXDriver aelfred2 = new SAXDriver (); |
|
|
private EventFilter filter = new EventFilter (); |
|
|
private boolean isValidating; |
|
|
private boolean active; |
|
76 |
|
|
77 |
|
static class FatalErrorHandler |
78 |
/** Constructs a SAX Parser. */ |
extends DefaultHandler2 |
79 |
public XmlReader () |
{ |
80 |
{ } |
|
81 |
|
public void error(SAXParseException e) |
82 |
/** |
throws SAXException |
83 |
* Constructs a SAX Parser, optionally treating validity errors |
{ |
84 |
* as if they were fatal errors. |
throw e; |
85 |
*/ |
} |
86 |
public XmlReader (boolean invalidIsFatal) |
|
87 |
{ |
} |
88 |
if (invalidIsFatal) |
|
89 |
setErrorHandler (new DefaultHandler2 () { |
private SAXDriver aelfred2 = new SAXDriver(); |
90 |
public void error (SAXParseException e) |
private EventFilter filter = new EventFilter(); |
91 |
throws SAXException |
private boolean isValidating; |
92 |
{ throw e; } |
private boolean active; |
93 |
}); |
|
94 |
} |
/** |
95 |
|
* Constructs a SAX Parser. |
96 |
/** |
*/ |
97 |
* <b>SAX2</b>: Returns the object used to report the logical |
public XmlReader() |
98 |
* content of an XML document. |
{ |
99 |
*/ |
} |
100 |
public ContentHandler getContentHandler () |
|
101 |
{ return filter.getContentHandler (); } |
/** |
102 |
|
* Constructs a SAX Parser, optionally treating validity errors |
103 |
/** |
* as if they were fatal errors. |
104 |
* <b>SAX2</b>: Assigns the object used to report the logical |
*/ |
105 |
* content of an XML document. |
public XmlReader(boolean invalidIsFatal) |
106 |
* @exception IllegalStateException if called mid-parse |
{ |
107 |
*/ |
if (invalidIsFatal) |
108 |
public void setContentHandler (ContentHandler handler) |
{ |
109 |
{ |
setErrorHandler(new FatalErrorHandler()); |
110 |
if (active) |
} |
111 |
throw new IllegalStateException ("already parsing"); |
} |
112 |
filter.setContentHandler (handler); |
|
113 |
} |
/** |
114 |
|
* <b>SAX2</b>: Returns the object used to report the logical |
115 |
/** |
* content of an XML document. |
116 |
* <b>SAX2</b>: Returns the object used to process declarations related |
*/ |
117 |
* to notations and unparsed entities. |
public ContentHandler getContentHandler() |
118 |
*/ |
{ |
119 |
public DTDHandler getDTDHandler () |
return filter.getContentHandler(); |
120 |
{ return filter.getDTDHandler (); } |
} |
121 |
|
|
122 |
/** |
/** |
123 |
* <b>SAX1</b> Assigns DTD handler |
* <b>SAX2</b>: Assigns the object used to report the logical |
124 |
* @exception IllegalStateException if called mid-parse |
* content of an XML document. |
125 |
*/ |
* @exception IllegalStateException if called mid-parse |
126 |
public void setDTDHandler (DTDHandler handler) |
*/ |
127 |
{ |
public void setContentHandler(ContentHandler handler) |
128 |
if (active) |
{ |
129 |
throw new IllegalStateException ("already parsing"); |
if (active) |
130 |
filter.setDTDHandler (handler); |
{ |
131 |
} |
throw new IllegalStateException("already parsing"); |
132 |
|
} |
133 |
/** |
filter.setContentHandler(handler); |
134 |
* <b>SAX2</b>: Returns the object used when resolving external |
} |
135 |
* entities during parsing (both general and parameter entities). |
|
136 |
*/ |
/** |
137 |
public EntityResolver getEntityResolver () |
* <b>SAX2</b>: Returns the object used to process declarations related |
138 |
{ return aelfred2.getEntityResolver (); } |
* to notations and unparsed entities. |
139 |
|
*/ |
140 |
/** <b>SAX1</b> Assigns parser's entity resolver */ |
public DTDHandler getDTDHandler() |
141 |
public void setEntityResolver (EntityResolver handler) |
{ |
142 |
{ aelfred2.setEntityResolver (handler); } |
return filter.getDTDHandler(); |
143 |
|
} |
144 |
/** |
|
145 |
* <b>SAX2</b>: Returns the object used to receive callbacks for XML |
/** |
146 |
* errors of all levels (fatal, nonfatal, warning); this is never null; |
* <b>SAX1</b> Assigns DTD handler |
147 |
*/ |
* @exception IllegalStateException if called mid-parse |
148 |
public ErrorHandler getErrorHandler () |
*/ |
149 |
{ return aelfred2.getErrorHandler (); } |
public void setDTDHandler(DTDHandler handler) |
150 |
|
{ |
151 |
/** |
if (active) |
152 |
* <b>SAX1</b> Assigns error handler |
{ |
153 |
* @exception IllegalStateException if called mid-parse |
throw new IllegalStateException("already parsing"); |
154 |
*/ |
} |
155 |
public void setErrorHandler (ErrorHandler handler) |
filter.setDTDHandler(handler); |
156 |
{ |
} |
157 |
if (active) |
|
158 |
throw new IllegalStateException ("already parsing"); |
/** |
159 |
aelfred2.setErrorHandler (handler); |
* <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 |
* <b>SAX2</b>: Assigns the specified property. |
{ |
164 |
* @exception IllegalStateException if called mid-parse |
return aelfred2.getEntityResolver(); |
165 |
*/ |
} |
166 |
public void setProperty (String propertyId, Object value) |
|
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 |
throws SAXNotRecognizedException, SAXNotSupportedException |
203 |
{ |
{ |
204 |
if (active) |
if (active) |
205 |
throw new IllegalStateException ("already parsing"); |
{ |
206 |
if (getProperty (propertyId) != value) |
throw new IllegalStateException("already parsing"); |
207 |
filter.setProperty (propertyId, value); |
} |
208 |
} |
if (getProperty(propertyId) != value) |
209 |
|
{ |
210 |
/** |
filter.setProperty(propertyId, value); |
211 |
* <b>SAX2</b>: Returns the specified property. |
} |
212 |
*/ |
} |
213 |
public Object getProperty (String propertyId) |
|
214 |
|
/** |
215 |
|
* <b>SAX2</b>: Returns the specified property. |
216 |
|
*/ |
217 |
|
public Object getProperty(String propertyId) |
218 |
throws SAXNotRecognizedException |
throws SAXNotRecognizedException |
219 |
{ |
{ |
220 |
if ((SAXDriver.PROPERTY + "declaration-handler") |
if ((SAXDriver.PROPERTY + "declaration-handler").equals(propertyId) |
221 |
.equals (propertyId) |
|| (SAXDriver.PROPERTY + "lexical-handler").equals(propertyId)) |
222 |
|| (SAXDriver.PROPERTY + "lexical-handler") |
{ |
223 |
.equals (propertyId)) |
return filter.getProperty(propertyId); |
224 |
return filter.getProperty (propertyId); |
} |
225 |
throw new SAXNotRecognizedException (propertyId); |
throw new SAXNotRecognizedException(propertyId); |
226 |
} |
} |
227 |
|
|
228 |
private void forceValidating () |
private void forceValidating() |
229 |
throws SAXNotRecognizedException, SAXNotSupportedException |
throws SAXNotRecognizedException, SAXNotSupportedException |
230 |
{ |
{ |
231 |
aelfred2.setFeature ( |
aelfred2.setFeature(SAXDriver.FEATURE + "namespace-prefixes", |
232 |
SAXDriver.FEATURE + "namespace-prefixes", |
true); |
233 |
true); |
aelfred2.setFeature(SAXDriver.FEATURE + "external-general-entities", |
234 |
aelfred2.setFeature ( |
true); |
235 |
SAXDriver.FEATURE + "external-general-entities", |
aelfred2.setFeature(SAXDriver.FEATURE + "external-parameter-entities", |
236 |
true); |
true); |
237 |
aelfred2.setFeature ( |
} |
238 |
SAXDriver.FEATURE + "external-parameter-entities", |
|
239 |
true); |
/** |
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 |
* <b>SAX2</b>: Sets the state of features supported in this parser. |
*/ |
244 |
* Note that this parser requires reporting of namespace prefixes when |
public void setFeature(String featureId, boolean state) |
|
* validating. |
|
|
*/ |
|
|
public void setFeature (String featureId, boolean state) |
|
245 |
throws SAXNotRecognizedException, SAXNotSupportedException |
throws SAXNotRecognizedException, SAXNotSupportedException |
246 |
{ |
{ |
247 |
boolean value = getFeature (featureId); |
boolean value = getFeature(featureId); |
|
|
|
|
if (state == value) |
|
|
return; |
|
248 |
|
|
249 |
if ((SAXDriver.FEATURE + "validation").equals (featureId)) { |
if (state == value) |
250 |
if (active) |
{ |
251 |
throw new SAXNotSupportedException ("already parsing"); |
return; |
252 |
if (state) |
} |
253 |
forceValidating (); |
|
254 |
isValidating = state; |
if ((SAXDriver.FEATURE + "validation").equals(featureId)) |
255 |
} else |
{ |
256 |
aelfred2.setFeature (featureId, state); |
if (active) |
257 |
} |
{ |
258 |
|
throw new SAXNotSupportedException("already parsing"); |
259 |
/** |
} |
260 |
* <b>SAX2</b>: Tells whether this parser supports the specified feature. |
if (state) |
261 |
* At this time, this directly parallels the underlying SAXDriver, |
{ |
262 |
* except that validation is optionally supported. |
forceValidating(); |
263 |
* |
} |
264 |
* @see SAXDriver |
isValidating = state; |
265 |
*/ |
} |
266 |
public boolean getFeature (String featureId) |
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 |
throws SAXNotRecognizedException, SAXNotSupportedException |
281 |
{ |
{ |
282 |
if ((SAXDriver.FEATURE + "validation").equals (featureId)) |
if ((SAXDriver.FEATURE + "validation").equals(featureId)) |
283 |
return isValidating; |
{ |
284 |
|
return isValidating; |
285 |
return aelfred2.getFeature (featureId); |
} |
286 |
} |
|
287 |
|
return aelfred2.getFeature(featureId); |
288 |
/** |
} |
289 |
* <b>SAX1</b>: Sets the locale used for diagnostics; currently, |
|
290 |
* only locales using the English language are supported. |
/** |
291 |
* @param locale The locale for which diagnostics will be generated |
* <b>SAX1</b>: Sets the locale used for diagnostics; currently, |
292 |
*/ |
* only locales using the English language are supported. |
293 |
public void setLocale (Locale locale) |
* @param locale The locale for which diagnostics will be generated |
294 |
|
*/ |
295 |
|
public void setLocale(Locale locale) |
296 |
throws SAXException |
throws SAXException |
297 |
{ aelfred2.setLocale (locale); } |
{ |
298 |
|
aelfred2.setLocale(locale); |
299 |
/** |
} |
300 |
* <b>SAX1</b>: Preferred API to parse an XML document, using a |
|
301 |
* system identifier (URI). |
/** |
302 |
|
* <b>SAX1</b>: Preferred API to parse an XML document, using a |
303 |
|
* system identifier (URI). |
304 |
*/ |
*/ |
305 |
public void parse (String systemId) |
public void parse(String systemId) |
306 |
throws SAXException, IOException |
throws SAXException, IOException |
307 |
{ |
{ |
308 |
parse (new InputSource (systemId)); |
parse(new InputSource(systemId)); |
309 |
} |
} |
310 |
|
|
311 |
/** |
/** |
312 |
* <b>SAX1</b>: Underlying API to parse an XML document, used |
* <b>SAX1</b>: Underlying API to parse an XML document, used |
313 |
* directly when no URI is available. When this is invoked, |
* directly when no URI is available. When this is invoked, |
314 |
* and the parser is set to validate, some features will be |
* and the parser is set to validate, some features will be |
315 |
* automatically reset to appropriate values: for reporting |
* automatically reset to appropriate values: for reporting |
316 |
* namespace prefixes, and incorporating external entities. |
* namespace prefixes, and incorporating external entities. |
317 |
* |
* |
318 |
* @param source The XML input source. |
* @param source The XML input source. |
319 |
* |
* |
320 |
* @exception IllegalStateException if called mid-parse |
* @exception IllegalStateException if called mid-parse |
321 |
* @exception SAXException The handlers may throw any SAXException, |
* @exception SAXException The handlers may throw any SAXException, |
322 |
* and the parser normally throws SAXParseException objects. |
* and the parser normally throws SAXParseException objects. |
323 |
* @exception IOException IOExceptions are normally through through |
* @exception IOException IOExceptions are normally through through |
324 |
* the parser if there are problems reading the source document. |
* the parser if there are problems reading the source document. |
325 |
*/ |
*/ |
326 |
public void parse (InputSource source) |
public void parse(InputSource source) |
327 |
throws SAXException, IOException |
throws SAXException, IOException |
328 |
{ |
{ |
329 |
EventFilter next; |
EventFilter next; |
330 |
boolean nsdecls; |
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 |
|
|
|
synchronized (aelfred2) { |
|
|
if (active) |
|
|
throw new IllegalStateException ("already parsing"); |
|
|
active = true; |
|
|
} |
|
|
|
|
|
// set up the output pipeline |
|
|
if (isValidating) { |
|
|
forceValidating (); |
|
|
next = new ValidationConsumer (filter); |
|
|
} else |
|
|
next = filter; |
|
|
|
|
|
// connect pipeline and error handler |
|
|
// don't let _this_ call to bind() affect xmlns* attributes |
|
|
nsdecls = aelfred2.getFeature ( |
|
|
SAXDriver.FEATURE + "namespace-prefixes"); |
|
|
EventFilter.bind (aelfred2, next); |
|
|
if (!nsdecls) |
|
|
aelfred2.setFeature ( |
|
|
SAXDriver.FEATURE + "namespace-prefixes", |
|
|
false); |
|
|
|
|
|
// parse, clean up |
|
|
try { |
|
|
aelfred2.parse (source); |
|
|
} finally { |
|
|
active = false; |
|
|
} |
|
|
} |
|
373 |
} |
} |
374 |
|
|