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