1 /* Consumer.java --
2    Copyright (C) 2001,2004 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 
39 package gnu.xml.dom;
40 
41 import org.w3c.dom.DocumentType;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.Text;
44 
45 import org.xml.sax.Attributes;
46 import org.xml.sax.SAXException;
47 import org.xml.sax.ext.Attributes2;
48 
49 import gnu.xml.pipeline.DomConsumer;
50 import gnu.xml.pipeline.EventConsumer;
51 
52 
53 /**
54  * Event consumer which constructs DOM documents using the implementation
55  * in this package, using SAX2 events.  This packages various backdoors
56  * into this DOM implementation, as needed to address DOM requirements
57  * that can't be met by strictly conforming implementations of DOM.
58  *
59  * <p> These requirements all relate to {@link DocumentType} nodes and
60  * features of that node type.  These features are normally not used,
61  * because that interface only exposes a subset of the information found
62  * in DTDs.  More, that subset does not include the most important typing
63  * information.  For example, it excludes element content models and
64  * attribute typing.  It does expose some entity management issues,
65  * although entity management doesn't relate to document typing.
66  *
67  * <p> Note that SAX2 does not expose the literal text of the DTD's
68  * internal subset, so it will not be present in DOM trees constructed
69  * using this API.  (Though with a good SAX2 implementation, it could
70  * be partially recreated...)
71  *
72  * @author David Brownell
73  */
74 public class Consumer extends DomConsumer
75 {
76     /**
77      * Constructs an unconfigured event consumer,
78      * as a terminus in a SAX event pipeline.
79      */
80     // used by PipelineFactory [terminus]
Consumer()81     public Consumer ()
82     throws SAXException
83     {
84 	super (DomDocument.class);
85 	setHandler (new Backdoor (this));
86     }
87 
88     /**
89      * Constructs an unconfigured event consumer,
90      * as a stage in a SAX event pipeline.
91      */
92     // used by PipelineFactory [filter]
Consumer(EventConsumer next)93     public Consumer (EventConsumer next)
94     throws SAXException
95     {
96 	super (DomDocument.class, next);
97 	setHandler (new Backdoor (this));
98     }
99 
100     /**
101      * Implements the backdoors needed by DOM.
102      * All methods in this class use implementation-specific APIs that are
103      * implied by the DOM specification (needed to implement testable
104      * behavior) but which are excluded from the DOM specification.
105      */
106     public static class Backdoor extends DomConsumer.Handler
107     {
108 	/**
109 	 * Constructor.
110 	 * @param consumer must have been initialized to use the
111 	 *	{@link DomDocument} class (or a subclass) for
112 	 *	constructing DOM trees
113 	 */
Backdoor(DomConsumer consumer)114 	protected Backdoor (DomConsumer consumer)
115 	throws SAXException
116 	    { super (consumer); }
117 
118 	// helper routine
getDoctype()119 	private DomDoctype getDoctype ()
120 	throws SAXException
121 	{
122 	    DomDocument		doc = (DomDocument) getDocument ();
123 	    DocumentType	dt = doc.getDoctype ();
124 
125 	    if (dt == null)
126 		throw new SAXException ("doctype missing!");
127 	    return (DomDoctype) dt;
128 	}
129 
130 	// SAX2 "lexical" event
startDTD(String name, String publicId, String systemId)131 	public void startDTD (String name, String publicId, String systemId)
132 	throws SAXException
133 	{
134 	    DomDocument		doc = (DomDocument) getDocument ();
135 
136 	    super.startDTD (name, publicId, systemId);
137 	    // DOM L2 doctype creation model is bizarre
138 	    DomDoctype dt = new DomDoctype (doc, name, publicId, systemId);
139 	    doc.appendChild (dt);
140 	}
141 
142 	// SAX2 "lexical" event
endDTD()143 	public void endDTD ()
144 	throws SAXException
145 	{
146 	    super.endDTD ();
147 	    // DOM L2 has no way to make things readonly
148 	    getDoctype ().makeReadonly ();
149 	}
150 
151 	// SAX1 DTD event
notationDecl( String name, String publicId, String systemId )152 	public void notationDecl (
153 	    String name,
154 	    String publicId, String systemId
155 	) throws SAXException
156 	{
157 	    // DOM L2 can't create/save notation nodes
158 	    getDoctype ().declareNotation (name, publicId, systemId);
159 	}
160 
161 	// SAX1 DTD event
unparsedEntityDecl( String name, String publicId, String systemId, String notationName )162 	public void unparsedEntityDecl (
163 	    String name,
164 	    String publicId, String systemId,
165 	    String notationName
166 	) throws SAXException
167 	{
168 	    // DOM L2 can't create/save entity nodes
169 	    getDoctype ().declareEntity (name, publicId, systemId,
170 	    	notationName);
171 	}
172 
173 	// SAX2 declaration event
internalEntityDecl(String name, String value)174 	public void internalEntityDecl (String name, String value)
175 	throws SAXException
176 	{
177 	    // DOM L2 can't create/save entity nodes
178 	    // NOTE:  this doesn't save the value as a child of this
179 	    // node, though it could realistically do so.
180 	    getDoctype ().declareEntity (name, null, null, null);
181 	}
182 
183 	// SAX2 declaration event
externalEntityDecl( String name, String publicId, String systemId )184 	public void externalEntityDecl (
185 	    String name,
186 	    String publicId,
187 	    String systemId
188 	) throws SAXException
189 	{
190 	    // DOM L2 can't create/save entity nodes
191 	    // NOTE:  DOM allows for these to have children, if
192 	    // they don't have unbound namespace references.
193 	    getDoctype ().declareEntity (name, publicId, systemId, null);
194 	}
195 
196 	// SAX2 element
startElement( String uri, String localName, String qName, Attributes atts )197 	public void startElement (
198 	    String uri,
199 	    String localName,
200 	    String qName,
201 	    Attributes atts
202 	) throws SAXException
203 	{
204 	    Node		top;
205 
206 	    super.startElement (uri, localName, qName, atts);
207 
208 	    // might there be more work?
209 	    top = getTop ();
210 	    if (!top.hasAttributes () || !(atts instanceof Attributes2))
211 		return;
212 
213 	    // remember any attributes that got defaulted
214 	    DomNamedNodeMap	map = (DomNamedNodeMap) top.getAttributes ();
215 	    Attributes2		attrs = (Attributes2) atts;
216 	    int			length = atts.getLength ();
217 
218 	    //map.compact ();
219 	    for (int i = 0; i < length; i++) {
220 		if (attrs.isSpecified (i))
221 		    continue;
222 
223 		// value was defaulted.
224 		String		temp = attrs.getQName (i);
225 		DomAttr		attr;
226 
227 		if ("".equals (temp))
228 		    attr = (DomAttr) map.getNamedItemNS (attrs.getURI (i),
229 			    atts.getLocalName (i));
230 		else
231 		    attr = (DomAttr) map.getNamedItem (temp);
232 
233 		// DOM L2 can't write this flag, only read it
234 		attr.setSpecified (false);
235 	    }
236 	}
237 
endElement( String uri, String localName, String qName )238 	public void endElement (
239 	    String uri,
240 	    String localName,
241 	    String qName
242 	) throws SAXException
243 	{
244 	    DomNode	top = (DomNode) getTop ();
245 	    top.compact ();
246 	    super.endElement (uri, localName, qName);
247 	}
248 
createText( boolean isCDATA, char buf [], int off, int len )249 	protected Text createText (
250 	    boolean	isCDATA,
251 	    char	buf [],
252 	    int		off,
253 	    int		len
254 	) {
255 	    DomDocument	doc = (DomDocument) getDocument ();
256 
257 	    if (isCDATA)
258 		return doc.createCDATASection (buf, off, len);
259 	    else
260 		return doc.createTextNode (buf, off, len);
261 	}
262 
elementDecl(String name, String model)263         public void elementDecl(String name, String model)
264           throws SAXException
265         {
266           getDoctype().elementDecl(name, model);
267         }
268 
attributeDecl( String ename, String aname, String type, String mode, String value )269 	public void attributeDecl (
270 	    String	ename,
271 	    String	aname,
272 	    String	type,
273 	    String	mode,
274 	    String	value
275 	) throws SAXException
276 	{
277           getDoctype().attributeDecl(ename, aname, type, mode, value);
278             /*
279 	    if (value == null && !"ID".equals (type))
280 		return;
281 
282 	    DomDoctype.ElementInfo	info;
283 
284 	    info = getDoctype ().getElementInfo (ename);
285 	    if (value != null)
286 		info.setAttrDefault (aname, value);
287 	    if ("ID".equals (type))
288 		info.setIdAttr (aname);
289                 */
290 
291 	}
292 
293 	// force duplicate name checking off while we're
294 	// using parser output (don't duplicate the work)
startDocument()295 	public void startDocument () throws SAXException
296 	{
297 	    super.startDocument ();
298 
299             DomDocument doc = (DomDocument) getDocument ();
300             doc.setStrictErrorChecking(false);
301             doc.setBuilding(true);
302 	}
303 
304         /**
305          * Required by DOM Level 3 to report document parameters
306          */
xmlDecl(String version, String encoding, boolean standalone, String inputEncoding)307         public void xmlDecl(String version,
308                             String encoding,
309                             boolean standalone,
310                             String inputEncoding)
311           throws SAXException
312         {
313           super.xmlDecl(version, encoding, standalone, inputEncoding);
314 
315           DomDocument doc = (DomDocument) getDocument();
316           doc.setXmlEncoding(encoding);
317           doc.setInputEncoding(inputEncoding);
318         }
319 
endDocument()320 	public void endDocument ()
321 	throws SAXException
322 	{
323 	    DomDocument doc = (DomDocument) getDocument ();
324 	    doc.setStrictErrorChecking(true);
325             doc.setBuilding(false);
326 	    doc.compact ();
327             DomDoctype doctype = (DomDoctype) doc.getDoctype();
328             if (doctype != null)
329               {
330                 doctype.makeReadonly();
331               }
332 	    super.endDocument ();
333 	}
334 
335 	// these three methods collaborate to populate entity
336 	// refs, marking contents readonly on end-of-entity
337 
canPopulateEntityRefs()338 	public boolean canPopulateEntityRefs ()
339 	    { return true; }
340 
startEntity(String name)341 	public void startEntity (String name)
342 	throws SAXException
343 	{
344 	    if (name.charAt (0) == '%' || "[dtd]".equals (name))
345 		return;
346 	    super.startEntity (name);
347 
348 	    DomNode	top = (DomNode) getTop ();
349 
350 	    if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE)
351 		top.readonly = false;
352 	}
353 
endEntity(String name)354 	public void endEntity (String name)
355 	throws SAXException
356 	{
357 	    if (name.charAt (0) == '%' || "[dtd]".equals (name))
358 		return;
359 	    DomNode	top = (DomNode) getTop ();
360 
361 	    if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE) {
362 		top.compact ();
363 		top.makeReadonly ();
364 	    }
365 	    super.endEntity (name);
366 	}
367     }
368 }
369