Source for gnu.xml.dom.ls.SAXEventSink

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