Source for gnu.xml.dom.Consumer

   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]
  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]
  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:      */
 114:     protected Backdoor (DomConsumer consumer)
 115:     throws SAXException
 116:         { super (consumer); }
 117: 
 118:     // helper routine
 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
 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
 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
 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
 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
 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
 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
 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: 
 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: 
 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: 
 263:         public void elementDecl(String name, String model)
 264:           throws SAXException
 265:         {
 266:           getDoctype().elementDecl(name, model);
 267:         }
 268: 
 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)
 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:     public void endDocument ()
 305:     throws SAXException
 306:     {
 307:         DomDocument doc = (DomDocument) getDocument ();
 308:         doc.setStrictErrorChecking(true);
 309:             doc.setBuilding(false);
 310:         doc.compact ();
 311:             DomDoctype doctype = (DomDoctype) doc.getDoctype();
 312:             if (doctype != null)
 313:               {
 314:                 doctype.makeReadonly();
 315:               }
 316:         super.endDocument ();
 317:     }
 318: 
 319:     // these three methods collaborate to populate entity
 320:     // refs, marking contents readonly on end-of-entity
 321: 
 322:     public boolean canPopulateEntityRefs ()
 323:         { return true; }
 324: 
 325:     public void startEntity (String name)
 326:     throws SAXException
 327:     {
 328:         if (name.charAt (0) == '%' || "[dtd]".equals (name))
 329:         return;
 330:         super.startEntity (name);
 331: 
 332:         DomNode    top = (DomNode) getTop ();
 333: 
 334:         if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE)
 335:         top.readonly = false;
 336:     }
 337: 
 338:     public void endEntity (String name)
 339:     throws SAXException
 340:     {
 341:         if (name.charAt (0) == '%' || "[dtd]".equals (name))
 342:         return;
 343:         DomNode    top = (DomNode) getTop ();
 344: 
 345:         if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE) {
 346:         top.compact ();
 347:         top.makeReadonly ();
 348:         }
 349:         super.endEntity (name);
 350:     }
 351:     }
 352: }