Source code: gnu/xml/dom/ls/SAXEventSink.java
1 /* SAXEventSink.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.dom.ls;
39
40 import java.util.Iterator;
41 import java.util.LinkedList;
42 import java.util.List;
43 import javax.xml.XMLConstants;
44 import org.w3c.dom.Attr;
45 import org.w3c.dom.DocumentType;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.Entity;
48 import org.w3c.dom.NamedNodeMap;
49 import org.w3c.dom.Node;
50 import org.w3c.dom.Text;
51 import org.xml.sax.Attributes;
52 import org.xml.sax.DTDHandler;
53 import org.xml.sax.Locator;
54 import org.xml.sax.SAXException;
55 import org.xml.sax.ext.Attributes2;
56 import org.xml.sax.ext.DeclHandler;
57 import org.xml.sax.ext.LexicalHandler;
58 import gnu.xml.aelfred2.ContentHandler2;
59 import gnu.xml.dom.DomAttr;
60 import gnu.xml.dom.DomDocument;
61 import gnu.xml.dom.DomDoctype;
62
63 /**
64 * A SAX content and lexical handler used to construct a DOM document.
65 *
66 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
67 */
68 class SAXEventSink
69 implements ContentHandler2, LexicalHandler, DTDHandler, DeclHandler
70 {
71
72 private static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
73 private static final String XMLNS_PREFIX = XMLConstants.XMLNS_ATTRIBUTE;
74
75 boolean namespaceAware;
76 boolean ignoreWhitespace;
77 boolean expandEntityReferences;
78 boolean ignoreComments;
79 boolean coalescing;
80
81 DomDocument doc; // document being constructed
82 Node ctx; // current context (parent node)
83 LinkedList entityCtx; // entity context
84 List pending; // namespace nodes waiting for a declaring element
85 Locator locator;
86 boolean inCDATA;
87 boolean inDTD;
88 boolean interrupted;
89
90 void interrupt()
91 {
92 interrupted = true;
93 }
94
95 // -- ContentHandler2 --
96
97 public void setDocumentLocator(Locator locator)
98 {
99 this.locator = locator;
100 }
101
102 public void startDocument()
103 throws SAXException
104 {
105 if (namespaceAware)
106 {
107 pending = new LinkedList();
108 }
109 doc = new DomDocument();
110 doc.setStrictErrorChecking(false);
111 doc.setBuilding(true);
112 ctx = doc;
113 }
114
115 public void xmlDecl(String version, String encoding, boolean standalone,
116 String inputEncoding)
117 throws SAXException
118 {
119 if (interrupted)
120 {
121 return;
122 }
123 doc.setXmlVersion(version);
124 doc.setXmlEncoding(encoding);
125 doc.setXmlStandalone(standalone);
126 doc.setInputEncoding(inputEncoding);
127 }
128
129 public void endDocument()
130 throws SAXException
131 {
132 doc.setStrictErrorChecking(true);
133 doc.setBuilding(false);
134 DomDoctype doctype = (DomDoctype) doc.getDoctype();
135 if (doctype != null)
136 {
137 doctype.makeReadonly();
138 }
139 ctx = null;
140 locator = null;
141 }
142
143 public void startPrefixMapping(String prefix, String uri)
144 throws SAXException
145 {
146 if (namespaceAware)
147 {
148 String nsName = (prefix != null && prefix.length() > 0) ?
149 XMLNS_PREFIX + ":" + prefix : XMLNS_PREFIX;
150 DomAttr ns = (DomAttr) doc.createAttributeNS(XMLNS_URI, nsName);
151 ns.setNodeValue(uri);
152 if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
153 {
154 // Add to owner element
155 Node target = ((Attr) ctx).getOwnerElement();
156 target.getAttributes().setNamedItemNS(ns);
157 }
158 else
159 {
160 // Add to pending list; namespace node will be inserted when
161 // element is seen
162 pending.add(ns);
163 }
164 }
165 }
166
167 public void endPrefixMapping(String prefix)
168 throws SAXException
169 {
170 }
171
172 public void startElement(String uri, String localName, String qName,
173 Attributes atts)
174 throws SAXException
175 {
176 if (interrupted)
177 {
178 return;
179 }
180 Element element = createElement(uri, localName, qName, atts);
181 // add element to context
182 ctx.appendChild(element);
183 ctx = element;
184 }
185
186 protected Element createElement(String uri, String localName, String qName,
187 Attributes atts)
188 throws SAXException
189 {
190 // create element node
191 Element element = namespaceAware ?
192 doc.createElementNS(uri, qName) :
193 doc.createElement(qName);
194 NamedNodeMap attrs = element.getAttributes();
195 if (namespaceAware && !pending.isEmpty())
196 {
197 // add pending namespace nodes
198 for (Iterator i = pending.iterator(); i.hasNext(); )
199 {
200 Node ns = (Node) i.next();
201 attrs.setNamedItemNS(ns);
202 }
203 pending.clear();
204 }
205 // add attributes
206 int len = atts.getLength();
207 for (int i = 0; i < len; i++)
208 {
209 // create attribute
210 Attr attr = createAttr(atts, i);
211 if (attr != null)
212 {
213 // add attribute to element
214 if (namespaceAware)
215 {
216 attrs.setNamedItemNS(attr);
217 }
218 else
219 {
220 attrs.setNamedItem(attr);
221 }
222 }
223 }
224 return element;
225 }
226
227 protected Attr createAttr(Attributes atts, int index)
228 {
229 DomAttr attr;
230 if (namespaceAware)
231 {
232 String a_uri = atts.getURI(index);
233 String a_qName = atts.getQName(index);
234 attr = (DomAttr) doc.createAttributeNS(a_uri, a_qName);
235 }
236 else
237 {
238 String a_qName = atts.getQName(index);
239 attr = (DomAttr) doc.createAttribute(a_qName);
240 }
241 attr.setNodeValue(atts.getValue(index));
242 if (atts instanceof Attributes2)
243 {
244 Attributes2 atts2 = (Attributes2) atts;
245 // TODO attr.setDeclared(atts2.isDeclared(index));
246 attr.setSpecified(atts2.isSpecified(index));
247 }
248 return attr;
249 }
250
251 public void endElement(String uri, String localName, String qName)
252 throws SAXException
253 {
254 if (interrupted)
255 {
256 return;
257 }
258 if (namespaceAware)
259 {
260 pending.clear();
261 }
262 ctx = ctx.getParentNode();
263 }
264
265 public void characters(char[] c, int off, int len)
266 throws SAXException
267 {
268 if (interrupted)
269 {
270 return;
271 }
272 ctx.appendChild(createText(c, off, len));
273 }
274
275 protected Text createText(char[] c, int off, int len)
276 throws SAXException
277 {
278 Text text = (inCDATA && !coalescing) ?
279 doc.createCDATASection(new String(c, off, len)) :
280 doc.createTextNode(new String(c, off, len));
281 return text;
282 }
283
284 public void ignorableWhitespace(char[] c, int off, int len)
285 throws SAXException
286 {
287 if (interrupted)
288 {
289 return;
290 }
291 if (!ignoreWhitespace)
292 {
293 characters(c, off, len);
294 }
295 }
296
297 public void processingInstruction(String target, String data)
298 throws SAXException
299 {
300 if (interrupted)
301 {
302 return;
303 }
304 if (!inDTD)
305 {
306 Node pi = createProcessingInstruction(target, data);
307 ctx.appendChild(pi);
308 }
309 }
310
311 protected Node createProcessingInstruction(String target, String data)
312 {
313 return doc.createProcessingInstruction(target, data);
314 }
315
316 public void skippedEntity(String name)
317 throws SAXException
318 {
319 // This callback is totally pointless
320 }
321
322 // -- LexicalHandler --
323
324 public void startDTD(String name, String publicId, String systemId)
325 throws SAXException
326 {
327 if (interrupted)
328 {
329 return;
330 }
331 Node doctype = createDocumentType(name, publicId, systemId);
332 doc.appendChild(doctype);
333 ctx = doctype;
334 inDTD = true;
335 }
336
337 protected Node createDocumentType(String name, String publicId,
338 String systemId)
339 {
340 return new DomDoctype(doc, name, publicId, systemId);
341 }
342
343 public void endDTD()
344 throws SAXException
345 {
346 if (interrupted)
347 {
348 return;
349 }
350 inDTD = false;
351 ctx = ctx.getParentNode();
352 }
353
354 public void startEntity(String name)
355 throws SAXException
356 {
357 DocumentType doctype = doc.getDoctype();
358 if (doctype == null)
359 {
360 throw new SAXException("SAX parser error: " +
361 "reference to entity in undeclared doctype");
362 }
363 if ("[dtd]".equals(name) || name.charAt(0) == '%')
364 {
365 // Ignore DTD and parameter entities
366 ctx = doctype;
367 return;
368 }
369 if ("lt".equals(name) ||
370 "gt".equals(name) ||
371 "amp".equals(name) ||
372 "apos".equals(name) ||
373 "quot".equals(name))
374 {
375 return;
376 }
377 // Get entity
378 NamedNodeMap entities = doctype.getEntities();
379 Entity entity = (Entity) entities.getNamedItem(name);
380 if (entity == null)
381 {
382 throw new SAXException("SAX parser error: " +
383 "reference to undeclared entity: " + name);
384 }
385 pushEntity(entity);
386 }
387
388 public void endEntity(String name)
389 throws SAXException
390 {
391 if ("[dtd]".equals(name) || name.charAt(0) == '%')
392 {
393 // Ignore DTD and parameter entities
394 return;
395 }
396 if ("lt".equals(name) ||
397 "gt".equals(name) ||
398 "amp".equals(name) ||
399 "apos".equals(name) ||
400 "quot".equals(name))
401 {
402 return;
403 }
404 // Get entity
405 Entity entity = popEntity();
406 // TODO resolve external entities to ensure that entity has content
407 if (expandEntityReferences)
408 {
409 // Get entity content
410 for (Node child = entity.getFirstChild(); child != null;
411 child = child.getNextSibling())
412 {
413 ctx.appendChild(child);
414 }
415 }
416 else
417 {
418 Node entityReference = doc.createEntityReference(name);
419 ctx.appendChild(entityReference);
420 }
421 }
422
423 void pushEntity(Node entity)
424 {
425 if (entityCtx == null)
426 {
427 entityCtx = new LinkedList();
428 }
429 entityCtx.addLast(ctx);
430 ctx = entity;
431 }
432
433 Entity popEntity()
434 {
435 Entity ret = (Entity) ctx;
436 ctx = (Node) entityCtx.removeLast();
437 return ret;
438 }
439
440 public void startCDATA()
441 throws SAXException
442 {
443 inCDATA = true;
444 }
445
446 public void endCDATA()
447 throws SAXException
448 {
449 inCDATA = false;
450 }
451
452 public void comment(char[] c, int off, int len)
453 throws SAXException
454 {
455 if (interrupted)
456 {
457 return;
458 }
459 if (!inDTD)
460 {
461 Node comment = createComment(c, off, len);
462 ctx.appendChild(comment);
463 }
464 }
465
466 protected Node createComment(char[] c, int off, int len)
467 {
468 return doc.createComment(new String(c, off, len));
469 }
470
471 // -- DTDHandler --
472
473 public void notationDecl(String name, String publicId, String systemId)
474 throws SAXException
475 {
476 if (interrupted)
477 {
478 return;
479 }
480 DomDoctype doctype = (DomDoctype) ctx;
481 doctype.declareNotation(name, publicId, systemId);
482 }
483
484 public void unparsedEntityDecl(String name, String publicId, String systemId,
485 String notationName)
486 throws SAXException
487 {
488 if (interrupted)
489 {
490 return;
491 }
492 DomDoctype doctype = (DomDoctype) ctx;
493 Entity entity = doctype.declareEntity(name, publicId, systemId,
494 notationName);
495 }
496
497 // -- DeclHandler --
498
499 public void elementDecl(String name, String model)
500 throws SAXException
501 {
502 if (interrupted)
503 {
504 return;
505 }
506 // Ignore fake element declarations generated by ValidationConsumer.
507 // If an element is not really declared in the DTD it will not be
508 // declared in the document model.
509 if (!(ctx instanceof DomDoctype))
510 {
511 return;
512 }
513 DomDoctype doctype = (DomDoctype) ctx;
514 doctype.elementDecl(name, model);
515 }
516
517 public void attributeDecl(String eName, String aName, String type,
518 String mode, String value)
519 throws SAXException
520 {
521 if (interrupted)
522 {
523 return;
524 }
525 DomDoctype doctype = (DomDoctype) ctx;
526 doctype.attributeDecl(eName, aName, type, mode, value);
527 }
528
529 public void internalEntityDecl(String name, String value)
530 throws SAXException
531 {
532 if (interrupted)
533 {
534 return;
535 }
536 DomDoctype doctype = (DomDoctype) ctx;
537 Entity entity = doctype.declareEntity(name, null, null, null);
538 if (entity != null)
539 {
540 Node text = doc.createTextNode(value);
541 entity.appendChild(text);
542 }
543 }
544
545 public void externalEntityDecl(String name, String publicId, String systemId)
546 throws SAXException
547 {
548 if (interrupted)
549 {
550 return;
551 }
552 DomDoctype doctype = (DomDoctype) ctx;
553 Entity entity = doctype.declareEntity(name, publicId, systemId, null);
554 }
555
556 }
557