KickJava   Java API By Example, From Geeks To Geeks.

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


1 package net.sf.saxon.event;
2 import net.sf.saxon.Configuration;
3 import net.sf.saxon.om.Name;
4 import net.sf.saxon.om.NamePool;
5 import net.sf.saxon.om.XMLChar;
6 import net.sf.saxon.om.NodeInfo;
7 import net.sf.saxon.style.StandardNames;
8 import net.sf.saxon.tinytree.CharSlice;
9 import net.sf.saxon.trans.XPathException;
10 import net.sf.saxon.type.ValidationException;
11 import org.xml.sax.*;
12 import org.xml.sax.ext.LexicalHandler JavaDoc;
13
14 import javax.xml.transform.TransformerException JavaDoc;
15 import java.net.URI JavaDoc;
16 import java.net.URISyntaxException JavaDoc;
17 import java.util.HashMap JavaDoc;
18
19 /**
20   * ReceivingContentHandler is a glue class that provides a standard SAX ContentHandler
21   * interface to a Saxon Receiver. To achieve this it needs to map names supplied
22   * as strings to numeric name codes, for which purpose it needs access to a name
23   * pool. The class also performs the function of assembling adjacent text nodes.
24   * <p>The class was previously named ContentEmitter.</p>
25   * @author Michael H. Kay
26   */

27
28 public class ReceivingContentHandler
29         implements ContentHandler, LexicalHandler JavaDoc, DTDHandler, SaxonLocator
30 {
31     private NamePool pool;
32     private PipelineConfiguration pipe;
33     private Receiver receiver;
34     private boolean inDTD = false; // true while processing the DTD
35
private Locator locator; // a SAX Locator
36

37     // buffer for accumulating character data, until the next markup event is received
38

39     private char[] buffer = new char[4096];
40     private int used = 0;
41     private CharSlice slice = new CharSlice(buffer, 0, 0);
42
43     // array for accumulating namespace information
44

45     private int[] namespaces = new int[50];
46     private int namespacesUsed = 0;
47
48     //private boolean isStyleSheet = false;
49

50     /**
51     * create a ReceivingContentHandler and initialise variables
52     */

53
54     public ReceivingContentHandler() {
55     }
56
57     public void setReceiver(Receiver e) {
58         receiver = e;
59     }
60
61     public void setPipelineConfiguration(PipelineConfiguration pipe) {
62         this.pipe = pipe;
63         pipe.setLocationProvider(this);
64         this.pool = pipe.getConfiguration().getNamePool();
65
66     }
67
68     public PipelineConfiguration getPipelineConfiguration() {
69         return pipe;
70     }
71
72     public Configuration getConfiguration() {
73         return pipe.getConfiguration();
74     }
75
76     /**
77     * Callback interface for SAX: not for application use
78     */

79
80     public void startDocument () throws SAXException {
81         // System.err.println("ReceivingContentHandler#startDocument");
82
try {
83             used = 0;
84             namespacesUsed = 0;
85             pipe.setLocationProvider(this);
86             receiver.setPipelineConfiguration(pipe);
87             receiver.open();
88             receiver.startDocument(0);
89         } catch (XPathException err) {
90             throw new SAXException(err);
91         }
92     }
93
94     /**
95     * Callback interface for SAX: not for application use
96     */

97
98     public void endDocument () throws SAXException {
99         try {
100             flush();
101             receiver.endDocument();
102             receiver.close();
103         } catch (ValidationException err) {
104             err.setLocator(locator);
105             throw new SAXException(err);
106         } catch (XPathException err) {
107             throw new SAXException(err);
108         }
109     }
110
111     /**
112     * Callback interface for SAX: not for application use
113     */

114
115     public void setDocumentLocator (Locator locator) {
116         this.locator = locator;
117     }
118
119     /**
120     * Callback interface for SAX: not for application use
121     */

122
123     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException {
124         //System.err.println("StartPrefixMapping " + prefix + "=" + uri);
125
if (namespacesUsed >= namespaces.length) {
126             int[] n2 = new int[namespacesUsed * 2];
127             System.arraycopy(namespaces, 0, n2, 0, namespacesUsed);
128             namespaces = n2;
129         }
130         namespaces[namespacesUsed++] = pool.allocateNamespaceCode(prefix, uri);
131     }
132
133     /**
134     * Callback interface for SAX: not for application use
135     */

136
137     public void endPrefixMapping(String JavaDoc prefix) throws SAXException {}
138
139     /**
140     * Callback interface for SAX: not for application use
141     */

142     public void startElement (String JavaDoc uri, String JavaDoc localname, String JavaDoc rawname, Attributes atts)
143     throws SAXException
144     {
145         //System.err.println("ReceivingContentHandler#startElement " + uri + "," + localname + "," + rawname + " at line " + locator.getLineNumber());
146
//for (int a=0; a<atts.getLength(); a++) {
147
// System.err.println(" Attribute " + atts.getURI(a) + "/" + atts.getLocalName(a) + "/" + atts.getQName(a));
148
//}
149
try {
150             flush();
151
152             int nameCode = getNameCode(uri, localname, rawname);
153             receiver.startElement(nameCode, StandardNames.XDT_UNTYPED, 0, 0);
154
155             for (int n=0; n<namespacesUsed; n++) {
156                 receiver.namespace(namespaces[n], 0);
157             }
158
159
160             for (int a=0; a<atts.getLength(); a++) {
161                 int properties = 0;
162                 int attCode = getNameCode(atts.getURI(a), atts.getLocalName(a), atts.getQName(a));
163                 String JavaDoc type = atts.getType(a);
164                 int typeCode = StandardNames.XDT_UNTYPED_ATOMIC;
165                 if (getConfiguration().isRetainDTDAttributeTypes()) {
166                     if (type.equals("CDATA")) {
167                         // no action
168
} else if (type.equals("ID")) {
169                         typeCode = StandardNames.XS_ID;
170                     } else if (type.equals("IDREF")) {
171                         typeCode = StandardNames.XS_IDREF;
172                     } else if (type.equals("IDREFS")) {
173                         typeCode = StandardNames.XS_IDREFS;
174                     } else if (type.equals("NMTOKEN")) {
175                         typeCode = StandardNames.XS_NMTOKEN;
176                     } else if (type.equals("NMTOKENS")) {
177                         typeCode = StandardNames.XS_NMTOKENS;
178                     } else if (type.equals("ENTITY")) {
179                         typeCode = StandardNames.XS_ENTITY;
180                     } else if (type.equals("ENTITIES")) {
181                         typeCode = StandardNames.XS_ENTITIES;
182                     }
183                 } else {
184                     if (type.equals("ID")) {
185                         typeCode = StandardNames.XS_ID | NodeInfo.IS_DTD_TYPE;
186                     } else if (type.equals("IDREF")) {
187                         typeCode = StandardNames.XS_IDREF | NodeInfo.IS_DTD_TYPE;
188                     } else if (type.equals("IDREFS")) {
189                         typeCode = StandardNames.XS_IDREFS | NodeInfo.IS_DTD_TYPE;
190                     }
191                 }
192
193                 receiver.attribute(attCode, typeCode, atts.getValue(a), 0, properties);
194             }
195
196             receiver.startContent();
197
198             namespacesUsed = 0;
199 // } catch (ValidationException err) {
200
// err.setLocator(locator);
201
// throw new SAXException(err);
202
} catch (XPathException err) {
203             throw new SAXException(err);
204         }
205     }
206
207     private int getNameCode(String JavaDoc uri, String JavaDoc localname, String JavaDoc rawname) throws SAXException {
208         // System.err.println("URI=" + uri + " local=" + " raw=" + rawname);
209
// The XML parser isn't required to report the rawname (qname), though all known parsers do.
210
// If none is provided, we give up
211
if (rawname.equals("")) {
212             throw new SAXException("Saxon requires an XML parser that reports the QName of each element");
213         }
214         // It's also possible (especially when using a TransformerHandler) that the parser
215
// has been configured to report the QName rather than the localname+URI
216
if (localname.equals("")) {
217             throw new SAXException("Parser configuration problem: namespace reporting is not enabled");
218         }
219
220         // Following code maintains a local cache to remember all the namecodes that have been
221
// allocated, which reduces contention on the NamePool. It also avoid parsing the lexical QName
222
// when the same name is used repeatedly. We also get a tiny improvement by avoiding the first hash
223
// table lookup for names in the null namespace.
224

225         HashMap JavaDoc map2 = (uri.equals("") ? noNamespaceMap : (HashMap JavaDoc)cache.get(uri));
226         if (map2 == null) {
227             map2 = new HashMap JavaDoc(50);
228             cache.put(uri, map2);
229             if (uri.equals("")) {
230                 noNamespaceMap = map2;
231             }
232         }
233
234         Integer JavaDoc n = (Integer JavaDoc)map2.get(rawname);
235         if (n == null) {
236             String JavaDoc prefix = Name.getPrefix(rawname);
237             int nc = pool.allocate(prefix, uri, localname);
238             n = new Integer JavaDoc(nc);
239             map2.put(rawname, n);
240             return nc;
241         } else {
242             return n.intValue();
243         }
244
245     }
246
247     /**
248      * A local cache is used to avoid allocating namecodes for the same name more than once.
249      * This reduces contention on the NamePool. This is a two-level hashmap: the first level
250      * has the namespace URI as its key, and returns a HashMap which maps lexical QNames to integer
251      * namecodes.
252      */

253
254     private HashMap JavaDoc cache = new HashMap JavaDoc(10);
255     private HashMap JavaDoc noNamespaceMap;
256
257
258     /**
259     * Callback interface for SAX: not for application use
260     */

261
262     public void endElement (String JavaDoc uri, String JavaDoc localname, String JavaDoc rawname) throws SAXException {
263         //System.err.println("ReceivingContentHandler#End element " + rawname);
264
try {
265             flush();
266             receiver.endElement();
267         } catch (ValidationException err) {
268             err.setLocator(locator);
269             if (!err.hasBeenReported()) {
270                 try {
271                     pipe.getErrorListener().fatalError(err);
272                 } catch (TransformerException JavaDoc e) {
273                     //
274
}
275             }
276             throw new SAXException(err);
277         } catch (XPathException err) {
278             throw new SAXException(err);
279         }
280     }
281
282     /**
283     * Callback interface for SAX: not for application use
284     */

285
286     public void characters (char ch[], int start, int length) throws SAXException {
287         // System.err.println("characters (" + length + ")");
288
// need to concatenate chunks of text before we can decide whether a node is all-white
289

290         while (used + length > buffer.length) {
291             char[] newbuffer = new char[buffer.length*2];
292             System.arraycopy(buffer, 0, newbuffer, 0, used);
293             buffer = newbuffer;
294             slice = new CharSlice(buffer, 0, 0);
295         }
296         System.arraycopy(ch, start, buffer, used, length);
297         used += length;
298     }
299
300     /**
301     * Callback interface for SAX: not for application use
302     */

303
304     public void ignorableWhitespace (char ch[], int start, int length) throws SAXException {
305         characters(ch, start, length);
306     }
307
308     /**
309     * Callback interface for SAX: not for application use<BR>
310     */

311
312     public void processingInstruction (String JavaDoc name, String JavaDoc remainder) throws SAXException {
313         try {
314             flush();
315             if (!inDTD) {
316                 if (name==null) {
317                     // trick used by some SAX1 parsers to notify a comment
318
comment(remainder.toCharArray(), 0, remainder.length());
319                 } else {
320                     // some parsers allow through PI names containing colons
321
if (!XMLChar.isValidNCName(name)) {
322                         throw new SAXException("Invalid processing instruction name (" + name + ')');
323                     }
324                     receiver.processingInstruction(name, remainder, 0, 0);
325                 }
326             }
327         } catch (XPathException err) {
328             throw new SAXException(err);
329         }
330     }
331
332     /**
333     * Callback interface for SAX (part of LexicalHandler interface): not for application use
334     */

335
336     public void comment (char ch[], int start, int length) throws SAXException {
337         try {
338             flush();
339             if (!inDTD) {
340                 receiver.comment(new CharSlice(ch, start, length), 0, 0);
341             }
342         } catch (XPathException err) {
343             throw new SAXException(err);
344         }
345     }
346
347     /**
348     * Flush buffer for accumulated character data, suppressing white space if appropriate
349     */

350
351     private void flush() throws XPathException {
352         if (used > 0) {
353             slice.setLength(used);
354             receiver.characters(slice, 0, 0);
355             used = 0;
356         }
357     }
358
359     public void skippedEntity(String JavaDoc name) throws SAXException {}
360
361     // No-op methods to satisfy lexical handler interface
362

363     /**
364     * Register the start of the DTD. Comments in the DTD are skipped because they
365     * are not part of the XPath data model
366     */

367
368     public void startDTD (String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) throws SAXException {
369         inDTD = true;
370     }
371
372     /**
373     * Register the end of the DTD. Comments in the DTD are skipped because they
374     * are not part of the XPath data model
375     */

376
377     public void endDTD () throws SAXException {
378         inDTD = false;
379     }
380
381     public void startEntity (String JavaDoc name) throws SAXException {};
382
383     public void endEntity (String JavaDoc name) throws SAXException {};
384
385     public void startCDATA () throws SAXException {};
386
387     public void endCDATA () throws SAXException {};
388
389     //////////////////////////////////////////////////////////////////////////////
390
// Implement DTDHandler interface
391
//////////////////////////////////////////////////////////////////////////////
392

393
394     public void notationDecl( String JavaDoc name,
395                                     String JavaDoc publicId,
396                                     String JavaDoc systemId) throws SAXException
397     {}
398
399
400     public void unparsedEntityDecl( String JavaDoc name,
401                                     String JavaDoc publicId,
402                                     String JavaDoc systemId,
403                                     String JavaDoc notationName) throws SAXException
404     {
405         //System.err.println("Unparsed entity " + name + "=" + systemId);
406

407         // Some SAX parsers report the systemId as written. We need to turn it into
408
// an absolute URL.
409

410         String JavaDoc uri = systemId;
411         if (locator!=null) {
412             try {
413                 String JavaDoc baseURI = locator.getSystemId();
414                 URI JavaDoc absoluteURI = new URI JavaDoc(baseURI).resolve(systemId);
415                 uri = absoluteURI.toString();
416             } catch (URISyntaxException JavaDoc err) {}
417         }
418         try {
419             receiver.setUnparsedEntity(name, uri, publicId);
420         } catch (XPathException err) {
421             throw new SAXException(err);
422         }
423     }
424
425     // implement the SaxonLocator interface. This is needed to bridge a SAX Locator to a JAXP SourceLocator
426

427     /**
428      * Return the public identifier for the current document event.
429      * @return A string containing the system identifier, or
430      * null if none is available.
431      */

432
433     public String JavaDoc getSystemId() {
434         if (locator == null) {
435             return null;
436         } else {
437             return locator.getSystemId();
438         }
439     }
440
441     /**
442      * Return the public identifier for the current document event.
443      * @return A string containing the public identifier, or
444      * null if none is available.
445      */

446
447     public String JavaDoc getPublicId() {
448         if (locator==null) {
449             return null;
450         } else {
451             return locator.getPublicId();
452         }
453     }
454
455     /**
456      * Return the line number where the current document event ends.
457      * @return The line number, or -1 if none is available.
458      */

459
460     public int getLineNumber() {
461         if (locator==null) {
462             return -1;
463         } else {
464             return locator.getLineNumber();
465         }
466     }
467
468     /**
469      * Return the character position where the current document event ends.
470      * @return The column number, or -1 if none is available.
471      */

472
473     public int getColumnNumber() {
474         if (locator==null) {
475             return -1;
476         } else {
477             return locator.getColumnNumber();
478         }
479     }
480
481     public String JavaDoc getSystemId(int locationId) {
482         return getSystemId();
483     }
484
485     public int getLineNumber(int locationId) {
486         return getLineNumber();
487     }
488
489 } // end of class ReceivingContentHandler
490

491 //
492
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
493
// you may not use this file except in compliance with the License. You may obtain a copy of the
494
// License at http://www.mozilla.org/MPL/
495
//
496
// Software distributed under the License is distributed on an "AS IS" basis,
497
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
498
// See the License for the specific language governing rights and limitations under the License.
499
//
500
// The Original Code is: all this file.
501
//
502
// The Initial Developer of the Original Code is Michael H. Kay.
503
//
504
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
505
//
506
// Contributor(s): none.
507
//
508
Popular Tags