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