001: package net.sf.saxon.tinytree;
002:
003: import net.sf.saxon.event.Builder;
004: import net.sf.saxon.event.LocationProvider;
005: import net.sf.saxon.event.ReceiverOptions;
006: import net.sf.saxon.om.FastStringBuffer;
007: import net.sf.saxon.style.StandardNames;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.Type;
010:
011: /**
012: * The TinyBuilder class is responsible for taking a stream of SAX events and constructing
013: * a Document tree, using the "TinyTree" implementation.
014: *
015: * @author Michael H. Kay
016: */
017:
018: public class TinyBuilder extends Builder {
019:
020: public static final int PARENT_POINTER_INTERVAL = 10;
021: // a lower value allocates more parent pointers which takes more space but reduces
022: // the length of parent searches
023:
024: private TinyTree tree;
025:
026: private int currentDepth = 0;
027: private int nodeNr = 0; // this is the local sequence within this document
028: private boolean ended = false;
029: private int[] sizeParameters; // estimate of number of nodes, attributes, namespaces, characters
030:
031: public void setSizeParameters(int[] params) {
032: sizeParameters = params;
033: }
034:
035: public int[] getSizeParameters() {
036: int[] params = { tree.numberOfNodes, tree.numberOfAttributes,
037: tree.numberOfNamespaces, tree.charBufferLength };
038: return params;
039: }
040:
041: private int[] prevAtDepth = new int[100];
042: // this array is scaffolding used while constructing the tree, it is
043: // not present in the final tree. For each level of the tree, it records the
044: // node number of the most recent node at that level.
045:
046: private int[] siblingsAtDepth = new int[100];
047: // more scaffolding. For each level of the tree, this array records the
048: // number of siblings processed at that level. When this exceeds a threshold value,
049: // a dummy node is inserted into the arrays to contain a parent pointer: this it to
050: // prevent excessively long searches for a parent node, which is normally found by
051: // scanning the siblings.
052:
053: private boolean isIDElement = false;
054:
055: public TinyTree getTree() {
056: return tree;
057: }
058:
059: /**
060: * Open the event stream
061: */
062:
063: public void open() throws XPathException {
064: if (started) {
065: // this happens when using an IdentityTransformer
066: return;
067: }
068: if (tree == null) {
069: if (sizeParameters == null) {
070: tree = new TinyTree();
071: } else {
072: tree = new TinyTree(sizeParameters[0],
073: sizeParameters[1], sizeParameters[2],
074: sizeParameters[3]);
075: }
076: tree.setConfiguration(config);
077: currentDepth = 0;
078: if (lineNumbering) {
079: tree.setLineNumbering();
080: }
081: }
082: super .open();
083: }
084:
085: /**
086: * Write a document node to the tree
087: */
088:
089: public void startDocument(int properties) throws XPathException {
090: // if (currentDepth == 0 && tree.numberOfNodes != 0) {
091: // System.err.println("**** FOREST DOCUMENT ****");
092: // }
093: if ((started && !ended) || currentDepth > 0) {
094: // this happens when using an IdentityTransformer, or when copying a document node to form
095: // the content of an element
096: return;
097: }
098: started = true;
099:
100: //if (currentRoot==null) {
101: // normal case
102: currentRoot = new TinyDocumentImpl(tree);
103: TinyDocumentImpl doc = (TinyDocumentImpl) currentRoot;
104: doc.setSystemId(getSystemId());
105: doc.setConfiguration(config);
106: //tree.document = doc;
107: // } else {
108: // // document node supplied by user
109: // if (!(currentRoot instanceof TinyDocumentImpl)) {
110: // throw new DynamicError("Document node supplied is of wrong kind (" +
111: // currentRoot.getClass().getName() + ')');
112: // }
113: // if (currentRoot.hasChildNodes()) {
114: // throw new DynamicError("Supplied document is not empty");
115: // }
116: // //currentRoot.setConfiguration(config);
117: // }
118:
119: currentDepth = 0;
120: tree.addDocumentNode((TinyDocumentImpl) currentRoot);
121: prevAtDepth[0] = 0;
122: prevAtDepth[1] = -1;
123: siblingsAtDepth[0] = 0;
124: siblingsAtDepth[1] = 0;
125: tree.next[0] = -1;
126:
127: currentDepth++;
128:
129: super .startDocument(0);
130:
131: }
132:
133: /**
134: * Callback interface for SAX: not for application use
135: */
136:
137: public void endDocument() throws XPathException {
138: // System.err.println("TinyBuilder: " + this + " End document");
139:
140: if (currentDepth > 1)
141: return;
142: // happens when copying a document node as the child of an element
143:
144: if (ended)
145: return; // happens when using an IdentityTransformer
146: ended = true;
147:
148: prevAtDepth[currentDepth] = -1;
149:
150: //super.close();
151: }
152:
153: public void close() throws XPathException {
154: tree.addNode(Type.STOPPER, 0, 0, 0, 0);
155: tree.condense();
156: super .close();
157: }
158:
159: /**
160: * Notify the start tag of an element
161: */
162:
163: public void startElement(int nameCode, int typeCode,
164: int locationId, int properties) throws XPathException {
165: // if (currentDepth == 0 && tree.numberOfNodes != 0) {
166: // System.err.println("**** FOREST ELEMENT ****");
167: // }
168:
169: // if the number of siblings exceeds a certain threshold, add a parent pointer, in the form
170: // of a pseudo-node
171: if (siblingsAtDepth[currentDepth] > PARENT_POINTER_INTERVAL) {
172: nodeNr = tree.addNode(Type.PARENT_POINTER, currentDepth,
173: prevAtDepth[currentDepth - 1], 0, 0);
174: int prev = prevAtDepth[currentDepth];
175: if (prev > 0) {
176: tree.next[prev] = nodeNr;
177: }
178: tree.next[nodeNr] = prevAtDepth[currentDepth - 1];
179: prevAtDepth[currentDepth] = nodeNr;
180: siblingsAtDepth[currentDepth] = 0;
181: }
182:
183: // now add the element node itself
184: nodeNr = tree.addNode(Type.ELEMENT, currentDepth, -1, -1,
185: nameCode);
186:
187: isIDElement = false;
188: if (typeCode != StandardNames.XDT_UNTYPED && typeCode != -1) {
189: tree.setElementAnnotation(nodeNr, typeCode);
190: if (tree.isIDCode(typeCode)) {
191: isIDElement = true;
192: }
193: }
194:
195: if (currentDepth == 0) {
196: prevAtDepth[0] = nodeNr;
197: prevAtDepth[1] = -1;
198: //tree.next[0] = -1;
199: currentRoot = tree.getNode(nodeNr);
200: } else {
201: int prev = prevAtDepth[currentDepth];
202: if (prev > 0) {
203: tree.next[prev] = nodeNr;
204: }
205: tree.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
206: prevAtDepth[currentDepth] = nodeNr;
207: siblingsAtDepth[currentDepth]++;
208: }
209: currentDepth++;
210:
211: if (currentDepth == prevAtDepth.length) {
212: int[] p2 = new int[currentDepth * 2];
213: System.arraycopy(prevAtDepth, 0, p2, 0, currentDepth);
214: prevAtDepth = p2;
215: p2 = new int[currentDepth * 2];
216: System.arraycopy(siblingsAtDepth, 0, p2, 0, currentDepth);
217: siblingsAtDepth = p2;
218: }
219: prevAtDepth[currentDepth] = -1;
220: siblingsAtDepth[currentDepth] = 0;
221:
222: LocationProvider locator = pipe.getLocationProvider();
223: if (locator != null) {
224: tree.setSystemId(nodeNr, locator.getSystemId(locationId));
225: if (lineNumbering) {
226: tree.setLineNumber(nodeNr, locator
227: .getLineNumber(locationId));
228: }
229: }
230: }
231:
232: public void namespace(int namespaceCode, int properties)
233: throws XPathException {
234: tree.addNamespace(nodeNr, namespaceCode);
235: }
236:
237: public void attribute(int nameCode, int typeCode,
238: CharSequence value, int locationId, int properties)
239: throws XPathException {
240: // System.err.println("attribute " + nameCode + "=" + value);
241:
242: properties &= ~ReceiverOptions.DISABLE_ESCAPING;
243: tree.addAttribute(currentRoot, nodeNr, nameCode, typeCode,
244: value, properties);
245: }
246:
247: public void startContent() {
248: nodeNr++;
249: }
250:
251: /**
252: * Callback interface for SAX: not for application use
253: */
254:
255: public void endElement() throws XPathException {
256: prevAtDepth[currentDepth] = -1;
257: siblingsAtDepth[currentDepth] = 0;
258: currentDepth--;
259: if (isIDElement) {
260: tree.indexIDElement(currentRoot, prevAtDepth[currentDepth],
261: config.getNameChecker());
262: isIDElement = false;
263: }
264: }
265:
266: /**
267: * Callback interface for SAX: not for application use
268: */
269:
270: public void characters(CharSequence chars, int locationId,
271: int properties) throws XPathException {
272: if (chars.length() > 0) {
273: //properties &= ~ReceiverOptions.DISABLE_ESCAPING;
274: int bufferStart = tree.charBufferLength;
275: tree.appendChars(chars);
276: int n = tree.numberOfNodes - 1;
277: if (tree.nodeKind[n] == Type.TEXT
278: && tree.depth[n] == currentDepth) {
279: // merge this text node with the previous text node
280: tree.beta[n] += chars.length();
281: } else {
282: nodeNr = tree.addNode(Type.TEXT, currentDepth,
283: bufferStart, chars.length(), -1);
284:
285: int prev = prevAtDepth[currentDepth];
286: if (prev > 0) {
287: tree.next[prev] = nodeNr;
288: }
289: tree.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
290: prevAtDepth[currentDepth] = nodeNr;
291: siblingsAtDepth[currentDepth]++;
292: }
293: }
294: }
295:
296: /**
297: * Callback interface for SAX: not for application use<BR>
298: */
299:
300: public void processingInstruction(String piname,
301: CharSequence remainder, int locationId, int properties)
302: throws XPathException {
303: if (tree.commentBuffer == null) {
304: tree.commentBuffer = new FastStringBuffer(200);
305: }
306: int s = tree.commentBuffer.length();
307: tree.commentBuffer.append(remainder.toString());
308: int nameCode = namePool.allocate("", "", piname);
309:
310: nodeNr = tree.addNode(Type.PROCESSING_INSTRUCTION,
311: currentDepth, s, remainder.length(), nameCode);
312:
313: int prev = prevAtDepth[currentDepth];
314: if (prev > 0) {
315: tree.next[prev] = nodeNr;
316: }
317: tree.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
318: prevAtDepth[currentDepth] = nodeNr;
319: siblingsAtDepth[currentDepth]++;
320:
321: LocationProvider locator = pipe.getLocationProvider();
322: if (locator != null) {
323: tree.setSystemId(nodeNr, locator.getSystemId(locationId));
324: if (lineNumbering) {
325: tree.setLineNumber(nodeNr, locator
326: .getLineNumber(locationId));
327: }
328: }
329: }
330:
331: /**
332: * Callback interface for SAX: not for application use
333: */
334:
335: public void comment(CharSequence chars, int locationId,
336: int properties) throws XPathException {
337: if (tree.commentBuffer == null) {
338: tree.commentBuffer = new FastStringBuffer(200);
339: }
340: int s = tree.commentBuffer.length();
341: tree.commentBuffer.append(chars.toString());
342: nodeNr = tree.addNode(Type.COMMENT, currentDepth, s, chars
343: .length(), -1);
344:
345: int prev = prevAtDepth[currentDepth];
346: if (prev > 0) {
347: tree.next[prev] = nodeNr;
348: }
349: tree.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
350: prevAtDepth[currentDepth] = nodeNr;
351: siblingsAtDepth[currentDepth]++;
352:
353: }
354:
355: /**
356: * Set an unparsed entity in the document
357: */
358:
359: public void setUnparsedEntity(String name, String uri,
360: String publicId) {
361: ((TinyDocumentImpl) currentRoot).setUnparsedEntity(name, uri,
362: publicId);
363: }
364:
365: }
366:
367: //
368: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
369: // you may not use this file except in compliance with the License. You may obtain a copy of the
370: // License at http://www.mozilla.org/MPL/
371: //
372: // Software distributed under the License is distributed on an "AS IS" basis,
373: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
374: // See the License for the specific language governing rights and limitations under the License.
375: //
376: // The Original Code is: all this file.
377: //
378: // The Initial Developer of the Original Code is Michael H. Kay.
379: //
380: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
381: //
382: // Contributor(s): none.
383: //
|