KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > tinytree > TinyDocumentImpl


1 package com.icl.saxon.tinytree;
2 import com.icl.saxon.om.*;
3 import com.icl.saxon.om.Axis;
4 import com.icl.saxon.Controller;
5 import com.icl.saxon.Context;
6 import com.icl.saxon.PreparedStyleSheet;
7 import com.icl.saxon.PIGrabber;
8 import com.icl.saxon.KeyManager;
9 import com.icl.saxon.expr.NodeSetExtent;
10 import com.icl.saxon.expr.XPathException;
11 import com.icl.saxon.output.Outputter;
12 import com.icl.saxon.sort.LocalOrderComparer;
13 import com.icl.saxon.pattern.Pattern;
14 import com.icl.saxon.pattern.AnyNodeTest;
15 import com.icl.saxon.tree.LineNumberMap;
16 import com.icl.saxon.tree.SystemIdMap;
17 import java.util.Hashtable JavaDoc;
18 import java.net.URL JavaDoc;
19
20 import org.w3c.dom.*;
21 import javax.xml.transform.TransformerException JavaDoc;
22
23
24 /**
25   * A node in the XML parse tree representing the Document itself (or equivalently, the root
26   * node of the Document).<P>
27   * @author <A HREF="mailto:mhkay@iclway.co.uk>Michael H. Kay</A>
28   * @version 26 April 1999
29   */

30
31 public final class TinyDocumentImpl extends TinyParentNodeImpl
32     implements DocumentInfo, Document {
33
34     private Hashtable JavaDoc idTable = null;
35     private NamePool namePool;
36     private Hashtable JavaDoc elementList = null;
37     private boolean usesNamespaces = false;
38     private Hashtable JavaDoc entityTable = null;
39
40     // the contents of the document
41

42     protected char[] charBuffer = new char[4000];
43     protected int charBufferLength = 0;
44     protected StringBuffer JavaDoc commentBuffer = new StringBuffer JavaDoc(500);
45
46     protected int numberOfNodes = 0; // excluding attributes and namespaces
47
protected int lastLevelOneNode = -1;
48
49     protected byte[] nodeType = new byte[4000];
50     protected short[] depth = new short[4000];
51     /*NEXT*/ protected int[] next = new int[4000];
52     protected int[] offset = new int[4000];
53     protected int[] length = new int[4000];
54     protected int[] nameCode = new int[4000];
55     // the prior array indexes preceding-siblings; it is constructed only when required
56
protected int[] prior = null;
57
58     protected int numberOfAttributes = 0;
59     protected int[] attParent = new int[100];
60     protected int[] attCode = new int[100];
61     protected String JavaDoc[] attValue = new String JavaDoc[100];
62
63     protected int numberOfNamespaces = 0;
64     protected int[] namespaceParent = new int[20];
65     protected int[] namespaceCode = new int[20];
66     
67     private LineNumberMap lineNumberMap;
68     private SystemIdMap systemIdMap = new SystemIdMap();
69
70     // list of indexes for keys. Each entry is a triple: KeyManager, Fingerprint of Name of Key, Hashtable.
71
// This reflects the fact that the same document may contain indexes for more than one stylesheet.
72

73     private Object JavaDoc[] index = new Object JavaDoc[30];
74     private int indexEntriesUsed = 0;
75
76
77     public TinyDocumentImpl() {
78         nodeNr = 0;
79         document = this;
80     }
81
82     /**
83     * Set the name pool used for all names in this document
84     */

85     
86     public void setNamePool(NamePool pool) {
87         namePool = pool;
88         addNamespace(0, pool.getNamespaceCode("xml", Namespace.XML));
89     }
90     
91     /**
92     * Get the name pool used for the names in this document
93     */

94     
95     public NamePool getNamePool() {
96         return namePool;
97     }
98
99     protected void ensureNodeCapacity() {
100         if (nodeType.length < numberOfNodes+1) {
101             int k = numberOfNodes*2;
102
103             byte[] nodeType2 = new byte[k];
104             /*NEXT*/ int[] next2 = new int[k];
105             short[] depth2 = new short[k];
106             int[] offset2 = new int[k];
107             int[] length2 = new int[k];
108             int[] nameCode2 = new int[k];
109
110             System.arraycopy(nodeType, 0, nodeType2, 0, numberOfNodes);
111             /*NEXT*/ System.arraycopy(next, 0, next2, 0, numberOfNodes);
112             System.arraycopy(depth, 0, depth2, 0, numberOfNodes);
113             System.arraycopy(offset, 0, offset2, 0, numberOfNodes);
114             System.arraycopy(length, 0, length2, 0, numberOfNodes);
115             System.arraycopy(nameCode, 0, nameCode2, 0, numberOfNodes);
116
117             nodeType = nodeType2;
118             /*NEXT*/ next = next2;
119             depth = depth2;
120             offset = offset2;
121             length = length2;
122             nameCode = nameCode2;
123         }
124     }
125
126     protected void ensureAttributeCapacity() {
127         if (attParent.length < numberOfAttributes+1) {
128             int k = numberOfAttributes*2;
129
130             int[] attParent2 = new int[k];
131             int[] attCode2 = new int[k];
132             //byte[] attType2 = new byte[k];
133
String JavaDoc[] attValue2 = new String JavaDoc[k];
134
135
136             System.arraycopy(attParent, 0, attParent2, 0, numberOfAttributes);
137             System.arraycopy(attCode, 0, attCode2, 0, numberOfAttributes);
138             //System.arraycopy(attType, 0, attType2, 0, numberOfAttributes);
139
System.arraycopy(attValue, 0, attValue2, 0, numberOfAttributes);
140
141             attParent = attParent2;
142             attCode = attCode2;
143             //attType = attType2;
144
attValue = attValue2;
145         }
146     }
147
148     protected void ensureNamespaceCapacity() {
149         if (namespaceParent.length < numberOfNamespaces+1) {
150             int k = numberOfNamespaces*2;
151
152             int[] namespaceParent2 = new int[k];
153             int[] namespaceCode2 = new int[k];
154
155             System.arraycopy(namespaceParent, 0, namespaceParent2, 0, numberOfNamespaces);
156             System.arraycopy(namespaceCode, 0, namespaceCode2, 0, numberOfNamespaces);
157
158             namespaceParent = namespaceParent2;
159             namespaceCode = namespaceCode2;
160         }
161     }
162
163     protected void addNode(short type0, int depth0, int offset0, int length0, int nameCode0) {
164         ensureNodeCapacity();
165         nodeType[numberOfNodes] = (byte)type0;
166         depth[numberOfNodes] = (short)depth0;
167         offset[numberOfNodes] = offset0;
168         length[numberOfNodes] = length0;
169         nameCode[numberOfNodes] = nameCode0;
170         /*NEXT*/ next[numberOfNodes] = -1; // safety precaution, esp for preview mode
171

172         if (depth0 == 1) lastLevelOneNode = numberOfNodes;
173         
174         numberOfNodes++;
175     }
176     
177     protected void appendChars(char[] chars, int start, int length) {
178         while (charBuffer.length < charBufferLength + length) {
179             char[] ch2 = new char[charBuffer.length * 2];
180             System.arraycopy(charBuffer, 0, ch2, 0, charBufferLength);
181             charBuffer = ch2;
182         }
183         System.arraycopy(chars, start, charBuffer, charBufferLength, length);
184         charBufferLength += length;
185     }
186     
187     /**
188     * Truncate the tree: used in preview mode to delete an element after it has
189     * been processed
190     */

191     
192     protected void truncate(int nodes) {
193         
194         if (nodes==numberOfNodes) return;
195
196         // shrink the text buffer
197
for (int i=nodes; i<numberOfNodes; i++) {
198             if (nodeType[i]==NodeInfo.TEXT) {
199                 charBufferLength = offset[i];
200                 break;
201             }
202         }
203         
204         // shrink the attributes array
205
for (int i=nodes; i<numberOfNodes; i++) {
206             if (nodeType[i]==NodeInfo.ELEMENT && offset[i]>=0) {
207                 numberOfAttributes = offset[i];
208                 break;
209             }
210         }
211
212         // shrink the namespace array
213
for (int i=nodes; i<numberOfNodes; i++) {
214             if (nodeType[i]==NodeInfo.ELEMENT && length[i]>=0) {
215                 numberOfNamespaces = length[i];
216                 break;
217             }
218         }
219         
220         // TODO: shrink the comment buffer
221

222         // shrink the main node array
223
numberOfNodes = nodes;
224         
225         // kill the prior index, to be rebuilt when needed
226
prior = null;
227         
228         // add a dummy node at the end, because some axes such as "following"
229
// can otherwise walk off the end
230

231         nodeType[nodes] = (byte)NodeInfo.ROOT;
232         depth[nodes] = 0;
233         // System.err.println("After truncate:"); diagnosticDump();
234
}
235
236     /**
237     * On demand, make an index for quick access to preceding-sibling nodes
238     */

239     
240     protected void ensurePriorIndex() {
241         if (prior==null) {
242             makePriorIndex();
243         }
244     }
245     
246     private synchronized void makePriorIndex() {
247         prior = new int[numberOfNodes];
248         for (int i=0; i<numberOfNodes; i++) {
249             prior[i] = -1;
250         }
251         for (int i=0; i<numberOfNodes; i++) {
252             int nextNode = next[i];
253             if (nextNode!=-1) {
254                 prior[nextNode] = i;
255             }
256         }
257     }
258  
259
260     protected void addAttribute(int parent0, int code0, String JavaDoc type0, String JavaDoc value0) {
261         ensureAttributeCapacity();
262         attParent[numberOfAttributes] = parent0;
263         attCode[numberOfAttributes] = code0;
264         attValue[numberOfAttributes] = value0;
265         numberOfAttributes++;
266         
267         if (type0.equals("ID")) {
268             if (idTable==null) {
269                 idTable = new Hashtable JavaDoc();
270             }
271             NodeInfo e = getNode(parent0);
272             registerID(e, value0);
273         }
274     }
275
276     protected void addNamespace(int parent0, int nscode0 ) {
277         usesNamespaces = true;
278         ensureNamespaceCapacity();
279         namespaceParent[numberOfNamespaces] = parent0;
280         namespaceCode[numberOfNamespaces] = nscode0;
281         numberOfNamespaces++;
282     }
283
284     public TinyNodeImpl getNode(int nr) {
285         switch ((short)nodeType[nr]) {
286             case NodeInfo.ROOT:
287                 return this;
288             case NodeInfo.ELEMENT:
289                 return new TinyElementImpl(this, nr);
290             case NodeInfo.TEXT:
291                 return new TinyTextImpl(this, nr);
292             case NodeInfo.COMMENT:
293                 return new TinyCommentImpl(this, nr);
294             case NodeInfo.PI:
295                 return new TinyProcInstImpl(this, nr);
296         }
297         return null;
298     }
299
300     /**
301     * Get the node sequence number (in document order). Sequence numbers are monotonic but not
302     * consecutive.
303     */

304
305     public long getSequenceNumber() {
306         return 0;
307     }
308
309     /**
310     * Make a (transient) attribute node from the array of attributes
311     */

312
313     protected TinyAttributeImpl getAttributeNode(int nr) {
314         return new TinyAttributeImpl(this, nr);
315     }
316
317     /**
318     * determine whether this document uses namespaces
319     */

320
321     protected boolean isUsingNamespaces() {
322         return usesNamespaces;
323     }
324
325     /**
326     * Make a (transient) namespace node from the array of namespace declarations
327     */

328
329     protected TinyNamespaceImpl getNamespaceNode(int nr) {
330         return new TinyNamespaceImpl(this, nr);
331     }
332
333     /**
334     * Set the system id of this node
335     */

336
337     public void setSystemId(String JavaDoc uri) {
338         //if (uri==null) {
339
// throw new IllegalArgumentException("System ID must not be null");
340
//}
341
if (uri==null) {
342             uri = "";
343         }
344         systemIdMap.setSystemId(nodeNr, uri);
345     }
346         
347     /**
348     * Get the system id of this root node
349     */

350
351     public String JavaDoc getSystemId() {
352         return systemIdMap.getSystemId(nodeNr);
353     }
354
355     /**
356     * Get the base URI of this root node. For a root node the base URI is the same as the
357     * System ID.
358     */

359
360     public String JavaDoc getBaseURI() {
361         return getSystemId();
362     }
363
364     /**
365     * Set the system id of an element in the document
366     */

367
368     protected void setSystemId(int seq, String JavaDoc uri) {
369         //if (uri==null) {
370
// throw new IllegalArgumentException("System ID must not be null");
371
//}
372
if (uri==null) {
373             uri = "";
374         }
375         systemIdMap.setSystemId(seq, uri);
376     }
377         
378
379     /**
380     * Get the system id of an element in the document
381     */

382
383     protected String JavaDoc getSystemId(int seq) {
384         return systemIdMap.getSystemId(seq);
385     }
386
387
388     /**
389     * Set line numbering on
390     */

391
392     public void setLineNumbering() {
393         lineNumberMap = new LineNumberMap();
394         lineNumberMap.setLineNumber(0, 0);
395     }
396
397     /**
398     * Set the line number for an element. Ignored if line numbering is off.
399     */

400
401     protected void setLineNumber(int sequence, int line) {
402         if (lineNumberMap != null) {
403             lineNumberMap.setLineNumber(sequence, line);
404         }
405     }
406
407     /**
408     * Get the line number for an element. Return -1 if line numbering is off.
409     */

410
411     protected int getLineNumber(int sequence) {
412         if (lineNumberMap != null) {
413             return lineNumberMap.getLineNumber(sequence);
414         }
415         return -1;
416     }
417
418     /**
419     * Get the line number of this root node.
420     * @return 0 always
421     */

422
423     public int getLineNumber() {
424         return 0;
425     }
426
427     /**
428     * Return the type of node.
429     * @return NodeInfo.ROOT (always)
430     */

431
432     public final short getNodeType() {
433         return ROOT;
434     }
435
436     /**
437      * Find the parent node of this node.
438      * @return The Node object describing the containing element or root node.
439      */

440
441     public NodeInfo getParent() {
442         return null;
443     }
444     
445     /**
446     * Get the root (document) node
447     * @return the DocumentInfo representing this document
448     */

449
450     public DocumentInfo getDocumentRoot() {
451         return this;
452     }
453
454     /**
455     * Get a character string that uniquely identifies this node within the document
456     * @return the empty string
457     */

458
459     public String JavaDoc generateId() {
460         return "";
461     }
462
463     /**
464     * Get a unique number identifying this document
465     */

466
467     //public int getDocumentNumber() {
468
// return documentNumber;
469
//}
470

471     /**
472     * Get a list of all elements with a given name. This is implemented
473     * as a memo function: the first time it is called for a particular
474     * element type, it remembers the result for next time.
475     */

476
477     protected AxisEnumeration getAllElements(int fingerprint) {
478         Integer JavaDoc key = new Integer JavaDoc(fingerprint);
479         if (elementList==null) {
480             elementList = new Hashtable JavaDoc();
481         }
482         NodeSetExtent list = (NodeSetExtent)elementList.get(key);
483         if (list==null) {
484             list = new NodeSetExtent(LocalOrderComparer.getInstance());
485             list.setSorted(true);
486             for (int i=1; i<numberOfNodes; i++) {
487                 if (nodeType[i]==NodeInfo.ELEMENT &&
488                         (nameCode[i] & 0xfffff ) == fingerprint) {
489                     list.append(getNode(i));
490                 }
491             }
492             elementList.put(key, list);
493         }
494         return (AxisEnumeration)list.enumerate();
495     }
496                     
497     /**
498     * Register a unique element ID. Fails if there is already an element with that ID.
499     * @param e The NodeInfo (always an element) having a particular unique ID value
500     * @param id The unique ID value
501     */

502
503     private void registerID(NodeInfo e, String JavaDoc id) {
504         // the XPath spec (5.2.1) says ignore the second ID if it's not unique
505
NodeInfo old = (NodeInfo)idTable.get(id);
506         if (old==null) {
507             idTable.put(id, e);
508         }
509         
510     }
511
512     /**
513     * Get the element with a given ID.
514     * @param id The unique ID of the required element, previously registered using registerID()
515     * @return The NodeInfo (always an Element) for the given ID if one has been registered,
516     * otherwise null.
517     */

518
519     public NodeInfo selectID(String JavaDoc id) {
520         if (idTable==null) return null; // no ID values found
521
return (NodeInfo)idTable.get(id);
522     }
523
524     /**
525     * Get the index for a given key
526     * @param keymanager The key manager managing this key
527     * @int fingerprint The fingerprint of the name of the key (unique with the key manager)
528     * @return The index, if one has been built, in the form of a Hashtable that
529     * maps the key value to a set of nodes having that key value. If no index
530     * has been built, returns null.
531     */

532
533     public synchronized Hashtable JavaDoc getKeyIndex(KeyManager keymanager, int fingerprint) {
534         for (int k=0; k<indexEntriesUsed; k+=3) {
535             if (((KeyManager)index[k])==keymanager &&
536                      ((Integer JavaDoc)index[k+1]).intValue() == fingerprint) {
537                 Object JavaDoc ix = index[k+2];
538                 return (Hashtable JavaDoc)index[k+2];
539             }
540         }
541         return null;
542     }
543
544     /**
545     * Set the index for a given key. The method is synchronized because the same document
546     * can be used by several stylesheets at the same time.
547     * @param keymanager The key manager managing this key
548     * @param fingerprint The fingerprint of the name of the key (unique with the key manager)
549     * @param keyindex the index, in the form of a Hashtable that
550     * maps the key value to a set of nodes having that key value. Or the String
551     * "under construction", indicating that the index is being built.
552     */

553
554     public synchronized void setKeyIndex(KeyManager keymanager, int fingerprint, Hashtable JavaDoc keyindex) {
555         for (int k=0; k<indexEntriesUsed; k+=3) {
556             if (((KeyManager)index[k])==keymanager &&
557                      ((Integer JavaDoc)index[k+1]).intValue()==fingerprint) {
558                 index[k+2] = keyindex;
559                 return;
560             }
561         }
562
563         if (indexEntriesUsed+3 >= index.length) {
564             Object JavaDoc[] index2 = new Object JavaDoc[indexEntriesUsed*2];
565             System.arraycopy(index, 0, index2, 0, indexEntriesUsed);
566             index = index2;
567         }
568         index[indexEntriesUsed++] = keymanager;
569         index[indexEntriesUsed++] = new Integer JavaDoc(fingerprint);
570         index[indexEntriesUsed++] = keyindex;
571     }
572
573     /**
574     * Set an unparsed entity URI associated with this document. For system use only, while
575     * building the document.
576     */

577
578     protected void setUnparsedEntity(String JavaDoc name, String JavaDoc uri) {
579         if (entityTable==null) {
580             entityTable = new Hashtable JavaDoc();
581         }
582         entityTable.put(name, uri);
583     }
584
585     /**
586     * Get the unparsed entity with a given name
587     * @param name the name of the entity
588     * @return the URI of the entity if there is one, or empty string if not
589     */

590
591     public String JavaDoc getUnparsedEntity(String JavaDoc name) {
592         if (entityTable==null) {
593             return "";
594         }
595         String JavaDoc uri = (String JavaDoc)entityTable.get(name);
596         return (uri==null ? "" : uri);
597     }
598
599     /**
600     * Copy this node to a given outputter
601     */

602
603     public void copy(Outputter out) throws TransformerException JavaDoc {
604
605         // TODO: this could be optimized by walking all the descendants in order,
606
// instead of doing a recursive tree walk. It would be necessary to maintain
607
// a stack, so that end tags could be written when the depth decreases.
608

609         // output the children
610

611         AxisEnumeration children =
612             getEnumeration(Axis.CHILD, AnyNodeTest.getInstance());
613                     
614         while (children.hasMoreElements()) {
615             children.nextElement().copy(out);
616         }
617     }
618
619     /**
620     * Produce diagnostic print of main tree arrays
621     */

622     
623     public void diagnosticDump() {
624         System.err.println("Node\ttype\tdepth\toffset\tlength");
625         for (int i=0; i<numberOfNodes; i++) {
626             System.err.println(i + "\t" + nodeType[i] + "\t" + depth[i] + "\t" +
627                                      offset[i] + "\t" + length[i] + "\t" + Navigator.getPath(getNode(i)));
628         }
629     }
630     
631 }
632
633 //
634
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
635
// you may not use this file except in compliance with the License. You may obtain a copy of the
636
// License at http://www.mozilla.org/MPL/
637
//
638
// Software distributed under the License is distributed on an "AS IS" basis,
639
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
640
// See the License for the specific language governing rights and limitations under the License.
641
//
642
// The Original Code is: all this file except PB-SYNC section.
643
//
644
// The Initial Developer of the Original Code is
645
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
646
//
647
// Portions marked PB-SYNC are Copyright (C) Peter Bryant (pbryant@bigfoot.com). All Rights Reserved.
648
//
649
// Contributor(s): Michael Kay, Peter Bryant.
650
//
651
Popular Tags