OSDN Git Service

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