001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: SAXImpl.java 1225579 2011-12-29 15:56:06Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.dom;
023    
024    import java.util.Enumeration;
025    import java.util.Iterator;
026    import java.util.Map;
027    
028    import javax.xml.transform.Source;
029    import javax.xml.transform.dom.DOMSource;
030    
031    import org.apache.xalan.xsltc.DOM;
032    import org.apache.xalan.xsltc.DOMEnhancedForDTM;
033    import org.apache.xalan.xsltc.StripFilter;
034    import org.apache.xalan.xsltc.TransletException;
035    import org.apache.xalan.xsltc.runtime.BasisLibrary;
036    import org.apache.xalan.xsltc.runtime.Hashtable;
037    import org.apache.xml.dtm.Axis;
038    import org.apache.xml.dtm.DTM;
039    import org.apache.xml.dtm.DTMAxisIterator;
040    import org.apache.xml.dtm.DTMManager;
041    import org.apache.xml.dtm.DTMWSFilter;
042    import org.apache.xml.dtm.ref.DTMAxisIterNodeList;
043    import org.apache.xml.dtm.ref.DTMDefaultBase;
044    import org.apache.xml.dtm.ref.DTMNodeProxy;
045    import org.apache.xml.dtm.ref.EmptyIterator;
046    import org.apache.xml.dtm.ref.sax2dtm.SAX2DTM2;
047    import org.apache.xml.serializer.SerializationHandler;
048    import org.apache.xml.serializer.ToXMLSAXHandler;
049    import org.apache.xml.utils.SystemIDResolver;
050    import org.apache.xml.utils.XMLStringFactory;
051    import org.w3c.dom.Document;
052    import org.w3c.dom.DocumentType;
053    import org.w3c.dom.Entity;
054    import org.w3c.dom.NamedNodeMap;
055    import org.w3c.dom.Node;
056    import org.w3c.dom.NodeList;
057    import org.xml.sax.Attributes;
058    import org.xml.sax.SAXException;
059    
060    
061    /**
062     * SAXImpl is the core model for SAX input source. SAXImpl objects are
063     * usually created from an XSLTCDTMManager.
064     *
065     * <p>DOMSource inputs are handled using DOM2SAX + SAXImpl. SAXImpl has a
066     * few specific fields (e.g. _node2Ids, _document) to keep DOM-related
067     * information. They are used when the processing behavior between DOM and
068     * SAX has to be different. Examples of these include id function and 
069     * unparsed entity.
070     *
071     * <p>SAXImpl extends SAX2DTM2 instead of SAX2DTM for better performance.
072     * @author Jacek Ambroziak
073     * @author Santiago Pericas-Geertsen
074     * @author Morten Jorgensen
075     * @author Douglas Sellers <douglasjsellers@hotmail.com>
076     */
077    public final class SAXImpl extends SAX2DTM2
078                               implements DOMEnhancedForDTM, DOMBuilder
079    {
080        
081        /* ------------------------------------------------------------------- */
082        /* DOMBuilder fields BEGIN                                             */
083        /* ------------------------------------------------------------------- */
084    
085        // Namespace prefix-to-uri mapping stuff
086        private int       _uriCount     = 0;
087        private int       _prefixCount  = 0;
088    
089        // Stack used to keep track of what whitespace text nodes are protected
090        // by xml:space="preserve" attributes and which nodes that are not.
091        private int[]   _xmlSpaceStack;
092        private int     _idx = 1;
093        private boolean _preserve = false;
094    
095        private static final String XML_STRING = "xml:";
096        private static final String XML_PREFIX   = "xml";   
097        private static final String XMLSPACE_STRING = "xml:space";
098        private static final String PRESERVE_STRING = "preserve";
099        private static final String XMLNS_PREFIX = "xmlns";
100        private static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
101    
102        private boolean _escaping = true;
103        private boolean _disableEscaping = false;
104        private int _textNodeToProcess = DTM.NULL;
105    
106        /* ------------------------------------------------------------------- */
107        /* DOMBuilder fields END                                               */
108        /* ------------------------------------------------------------------- */
109    
110        // empty String for null attribute values
111        private final static String EMPTYSTRING = "";
112    
113        // empty iterator to be returned when there are no children
114        private final static DTMAxisIterator EMPTYITERATOR = EmptyIterator.getInstance();
115        // The number of expanded names
116        private int _namesSize = -1;
117    
118        // Namespace related stuff
119        private Hashtable _nsIndex = new Hashtable();
120       
121        // The initial size of the text buffer
122        private int _size = 0;
123        
124        // Tracks which textnodes are not escaped
125        private BitArray  _dontEscape = null;
126    
127        // The URI to this document
128        private String    _documentURI = null;
129        static private int _documentURIIndex = 0;
130    
131        // The owner Document when the input source is DOMSource.
132        private Document _document;
133    
134        // The hashtable for org.w3c.dom.Node to node id mapping.
135        // This is only used when the input is a DOMSource and the
136        // buildIdIndex flag is true.
137        private Hashtable _node2Ids = null;
138    
139        // True if the input source is a DOMSource.
140        private boolean _hasDOMSource = false;
141    
142        // The DTMManager
143        private XSLTCDTMManager _dtmManager;
144    
145        // Support for access/navigation through org.w3c.dom API
146        private Node[] _nodes;
147        private NodeList[] _nodeLists;
148        private final static String XML_LANG_ATTRIBUTE =
149            "http://www.w3.org/XML/1998/namespace:@lang";
150    
151        /**
152         * Define the origin of the document from which the tree was built
153         */
154        public void setDocumentURI(String uri) {
155            if (uri != null) {
156                setDocumentBaseURI(SystemIDResolver.getAbsoluteURI(uri));
157            }
158        }
159    
160        /**
161         * Returns the origin of the document from which the tree was built
162         */
163        public String getDocumentURI() {
164            String baseURI = getDocumentBaseURI();
165            return (baseURI != null) ? baseURI : "rtf" + _documentURIIndex++;
166        }
167    
168        public String getDocumentURI(int node) {
169            return getDocumentURI();
170        }
171    
172        public void setupMapping(String[] names, String[] urisArray,
173                                 int[] typesArray, String[] namespaces) {
174            // This method only has a function in DOM adapters
175        }
176    
177        /**
178         * Lookup a namespace URI from a prefix starting at node. This method
179         * is used in the execution of xsl:element when the prefix is not known
180         * at compile time.
181         */
182        public String lookupNamespace(int node, String prefix)
183            throws TransletException
184        {
185            int anode, nsnode;
186            final AncestorIterator ancestors = new AncestorIterator();
187    
188            if (isElement(node)) {
189                ancestors.includeSelf();
190            }
191    
192            ancestors.setStartNode(node);
193            while ((anode = ancestors.next()) != DTM.NULL) {
194                final NamespaceIterator namespaces = new NamespaceIterator();
195    
196                namespaces.setStartNode(anode);
197                while ((nsnode = namespaces.next()) != DTM.NULL) {
198                    if (getLocalName(nsnode).equals(prefix)) {
199                        return getNodeValue(nsnode);
200                    }
201                }
202            }
203    
204            BasisLibrary.runTimeError(BasisLibrary.NAMESPACE_PREFIX_ERR, prefix);
205            return null;
206        }
207    
208        /**
209         * Returns 'true' if a specific node is an element (of any type)
210         */
211        public boolean isElement(final int node) {
212            return getNodeType(node) == DTM.ELEMENT_NODE;
213        }
214    
215        /**
216         * Returns 'true' if a specific node is an attribute (of any type)
217         */
218        public boolean isAttribute(final int node) {
219            return getNodeType(node) == DTM.ATTRIBUTE_NODE;
220        }
221    
222        /**
223         * Returns the number of nodes in the tree (used for indexing)
224         */
225        public int getSize() {
226            return getNumberOfNodes();
227        }
228    
229        /**
230         * Part of the DOM interface - no function here.
231         */
232        public void setFilter(StripFilter filter) {
233        }
234    
235    
236        /**
237         * Returns true if node1 comes before node2 in document order
238         */
239        public boolean lessThan(int node1, int node2) {
240            if (node1 == DTM.NULL) {
241                return false;
242            }
243    
244            if (node2 == DTM.NULL) {
245                return true;
246            }
247    
248            return (node1 < node2);
249        }
250    
251        /**
252         * Create an org.w3c.dom.Node from a node in the tree
253         */
254        public Node makeNode(int index) {
255            if (_nodes == null) {
256                _nodes = new Node[_namesSize];
257            }
258    
259            int nodeID = makeNodeIdentity(index);
260            if (nodeID < 0) {
261                return null;
262            }
263            else if (nodeID < _nodes.length) {
264                return (_nodes[nodeID] != null) ? _nodes[nodeID] 
265                    : (_nodes[nodeID] = new DTMNodeProxy((DTM)this, index));
266            }
267            else {
268                return new DTMNodeProxy((DTM)this, index);
269            }
270        }
271    
272        /**
273         * Create an org.w3c.dom.Node from a node in an iterator
274         * The iterator most be started before this method is called
275         */
276        public Node makeNode(DTMAxisIterator iter) {
277            return makeNode(iter.next());
278        }
279    
280        /**
281         * Create an org.w3c.dom.NodeList from a node in the tree
282         */
283        public NodeList makeNodeList(int index) {
284            if (_nodeLists == null) {
285                _nodeLists = new NodeList[_namesSize];
286            }
287            
288            int nodeID = makeNodeIdentity(index);
289            if (nodeID < 0) {
290                return null;
291            }
292            else if (nodeID < _nodeLists.length) {
293                return (_nodeLists[nodeID] != null) ? _nodeLists[nodeID]
294                       : (_nodeLists[nodeID] = new DTMAxisIterNodeList(this,
295                                                     new SingletonIterator(index)));
296        }
297            else {
298                return new DTMAxisIterNodeList(this, new SingletonIterator(index));
299            }
300        }
301    
302        /**
303         * Create an org.w3c.dom.NodeList from a node iterator
304         * The iterator most be started before this method is called
305         */
306        public NodeList makeNodeList(DTMAxisIterator iter) {
307            return new DTMAxisIterNodeList(this, iter);
308        }
309    
310        /**
311         * Iterator that returns the namespace nodes as defined by the XPath data
312         * model for a given node, filtered by extended type ID.
313         */
314        public class TypedNamespaceIterator extends NamespaceIterator {
315            
316            private  String _nsPrefix;
317    
318            /**
319             * Constructor TypedChildrenIterator
320             *
321             *
322             * @param nodeType The extended type ID being requested.
323             */
324            public TypedNamespaceIterator(int nodeType) { 
325                super();
326                if(m_expandedNameTable != null){
327                    _nsPrefix = m_expandedNameTable.getLocalName(nodeType);
328                }
329            }
330    
331           /**
332            * Get the next node in the iteration.
333            *
334            * @return The next node handle in the iteration, or END.
335            */
336            public int next() {
337                if ((_nsPrefix == null) ||(_nsPrefix.length() == 0) ){
338                    return (END);
339                }          
340                int node = END;
341                for (node = super.next(); node != END; node = super.next()) {
342                    if (_nsPrefix.compareTo(getLocalName(node))== 0) {
343                        return returnNode(node);
344                    }
345                }
346                return (END);
347            }
348        }  // end of TypedNamespaceIterator
349    
350    
351    
352        /**************************************************************
353         * This is a specialised iterator for predicates comparing node or
354         * attribute values to variable or parameter values.
355         */
356        private final class NodeValueIterator extends InternalAxisIteratorBase
357        {
358    
359            private DTMAxisIterator _source;
360            private String _value;
361            private boolean _op;
362            private final boolean _isReverse;
363            private int _returnType = RETURN_PARENT;
364    
365            public NodeValueIterator(DTMAxisIterator source, int returnType,
366                                     String value, boolean op)
367            {
368                _source = source;
369                _returnType = returnType;
370                _value = value;
371                _op = op;
372                _isReverse = source.isReverse();
373            }
374    
375            public boolean isReverse()
376            {
377                return _isReverse;
378            }
379    
380            public DTMAxisIterator cloneIterator()
381            {
382                try {
383                    NodeValueIterator clone = (NodeValueIterator)super.clone();
384                    clone._isRestartable = false;
385                    clone._source = _source.cloneIterator();
386                    clone._value = _value;
387                    clone._op = _op;
388                    return clone.reset();
389                }
390                catch (CloneNotSupportedException e) {
391                    BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
392                                              e.toString());
393                    return null;
394                }
395            }
396      
397            public void setRestartable(boolean isRestartable)
398            {
399                _isRestartable = isRestartable;
400                _source.setRestartable(isRestartable);
401            }
402    
403            public DTMAxisIterator reset()
404            {
405                _source.reset();
406                return resetPosition();
407            }
408    
409            public int next()
410            {
411                int node;
412                while ((node = _source.next()) != END) {
413                    String val = getStringValueX(node);
414                    if (_value.equals(val) == _op) {
415                        if (_returnType == RETURN_CURRENT) {
416                            return returnNode(node);
417                        }
418                        else {
419                            return returnNode(getParent(node));
420                        }
421                    }
422                }
423                return END;
424            }
425    
426            public DTMAxisIterator setStartNode(int node)
427            {
428                if (_isRestartable) {
429                    _source.setStartNode(_startNode = node);
430                    return resetPosition();
431                }
432                return this;
433            }
434    
435            public void setMark()
436            {
437                _source.setMark();
438            }
439    
440            public void gotoMark()
441            {
442                _source.gotoMark();
443            }
444        } // end NodeValueIterator
445    
446        public DTMAxisIterator getNodeValueIterator(DTMAxisIterator iterator, int type,
447                                                 String value, boolean op)
448        {
449            return(DTMAxisIterator)(new NodeValueIterator(iterator, type, value, op));
450        }
451    
452        /**
453         * Encapsulates an iterator in an OrderedIterator to ensure node order
454         */
455        public DTMAxisIterator orderNodes(DTMAxisIterator source, int node)
456        {
457            return new DupFilterIterator(source);
458        }
459    
460        /**
461         * Returns singleton iterator containg the document root
462         * Works for them main document (mark == 0).  It cannot be made
463         * to point to any other node through setStartNode().
464         */
465        public DTMAxisIterator getIterator()
466        {
467            return new SingletonIterator(getDocument(), true);
468        }
469    
470         /**
471         * Get mapping from DOM namespace types to external namespace types
472         */
473        public int getNSType(int node)
474        {
475            String s = getNamespaceURI(node);
476            if (s == null) {
477                return 0;
478            }
479            int eType = getIdForNamespace(s);
480            return ((Integer)_nsIndex.get(new Integer(eType))).intValue();        
481        }
482        
483        
484    
485        /**
486         * Returns the namespace type of a specific node
487         */
488        public int getNamespaceType(final int node)
489        {
490            return super.getNamespaceType(node);
491        }
492    
493        /**
494         * Sets up a translet-to-dom type mapping table
495         */
496        private int[] setupMapping(String[] names, String[] uris, int[] types, int nNames) {
497            // Padding with number of names, because they
498            // may need to be added, i.e for RTFs. See copy03  
499            final int[] result = new int[m_expandedNameTable.getSize()];
500            for (int i = 0; i < nNames; i++)      {
501                //int type = getGeneralizedType(namesArray[i]);
502                int type = m_expandedNameTable.getExpandedTypeID(uris[i], names[i], types[i], false);
503                result[type] = type;
504            }
505            return result;
506        }
507    
508        /**
509         * Returns the internal type associated with an expanded QName
510         */
511        public int getGeneralizedType(final String name) {
512            return getGeneralizedType(name, true);
513        }
514    
515        /**
516         * Returns the internal type associated with an expanded QName
517         */
518        public int getGeneralizedType(final String name, boolean searchOnly) {
519            String lName, ns = null;
520            int index = -1;
521            int code;
522    
523            // Is there a prefix?
524            if ((index = name.lastIndexOf(':'))> -1) {
525                ns = name.substring(0, index);
526            }
527    
528            // Local part of name is after colon.  lastIndexOf returns -1 if
529            // there is no colon, so lNameStartIdx will be zero in that case.
530            int lNameStartIdx = index+1;
531    
532            // Distinguish attribute and element names.  Attribute has @ before
533            // local part of name.
534            if (name.charAt(lNameStartIdx) == '@') {
535                code = DTM.ATTRIBUTE_NODE;
536                lNameStartIdx++;
537            }
538            else {
539                code = DTM.ELEMENT_NODE;
540            }
541    
542            // Extract local name
543            lName = (lNameStartIdx == 0) ? name : name.substring(lNameStartIdx);
544    
545            return m_expandedNameTable.getExpandedTypeID(ns, lName, code, searchOnly);
546        }
547    
548        /**
549         * Get mapping from DOM element/attribute types to external types
550         */
551        public short[] getMapping(String[] names, String[] uris, int[] types)
552        {
553            // Delegate the work to getMapping2 if the document is not fully built.
554            // Some of the processing has to be different in this case.
555            if (_namesSize < 0) {
556                return getMapping2(names, uris, types);
557            }
558    
559            int i;
560            final int namesLength = names.length;
561            final int exLength = m_expandedNameTable.getSize();
562          
563            final short[] result = new short[exLength];
564    
565            // primitive types map to themselves
566            for (i = 0; i < DTM.NTYPES; i++) {
567                result[i] = (short)i;
568            }
569            
570            for (i = NTYPES; i < exLength; i++) { 
571                result[i] = m_expandedNameTable.getType(i);
572            }
573            
574            // actual mapping of caller requested names
575            for (i = 0; i < namesLength; i++) {
576                int genType = m_expandedNameTable.getExpandedTypeID(uris[i],
577                                                                    names[i],
578                                                                    types[i],
579                                                                    true);
580                if (genType >= 0 && genType < exLength) {
581                    result[genType] = (short)(i + DTM.NTYPES);
582                }
583            }
584    
585            return result;
586        }
587    
588        /**
589         * Get mapping from external element/attribute types to DOM types
590         */
591        public int[] getReverseMapping(String[] names, String[] uris, int[] types)
592        {
593            int i;
594            final int[] result = new int[names.length + DTM.NTYPES];
595            
596            // primitive types map to themselves
597            for (i = 0; i < DTM.NTYPES; i++) {
598                result[i] = i;
599            }
600            
601            // caller's types map into appropriate dom types
602            for (i = 0; i < names.length; i++) {
603                int type = m_expandedNameTable.getExpandedTypeID(uris[i], names[i], types[i], true);
604                result[i+DTM.NTYPES] = type;
605            }
606            return(result);
607        }
608        
609        /**
610         * Get mapping from DOM element/attribute types to external types.
611         * This method is used when the document is not fully built.
612         */
613        private short[] getMapping2(String[] names, String[] uris, int[] types)
614        {
615            int i;
616            final int namesLength = names.length;
617            final int exLength = m_expandedNameTable.getSize();
618            int[] generalizedTypes = null;
619            if (namesLength > 0) {
620                generalizedTypes = new int[namesLength];
621            }
622    
623            int resultLength = exLength;
624    
625            for (i = 0; i < namesLength; i++) {
626                // When the document is not fully built, the searchOnly
627                // flag should be set to false. That means we should add
628                // the type if it is not already in the expanded name table.
629                //generalizedTypes[i] = getGeneralizedType(names[i], false);
630                generalizedTypes[i] =
631                    m_expandedNameTable.getExpandedTypeID(uris[i],
632                                                          names[i],
633                                                          types[i],
634                                                          false);
635                if (_namesSize < 0 && generalizedTypes[i] >= resultLength) {
636                    resultLength = generalizedTypes[i] + 1;
637                }
638            }
639    
640            final short[] result = new short[resultLength];
641    
642            // primitive types map to themselves
643            for (i = 0; i < DTM.NTYPES; i++) {
644                result[i] = (short)i;
645            }
646    
647            for (i = NTYPES; i < exLength; i++) {
648                result[i] = m_expandedNameTable.getType(i);
649            }
650    
651            // actual mapping of caller requested names
652            for (i = 0; i < namesLength; i++) {
653                int genType = generalizedTypes[i];
654                if (genType >= 0 && genType < resultLength) {
655                    result[genType] = (short)(i + DTM.NTYPES);
656                }
657            }
658    
659            return(result);
660        }
661        /**
662         * Get mapping from DOM namespace types to external namespace types
663         */
664        public short[] getNamespaceMapping(String[] namespaces)
665        {
666            int i;
667            final int nsLength = namespaces.length;
668            final int mappingLength = _uriCount;
669    
670            final short[] result = new short[mappingLength];
671    
672            // Initialize all entries to -1
673            for (i=0; i<mappingLength; i++) {
674                result[i] = (short)(-1);
675            }
676    
677            for (i=0; i<nsLength; i++) {
678                int eType = getIdForNamespace(namespaces[i]); 
679                Integer type = (Integer)_nsIndex.get(new Integer(eType));
680                if (type != null) {
681                    result[type.intValue()] = (short)i;
682                }
683            }
684    
685            return(result);
686        }
687    
688        /**
689         * Get mapping from external namespace types to DOM namespace types
690         */
691        public short[] getReverseNamespaceMapping(String[] namespaces)
692        {
693            int i;
694            final int length = namespaces.length;
695            final short[] result = new short[length];
696    
697            for (i = 0; i < length; i++) {
698                int eType = getIdForNamespace(namespaces[i]);
699                Integer type = (Integer)_nsIndex.get(new Integer(eType));
700                result[i] = (type == null) ? -1 : type.shortValue();
701            }
702    
703            return result;
704        }
705    
706        /**
707         * Construct a SAXImpl object using the default block size.
708         */
709        public SAXImpl(XSLTCDTMManager mgr, Source source,
710                       int dtmIdentity, DTMWSFilter whiteSpaceFilter,
711                       XMLStringFactory xstringfactory,
712                       boolean doIndexing, boolean buildIdIndex)
713        {
714            this(mgr, source, dtmIdentity, whiteSpaceFilter, xstringfactory,
715                doIndexing, DEFAULT_BLOCKSIZE, buildIdIndex, false);
716        }
717        
718        /**
719         * Construct a SAXImpl object using the given block size.
720         */
721        public SAXImpl(XSLTCDTMManager mgr, Source source,
722                       int dtmIdentity, DTMWSFilter whiteSpaceFilter,
723                       XMLStringFactory xstringfactory,
724                       boolean doIndexing, int blocksize, 
725                       boolean buildIdIndex,
726                       boolean newNameTable)
727        {
728            super(mgr, source, dtmIdentity, whiteSpaceFilter, xstringfactory,
729                doIndexing, blocksize, false, buildIdIndex, newNameTable);
730          
731            _dtmManager = mgr;      
732            _size = blocksize;
733          
734            // Use a smaller size for the space stack if the blocksize is small
735            _xmlSpaceStack = new int[blocksize <= 64 ? 4 : 64];
736                                      
737            /* From DOMBuilder */ 
738            _xmlSpaceStack[0] = DTMDefaultBase.ROOTNODE;
739          
740            // If the input source is DOMSource, set the _document field and
741            // create the node2Ids table.
742            if (source instanceof DOMSource) {
743                _hasDOMSource = true;
744                DOMSource domsrc = (DOMSource)source;
745                Node node = domsrc.getNode();
746                if (node instanceof Document) {
747                    _document = (Document)node;
748                }
749                else {
750                    _document = node.getOwnerDocument();
751                }
752                _node2Ids = new Hashtable();
753            }                          
754        }
755            
756        /**
757        * Migrate a DTM built with an old DTMManager to a new DTMManager.
758        * After the migration, the new DTMManager will treat the DTM as
759        * one that is built by itself.
760        * This is used to support DTM sharing between multiple transformations.
761        * @param manager the DTMManager
762        */
763        public void migrateTo(DTMManager manager) {
764            super.migrateTo(manager);
765            if (manager instanceof XSLTCDTMManager) {
766                _dtmManager = (XSLTCDTMManager)manager;
767            }
768        }
769            
770        /**
771         * Return the node identity for a given id String
772         * 
773         * @param idString The id String
774         * @return The identity of the node whose id is the given String.
775         */
776        public int getElementById(String idString)
777        {
778            Node node = _document.getElementById(idString);
779            if (node != null) {
780                Integer id = (Integer)_node2Ids.get(node);
781                return (id != null) ? id.intValue() : DTM.NULL;
782            }
783            else {
784                return DTM.NULL;
785            }
786        }
787        
788        /**
789         * Return true if the input source is DOMSource.
790         */
791        public boolean hasDOMSource()
792        {
793            return _hasDOMSource;   
794        }
795    
796        /*---------------------------------------------------------------------------*/
797        /* DOMBuilder methods begin                                                  */
798        /*---------------------------------------------------------------------------*/
799        
800        /**
801         * Call this when an xml:space attribute is encountered to
802         * define the whitespace strip/preserve settings.
803         */
804        private void xmlSpaceDefine(String val, final int node)
805        {
806            final boolean setting = val.equals(PRESERVE_STRING);
807            if (setting != _preserve) {
808                _xmlSpaceStack[_idx++] = node;
809                _preserve = setting;
810            }
811        }
812    
813        /**
814         * Call this from endElement() to revert strip/preserve setting
815         * to whatever it was before the corresponding startElement().
816         */
817        private void xmlSpaceRevert(final int node)
818        {
819            if (node == _xmlSpaceStack[_idx - 1]) {
820                _idx--;
821                _preserve = !_preserve;
822            }
823        }
824    
825        /**
826         * Find out whether or not to strip whitespace nodes.
827         *
828         *
829         * @return whether or not to strip whitespace nodes.
830         */
831        protected boolean getShouldStripWhitespace()
832        {
833            return _preserve ? false : super.getShouldStripWhitespace();
834        }
835    
836        /**
837         * Creates a text-node and checks if it is a whitespace node.
838         */
839        private void handleTextEscaping() {
840            if (_disableEscaping && _textNodeToProcess != DTM.NULL
841                && _type(_textNodeToProcess) == DTM.TEXT_NODE) {
842                if (_dontEscape == null) {
843                    _dontEscape = new BitArray(_size);
844                }
845              
846                // Resize the _dontEscape BitArray if necessary.
847                if (_textNodeToProcess >= _dontEscape.size()) {
848                    _dontEscape.resize(_dontEscape.size() * 2);
849                }
850              
851                _dontEscape.setBit(_textNodeToProcess);
852                _disableEscaping = false;
853            }
854            _textNodeToProcess = DTM.NULL;
855        }
856    
857    
858        /****************************************************************/
859        /*               SAX Interface Starts Here                      */
860        /****************************************************************/
861    
862        /**
863         * SAX2: Receive notification of character data.
864         */
865        public void characters(char[] ch, int start, int length) throws SAXException
866        {
867            super.characters(ch, start, length);
868            
869            _disableEscaping = !_escaping;  
870            _textNodeToProcess = getNumberOfNodes();
871        }
872    
873        /**
874         * SAX2: Receive notification of the beginning of a document.
875         */
876        public void startDocument() throws SAXException
877        {
878            super.startDocument();
879    
880            _nsIndex.put(new Integer(0), new Integer(_uriCount++));
881            definePrefixAndUri(XML_PREFIX, XML_URI);
882        }
883    
884        /**
885         * SAX2: Receive notification of the end of a document.
886         */
887        public void endDocument() throws SAXException
888        {
889            super.endDocument();
890            
891            handleTextEscaping();
892            _namesSize = m_expandedNameTable.getSize();
893        }
894    
895        /**
896         * Specialized interface used by DOM2SAX. This one has an extra Node
897         * parameter to build the Node -> id map.
898         */
899        public void startElement(String uri, String localName,
900                                 String qname, Attributes attributes,
901                                 Node node)
902            throws SAXException
903        {
904            this.startElement(uri, localName, qname, attributes);
905            
906            if (m_buildIdIndex) {
907                _node2Ids.put(node, new Integer(m_parents.peek()));
908            }
909        }
910        
911        /**
912         * SAX2: Receive notification of the beginning of an element.
913         */
914        public void startElement(String uri, String localName,
915                     String qname, Attributes attributes)
916            throws SAXException
917        {
918            super.startElement(uri, localName, qname, attributes);
919            
920            handleTextEscaping();
921    
922            if (m_wsfilter != null) {
923                // Look for any xml:space attributes
924                // Depending on the implementation of attributes, this
925                // might be faster than looping through all attributes. ILENE
926                final int index = attributes.getIndex(XMLSPACE_STRING);
927                if (index >= 0) {
928                    xmlSpaceDefine(attributes.getValue(index), m_parents.peek());
929                }
930            }
931        }
932    
933        /**
934         * SAX2: Receive notification of the end of an element.
935         */
936        public void endElement(String namespaceURI, String localName, String qname)
937            throws SAXException
938        {
939            super.endElement(namespaceURI, localName, qname);
940            
941            handleTextEscaping();
942    
943            // Revert to strip/preserve-space setting from before this element
944            if (m_wsfilter != null) {
945                xmlSpaceRevert(m_previous);
946            }
947        }
948    
949        /**
950         * SAX2: Receive notification of a processing instruction.
951         */
952        public void processingInstruction(String target, String data)
953            throws SAXException
954        {
955            super.processingInstruction(target, data);
956            handleTextEscaping();
957        }
958    
959        /**
960         * SAX2: Receive notification of ignorable whitespace in element
961         * content. Similar to characters(char[], int, int).
962         */
963        public void ignorableWhitespace(char[] ch, int start, int length)
964            throws SAXException
965        {
966            super.ignorableWhitespace(ch, start, length);
967            _textNodeToProcess = getNumberOfNodes();
968        }
969    
970        /**
971         * SAX2: Begin the scope of a prefix-URI Namespace mapping.
972         */
973        public void startPrefixMapping(String prefix, String uri)
974            throws SAXException
975        {
976            super.startPrefixMapping(prefix, uri);
977            handleTextEscaping();
978    
979            definePrefixAndUri(prefix, uri);
980        }
981    
982        private void definePrefixAndUri(String prefix, String uri) 
983            throws SAXException 
984        {
985            // Check if the URI already exists before pushing on stack
986            Integer eType = new Integer(getIdForNamespace(uri));
987            if ((Integer)_nsIndex.get(eType) == null) {
988                _nsIndex.put(eType, new Integer(_uriCount++));
989            }
990        }
991     
992        /**
993         * SAX2: Report an XML comment anywhere in the document.
994         */
995        public void comment(char[] ch, int start, int length)
996            throws SAXException
997        {
998            super.comment(ch, start, length);
999            handleTextEscaping();
1000        }
1001    
1002        public boolean setEscaping(boolean value) {
1003            final boolean temp = _escaping;
1004            _escaping = value; 
1005            return temp;
1006        }
1007       
1008       /*---------------------------------------------------------------------------*/
1009       /* DOMBuilder methods end                                                    */
1010       /*---------------------------------------------------------------------------*/
1011    
1012        /**
1013         * Prints the whole tree to standard output
1014         */
1015        public void print(int node, int level)
1016        {
1017            switch(getNodeType(node))
1018            {
1019                case DTM.ROOT_NODE:
1020                case DTM.DOCUMENT_NODE:
1021                    print(getFirstChild(node), level);
1022                    break;
1023                case DTM.TEXT_NODE:
1024                case DTM.COMMENT_NODE:
1025                case DTM.PROCESSING_INSTRUCTION_NODE:
1026                    System.out.print(getStringValueX(node));
1027                    break;
1028                default:
1029                    final String name = getNodeName(node);
1030                    System.out.print("<" + name);
1031                    for (int a = getFirstAttribute(node); a != DTM.NULL; a = getNextAttribute(a))
1032                    {
1033                        System.out.print("\n" + getNodeName(a) + "=\"" + getStringValueX(a) + "\"");
1034                    }
1035                    System.out.print('>');
1036                    for (int child = getFirstChild(node); child != DTM.NULL;
1037                        child = getNextSibling(child)) {
1038                        print(child, level + 1);
1039                    }
1040                    System.out.println("</" + name + '>');
1041                    break;
1042            }
1043        }
1044    
1045        /**
1046         * Returns the name of a node (attribute or element).
1047         */
1048        public String getNodeName(final int node)
1049        {
1050            // Get the node type and make sure that it is within limits
1051            int nodeh = node;
1052            final short type = getNodeType(nodeh);
1053            switch(type)
1054            {
1055                case DTM.ROOT_NODE:
1056                case DTM.DOCUMENT_NODE:
1057                case DTM.TEXT_NODE:
1058                case DTM.COMMENT_NODE:
1059                    return EMPTYSTRING;
1060                case DTM.NAMESPACE_NODE:
1061                    return this.getLocalName(nodeh);
1062                default:
1063                    return super.getNodeName(nodeh);
1064            }
1065        }    
1066    
1067        /**
1068         * Returns the namespace URI to which a node belongs
1069         */
1070        public String getNamespaceName(final int node)
1071        {
1072            if (node == DTM.NULL) {
1073                return "";
1074            }
1075            
1076            String s;
1077            return (s = getNamespaceURI(node)) == null ? EMPTYSTRING : s;
1078        }
1079    
1080     
1081        /**
1082         * Returns the attribute node of a given type (if any) for an element
1083         */
1084        public int getAttributeNode(final int type, final int element)
1085        {
1086            for (int attr = getFirstAttribute(element);
1087               attr != DTM.NULL;
1088               attr = getNextAttribute(attr))
1089            {
1090                if (getExpandedTypeID(attr) == type) return attr;
1091            }
1092            return DTM.NULL;
1093        }
1094    
1095        /**
1096         * Returns the value of a given attribute type of a given element
1097         */
1098        public String getAttributeValue(final int type, final int element)
1099        {
1100            final int attr = getAttributeNode(type, element);
1101            return (attr != DTM.NULL) ? getStringValueX(attr) : EMPTYSTRING;
1102        }
1103    
1104        /**
1105         * This method is for testing/debugging only
1106         */
1107        public String getAttributeValue(final String name, final int element)
1108        {
1109            return getAttributeValue(getGeneralizedType(name), element);
1110        }
1111    
1112        /**
1113         * Returns an iterator with all the children of a given node
1114         */
1115        public DTMAxisIterator getChildren(final int node)
1116        {
1117            return (new ChildrenIterator()).setStartNode(node);
1118        }
1119    
1120        /**
1121         * Returns an iterator with all children of a specific type
1122         * for a given node (element)
1123         */
1124        public DTMAxisIterator getTypedChildren(final int type)
1125        {
1126            return(new TypedChildrenIterator(type));
1127        }
1128    
1129        /**
1130         * This is a shortcut to the iterators that implement the
1131         * supported XPath axes (only namespace::) is not supported.
1132         * Returns a bare-bones iterator that must be initialized
1133         * with a start node (using iterator.setStartNode()).
1134         */
1135        public DTMAxisIterator getAxisIterator(final int axis)
1136        {
1137            switch (axis)
1138            {
1139                case Axis.SELF:
1140                    return new SingletonIterator();
1141                case Axis.CHILD:
1142                    return new ChildrenIterator();
1143                case Axis.PARENT:
1144                    return new ParentIterator();
1145                case Axis.ANCESTOR:
1146                    return new AncestorIterator();
1147                case Axis.ANCESTORORSELF:
1148                    return (new AncestorIterator()).includeSelf();
1149                case Axis.ATTRIBUTE:
1150                    return new AttributeIterator();
1151                case Axis.DESCENDANT:
1152                    return new DescendantIterator();
1153                case Axis.DESCENDANTORSELF:
1154                    return (new DescendantIterator()).includeSelf();
1155                case Axis.FOLLOWING:
1156                    return new FollowingIterator();
1157                case Axis.PRECEDING:
1158                    return new PrecedingIterator();
1159                case Axis.FOLLOWINGSIBLING:
1160                    return new FollowingSiblingIterator();
1161                case Axis.PRECEDINGSIBLING:
1162                    return new PrecedingSiblingIterator();
1163                case Axis.NAMESPACE:
1164                    return new NamespaceIterator();
1165                case Axis.ROOT:
1166                    return new RootIterator();
1167                default:
1168                    BasisLibrary.runTimeError(BasisLibrary.AXIS_SUPPORT_ERR, 
1169                            Axis.getNames(axis));
1170            }
1171            return null;
1172        }
1173    
1174        /**
1175         * Similar to getAxisIterator, but this one returns an iterator
1176         * containing nodes of a typed axis (ex.: child::foo)
1177         */
1178        public DTMAxisIterator getTypedAxisIterator(int axis, int type)
1179        {
1180            // Most common case handled first
1181            if (axis == Axis.CHILD) {
1182                return new TypedChildrenIterator(type);
1183            }
1184    
1185            if (type == NO_TYPE) {
1186                return(EMPTYITERATOR);
1187            }
1188    
1189            switch (axis)
1190            {
1191                case Axis.SELF:
1192                    return new TypedSingletonIterator(type);
1193                case Axis.CHILD:
1194                    return new TypedChildrenIterator(type);
1195                case Axis.PARENT:
1196                    return new ParentIterator().setNodeType(type);
1197                case Axis.ANCESTOR:
1198                    return new TypedAncestorIterator(type);
1199                case Axis.ANCESTORORSELF:
1200                    return (new TypedAncestorIterator(type)).includeSelf();
1201                case Axis.ATTRIBUTE:
1202                    return new TypedAttributeIterator(type);
1203                case Axis.DESCENDANT:
1204                    return new TypedDescendantIterator(type);
1205                case Axis.DESCENDANTORSELF:
1206                    return (new TypedDescendantIterator(type)).includeSelf();
1207                case Axis.FOLLOWING:
1208                    return new TypedFollowingIterator(type);
1209                case Axis.PRECEDING:
1210                    return new TypedPrecedingIterator(type);
1211                case Axis.FOLLOWINGSIBLING:
1212                    return new TypedFollowingSiblingIterator(type);
1213                case Axis.PRECEDINGSIBLING:
1214                    return new TypedPrecedingSiblingIterator(type);
1215                case Axis.NAMESPACE:
1216                    return  new TypedNamespaceIterator(type);
1217                case Axis.ROOT:
1218                    return new TypedRootIterator(type);
1219                default:
1220                    BasisLibrary.runTimeError(BasisLibrary.TYPED_AXIS_SUPPORT_ERR, 
1221                            Axis.getNames(axis));
1222            }
1223            return null;
1224        }
1225    
1226        /**
1227         * Do not think that this returns an iterator for the namespace axis.
1228         * It returns an iterator with nodes that belong in a certain namespace,
1229         * such as with <xsl:apply-templates select="blob/foo:*"/>
1230         * The 'axis' specifies the axis for the base iterator from which the
1231         * nodes are taken, while 'ns' specifies the namespace URI type.
1232         */
1233        public DTMAxisIterator getNamespaceAxisIterator(int axis, int ns)
1234        {
1235    
1236            DTMAxisIterator iterator = null;
1237    
1238            if (ns == NO_TYPE) {
1239                return EMPTYITERATOR;
1240            }
1241            else {
1242                switch (axis) {
1243                    case Axis.CHILD:
1244                        return new NamespaceChildrenIterator(ns);
1245                    case Axis.ATTRIBUTE:
1246                        return new NamespaceAttributeIterator(ns);
1247                    default:
1248                        return new NamespaceWildcardIterator(axis, ns);
1249                }
1250            }
1251        }
1252    
1253        /**
1254         * Iterator that handles node tests that test for a namespace, but have
1255         * a wild card for the local name of the node, i.e., node tests of the
1256         * form <axis>::<prefix>:*
1257         */
1258        public final class NamespaceWildcardIterator
1259            extends InternalAxisIteratorBase
1260        {
1261            /**
1262             * The namespace type index.
1263             */
1264            protected int m_nsType;
1265    
1266            /**
1267             * A nested typed axis iterator that retrieves nodes of the principal
1268             * node kind for that axis.
1269             */
1270            protected DTMAxisIterator m_baseIterator;
1271    
1272            /**
1273             * Constructor NamespaceWildcard
1274             *
1275             * @param axis The axis that this iterator will traverse
1276             * @param nsType The namespace type index
1277             */
1278            public NamespaceWildcardIterator(int axis, int nsType) {
1279                m_nsType = nsType;
1280    
1281                // Create a nested iterator that will select nodes of
1282                // the principal node kind for the selected axis.
1283                switch (axis) {
1284                    case Axis.ATTRIBUTE: {
1285                        // For "attribute::p:*", the principal node kind is
1286                        // attribute
1287                        m_baseIterator = getAxisIterator(axis);
1288                    }
1289                    case Axis.NAMESPACE: {
1290                        // This covers "namespace::p:*".  It is syntactically
1291                        // correct, though it doesn't make much sense.
1292                        m_baseIterator = getAxisIterator(axis);
1293                    }
1294                    default: {
1295                        // In all other cases, the principal node kind is
1296                        // element
1297                        m_baseIterator = getTypedAxisIterator(axis,
1298                                                              DTM.ELEMENT_NODE);
1299                    }
1300                }
1301            }
1302    
1303            /**
1304             * Set start to END should 'close' the iterator,
1305             * i.e. subsequent call to next() should return END.
1306             *
1307             * @param node Sets the root of the iteration.
1308             *
1309             * @return A DTMAxisIterator set to the start of the iteration.
1310             */
1311            public DTMAxisIterator setStartNode(int node) {
1312                if (_isRestartable) {
1313                    _startNode = node;
1314                    m_baseIterator.setStartNode(node);
1315                    resetPosition();
1316                }
1317                return this;
1318            }
1319    
1320            /**
1321             * Get the next node in the iteration.
1322             *
1323             * @return The next node handle in the iteration, or END.
1324             */
1325            public int next() {
1326                int node;
1327    
1328                while ((node = m_baseIterator.next()) != END) {
1329                    // Return only nodes that are in the selected namespace
1330                    if (getNSType(node) == m_nsType) {
1331                        return returnNode(node);
1332                    }
1333                }
1334    
1335                return END;
1336            }
1337    
1338            /**
1339             * Returns a deep copy of this iterator.  The cloned iterator is not
1340             * reset.
1341             *
1342             * @return a deep copy of this iterator.
1343             */
1344            public DTMAxisIterator cloneIterator() {
1345                try {
1346                    DTMAxisIterator nestedClone = m_baseIterator.cloneIterator();
1347                    NamespaceWildcardIterator clone =
1348                        (NamespaceWildcardIterator) super.clone();
1349    
1350                    clone.m_baseIterator = nestedClone;
1351                    clone.m_nsType = m_nsType;
1352                    clone._isRestartable = false;
1353    
1354                    return clone;
1355                } catch (CloneNotSupportedException e) {
1356                    BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
1357                                              e.toString());
1358                    return null;
1359                }
1360            }
1361    
1362            /**
1363             * True if this iterator has a reversed axis.
1364             *
1365             * @return <code>true</code> if this iterator is a reversed axis.
1366             */
1367            public boolean isReverse() {
1368                return m_baseIterator.isReverse();
1369            }
1370    
1371            public void setMark() {
1372                m_baseIterator.setMark();
1373            }
1374    
1375            public void gotoMark() {
1376                m_baseIterator.gotoMark();
1377            }
1378        }
1379    
1380        /**
1381         * Iterator that returns children within a given namespace for a
1382         * given node. The functionality chould be achieved by putting a
1383         * filter on top of a basic child iterator, but a specialised
1384         * iterator is used for efficiency (both speed and size of translet).
1385         */
1386        public final class NamespaceChildrenIterator
1387            extends InternalAxisIteratorBase
1388        {
1389    
1390            /** The extended type ID being requested. */
1391            private final int _nsType;
1392    
1393            /**
1394             * Constructor NamespaceChildrenIterator
1395             *
1396             *
1397             * @param type The extended type ID being requested.
1398             */
1399            public NamespaceChildrenIterator(final int type) {
1400                _nsType = type;
1401            }
1402    
1403            /**
1404             * Set start to END should 'close' the iterator,
1405             * i.e. subsequent call to next() should return END.
1406             *
1407             * @param node Sets the root of the iteration.
1408             *
1409             * @return A DTMAxisIterator set to the start of the iteration.
1410             */
1411            public DTMAxisIterator setStartNode(int node) {
1412                //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
1413                if (node == DTMDefaultBase.ROOTNODE) {
1414                    node = getDocument();
1415                }
1416    
1417                if (_isRestartable) {
1418                    _startNode = node;
1419                    _currentNode = (node == DTM.NULL) ? DTM.NULL : NOTPROCESSED;
1420    
1421                    return resetPosition();
1422                }
1423    
1424                return this;
1425            }
1426    
1427            /**
1428             * Get the next node in the iteration.
1429             *
1430             * @return The next node handle in the iteration, or END.
1431             */
1432            public int next() {
1433                if (_currentNode != DTM.NULL) {
1434                    for (int node = (NOTPROCESSED == _currentNode)
1435                                         ? _firstch(makeNodeIdentity(_startNode))
1436                                         : _nextsib(_currentNode);
1437                         node != END;
1438                         node = _nextsib(node)) {
1439                        int nodeHandle = makeNodeHandle(node);
1440    
1441                        if (getNSType(nodeHandle) == _nsType) {
1442                            _currentNode = node;
1443    
1444                            return returnNode(nodeHandle);
1445                        }
1446                    }
1447                }
1448    
1449                return END;
1450            }
1451        }  // end of NamespaceChildrenIterator
1452    
1453        /**
1454         * Iterator that returns attributes within a given namespace for a node.
1455         */
1456        public final class NamespaceAttributeIterator
1457                extends InternalAxisIteratorBase
1458        {
1459    
1460            /** The extended type ID being requested. */
1461            private final int _nsType;
1462    
1463            /**
1464             * Constructor NamespaceAttributeIterator
1465             *
1466             *
1467             * @param nsType The extended type ID being requested.
1468             */
1469            public NamespaceAttributeIterator(int nsType) {
1470                super();
1471    
1472                _nsType = nsType;
1473            }
1474    
1475            /**
1476             * Set start to END should 'close' the iterator,
1477             * i.e. subsequent call to next() should return END.
1478             *
1479             * @param node Sets the root of the iteration.
1480             *
1481             * @return A DTMAxisIterator set to the start of the iteration.
1482             */
1483            public DTMAxisIterator setStartNode(int node) {
1484                //%HZ%: Added reference to DTMDefaultBase.ROOTNODE back in, temporarily
1485                if (node == DTMDefaultBase.ROOTNODE) {
1486                    node = getDocument();
1487                }
1488    
1489                if (_isRestartable) {
1490                    int nsType = _nsType;
1491    
1492                    _startNode = node;
1493    
1494                    for (node = getFirstAttribute(node);
1495                         node != END;
1496                         node = getNextAttribute(node)) {
1497                        if (getNSType(node) == nsType) {
1498                            break;
1499                        }
1500                    }
1501    
1502                    _currentNode = node;
1503                    return resetPosition();
1504                }
1505    
1506                return this;
1507            }
1508    
1509            /**
1510             * Get the next node in the iteration.
1511             *
1512             * @return The next node handle in the iteration, or END.
1513             */
1514            public int next() {
1515                int node = _currentNode;
1516                int nsType = _nsType;
1517                int nextNode;
1518    
1519                if (node == END) {
1520                    return END;
1521                }
1522    
1523                for (nextNode = getNextAttribute(node);
1524                     nextNode != END;
1525                     nextNode = getNextAttribute(nextNode)) {
1526                    if (getNSType(nextNode) == nsType) {
1527                        break;
1528                    }
1529                }
1530    
1531                _currentNode = nextNode;
1532    
1533                return returnNode(node);
1534            }
1535        }  // end of NamespaceAttributeIterator
1536    
1537        /**
1538         * Returns an iterator with all descendants of a node that are of
1539         * a given type.
1540         */
1541        public DTMAxisIterator getTypedDescendantIterator(int type)
1542        {
1543            return new TypedDescendantIterator(type);
1544        }
1545    
1546        /**
1547         * Returns the nth descendant of a node
1548         */
1549        public DTMAxisIterator getNthDescendant(int type, int n, boolean includeself)
1550        {
1551            DTMAxisIterator source = (DTMAxisIterator) new TypedDescendantIterator(type);
1552            return new NthDescendantIterator(n);
1553        }
1554    
1555        /**
1556         * Copy the string value of a node directly to an output handler
1557         */
1558        public void characters(final int node, SerializationHandler handler)
1559            throws TransletException
1560        {
1561            if (node != DTM.NULL) {
1562                try {
1563                    dispatchCharactersEvents(node, handler, false);
1564                } catch (SAXException e) {
1565                    throw new TransletException(e);
1566                }
1567            }
1568        }
1569        
1570        /**
1571         * Copy a node-set to an output handler
1572         */
1573        public void copy(DTMAxisIterator nodes, SerializationHandler handler)
1574            throws TransletException
1575        {
1576            int node;
1577            while ((node = nodes.next()) != DTM.NULL) {
1578                copy(node, handler);
1579            }
1580        }
1581    
1582        /**
1583         * Copy the whole tree to an output handler
1584         */
1585        public void copy(SerializationHandler handler) throws TransletException
1586        {
1587            copy(getDocument(), handler);
1588        }
1589    
1590        /**
1591         * Performs a deep copy (ref. XSLs copy-of())
1592         *
1593         * TODO: Copy namespace declarations. Can't be done until we
1594         *       add namespace nodes and keep track of NS prefixes
1595         * TODO: Copy comment nodes
1596         */
1597        public void copy(final int node, SerializationHandler handler)
1598            throws TransletException
1599        {
1600            copy(node, handler, false );
1601        }
1602    
1603    
1604     private final void copy(final int node, SerializationHandler handler, boolean isChild)
1605            throws TransletException
1606        {
1607         int nodeID = makeNodeIdentity(node);
1608            int eType = _exptype2(nodeID);
1609            int type = _exptype2Type(eType);
1610    
1611            try {
1612                switch(type)
1613                {
1614                    case DTM.ROOT_NODE:
1615                    case DTM.DOCUMENT_NODE:
1616                        for(int c = _firstch2(nodeID); c != DTM.NULL; c = _nextsib2(c)) {
1617                            copy(makeNodeHandle(c), handler, true);
1618                        }
1619                        break;
1620                    case DTM.PROCESSING_INSTRUCTION_NODE:
1621                        copyPI(node, handler);
1622                        break;
1623                    case DTM.COMMENT_NODE:
1624                        handler.comment(getStringValueX(node));
1625                        break;
1626                    case DTM.TEXT_NODE:
1627                        boolean oldEscapeSetting = false;
1628                        boolean escapeBit = false;
1629    
1630                        if (_dontEscape != null) {
1631                            escapeBit = _dontEscape.getBit(getNodeIdent(node));
1632                            if (escapeBit) {
1633                                oldEscapeSetting = handler.setEscaping(false);
1634                            }
1635                        }
1636                        
1637                        copyTextNode(nodeID, handler);
1638            
1639                        if (escapeBit) {
1640                            handler.setEscaping(oldEscapeSetting);
1641                        }
1642                        break;
1643                    case DTM.ATTRIBUTE_NODE:
1644                        copyAttribute(nodeID, eType, handler);
1645                        break;
1646                    case DTM.NAMESPACE_NODE:
1647                        handler.namespaceAfterStartElement(getNodeNameX(node), getNodeValue(node));
1648                        break;
1649                    default:
1650                        if (type == DTM.ELEMENT_NODE) 
1651                        {
1652                            // Start element definition
1653                            final String name = copyElement(nodeID, eType, handler);
1654                            //if(isChild) => not to copy any namespaces  from parents
1655                            // else copy all namespaces in scope
1656                            copyNS(nodeID, handler,!isChild);
1657                            copyAttributes(nodeID, handler);
1658                            // Copy element children
1659                            for (int c = _firstch2(nodeID); c != DTM.NULL; c = _nextsib2(c)) {
1660                                copy(makeNodeHandle(c), handler, true);
1661                            }
1662              
1663                            // Close element definition
1664                            handler.endElement(name);
1665                        }
1666                        // Shallow copy of attribute to output handler
1667                        else {
1668                            final String uri = getNamespaceName(node);
1669                            if (uri.length() != 0) {
1670                                final String prefix = getPrefix(node);
1671                                handler.namespaceAfterStartElement(prefix, uri);
1672                            }
1673                            handler.addAttribute(getNodeName(node), getNodeValue(node));
1674                        }
1675                        break;
1676                }
1677            } 
1678            catch (Exception e) {
1679                throw new TransletException(e);
1680            }
1681        
1682        }
1683        /**
1684         * Copies a processing instruction node to an output handler
1685         */
1686        private void copyPI(final int node, SerializationHandler handler)
1687            throws TransletException
1688        {
1689            final String target = getNodeName(node);
1690            final String value = getStringValueX(node);
1691          
1692            try {
1693                handler.processingInstruction(target, value);
1694            } catch (Exception e) {
1695                throw new TransletException(e);
1696            }
1697        }
1698    
1699        /**
1700         * Performs a shallow copy (ref. XSLs copy())
1701         */
1702        public String shallowCopy(final int node, SerializationHandler handler)
1703            throws TransletException
1704        {
1705            int nodeID = makeNodeIdentity(node);
1706            int exptype = _exptype2(nodeID);
1707            int type = _exptype2Type(exptype);
1708          
1709            try {
1710                switch(type)
1711                {
1712                    case DTM.ELEMENT_NODE:
1713                        final String name = copyElement(nodeID, exptype, handler);
1714                        copyNS(nodeID, handler, true);
1715                        return name;
1716                    case DTM.ROOT_NODE:
1717                    case DTM.DOCUMENT_NODE:
1718                        return EMPTYSTRING;
1719                    case DTM.TEXT_NODE:
1720                        copyTextNode(nodeID, handler);
1721                        return null;
1722                    case DTM.PROCESSING_INSTRUCTION_NODE:
1723                        copyPI(node, handler);
1724                        return null;
1725                    case DTM.COMMENT_NODE:
1726                        handler.comment(getStringValueX(node));
1727                        return null;
1728                    case DTM.NAMESPACE_NODE:
1729                        handler.namespaceAfterStartElement(getNodeNameX(node), getNodeValue(node));
1730                        return null;
1731                    case DTM.ATTRIBUTE_NODE:
1732                        copyAttribute(nodeID, exptype, handler);
1733                        return null;  
1734                    default:
1735                        final String uri1 = getNamespaceName(node);
1736                        if (uri1.length() != 0) {
1737                            final String prefix = getPrefix(node);
1738                            handler.namespaceAfterStartElement(prefix, uri1);
1739                        }
1740                        handler.addAttribute(getNodeName(node), getNodeValue(node));
1741                        return null;
1742                }
1743            } catch (Exception e) {
1744                throw new TransletException(e);
1745            }   
1746        }
1747        
1748        /**
1749         * Returns a node' defined language for a node (if any)
1750         */
1751        public String getLanguage(int node)
1752        {
1753            int parent = node;
1754            while (DTM.NULL != parent) {
1755                if (DTM.ELEMENT_NODE == getNodeType(parent)) {
1756                    int langAttr = getAttributeNode(parent, "http://www.w3.org/XML/1998/namespace", "lang");
1757    
1758                    if (DTM.NULL != langAttr) {
1759                        return getNodeValue(langAttr);     
1760                    }
1761                }
1762    
1763                parent = getParent(parent);
1764            }      
1765            return(null);
1766        }
1767    
1768        /**
1769         * Returns an instance of the DOMBuilder inner class
1770         * This class will consume the input document through a SAX2
1771         * interface and populate the tree.
1772         */
1773        public DOMBuilder getBuilder()
1774        {
1775            return this;
1776        }
1777        
1778        /**
1779         * Return a SerializationHandler for output handling.
1780         * This method is used by Result Tree Fragments.
1781         */
1782        public SerializationHandler getOutputDomBuilder()
1783        {
1784            return new ToXMLSAXHandler(this, "UTF-8");
1785        }
1786        
1787        /**
1788         * Return a instance of a DOM class to be used as an RTF
1789         */ 
1790        public DOM getResultTreeFrag(int initSize, int rtfType)
1791        {
1792            return getResultTreeFrag(initSize, rtfType, true);
1793        }
1794            
1795        /**
1796         * Return a instance of a DOM class to be used as an RTF
1797         *
1798         * @param initSize The initial size of the DOM.
1799         * @param rtfType The type of the RTF
1800         * @param addToManager true if the RTF should be registered with the DTMManager.
1801         * @return The DOM object which represents the RTF.
1802         */ 
1803        public DOM getResultTreeFrag(int initSize, int rtfType, boolean addToManager)
1804        {
1805            if (rtfType == DOM.SIMPLE_RTF) {
1806                if (addToManager) {
1807                    int dtmPos = _dtmManager.getFirstFreeDTMID();
1808                    SimpleResultTreeImpl rtf = new SimpleResultTreeImpl(_dtmManager,
1809                                               dtmPos << DTMManager.IDENT_DTM_NODE_BITS);
1810                    _dtmManager.addDTM(rtf, dtmPos, 0);
1811                    return rtf;
1812                }
1813                else {
1814                    return new SimpleResultTreeImpl(_dtmManager, 0);
1815                }
1816            }
1817            else if (rtfType == DOM.ADAPTIVE_RTF) {
1818                if (addToManager) {
1819                    int dtmPos = _dtmManager.getFirstFreeDTMID();
1820                    AdaptiveResultTreeImpl rtf = new AdaptiveResultTreeImpl(_dtmManager,
1821                                           dtmPos << DTMManager.IDENT_DTM_NODE_BITS,
1822                                           m_wsfilter, initSize, m_buildIdIndex);
1823                    _dtmManager.addDTM(rtf, dtmPos, 0);
1824                    return rtf;
1825            
1826                }
1827                else {
1828                    return new AdaptiveResultTreeImpl(_dtmManager, 0,
1829                                           m_wsfilter, initSize, m_buildIdIndex);
1830                }           
1831            }
1832            else {
1833                return (DOM) _dtmManager.getDTM(null, true, m_wsfilter,
1834                                                true, false, false,
1835                                                initSize, m_buildIdIndex);
1836            }
1837        }
1838    
1839        /**
1840         * %HZ% Need Javadoc
1841         */
1842        public Hashtable getElementsWithIDs() {
1843            if (m_idAttributes == null) {
1844                return null;
1845            }
1846    
1847            // Convert a java.util.Hashtable to an xsltc.runtime.Hashtable
1848            Iterator idEntries = m_idAttributes.entrySet().iterator();
1849            if (!idEntries.hasNext()) {
1850                return null;
1851            }
1852    
1853            Hashtable idAttrsTable = new Hashtable();
1854            
1855            while (idEntries.hasNext()) {
1856                Map.Entry entry = (Map.Entry) idEntries.next();
1857                idAttrsTable.put(entry.getKey(), entry.getValue());
1858            }
1859    
1860            return idAttrsTable;
1861        }
1862    
1863        /**
1864         * The getUnparsedEntityURI function returns the URI of the unparsed
1865         * entity with the specified name in the same document as the context
1866         * node (see [3.3 Unparsed Entities]). It returns the empty string if
1867         * there is no such entity.
1868         */
1869        public String getUnparsedEntityURI(String name)
1870        {
1871            // Special handling for DOM input
1872            if (_document != null) {
1873                String uri = "";
1874                DocumentType doctype = _document.getDoctype();
1875                if (doctype != null) {
1876                    NamedNodeMap entities = doctype.getEntities();
1877                    
1878                    if (entities == null) {
1879                        return uri;
1880                    }
1881                    
1882                    Entity entity = (Entity) entities.getNamedItem(name);
1883                    
1884                    if (entity == null) {
1885                        return uri;
1886                    }
1887                    
1888                    String notationName = entity.getNotationName();
1889                    if (notationName != null) {
1890                        uri = entity.getSystemId();
1891                        if (uri == null) {
1892                            uri = entity.getPublicId();
1893                        }
1894                    }
1895                }
1896                return uri;
1897            }
1898            else {
1899                return super.getUnparsedEntityURI(name);
1900            }       
1901        }
1902    
1903    }