Frames | No Frames |
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 <!DOCTYPE ...> 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: }