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