001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.dom;
019:
020: import org.apache.xerces.util.URI;
021: import org.w3c.dom.DocumentType;
022: import org.w3c.dom.EntityReference;
023: import org.w3c.dom.NamedNodeMap;
024: import org.w3c.dom.Node;
025:
026: /**
027: * EntityReference models the XML &entityname; syntax, when used for
028: * entities defined by the DOM. Entities hardcoded into XML, such as
029: * character entities, should instead have been translated into text
030: * by the code which generated the DOM tree.
031: * <P>
032: * An XML processor has the alternative of fully expanding Entities
033: * into the normal document tree. If it does so, no EntityReference nodes
034: * will appear.
035: * <P>
036: * Similarly, non-validating XML processors are not required to read
037: * or process entity declarations made in the external subset or
038: * declared in external parameter entities. Hence, some applications
039: * may not make the replacement value available for Parsed Entities
040: * of these types.
041: * <P>
042: * EntityReference behaves as a read-only node, and the children of
043: * the EntityReference (which reflect those of the Entity, and should
044: * also be read-only) give its replacement value, if any. They are
045: * supposed to automagically stay in synch if the DocumentType is
046: * updated with new values for the Entity.
047: * <P>
048: * The defined behavior makes efficient storage difficult for the DOM
049: * implementor. We can't just look aside to the Entity's definition
050: * in the DocumentType since those nodes have the wrong parent (unless
051: * we can come up with a clever "imaginary parent" mechanism). We
052: * must at least appear to clone those children... which raises the
053: * issue of keeping the reference synchronized with its parent.
054: * This leads me back to the "cached image of centrally defined data"
055: * solution, much as I dislike it.
056: * <P>
057: * For now I have decided, since REC-DOM-Level-1-19980818 doesn't
058: * cover this in much detail, that synchronization doesn't have to be
059: * considered while the user is deep in the tree. That is, if you're
060: * looking within one of the EntityReferennce's children and the Entity
061: * changes, you won't be informed; instead, you will continue to access
062: * the same object -- which may or may not still be part of the tree.
063: * This is the same behavior that obtains elsewhere in the DOM if the
064: * subtree you're looking at is deleted from its parent, so it's
065: * acceptable here. (If it really bothers folks, we could set things
066: * up so deleted subtrees are walked and marked invalid, but that's
067: * not part of the DOM's defined behavior.)
068: * <P>
069: * As a result, only the EntityReference itself has to be aware of
070: * changes in the Entity. And it can take advantage of the same
071: * structure-change-monitoring code I implemented to support
072: * DeepNodeList.
073: *
074: * @xerces.internal
075: *
076: * @author Arnaud Le Hors, IBM
077: * @author Joe Kesselman, IBM
078: * @author Andy Clark, IBM
079: * @author Ralf Pfeiffer, IBM
080: * @version $Id: EntityReferenceImpl.java 447266 2006-09-18 05:57:49Z mrglavas $
081: * @since PR-DOM-Level-1-19980818.
082: */
083: public class EntityReferenceImpl extends ParentNode implements
084: EntityReference {
085:
086: //
087: // Constants
088: //
089:
090: /** Serialization version. */
091: static final long serialVersionUID = -7381452955687102062L;
092:
093: //
094: // Data
095: //
096:
097: /** Name of Entity referenced */
098: protected String name;
099: /** Base URI*/
100: protected String baseURI;
101:
102: /** Entity changes. */
103: //protected int entityChanges = -1;
104: /** Enable synchronize. */
105: //protected boolean fEnableSynchronize = false;
106: //
107: // Constructors
108: //
109: /** Factory constructor. */
110: public EntityReferenceImpl(CoreDocumentImpl ownerDoc, String name) {
111: super (ownerDoc);
112: this .name = name;
113: isReadOnly(true);
114: needsSyncChildren(true);
115: }
116:
117: //
118: // Node methods
119: //
120:
121: /**
122: * A short integer indicating what type of node this is. The named
123: * constants for this value are defined in the org.w3c.dom.Node interface.
124: */
125: public short getNodeType() {
126: return Node.ENTITY_REFERENCE_NODE;
127: }
128:
129: /**
130: * Returns the name of the entity referenced
131: */
132: public String getNodeName() {
133: if (needsSyncData()) {
134: synchronizeData();
135: }
136: return name;
137: }
138:
139: /** Clone node. */
140: public Node cloneNode(boolean deep) {
141: EntityReferenceImpl er = (EntityReferenceImpl) super
142: .cloneNode(deep);
143: er.setReadOnly(true, deep);
144: return er;
145: }
146:
147: /**
148: * Returns the absolute base URI of this node or null if the implementation
149: * wasn't able to obtain an absolute URI. Note: If the URI is malformed, a
150: * null is returned.
151: *
152: * @return The absolute base URI of this node or null.
153: * @since DOM Level 3
154: */
155: public String getBaseURI() {
156: if (needsSyncData()) {
157: synchronizeData();
158: }
159: if (baseURI == null) {
160: DocumentType doctype;
161: NamedNodeMap entities;
162: EntityImpl entDef;
163: if (null != (doctype = getOwnerDocument().getDoctype())
164: && null != (entities = doctype.getEntities())) {
165:
166: entDef = (EntityImpl) entities
167: .getNamedItem(getNodeName());
168: if (entDef != null) {
169: return entDef.getBaseURI();
170: }
171: }
172: } else if (baseURI != null && baseURI.length() != 0) {// attribute value is always empty string
173: try {
174: return new URI(baseURI).toString();
175: } catch (org.apache.xerces.util.URI.MalformedURIException e) {
176: // REVISIT: what should happen in this case?
177: return null;
178: }
179: }
180: return baseURI;
181: }
182:
183: /** NON-DOM: set base uri*/
184: public void setBaseURI(String uri) {
185: if (needsSyncData()) {
186: synchronizeData();
187: }
188: baseURI = uri;
189: }
190:
191: /**
192: * NON-DOM: compute string representation of the entity reference.
193: * This method is used to retrieve a string value for an attribute node that has child nodes.
194: * @return String representing a value of this entity ref. or
195: * null if any node other than EntityReference, Text is encountered
196: * during computation
197: */
198: protected String getEntityRefValue() {
199: if (needsSyncChildren()) {
200: synchronizeChildren();
201: }
202:
203: String value = "";
204: if (firstChild != null) {
205: if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
206: value = ((EntityReferenceImpl) firstChild)
207: .getEntityRefValue();
208: } else if (firstChild.getNodeType() == Node.TEXT_NODE) {
209: value = firstChild.getNodeValue();
210: } else {
211: // invalid to have other types of nodes in attr value
212: return null;
213: }
214:
215: if (firstChild.nextSibling == null) {
216: return value;
217: } else {
218: StringBuffer buff = new StringBuffer(value);
219: ChildNode next = firstChild.nextSibling;
220: while (next != null) {
221:
222: if (next.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
223: value = ((EntityReferenceImpl) next)
224: .getEntityRefValue();
225: } else if (next.getNodeType() == Node.TEXT_NODE) {
226: value = next.getNodeValue();
227: } else {
228: // invalid to have other types of nodes in attr value
229: return null;
230: }
231: buff.append(value);
232: next = next.nextSibling;
233:
234: }
235: return buff.toString();
236: }
237: }
238: return "";
239: }
240:
241: /**
242: * EntityReference's children are a reflection of those defined in the
243: * named Entity. This method creates them if they haven't been created yet.
244: * This doesn't support editing the Entity though, since this only called
245: * once for all.
246: */
247: protected void synchronizeChildren() {
248: // no need to synchronize again
249: needsSyncChildren(false);
250:
251: DocumentType doctype;
252: NamedNodeMap entities;
253: EntityImpl entDef;
254: if (null != (doctype = getOwnerDocument().getDoctype())
255: && null != (entities = doctype.getEntities())) {
256:
257: entDef = (EntityImpl) entities.getNamedItem(getNodeName());
258:
259: // No Entity by this name, stop here.
260: if (entDef == null)
261: return;
262:
263: // If entity's definition exists, clone its kids
264: isReadOnly(false);
265: for (Node defkid = entDef.getFirstChild(); defkid != null; defkid = defkid
266: .getNextSibling()) {
267: Node newkid = defkid.cloneNode(true);
268: insertBefore(newkid, null);
269: }
270: setReadOnly(true, true);
271: }
272: }
273:
274: /**
275: * NON-DOM: sets the node and its children value.
276: * <P>
277: * Note: make sure that entity reference and its kids could be set readonly.
278: */
279: public void setReadOnly(boolean readOnly, boolean deep) {
280:
281: if (needsSyncData()) {
282: synchronizeData();
283: }
284: if (deep) {
285:
286: if (needsSyncChildren()) {
287: synchronizeChildren();
288: }
289: // Recursively set kids
290: for (ChildNode mykid = firstChild; mykid != null; mykid = mykid.nextSibling) {
291:
292: mykid.setReadOnly(readOnly, true);
293:
294: }
295: }
296: isReadOnly(readOnly);
297: } // setReadOnly(boolean,boolean)
298:
299: /**
300: * Enable the synchronize method which may do cloning. This method is enabled
301: * when the parser is done with an EntityReference.
302: /***
303: // revisit: enable editing of Entity
304: public void enableSynchronize(boolean enableSynchronize) {
305: fEnableSynchronize= enableSynchronize;
306: }
307: /***/
308:
309: /**
310: * EntityReference's children are a reflection of those defined in the
311: * named Entity. This method updates them if the Entity is changed.
312: * <P>
313: * It is unclear what the least-cost resynch mechanism is.
314: * If we expect the kids to be shallow, and/or expect changes
315: * to the Entity contents to be rare, wiping them all out
316: * and recloning is simplest.
317: * <P>
318: * If we expect them to be deep,
319: * it might be better to first decide which kids (if any)
320: * persist, and keep the ones (if any) that are unchanged
321: * rather than doing all the work of cloning them again.
322: * But that latter gets into having to convolve the two child lists,
323: * insert new information in the right order (and possibly reorder
324: * the existing kids), and a few other complexities that I really
325: * don't want to deal with in this implementation.
326: * <P>
327: * Note that if we decide that we need to update the EntityReference's
328: * contents, we have to turn off the readOnly flag temporarily to do so.
329: * When we get around to adding multitasking support, this whole method
330: * should probably be an atomic operation.
331: *
332: * @see DocumentTypeImpl
333: * @see EntityImpl
334: */
335: // The Xerces parser invokes callbacks for startEnityReference
336: // the parsed value of the entity EACH TIME, so it is actually
337: // easier to create the nodes through the callbacks rather than
338: // clone the Entity.
339: /***
340: // revisit: enable editing of Entity
341: private void synchronize() {
342: if (!fEnableSynchronize) {
343: return;
344: }
345: DocumentType doctype;
346: NamedNodeMap entities;
347: EntityImpl entDef;
348: if (null != (doctype = getOwnerDocument().getDoctype()) &&
349: null != (entities = doctype.getEntities())) {
350:
351: entDef = (EntityImpl)entities.getNamedItem(getNodeName());
352:
353: // No Entity by this name. If we had a change count, reset it.
354: if(null==entDef)
355: entityChanges=-1;
356:
357: // If no kids availalble, wipe any pre-existing children.
358: // (See discussion above.)
359: // Note that we have to use the superclass to avoid recursion
360: // through Synchronize.
361: readOnly=false;
362: if(null==entDef || !entDef.hasChildNodes())
363: for(Node kid=super.getFirstChild();
364: kid!=null;
365: kid=super.getFirstChild())
366: removeChild(kid);
367:
368: // If entity's definition changed, clone its kids
369: // (See discussion above.)
370: if(null!=entDef && entDef.changes!=entityChanges) {
371: for(Node defkid=entDef.getFirstChild();
372: defkid!=null;
373: defkid=defkid.getNextSibling()) {
374:
375: NodeImpl newkid=(NodeImpl) defkid.cloneNode(true);
376: newkid.setReadOnly(true,true);
377: insertBefore(newkid,null);
378: }
379: entityChanges=entDef.changes;
380: }
381: readOnly=true;
382: }
383: }
384: /***/
385:
386: } // class EntityReferenceImpl
|