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