1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2002,2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.dom; 22 23 import com.sun.org.apache.xerces.internal.util.URI; 24 import org.w3c.dom.DocumentType; 25 import org.w3c.dom.EntityReference; 26 import org.w3c.dom.NamedNodeMap; 27 import org.w3c.dom.Node; 28 29 /** 30 * EntityReference models the XML &entityname; syntax, when used for 31 * entities defined by the DOM. Entities hardcoded into XML, such as 32 * character entities, should instead have been translated into text 33 * by the code which generated the DOM tree. 34 * <P> 35 * An XML processor has the alternative of fully expanding Entities 36 * into the normal document tree. If it does so, no EntityReference nodes 37 * will appear. 38 * <P> 39 * Similarly, non-validating XML processors are not required to read 40 * or process entity declarations made in the external subset or 41 * declared in external parameter entities. Hence, some applications 42 * may not make the replacement value available for Parsed Entities 43 * of these types. 44 * <P> 45 * EntityReference behaves as a read-only node, and the children of 46 * the EntityReference (which reflect those of the Entity, and should 47 * also be read-only) give its replacement value, if any. They are 48 * supposed to automagically stay in synch if the DocumentType is 49 * updated with new values for the Entity. 50 * <P> 51 * The defined behavior makes efficient storage difficult for the DOM 52 * implementor. We can't just look aside to the Entity's definition 53 * in the DocumentType since those nodes have the wrong parent (unless 54 * we can come up with a clever "imaginary parent" mechanism). We 55 * must at least appear to clone those children... which raises the 56 * issue of keeping the reference synchronized with its parent. 57 * This leads me back to the "cached image of centrally defined data" 58 * solution, much as I dislike it. 59 * <P> 60 * For now I have decided, since REC-DOM-Level-1-19980818 doesn't 61 * cover this in much detail, that synchronization doesn't have to be 62 * considered while the user is deep in the tree. That is, if you're 63 * looking within one of the EntityReferennce's children and the Entity 64 * changes, you won't be informed; instead, you will continue to access 65 * the same object -- which may or may not still be part of the tree. 66 * This is the same behavior that obtains elsewhere in the DOM if the 67 * subtree you're looking at is deleted from its parent, so it's 68 * acceptable here. (If it really bothers folks, we could set things 69 * up so deleted subtrees are walked and marked invalid, but that's 70 * not part of the DOM's defined behavior.) 71 * <P> 72 * As a result, only the EntityReference itself has to be aware of 73 * changes in the Entity. And it can take advantage of the same 74 * structure-change-monitoring code I implemented to support 75 * DeepNodeList. 76 * 77 * @xerces.internal 78 * 79 * @author Arnaud Le Hors, IBM 80 * @author Joe Kesselman, IBM 81 * @author Andy Clark, IBM 82 * @author Ralf Pfeiffer, IBM 83 * @since PR-DOM-Level-1-19980818. 84 */ 85 public class EntityReferenceImpl 86 extends ParentNode 87 implements EntityReference { 88 89 // 90 // Constants 91 // 92 93 /** Serialization version. */ 94 static final long serialVersionUID = -7381452955687102062L; 95 96 // 97 // Data 98 // 99 100 /** Name of Entity referenced */ 101 protected String name; 102 /** Base URI*/ 103 protected String baseURI; 104 105 106 /** Entity changes. */ 107 //protected int entityChanges = -1; 108 109 /** Enable synchronize. */ 110 //protected boolean fEnableSynchronize = false; 111 112 // 113 // Constructors 114 // 115 116 /** Factory constructor. */ 117 public EntityReferenceImpl(CoreDocumentImpl ownerDoc, String name) { 118 super(ownerDoc); 119 this.name = name; 120 isReadOnly(true); 121 needsSyncChildren(true); 122 } 123 124 // 125 // Node methods 126 // 127 128 /** 129 * A short integer indicating what type of node this is. The named 130 * constants for this value are defined in the org.w3c.dom.Node interface. 131 */ 132 public short getNodeType() { 133 return Node.ENTITY_REFERENCE_NODE; 134 } 135 136 /** 137 * Returns the name of the entity referenced 138 */ 139 public String getNodeName() { 140 if (needsSyncData()) { 141 synchronizeData(); 142 } 143 return name; 144 } 145 146 /** Clone node. */ 147 public Node cloneNode(boolean deep) { 148 EntityReferenceImpl er = (EntityReferenceImpl)super.cloneNode(deep); 149 er.setReadOnly(true, deep); 150 return er; 151 } 152 153 /** 154 * Returns the absolute base URI of this node or null if the implementation 155 * wasn't able to obtain an absolute URI. Note: If the URI is malformed, a 156 * null is returned. 157 * 158 * @return The absolute base URI of this node or null. 159 * @since DOM Level 3 160 */ 161 public String getBaseURI() { 162 if (needsSyncData()) { 163 synchronizeData(); 164 } 165 if (baseURI == null) { 166 DocumentType doctype; 167 NamedNodeMap entities; 168 EntityImpl entDef; 169 if (null != (doctype = getOwnerDocument().getDoctype()) && 170 null != (entities = doctype.getEntities())) { 171 172 entDef = (EntityImpl)entities.getNamedItem(getNodeName()); 173 if (entDef !=null) { 174 return entDef.getBaseURI(); 175 } 176 } 177 } else if (baseURI != null && baseURI.length() != 0 ) {// attribute value is always empty string 178 try { 179 return new URI(baseURI).toString(); 180 } 181 catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){ 182 // REVISIT: what should happen in this case? 183 return null; 184 } 185 } 186 return baseURI; 187 } 188 189 190 /** NON-DOM: set base uri*/ 191 public void setBaseURI(String uri){ 192 if (needsSyncData()) { 193 synchronizeData(); 194 } 195 baseURI = uri; 196 } 197 198 /** 199 * NON-DOM: compute string representation of the entity reference. 200 * This method is used to retrieve a string value for an attribute node that has child nodes. 201 * @return String representing a value of this entity ref. or 202 * null if any node other than EntityReference, Text is encountered 203 * during computation 204 */ 205 protected String getEntityRefValue (){ 206 if (needsSyncChildren()){ 207 synchronizeChildren(); 208 } 209 210 String value = ""; 211 if (firstChild != null){ 212 if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){ 213 value = ((EntityReferenceImpl)firstChild).getEntityRefValue(); 214 } 215 else if (firstChild.getNodeType() == Node.TEXT_NODE){ 216 value = firstChild.getNodeValue(); 217 } 218 else { 219 // invalid to have other types of nodes in attr value 220 return null; 221 } 222 223 if (firstChild.nextSibling == null){ 224 return value; 225 } 226 else { 227 StringBuffer buff = new StringBuffer(value); 228 ChildNode next = firstChild.nextSibling; 229 while (next != null){ 230 231 if (next.getNodeType() == Node.ENTITY_REFERENCE_NODE){ 232 value = ((EntityReferenceImpl)next).getEntityRefValue(); 233 } 234 else if (next.getNodeType() == Node.TEXT_NODE){ 235 value = next.getNodeValue(); 236 } 237 else { 238 // invalid to have other types of nodes in attr value 239 return null; 240 } 241 buff.append(value); 242 next = next.nextSibling; 243 244 } 245 return buff.toString(); 246 } 247 } 248 return ""; 249 } 250 251 /** 252 * EntityReference's children are a reflection of those defined in the 253 * named Entity. This method creates them if they haven't been created yet. 254 * This doesn't support editing the Entity though, since this only called 255 * once for all. 256 */ 257 protected void synchronizeChildren() { 258 // no need to synchronize again 259 needsSyncChildren(false); 260 261 DocumentType doctype; 262 NamedNodeMap entities; 263 EntityImpl entDef; 264 if (null != (doctype = getOwnerDocument().getDoctype()) && 265 null != (entities = doctype.getEntities())) { 266 267 entDef = (EntityImpl)entities.getNamedItem(getNodeName()); 268 269 // No Entity by this name, stop here. 270 if (entDef == null) 271 return; 272 273 // If entity's definition exists, clone its kids 274 isReadOnly(false); 275 for (Node defkid = entDef.getFirstChild(); 276 defkid != null; 277 defkid = defkid.getNextSibling()) { 278 Node newkid = defkid.cloneNode(true); 279 insertBefore(newkid, null); 280 } 281 setReadOnly(true, true); 282 } 283 } 284 285 286 /** 287 * NON-DOM: sets the node and its children value. 288 * <P> 289 * Note: make sure that entity reference and its kids could be set readonly. 290 */ 291 public void setReadOnly(boolean readOnly, boolean deep) { 292 293 if (needsSyncData()) { 294 synchronizeData(); 295 } 296 if (deep) { 297 298 if (needsSyncChildren()) { 299 synchronizeChildren(); 300 } 301 // Recursively set kids 302 for (ChildNode mykid = firstChild; 303 mykid != null; 304 mykid = mykid.nextSibling) { 305 306 mykid.setReadOnly(readOnly,true); 307 308 } 309 } 310 isReadOnly(readOnly); 311 } // setReadOnly(boolean,boolean) 312 313 314 /** 315 * Enable the synchronize method which may do cloning. This method is enabled 316 * when the parser is done with an EntityReference. 317 /*** 318 // revisit: enable editing of Entity 319 public void enableSynchronize(boolean enableSynchronize) { 320 fEnableSynchronize= enableSynchronize; 321 } 322 /***/ 323 324 /** 325 * EntityReference's children are a reflection of those defined in the 326 * named Entity. This method updates them if the Entity is changed. 327 * <P> 328 * It is unclear what the least-cost resynch mechanism is. 329 * If we expect the kids to be shallow, and/or expect changes 330 * to the Entity contents to be rare, wiping them all out 331 * and recloning is simplest. 332 * <P> 333 * If we expect them to be deep, 334 * it might be better to first decide which kids (if any) 335 * persist, and keep the ones (if any) that are unchanged 336 * rather than doing all the work of cloning them again. 337 * But that latter gets into having to convolve the two child lists, 338 * insert new information in the right order (and possibly reorder 339 * the existing kids), and a few other complexities that I really 340 * don't want to deal with in this implementation. 341 * <P> 342 * Note that if we decide that we need to update the EntityReference's 343 * contents, we have to turn off the readOnly flag temporarily to do so. 344 * When we get around to adding multitasking support, this whole method 345 * should probably be an atomic operation. 346 * 347 * @see DocumentTypeImpl 348 * @see EntityImpl 349 */ 350 // The Xerces parser invokes callbacks for startEnityReference 351 // the parsed value of the entity EACH TIME, so it is actually 352 // easier to create the nodes through the callbacks rather than 353 // clone the Entity. 354 /*** 355 // revisit: enable editing of Entity 356 private void synchronize() { 357 if (!fEnableSynchronize) { 358 return; 359 } 360 DocumentType doctype; 361 NamedNodeMap entities; 362 EntityImpl entDef; 363 if (null != (doctype = getOwnerDocument().getDoctype()) && 364 null != (entities = doctype.getEntities())) { 365 366 entDef = (EntityImpl)entities.getNamedItem(getNodeName()); 367 368 // No Entity by this name. If we had a change count, reset it. 369 if(null==entDef) 370 entityChanges=-1; 371 372 // If no kids availalble, wipe any pre-existing children. 373 // (See discussion above.) 374 // Note that we have to use the superclass to avoid recursion 375 // through Synchronize. 376 readOnly=false; 377 if(null==entDef || !entDef.hasChildNodes()) 378 for(Node kid=super.getFirstChild(); 379 kid!=null; 380 kid=super.getFirstChild()) 381 removeChild(kid); 382 383 // If entity's definition changed, clone its kids 384 // (See discussion above.) 385 if(null!=entDef && entDef.changes!=entityChanges) { 386 for(Node defkid=entDef.getFirstChild(); 387 defkid!=null; 388 defkid=defkid.getNextSibling()) { 389 390 NodeImpl newkid=(NodeImpl) defkid.cloneNode(true); 391 newkid.setReadOnly(true,true); 392 insertBefore(newkid,null); 393 } 394 entityChanges=entDef.changes; 395 } 396 readOnly=true; 397 } 398 } 399 /***/ 400 401 402 } // class EntityReferenceImpl