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