View Javadoc
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