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. */
EntityReferenceImpl(CoreDocumentImpl ownerDoc, String name)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      */
getNodeType()133     public short getNodeType() {
134         return Node.ENTITY_REFERENCE_NODE;
135     }
136 
137     /**
138      * Returns the name of the entity referenced
139      */
getNodeName()140     public String getNodeName() {
141         if (needsSyncData()) {
142             synchronizeData();
143         }
144         return name;
145     }
146 
147     /** Clone node. */
cloneNode(boolean deep)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      */
getBaseURI()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*/
setBaseURI(String 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          */
getEntityRefValue()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      */
synchronizeChildren()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      */
setReadOnly(boolean readOnly, boolean deep)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 startEntityReference
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
404