KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > event > ContentHandlerProxy


1 package net.sf.saxon.event;
2
3 import net.sf.saxon.om.AttributeCollectionImpl;
4 import net.sf.saxon.om.NamespaceConstant;
5 import net.sf.saxon.trans.DynamicError;
6 import net.sf.saxon.trans.SaxonErrorCode;
7 import net.sf.saxon.trans.XPathException;
8 import net.sf.saxon.type.SchemaException;
9 import net.sf.saxon.value.Whitespace;
10 import org.xml.sax.ContentHandler JavaDoc;
11 import org.xml.sax.Locator JavaDoc;
12 import org.xml.sax.SAXException JavaDoc;
13 import org.xml.sax.ext.LexicalHandler JavaDoc;
14
15 import javax.xml.transform.Result JavaDoc;
16 import java.util.Properties JavaDoc;
17 import java.util.Stack JavaDoc;
18
19 /**
20  * A ContentHandlerProxy is an Emitter that filters data before passing it to an
21  * underlying SAX2 ContentHandler. Relevant events (notably comments) can also be
22  * fed to a LexicalHandler.
23  * <p/>
24  * Note that in general the output passed to an Emitter
25  * corresponds to an External General Parsed Entity. A SAX2 ContentHandler only expects
26  * to deal with well-formed XML documents, so we only pass it the contents of the first
27  * element encountered, unless the saxon:require-well-formed output property is set to "no".
28  * </p><p>
29  * This ContentHandlerProxy provides no access to type information. For a ContentHandler that
30  * makes type information available, see {@link net.sf.saxon.dom.TypedContentHandler}
31  */

32
33 public class ContentHandlerProxy extends Emitter implements Locator JavaDoc {
34     protected ContentHandler JavaDoc handler;
35     protected LexicalHandler JavaDoc lexicalHandler;
36     private LocationProvider locationProvider;
37     private int depth = 0;
38     private boolean requireWellFormed = false;
39     private boolean undeclareNamespaces = false;
40     private Stack JavaDoc elementStack = new Stack JavaDoc();
41     private Stack JavaDoc namespaceStack = new Stack JavaDoc();
42     protected AttributeCollectionImpl pendingAttributes;
43     private int pendingElement = -1;
44     private int currentLocationId;
45
46     private static final String JavaDoc marker = "##";
47
48     /**
49      * Set the underlying content handler. This call is mandatory before using the Emitter.
50      */

51
52     public void setUnderlyingContentHandler(ContentHandler JavaDoc handler) {
53         this.handler = handler;
54         if (handler instanceof LexicalHandler JavaDoc) {
55             this.lexicalHandler = (LexicalHandler JavaDoc)handler;
56         }
57     }
58
59     /**
60      * Get the underlying content handler
61      */

62
63     public ContentHandler JavaDoc getUnderlyingContentHandler() {
64         return handler;
65     }
66
67     /**
68      * Set the Lexical Handler to be used. If called, this must be called AFTER
69      * setUnderlyingContentHandler()
70      */

71
72     public void setLexicalHandler(LexicalHandler JavaDoc handler) {
73         this.lexicalHandler = handler;
74     }
75
76     /**
77     * Set the pipeline configuration
78     */

79
80     public void setPipelineConfiguration(PipelineConfiguration config) {
81         super.setPipelineConfiguration(config);
82         this.locationProvider = config.getLocationProvider();
83     }
84
85     /**
86      * Set the output details.
87      */

88
89     public void setOutputProperties(Properties JavaDoc details) throws XPathException {
90         String JavaDoc prop = details.getProperty(SaxonOutputKeys.REQUIRE_WELL_FORMED);
91         if (prop != null) {
92             requireWellFormed = prop.equals("yes");
93         }
94         prop = details.getProperty(SaxonOutputKeys.UNDECLARE_PREFIXES);
95         if (prop != null) {
96             undeclareNamespaces = prop.equals("yes");
97         }
98         super.setOutputProperties(details);
99     }
100
101     /**
102      * Determine whether the content handler can handle a stream of events that is merely
103      * well-balanced, or whether it can only handle a well-formed sequence.
104      */

105
106     public boolean isRequireWellFormed() {
107         return requireWellFormed;
108     }
109
110     /**
111      * Indicate whether the content handler can handle a stream of events that is merely
112      * well-balanced, or whether it can only handle a well-formed sequence.
113      */

114
115     public void setRequireWellFormed(boolean wellFormed) {
116         requireWellFormed = wellFormed;
117     }
118
119     /**
120      * Determine whether namespace undeclaration events (for a non-null prefix) should be notified.
121      * The default is no, because some ContentHandlers (e.g. JDOM) can't cope with them.
122      *
123      * @return true if namespace undeclarations (xmlns:p="") are output
124      */

125
126     public boolean isUndeclareNamespaces() {
127         return undeclareNamespaces;
128     }
129
130     /**
131      * Determine whether namespace undeclaration events (for a non-null prefix) should be notified.
132      * The default is no, because some ContentHandlers (e.g. JDOM) can't cope with them.
133      *
134      * @param undeclareNamespaces true if namespace undeclarations (xmlns:p="") are to be output
135      */

136
137     public void setUndeclareNamespaces(boolean undeclareNamespaces) {
138         this.undeclareNamespaces = undeclareNamespaces;
139     }
140
141     /**
142      * Start of document
143      */

144
145     public void open() throws XPathException {
146         pendingAttributes = new AttributeCollectionImpl(getPipelineConfiguration().getConfiguration().getNamePool());
147         if (handler == null) {
148             throw new DynamicError("ContentHandlerProxy.startDocument(): no underlying handler provided");
149         }
150         try {
151             locationProvider = getPipelineConfiguration().getLocationProvider();
152             pendingAttributes.setLocationProvider(locationProvider);
153             handler.setDocumentLocator(this);
154             handler.startDocument();
155         } catch (SAXException JavaDoc err) {
156             throw new DynamicError(err);
157         }
158         depth = 0;
159     }
160
161     /**
162      * End of document
163      */

164
165     public void close() throws XPathException {
166         try {
167             handler.endDocument();
168         } catch (SAXException JavaDoc err) {
169             throw new DynamicError(err);
170         }
171     }
172
173     /**
174      * Start of a document node.
175      */

176
177     public void startDocument(int properties) throws XPathException {
178     }
179
180     /**
181      * Notify the end of a document node
182      */

183
184     public void endDocument() throws XPathException {
185     }
186
187     /**
188      * Notify the start of an element
189      */

190
191     public void startElement(int nameCode, int typeCode, int locationId, int properties) throws XPathException {
192         depth++;
193         if (depth <= 0 && requireWellFormed) {
194             notifyNotWellFormed();
195         }
196         pendingElement = nameCode;
197         currentLocationId = locationId;
198         namespaceStack.push(marker);
199     }
200
201     /**
202      * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
203      * any children for the element.
204      */

205
206     public void namespace(int namespaceCode, int properties) throws XPathException {
207         if (namespaceCode == NamespaceConstant.XML_NAMESPACE_CODE) {
208             return;
209         }
210         String JavaDoc prefix = namePool.getPrefixFromNamespaceCode(namespaceCode);
211         String JavaDoc uri = namePool.getURIFromNamespaceCode(namespaceCode);
212         if ((!undeclareNamespaces) && "".equals(uri) && !("".equals(prefix))) {
213             return;
214         }
215         try {
216             handler.startPrefixMapping(prefix, uri);
217             namespaceStack.push(prefix);
218         } catch (SAXException JavaDoc err) {
219             throw new DynamicError(err);
220         }
221     }
222
223     /**
224      * Notify an attribute. Attributes are notified after the startElement event, and before any
225      * children.
226      */

227
228     public void attribute(int nameCode, int typeCode, CharSequence JavaDoc value, int locationId, int properties)
229             throws XPathException {
230         int index = pendingAttributes.getIndexByFingerprint(nameCode & 0xfffff);
231         if (index < 0) {
232             pendingAttributes.addAttribute(nameCode, typeCode, value.toString(), locationId, properties);
233         } else {
234             pendingAttributes.setAttribute(index, nameCode, typeCode, value.toString(), locationId, properties);
235         }
236     }
237
238     /**
239      * Notify the start of the content, that is, the completion of all attributes and namespaces.
240      * Note that the initial receiver of output from XSLT instructions will not receive this event,
241      * it has to detect it itself. Note that this event is reported for every element even if it has
242      * no attributes, no namespaces, and no content.
243      */

244
245     public void startContent() throws XPathException {
246         try {
247             if (depth > 0 || !requireWellFormed) {
248                 String JavaDoc uri = namePool.getURI(pendingElement);
249                 String JavaDoc localName = namePool.getLocalName(pendingElement);
250                 String JavaDoc qname = namePool.getDisplayName(pendingElement);
251
252                 handler.startElement(uri,
253                         localName,
254                         qname,
255                         pendingAttributes);
256
257                 elementStack.push(uri);
258                 elementStack.push(localName);
259                 elementStack.push(qname);
260
261                 pendingAttributes.clear();
262                 pendingElement = -1;
263             }
264         } catch (SAXException JavaDoc err) {
265             Exception JavaDoc nested = err.getException();
266             if (nested instanceof XPathException) {
267                 throw (XPathException)nested;
268             } else if (nested instanceof SchemaException) {
269                 throw new DynamicError(nested);
270             } else {
271                 throw new DynamicError(err);
272             }
273         }
274     }
275
276     /**
277      * End of element
278      */

279
280     public void endElement() throws XPathException {
281         if (depth > 0) {
282             try {
283                 String JavaDoc qname = (String JavaDoc)elementStack.pop();
284                 String JavaDoc localName = (String JavaDoc)elementStack.pop();
285                 String JavaDoc uri = (String JavaDoc)elementStack.pop();
286                 handler.endElement(uri, localName, qname);
287             } catch (SAXException JavaDoc err) {
288                 throw new DynamicError(err);
289             }
290         }
291
292         while (true) {
293             String JavaDoc prefix = (String JavaDoc)namespaceStack.pop();
294             if (prefix.equals(marker)) {
295                 break;
296             }
297             try {
298                 handler.endPrefixMapping(prefix);
299             } catch (SAXException JavaDoc err) {
300                 throw new DynamicError(err);
301             }
302         }
303         depth--;
304         // if this was the outermost element, and well formed output is required
305
// then no further elements will be processed
306
if (requireWellFormed && depth <= 0) {
307             depth = Integer.MIN_VALUE; // crude but effective
308
}
309
310     }
311
312     /**
313      * Character data
314      */

315
316     public void characters(CharSequence JavaDoc chars, int locationId, int properties) throws XPathException {
317         currentLocationId = locationId;
318         boolean disable = ((properties & ReceiverOptions.DISABLE_ESCAPING) != 0);
319         if (disable) {
320             setEscaping(false);
321         }
322         try {
323             if (depth <= 0 && requireWellFormed) {
324                 if (Whitespace.isWhite(chars)) {
325                     // ignore top-level white space
326
} else {
327                     notifyNotWellFormed();
328                 }
329             } else {
330                 handler.characters(chars.toString().toCharArray(), 0, chars.length());
331             }
332         } catch (SAXException JavaDoc err) {
333             throw new DynamicError(err);
334         }
335         if (disable) {
336             setEscaping(true);
337         }
338     }
339
340     /**
341      * The following function is called when it is found that the output is not a well-formed document.
342      * Unless the ContentHandler accepts "balanced content", this is a fatal error.
343      */

344
345     protected void notifyNotWellFormed() throws XPathException {
346         DynamicError err = new DynamicError(
347                 "The result tree cannot be supplied to the ContentHandler because it is not well-formed XML");
348         err.setErrorCode(SaxonErrorCode.SXCH0002);
349         throw err;
350     }
351
352
353     /**
354      * Processing Instruction
355      */

356
357     public void processingInstruction(String JavaDoc target, CharSequence JavaDoc data, int locationId, int properties)
358             throws XPathException {
359         currentLocationId = locationId;
360         try {
361             handler.processingInstruction(target, data.toString());
362         } catch (SAXException JavaDoc err) {
363             throw new DynamicError(err);
364         }
365     }
366
367     /**
368      * Output a comment. Passes it on to the ContentHandler provided that the ContentHandler
369      * is also a SAX2 LexicalHandler.
370      */

371
372     public void comment(CharSequence JavaDoc chars, int locationId, int properties)
373             throws XPathException {
374         currentLocationId = locationId;
375         try {
376             if (lexicalHandler != null) {
377                 lexicalHandler.comment(chars.toString().toCharArray(), 0, chars.length());
378             }
379         } catch (SAXException JavaDoc err) {
380             throw new DynamicError(err);
381         }
382     }
383
384
385     /**
386      * Switch escaping on or off. This is called when the XSLT disable-output-escaping attribute
387      * is used to switch escaping on or off. It is not called for other sections of output (e.g.
388      * element names) where escaping is inappropriate. The action, as defined in JAXP 1.1, is
389      * to notify the request to the Content Handler using a processing instruction.
390      */

391
392     private void setEscaping(boolean escaping) {
393         try {
394             handler.processingInstruction((escaping ? Result.PI_ENABLE_OUTPUT_ESCAPING : PI_DISABLE_OUTPUT_ESCAPING),
395                     "");
396         } catch (SAXException JavaDoc err) {
397         }
398     }
399
400     ////////////////////////////////////////////////////////////////////
401
// Implementation of Locator interface
402
////////////////////////////////////////////////////////////////////
403

404     /**
405      * Get the Public ID
406      * @return null (always)
407      */

408
409     public String JavaDoc getPublicId() {
410         return null;
411     }
412
413     /**
414      * Get the System ID
415      * @return the system ID giving the location of the most recent event notified
416      */

417
418     public String JavaDoc getSystemId() {
419         return locationProvider.getSystemId(currentLocationId);
420     }
421
422     /**
423      * Get the line number
424      * @return the line number giving the location of the most recent event notified
425      */

426
427     public int getLineNumber() {
428         return locationProvider.getLineNumber(currentLocationId);
429     }
430
431     /**
432      * Get the column number
433      * @return -1 (always)
434      */

435
436     public int getColumnNumber() {
437         return -1;
438     }
439
440 }
441
442 //
443
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
444
// you may not use this file except in compliance with the License. You may obtain a copy of the
445
// License at http://www.mozilla.org/MPL/
446
//
447
// Software distributed under the License is distributed on an "AS IS" basis,
448
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
449
// See the License for the specific language governing rights and limitations under the License.
450
//
451
// The Original Code is: all this file.
452
//
453
// The Initial Developer of the Original Code is Michael H. Kay.
454
//
455
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
456
//
457
// Contributor(s): none.
458
//
459
Popular Tags