001: package net.sf.saxon.tree;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.event.Receiver;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.trans.XPathException;
007: import net.sf.saxon.type.Type;
008:
009: import java.util.ArrayList;
010: import java.util.HashMap;
011:
012: /**
013: * A node in the XML parse tree representing the Document itself (or equivalently, the root
014: * node of the Document).<P>
015: * @author Michael H. Kay
016: */
017:
018: public final class DocumentImpl extends ParentNodeImpl implements
019: DocumentInfo {
020:
021: //private static int nextDocumentNumber = 0;
022:
023: private ElementImpl documentElement;
024:
025: private HashMap idTable = null;
026: private int documentNumber;
027: private HashMap entityTable = null;
028: private HashMap elementList = null;
029: //private StringBuffer characterBuffer;
030: private Configuration config;
031: private LineNumberMap lineNumberMap;
032: private SystemIdMap systemIdMap = new SystemIdMap();
033:
034: public DocumentImpl() {
035: parent = null;
036: }
037:
038: /**
039: * Set the Configuration that contains this document
040: */
041:
042: public void setConfiguration(Configuration config) {
043: this .config = config;
044: documentNumber = config.getDocumentNumberAllocator()
045: .allocateDocumentNumber();
046: }
047:
048: /**
049: * Get the configuration previously set using setConfiguration
050: */
051:
052: public Configuration getConfiguration() {
053: return config;
054: }
055:
056: /**
057: * Get the name pool used for the names in this document
058: */
059:
060: public NamePool getNamePool() {
061: return config.getNamePool();
062: }
063:
064: /**
065: * Get the unique document number
066: */
067:
068: public int getDocumentNumber() {
069: return documentNumber;
070: }
071:
072: /**
073: * Set the top-level element of the document (variously called the root element or the
074: * document element). Note that a DocumentImpl may represent the root of a result tree
075: * fragment, in which case there is no document element.
076: * @param e the top-level element
077: */
078:
079: void setDocumentElement(ElementImpl e) {
080: documentElement = e;
081: }
082:
083: /**
084: * Set the system id of this node
085: */
086:
087: public void setSystemId(String uri) {
088: if (uri == null) {
089: uri = "";
090: }
091: systemIdMap.setSystemId(sequence, uri);
092: }
093:
094: /**
095: * Get the system id of this root node
096: */
097:
098: public String getSystemId() {
099: return systemIdMap.getSystemId(sequence);
100: }
101:
102: /**
103: * Get the base URI of this root node. For a root node the base URI is the same as the
104: * System ID.
105: */
106:
107: public String getBaseURI() {
108: return getSystemId();
109: }
110:
111: /**
112: * Set the system id of an element in the document
113: */
114:
115: void setSystemId(int seq, String uri) {
116: if (uri == null) {
117: uri = "";
118: }
119: systemIdMap.setSystemId(seq, uri);
120: }
121:
122: /**
123: * Get the system id of an element in the document
124: */
125:
126: String getSystemId(int seq) {
127: return systemIdMap.getSystemId(seq);
128: }
129:
130: /**
131: * Set line numbering on
132: */
133:
134: public void setLineNumbering() {
135: lineNumberMap = new LineNumberMap();
136: lineNumberMap.setLineNumber(sequence, 0);
137: }
138:
139: /**
140: * Set the line number for an element. Ignored if line numbering is off.
141: */
142:
143: void setLineNumber(int sequence, int line) {
144: if (lineNumberMap != null) {
145: lineNumberMap.setLineNumber(sequence, line);
146: }
147: }
148:
149: /**
150: * Get the line number for an element. Return -1 if line numbering is off.
151: */
152:
153: int getLineNumber(int sequence) {
154: if (lineNumberMap != null) {
155: return lineNumberMap.getLineNumber(sequence);
156: }
157: return -1;
158: }
159:
160: /**
161: * Get the line number of this root node.
162: * @return 0 always
163: */
164:
165: public int getLineNumber() {
166: return 0;
167: }
168:
169: /**
170: * Return the type of node.
171: * @return Type.DOCUMENT (always)
172: */
173:
174: public final int getNodeKind() {
175: return Type.DOCUMENT;
176: }
177:
178: /**
179: * Get next sibling - always null
180: * @return null
181: */
182:
183: public final NodeInfo getNextSibling() {
184: return null;
185: }
186:
187: /**
188: * Get previous sibling - always null
189: * @return null
190: */
191:
192: public final NodeInfo getPreviousSibling() {
193: return null;
194: }
195:
196: /**
197: * Get the root (outermost) element.
198: * @return the Element node for the outermost element of the document.
199: */
200:
201: public ElementImpl getDocumentElement() {
202: return documentElement;
203: }
204:
205: /**
206: * Get the root node
207: * @return the NodeInfo representing the root of this tree
208: */
209:
210: public NodeInfo getRoot() {
211: return this ;
212: }
213:
214: /**
215: * Get the root (document) node
216: * @return the DocumentInfo representing this document
217: */
218:
219: public DocumentInfo getDocumentRoot() {
220: return this ;
221: }
222:
223: /**
224: * Get a character string that uniquely identifies this node
225: * @return a string based on the document number
226: */
227:
228: public String generateId() {
229: return "d" + documentNumber;
230: }
231:
232: /**
233: * Get a list of all elements with a given name fingerprint
234: */
235:
236: AxisIterator getAllElements(int fingerprint) {
237: Integer elkey = new Integer(fingerprint);
238: if (elementList == null) {
239: elementList = new HashMap(500);
240: }
241: ArrayList list = (ArrayList) elementList.get(elkey);
242: if (list == null) {
243: list = new ArrayList(500);
244: NodeImpl next = getNextInDocument(this );
245: while (next != null) {
246: if (next.getNodeKind() == Type.ELEMENT
247: && next.getFingerprint() == fingerprint) {
248: list.add(next);
249: }
250: next = next.getNextInDocument(this );
251: }
252: elementList.put(elkey, list);
253: }
254: return new ListIterator(list);
255: }
256:
257: /**
258: * Index all the ID attributes. This is done the first time the id() function
259: * is used on this document
260: */
261:
262: private void indexIDs() {
263: if (idTable != null)
264: return; // ID's are already indexed
265: idTable = new HashMap(256);
266: NameChecker checker = getConfiguration().getNameChecker();
267:
268: NodeImpl curr = this ;
269: NodeImpl root = curr;
270: while (curr != null) {
271: if (curr.getNodeKind() == Type.ELEMENT) {
272: ElementImpl e = (ElementImpl) curr;
273: AttributeCollection atts = e.getAttributeList();
274: for (int i = 0; i < atts.getLength(); i++) {
275: if (atts.isId(i)
276: && checker.isValidNCName(atts.getValue(i)
277: .trim())) {
278: // don't index any invalid IDs - these can arise when using a non-validating parser
279: registerID(e, atts.getValue(i).trim());
280: }
281: }
282: }
283: curr = curr.getNextInDocument(root);
284: }
285: }
286:
287: /**
288: * Register a unique element ID. Fails if there is already an element with that ID.
289: * @param e The Element having a particular unique ID value
290: * @param id The unique ID value
291: */
292:
293: private void registerID(NodeInfo e, String id) {
294: // the XPath spec (5.2.1) says ignore the second ID if it's not unique
295: Object old = idTable.get(id);
296: if (old == null) {
297: idTable.put(id, e);
298: }
299:
300: }
301:
302: /**
303: * Get the element with a given ID.
304: * @param id The unique ID of the required element, previously registered using registerID()
305: * @return The NodeInfo for the given ID if one has been registered, otherwise null.
306: */
307:
308: public NodeInfo selectID(String id) {
309: if (idTable == null)
310: indexIDs();
311: return (NodeInfo) idTable.get(id);
312: }
313:
314: /**
315: * Set an unparsed entity URI associated with this document. For system use only, while
316: * building the document.
317: */
318:
319: void setUnparsedEntity(String name, String uri, String publicId) {
320: // System.err.println("setUnparsedEntity( " + name + "," + uri + ")");
321: if (entityTable == null) {
322: entityTable = new HashMap(10);
323: }
324: String[] ids = new String[2];
325: ids[0] = uri;
326: ids[1] = publicId;
327: entityTable.put(name, ids);
328: }
329:
330: /**
331: * Get the unparsed entity with a given name
332: * @param name the name of the entity
333: * @return if the entity exists, return an array of two Strings, the first holding the system ID
334: * of the entity, the second holding the public ID if there is one, or null if not. If the entity
335: * does not exist, return null. * @return the URI of the entity if there is one, or empty string if not
336: */
337:
338: public String[] getUnparsedEntity(String name) {
339: if (entityTable == null) {
340: return null;
341: }
342: return (String[]) entityTable.get(name);
343: }
344:
345: /**
346: * Copy this node to a given outputter
347: */
348:
349: public void copy(Receiver out, int whichNamespaces,
350: boolean copyAnnotations, int locationId)
351: throws XPathException {
352: out.startDocument(0);
353: NodeImpl next = (NodeImpl) getFirstChild();
354: while (next != null) {
355: next
356: .copy(out, whichNamespaces, copyAnnotations,
357: locationId);
358: next = (NodeImpl) next.getNextSibling();
359: }
360: out.endDocument();
361: }
362:
363: }
364:
365: //
366: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
367: // you may not use this file except in compliance with the License. You may obtain a copy of the
368: // License at http://www.mozilla.org/MPL/
369: //
370: // Software distributed under the License is distributed on an "AS IS" basis,
371: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
372: // See the License for the specific language governing rights and limitations under the License.
373: //
374: // The Original Code is: all this file
375: //
376: // The Initial Developer of the Original Code is Michael H. Kay.
377: //
378: // Contributor(s):
379: //
|