Source for gnu.xml.dom.DomDoctype

   1: /* DomDoctype.java --
   2:    Copyright (C) 1999,2000,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: package gnu.xml.dom;
  39: 
  40: import java.util.HashMap;
  41: import org.w3c.dom.DocumentType;
  42: import org.w3c.dom.DOMException;
  43: import org.w3c.dom.DOMImplementation;
  44: import org.w3c.dom.Entity;
  45: import org.w3c.dom.NamedNodeMap;
  46: import org.w3c.dom.Node;
  47: import org.w3c.dom.Notation;
  48: 
  49: /**
  50:  * <p> "DocumentType" implementation (with no extensions for supporting
  51:  * any document typing information).  This is a non-core DOM class,
  52:  * supporting the "XML" feature. </p>
  53:  *
  54:  * <p> <em>Few XML applications will actually care about this partial
  55:  * DTD support</em>, since it doesn't expose any (!) of the data typing
  56:  * facilities which can motivate applications to use DTDs.  It does not
  57:  * expose element content models, or information about attribute typing
  58:  * rules.  Plus the information it exposes isn't very useful; as one example,
  59:  * DOM exposes information about unparsed ENTITY objects, which is only used
  60:  * with certain element attributes, but does not expose the information about
  61:  * those attributes which is needed to apply that data! </p>
  62:  *
  63:  * <p> Also, note that there are no nonportable ways to associate even the
  64:  * notation and entity information exposed by DOM with a DocumentType.  While
  65:  * there is a DOM L2 method to construct a DocumentType, it only gives access
  66:  * to the textual content of the &lt;!DOCTYPE ...&gt; declaration.  </p>
  67:  *
  68:  * <p> In short, <em>you are strongly advised not to rely on this incomplete
  69:  * DTD functionality</em> in your application code.</p>
  70:  *
  71:  * @see DomEntity
  72:  * @see DomEntityReference
  73:  * @see DomNotation
  74:  *
  75:  * @author David Brownell
  76:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  77:  */
  78: public class DomDoctype
  79:   extends DomExtern
  80:   implements DocumentType
  81: {
  82: 
  83:   private DomNamedNodeMap notations;
  84:   private DomNamedNodeMap entities;
  85:   private final DOMImplementation implementation;
  86:   private String subset;
  87: 
  88:   private HashMap elements = new HashMap();
  89:   private boolean ids;
  90: 
  91:   /**
  92:    * Constructs a DocumentType node associated with the specified
  93:    * implementation, with the specified name.
  94:    *
  95:    * <p>This constructor should only be invoked by a DOMImplementation as
  96:    * part of its createDocumentType functionality, or through a subclass
  97:    * which is similarly used in a "Sub-DOM" style layer.
  98:    *
  99:    * <p> Note that at this time there is no standard SAX API granting
 100:    * access to the internal subset text, so that relying on that value
 101:    * is not currently portable.
 102:    *
 103:    * @param impl The implementation with which this object is associated
 104:    * @param name Name of this root element
 105:    * @param publicId If non-null, provides the external subset's
 106:    *    PUBLIC identifier
 107:    * @param systemId If non-null, provides the external subset's
 108:    *    SYSTEM identifier
 109:    * @param internalSubset Provides the literal value (unparsed, no
 110:    *    entities expanded) of the DTD's internal subset.
 111:    */
 112:   protected DomDoctype(DOMImplementation impl,
 113:                        String name,
 114:                        String publicId,
 115:                        String systemId,
 116:                        String internalSubset)
 117:   {
 118:     super(DOCUMENT_TYPE_NODE, null, name, publicId, systemId);
 119:     implementation = impl;
 120:     subset = internalSubset;
 121:   }
 122: 
 123:   /**
 124:    * JAXP builder constructor.
 125:    * @param doc the document
 126:    * @param name the name of the document element
 127:    * @param publicId the public ID of the document type declaration
 128:    * @param systemId the system ID of the document type declaration
 129:    */
 130:   public DomDoctype(DomDocument doc,
 131:                     String name,
 132:                     String publicId,
 133:                     String systemId)
 134:   {
 135:     super(DOCUMENT_TYPE_NODE, doc, name, publicId, systemId);
 136:     implementation = doc.getImplementation();
 137:   }
 138: 
 139:   /**
 140:    * <b>DOM L1</b>
 141:    * Returns the root element's name (just like getNodeName).
 142:    */
 143:   final public String getName()
 144:   {
 145:     return getNodeName();
 146:   }
 147: 
 148:   /**
 149:    * <b>DOM L1</b>
 150:    * Returns information about any general entities declared
 151:    * in the DTD.
 152:    *
 153:    * <p><em>Note:  DOM L1 doesn't throw a DOMException here, but
 154:    * then it doesn't have the strange construction rules of L2.</em>
 155:    *
 156:    * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
 157:    *    is not associated with a document.
 158:    */
 159:   public NamedNodeMap getEntities()
 160:   {
 161:     if (entities == null)
 162:       {
 163:         entities = new DomNamedNodeMap(this, Node.ENTITY_NODE);
 164:       }
 165:     return entities;
 166:   }
 167: 
 168:   /**
 169:    * Records the declaration of a general entity in this DocumentType.
 170:    *
 171:    * @param name Name of the entity
 172:    * @param publicId If non-null, provides the entity's PUBLIC identifier
 173:    * @param systemId Provides the entity's SYSTEM identifier
 174:    * @param notation If non-null, provides the entity's notation
 175:    *    (indicating an unparsed entity)
 176:    * @return The Entity that was declared, or null if the entity wasn't
 177:    *    recorded (because it's a parameter entity or because an entity with
 178:    *    this name was already declared).
 179:    *
 180:    * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
 181:    *    DocumentType is no longer writable.
 182:    * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
 183:    *    is not associated with a document.
 184:    */
 185:   public Entity declareEntity(String name,
 186:                               String publicId,
 187:                               String systemId,
 188:                               String notation)
 189:   {
 190:     DomEntity entity;
 191: 
 192:     if (name.charAt(0) == '%' || "[dtd]".equals(name))
 193:       {
 194:         return null;
 195:       }
 196:     if (isReadonly())
 197:       {
 198:         throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
 199:       }
 200:     getEntities();
 201: 
 202:     DomDocument.checkName(name, (owner != null) ?
 203:                           "1.1".equals(owner.getXmlVersion()) : false);
 204:     if (entities.getNamedItem(name) != null)
 205:       {
 206:         return null;
 207:       }
 208: 
 209:     entity = new DomEntity(owner, name, publicId, systemId, notation);
 210:     entities.setNamedItem(entity);
 211:     return entity;
 212:   }
 213: 
 214:   /**
 215:    * <b>DOM L1</b>
 216:    * Returns information about any notations declared in the DTD.
 217:    *
 218:    * <p><em>Note:  DOM L1 doesn't throw a DOMException here, but
 219:    * then it doesn't have the strange construction rules of L2.</em>
 220:    *
 221:    * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
 222:    *    is not associated with a document.
 223:    */
 224:   public NamedNodeMap getNotations()
 225:   {
 226:     if (notations == null)
 227:       {
 228:         notations = new DomNamedNodeMap(this, Node.NOTATION_NODE);
 229:       }
 230:     return notations;
 231:   }
 232: 
 233:   /**
 234:    * Records the declaration of a notation in this DocumentType.
 235:    *
 236:    * @param name Name of the notation
 237:    * @param publicId If non-null, provides the notation's PUBLIC identifier
 238:    * @param systemId If non-null, provides the notation's SYSTEM identifier
 239:    * @return The notation that was declared.
 240:    *
 241:    * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
 242:    *    DocumentType is no longer writable.
 243:    * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
 244:    *    is not associated with a document.
 245:    */
 246:   public Notation declareNotation(String name,
 247:                                   String publicId,
 248:                                   String systemId)
 249:   {
 250:     DomNotation notation;
 251: 
 252:     if (isReadonly())
 253:       {
 254:         throw new DomDOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR);
 255:       }
 256:     getNotations();
 257: 
 258:     DomDocument.checkName(name, (owner != null) ?
 259:                           "1.1".equals(owner.getXmlVersion()) : false);
 260: 
 261:     notation = new DomNotation(owner, name, publicId, systemId);
 262:     notations.setNamedItem(notation);
 263:     return notation;
 264:   }
 265: 
 266:   /**
 267:    * <b>DOM L2</b>
 268:    * Returns the internal subset of the document, as a string of unparsed
 269:    * XML declarations (and comments, PIs, whitespace); or returns null if
 270:    * there is no such subset.  There is no vendor-independent expectation
 271:    * that this attribute be set, or that declarations found in it be
 272:    * reflected in the <em>entities</em> or <em>notations</em> attributes
 273:    * of this Document "Type" object.
 274:    *
 275:    * <p> Some application-specific XML profiles require that documents
 276:    * only use specific PUBLIC identifiers, without an internal subset
 277:    * to modify the interperetation of the declarations associated with
 278:    * that PUBLIC identifier through some standard.
 279:    */
 280:   public String getInternalSubset()
 281:   {
 282:     return subset;
 283:   }
 284: 
 285:   /**
 286:    * The base URI of a DocumentType is always <code>null</code>.
 287:    * See the Infoset Mapping, appendix C.
 288:    */
 289:   public final String getBaseURI()
 290:   {
 291:     return null;
 292:   }
 293: 
 294:   /**
 295:    * Sets the internal "readonly" flag so the node and its associated
 296:    * data (only lists of entities and notations, no type information
 297:    * at the moment) can't be changed.
 298:    */
 299:   public void makeReadonly()
 300:   {
 301:     super.makeReadonly();
 302:     if (entities != null)
 303:       {
 304:         entities.makeReadonly();
 305:       }
 306:     if (notations != null)
 307:       {
 308:         notations.makeReadonly();
 309:       }
 310:   }
 311: 
 312:   void setOwner(DomDocument doc)
 313:   {
 314:     if (entities != null)
 315:       {
 316:         for (DomNode ctx = entities.first; ctx != null; ctx = ctx.next)
 317:           {
 318:             ctx.setOwner(doc);
 319:           }
 320:       }
 321:     if (notations != null)
 322:       {
 323:         for (DomNode ctx = notations.first; ctx != null; ctx = ctx.next)
 324:           {
 325:             ctx.setOwner(doc);
 326:           }
 327:       }
 328:     super.setOwner(doc);
 329:   }
 330: 
 331:   /**
 332:    * <b>DOM L2</b>
 333:    * Consults the DOM implementation to determine if the requested
 334:    * feature is supported.
 335:    */
 336:   final public boolean supports(String feature, String version)
 337:   {
 338:     return implementation.hasFeature(feature, version);
 339:   }
 340: 
 341:   /**
 342:    * Returns the implementation associated with this document type.
 343:    */
 344:   final public DOMImplementation getImplementation()
 345:   {
 346:     return implementation;
 347:   }
 348: 
 349:   public void elementDecl(String name, String model)
 350:   {
 351:     DTDElementTypeInfo info = getElementTypeInfo(name);
 352:     if (info == null)
 353:       {
 354:         info = new DTDElementTypeInfo(name, model);
 355:         elements.put(name, info);
 356:       }
 357:     else
 358:       {
 359:         info.model = model;
 360:       }
 361:   }
 362: 
 363:   DTDElementTypeInfo getElementTypeInfo(String name)
 364:   {
 365:     return (DTDElementTypeInfo) elements.get(name);
 366:   }
 367: 
 368:   public void attributeDecl(String eName, String aName, String type,
 369:                             String mode, String value)
 370:   {
 371:     DTDAttributeTypeInfo info = new DTDAttributeTypeInfo(eName, aName, type,
 372:                                                          mode, value);
 373:     DTDElementTypeInfo elementInfo = getElementTypeInfo(eName);
 374:     if (elementInfo == null)
 375:       {
 376:         elementInfo = new DTDElementTypeInfo(eName, null);
 377:         elements.put(eName, elementInfo);
 378:       }
 379:     elementInfo.setAttributeTypeInfo(aName, info);
 380:     if ("ID".equals(type))
 381:       {
 382:         ids = true;
 383:       }
 384:   }
 385: 
 386:   DTDAttributeTypeInfo getAttributeTypeInfo(String elementName, String name)
 387:   {
 388:     DTDElementTypeInfo elementInfo =
 389:       (DTDElementTypeInfo) elements.get(elementName);
 390:     return (elementInfo == null) ? null :
 391:       elementInfo.getAttributeTypeInfo(name);
 392:   }
 393: 
 394:   boolean hasIds()
 395:   {
 396:     return ids;
 397:   }
 398: 
 399:   public boolean isSameNode(Node arg)
 400:   {
 401:     if (equals(arg))
 402:       {
 403:         return true;
 404:       }
 405:     if (!(arg instanceof DocumentType))
 406:       {
 407:         return false;
 408:       }
 409:     DocumentType doctype = (DocumentType) arg;
 410:     if (!equal(getPublicId(), doctype.getPublicId()))
 411:       {
 412:         return false;
 413:       }
 414:     if (!equal(getSystemId(), doctype.getSystemId()))
 415:       {
 416:         return false;
 417:       }
 418:     if (!equal(getInternalSubset(), doctype.getInternalSubset()))
 419:       {
 420:         return false;
 421:       }
 422:     // TODO entities
 423:     // TODO notations
 424:     return true;
 425:   }
 426: 
 427:   /**
 428:    * Shallow clone of the doctype, except that associated
 429:    * entities and notations are (deep) cloned.
 430:    */
 431:   public Object clone()
 432:   {
 433:     DomDoctype node = (DomDoctype) super.clone();
 434: 
 435:     if (entities != null)
 436:       {
 437:         node.entities = new DomNamedNodeMap(node, Node.ENTITY_NODE);
 438:         for (DomNode ctx = entities.first; ctx != null; ctx = ctx.next)
 439:           {
 440:             node.entities.setNamedItem(ctx.cloneNode(true));
 441:           }
 442:       }
 443:     if (notations != null)
 444:       {
 445:         node.notations = new DomNamedNodeMap(node, Node.NOTATION_NODE);
 446:         for (DomNode ctx = notations.first; ctx != null; ctx = ctx.next)
 447:           {
 448:             node.notations.setNamedItem(ctx.cloneNode(true));
 449:           }
 450:       }
 451:     return node;
 452:   }
 453: 
 454: 
 455: }