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: MultiDOM.java 468651 2006-10-28 07:04:25Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.dom;
023    
024    import org.apache.xalan.xsltc.DOM;
025    import org.apache.xalan.xsltc.StripFilter;
026    import org.apache.xml.serializer.SerializationHandler;
027    import org.apache.xalan.xsltc.TransletException;
028    import org.apache.xalan.xsltc.runtime.BasisLibrary;
029    import org.apache.xalan.xsltc.runtime.Hashtable;
030    import org.apache.xml.dtm.DTM;
031    import org.apache.xml.dtm.Axis;
032    import org.apache.xml.dtm.DTMAxisIterator;
033    import org.apache.xml.dtm.DTMManager;
034    import org.apache.xml.dtm.ref.DTMAxisIteratorBase;
035    import org.apache.xml.dtm.ref.DTMDefaultBase;
036    import org.apache.xml.utils.SuballocatedIntVector;
037    
038    import org.w3c.dom.Node;
039    import org.w3c.dom.NodeList;
040    
041    /**
042     * @author Jacek Ambroziak
043     * @author Morten Jorgensen
044     * @author Erwin Bolwidt <ejb@klomp.org>
045     */
046    public final class MultiDOM implements DOM {
047    
048        private static final int NO_TYPE = DOM.FIRST_TYPE - 2;
049        private static final int INITIAL_SIZE = 4;
050        
051        private DOM[] _adapters;
052        private DOMAdapter _main;
053        private DTMManager _dtmManager;
054        private int _free;
055        private int _size;
056    
057        private Hashtable _documents = new Hashtable();
058    
059        private final class AxisIterator extends DTMAxisIteratorBase {
060            // constitutive data
061            private final int _axis;
062            private final int _type;
063            // implementation mechanism
064            private DTMAxisIterator _source;
065            private int _dtmId = -1;
066    
067            public AxisIterator(final int axis, final int type) {
068                _axis = axis;
069                _type = type;
070            }
071    
072            public int next() {
073                if (_source == null) {
074                    return(END);
075                }
076                return _source.next();
077            }
078    
079    
080            public void setRestartable(boolean flag) {
081                if (_source != null) {
082                    _source.setRestartable(flag);
083                }
084            }
085    
086            public DTMAxisIterator setStartNode(final int node) {
087                if (node == DTM.NULL) {
088                    return this;
089                }
090    
091                int dom = node >>> DTMManager.IDENT_DTM_NODE_BITS;
092    
093                // Get a new source first time and when mask changes
094                if (_source == null || _dtmId != dom) {
095                    if (_type == NO_TYPE) {
096                        _source = _adapters[dom].getAxisIterator(_axis);
097                    } else if (_axis == Axis.CHILD) {
098                        _source = _adapters[dom].getTypedChildren(_type);
099                    } else {
100                        _source = _adapters[dom].getTypedAxisIterator(_axis, _type);
101                    }
102                }
103    
104                _dtmId = dom;
105                _source.setStartNode(node);
106                return this;
107            }
108    
109            public DTMAxisIterator reset() {
110                if (_source != null) {
111                    _source.reset();
112                }
113                return this;
114            }
115        
116            public int getLast() {
117                if (_source != null) {
118                    return _source.getLast();
119                }
120                else {
121                    return END;
122                }
123            }
124    
125            public int getPosition() {
126                if (_source != null) {
127                    return _source.getPosition();
128                }
129                else {
130                    return END;
131                }
132            }
133        
134            public boolean isReverse() {
135                return Axis.isReverse(_axis);
136            }
137        
138            public void setMark() {
139                if (_source != null) {
140                    _source.setMark();
141                }
142            }
143        
144            public void gotoMark() {
145                if (_source != null) {
146                    _source.gotoMark();
147                }
148            }
149        
150            public DTMAxisIterator cloneIterator() {
151                final AxisIterator clone = new AxisIterator(_axis, _type);
152                if (_source != null) {
153                    clone._source = _source.cloneIterator();
154                }
155                clone._dtmId = _dtmId;
156                return clone;
157            }
158        } // end of AxisIterator
159    
160    
161        /**************************************************************
162         * This is a specialised iterator for predicates comparing node or
163         * attribute values to variable or parameter values.
164         */
165        private final class NodeValueIterator extends DTMAxisIteratorBase {
166    
167            private DTMAxisIterator _source;
168            private String _value;
169            private boolean _op;
170            private final boolean _isReverse;
171            private int _returnType = RETURN_PARENT;
172    
173            public NodeValueIterator(DTMAxisIterator source, int returnType,
174                                     String value, boolean op) {
175                _source = source;
176                _returnType = returnType;
177                _value = value;
178                _op = op;
179                _isReverse = source.isReverse();
180            }
181    
182            public boolean isReverse() {
183                return _isReverse;
184            }
185        
186            public DTMAxisIterator cloneIterator() {
187                try {
188                    NodeValueIterator clone = (NodeValueIterator)super.clone();
189                    clone._source = _source.cloneIterator();
190                    clone.setRestartable(false);
191                    return clone.reset();
192                }
193                catch (CloneNotSupportedException e) {
194                    BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
195                                              e.toString());
196                    return null;
197                }
198            }
199    
200    
201            public void setRestartable(boolean isRestartable) {
202                _isRestartable = isRestartable;
203                _source.setRestartable(isRestartable);
204            }
205    
206            public DTMAxisIterator reset() {
207                _source.reset();
208                return resetPosition();
209            }
210    
211            public int next() {
212    
213                int node;
214                while ((node = _source.next()) != END) {
215                    String val = getStringValueX(node);
216                    if (_value.equals(val) == _op) {
217                        if (_returnType == RETURN_CURRENT)
218                            return returnNode(node);
219                        else
220                            return returnNode(getParent(node));
221                    }
222                }
223                return END;
224            }
225    
226            public DTMAxisIterator setStartNode(int node) {
227                if (_isRestartable) {
228                    _source.setStartNode(_startNode = node); 
229                    return resetPosition();
230                }
231                return this;
232            }
233    
234            public void setMark() {
235                _source.setMark();
236            }
237    
238            public void gotoMark() {
239                _source.gotoMark();
240            }
241        }                       
242    
243        public MultiDOM(DOM main) {
244            _size = INITIAL_SIZE;
245            _free = 1;
246            _adapters = new DOM[INITIAL_SIZE];
247            DOMAdapter adapter = (DOMAdapter)main;
248            _adapters[0] = adapter;
249            _main = adapter;
250            DOM dom = adapter.getDOMImpl();
251            if (dom instanceof DTMDefaultBase) {
252                _dtmManager = ((DTMDefaultBase)dom).getManager();
253            }
254    
255            // %HZ% %REVISIT% Is this the right thing to do here?  In the old
256            // %HZ% %REVISIT% version, the main document did not get added through
257            // %HZ% %REVISIT% a call to addDOMAdapter, which meant it couldn't be
258            // %HZ% %REVISIT% found by a call to getDocumentMask.  The problem is
259            // %HZ% %REVISIT% TransformerHandler is typically constructed with a
260            // %HZ% %REVISIT% system ID equal to the stylesheet's URI; with SAX
261            // %HZ% %REVISIT% input, it ends up giving that URI to the document.
262            // %HZ% %REVISIT% Then, any references to document('') are resolved
263            // %HZ% %REVISIT% using the stylesheet's URI.
264            // %HZ% %REVISIT% MultiDOM.getDocumentMask is called to verify that
265            // %HZ% %REVISIT% a document associated with that URI has not been
266            // %HZ% %REVISIT% encountered, and that method ends up returning the
267            // %HZ% %REVISIT% mask of the main document, when what we really what
268            // %HZ% %REVISIT% is to read the stylesheet itself!
269            addDOMAdapter(adapter, false);
270        }
271    
272        public int nextMask() {
273            return _free;
274        }
275    
276        public void setupMapping(String[] names, String[] uris, int[] types, String[] namespaces) {
277            // This method only has a function in DOM adapters
278        }
279    
280        public int addDOMAdapter(DOMAdapter adapter) {
281            return addDOMAdapter(adapter, true);
282        }
283    
284        private int addDOMAdapter(DOMAdapter adapter, boolean indexByURI) {
285            // Add the DOM adapter to the array of DOMs
286            DOM dom = adapter.getDOMImpl();
287            
288            int domNo = 1;
289            int dtmSize = 1;
290            SuballocatedIntVector dtmIds = null;
291            if (dom instanceof DTMDefaultBase) {
292                DTMDefaultBase dtmdb = (DTMDefaultBase)dom;
293                dtmIds = dtmdb.getDTMIDs();
294                dtmSize = dtmIds.size();
295                domNo = dtmIds.elementAt(dtmSize-1) >>> DTMManager.IDENT_DTM_NODE_BITS;
296            }
297            else if (dom instanceof SimpleResultTreeImpl) {
298                SimpleResultTreeImpl simpleRTF = (SimpleResultTreeImpl)dom;
299                domNo = simpleRTF.getDocument() >>> DTMManager.IDENT_DTM_NODE_BITS;
300            }
301                      
302            if (domNo >= _size) {
303                int oldSize = _size;
304                do {
305                    _size *= 2;
306                } while (_size <= domNo);
307                
308                final DOMAdapter[] newArray = new DOMAdapter[_size];
309                System.arraycopy(_adapters, 0, newArray, 0, oldSize);
310                _adapters = newArray;
311            }
312            
313            _free = domNo + 1;
314            
315            if (dtmSize == 1) {
316                _adapters[domNo] = adapter;
317            }
318            else if (dtmIds != null) {
319                int domPos = 0;
320                for (int i = dtmSize - 1; i >= 0; i--) {
321                    domPos = dtmIds.elementAt(i) >>> DTMManager.IDENT_DTM_NODE_BITS;
322                    _adapters[domPos] = adapter;
323                }
324                domNo = domPos;
325            }
326    
327            // Store reference to document (URI) in hashtable
328            if (indexByURI) {
329                String uri = adapter.getDocumentURI(0);
330                _documents.put(uri, new Integer(domNo));
331            }
332            
333            // If the dom is an AdaptiveResultTreeImpl, we need to create a
334            // DOMAdapter around its nested dom object (if it is non-null) and
335            // add the DOMAdapter to the list.
336            if (dom instanceof AdaptiveResultTreeImpl) {
337                AdaptiveResultTreeImpl adaptiveRTF = (AdaptiveResultTreeImpl)dom;
338                DOM nestedDom = adaptiveRTF.getNestedDOM();
339                if (nestedDom != null) {
340                    DOMAdapter newAdapter = new DOMAdapter(nestedDom, 
341                                                           adapter.getNamesArray(),
342                                                           adapter.getUrisArray(),
343                                                           adapter.getTypesArray(),
344                                                           adapter.getNamespaceArray());
345                    addDOMAdapter(newAdapter);  
346                } 
347            }
348            
349            return domNo;
350        }
351            
352        public int getDocumentMask(String uri) {
353            Integer domIdx = (Integer)_documents.get(uri);
354            if (domIdx == null) {
355                return(-1);
356            } else {
357                return domIdx.intValue();
358            }
359        }
360        
361        public DOM getDOMAdapter(String uri) {
362            Integer domIdx = (Integer)_documents.get(uri);
363            if (domIdx == null) {
364                return(null);
365            } else {
366                return(_adapters[domIdx.intValue()]);
367            }
368        }
369        
370        public int getDocument() 
371        {
372            return _main.getDocument();
373        }
374    
375        public DTMManager getDTMManager() {
376            return _dtmManager;
377        }
378    
379        /** 
380          * Returns singleton iterator containing the document root 
381          */
382        public DTMAxisIterator getIterator() {
383            // main source document @ 0
384            return _main.getIterator();
385        }
386        
387        public String getStringValue() {
388            return _main.getStringValue();
389        }
390        
391        public DTMAxisIterator getChildren(final int node) {
392            return _adapters[getDTMId(node)].getChildren(node);
393        }
394        
395        public DTMAxisIterator getTypedChildren(final int type) {
396            return new AxisIterator(Axis.CHILD, type);
397        }
398        
399        public DTMAxisIterator getAxisIterator(final int axis) {
400            return new AxisIterator(axis, NO_TYPE);
401        }
402        
403        public DTMAxisIterator getTypedAxisIterator(final int axis, final int type)
404        {
405            return new AxisIterator(axis, type);
406        }
407    
408        public DTMAxisIterator getNthDescendant(int node, int n,
409                                                boolean includeself)
410        {
411            return _adapters[getDTMId(node)].getNthDescendant(node, n, includeself);
412        }
413    
414        public DTMAxisIterator getNodeValueIterator(DTMAxisIterator iterator,
415                                                    int type, String value,
416                                                    boolean op)
417        {
418            return(new NodeValueIterator(iterator, type, value, op));
419        }
420    
421        public DTMAxisIterator getNamespaceAxisIterator(final int axis,
422                                                        final int ns)
423        {
424            DTMAxisIterator iterator = _main.getNamespaceAxisIterator(axis, ns);
425            return(iterator);        
426        }
427    
428        public DTMAxisIterator orderNodes(DTMAxisIterator source, int node) {
429            return _adapters[getDTMId(node)].orderNodes(source, node);
430        }
431    
432        public int getExpandedTypeID(final int node) {
433            if (node != DTM.NULL) {
434                return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getExpandedTypeID(node);
435            }
436            else {
437                return DTM.NULL;
438            }
439        }
440    
441        public int getNamespaceType(final int node) {
442            return _adapters[getDTMId(node)].getNamespaceType(node);
443        }
444        
445        public int getNSType(int node)
446       {
447            return _adapters[getDTMId(node)].getNSType(node);
448       }
449        
450        public int getParent(final int node) {
451            if (node == DTM.NULL) {
452                return DTM.NULL;
453            }
454            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getParent(node);
455        }
456        
457        public int getAttributeNode(final int type, final int el) {
458            if (el == DTM.NULL) {
459                return DTM.NULL;
460            }
461            return _adapters[el >>> DTMManager.IDENT_DTM_NODE_BITS].getAttributeNode(type, el);
462        }
463        
464        public String getNodeName(final int node) {
465            if (node == DTM.NULL) {
466                return "";
467            }
468            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeName(node);
469        }
470        
471        public String getNodeNameX(final int node) {
472            if (node == DTM.NULL) {
473                return "";
474            }
475            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeNameX(node);
476        }
477    
478        public String getNamespaceName(final int node) {
479            if (node == DTM.NULL) {
480                return "";
481            }
482            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getNamespaceName(node);
483        }
484        
485        public String getStringValueX(final int node) {
486            if (node == DTM.NULL) {
487                return "";
488            }
489            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getStringValueX(node);
490        }
491        
492        public void copy(final int node, SerializationHandler handler)
493            throws TransletException
494        {
495            if (node != DTM.NULL) {
496                _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].copy(node, handler);
497            }
498        }
499        
500        public void copy(DTMAxisIterator nodes, SerializationHandler handler)
501                throws TransletException
502        {
503            int node;
504            while ((node = nodes.next()) != DTM.NULL) {
505                _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].copy(node, handler);
506            }
507        }
508    
509    
510        public String shallowCopy(final int node, SerializationHandler handler)
511                throws TransletException
512        {
513            if (node == DTM.NULL) {
514                return "";
515            }
516            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].shallowCopy(node, handler);
517        }
518        
519        public boolean lessThan(final int node1, final int node2) {
520            if (node1 == DTM.NULL) {
521                return true;
522            }
523            if (node2 == DTM.NULL) {
524                return false;
525            }
526            final int dom1 = getDTMId(node1);
527            final int dom2 = getDTMId(node2);
528            return dom1 == dom2 ? _adapters[dom1].lessThan(node1, node2)
529                                : dom1 < dom2;
530        }
531        
532        public void characters(final int textNode, SerializationHandler handler)
533                     throws TransletException
534        {
535            if (textNode != DTM.NULL) {
536                _adapters[textNode >>> DTMManager.IDENT_DTM_NODE_BITS].characters(textNode, handler);
537            }
538        }
539    
540        public void setFilter(StripFilter filter) {
541            for (int dom=0; dom<_free; dom++) {
542                if (_adapters[dom] != null) {
543                    _adapters[dom].setFilter(filter);
544                }
545            }
546        }
547    
548        public Node makeNode(int index) {
549            if (index == DTM.NULL) {
550                return null;
551            }
552            return _adapters[getDTMId(index)].makeNode(index);
553        }
554    
555        public Node makeNode(DTMAxisIterator iter) {
556            // TODO: gather nodes from all DOMs ?
557            return _main.makeNode(iter);
558        }
559    
560        public NodeList makeNodeList(int index) {
561            if (index == DTM.NULL) {
562                return null;
563            }
564            return _adapters[getDTMId(index)].makeNodeList(index);
565        }
566    
567        public NodeList makeNodeList(DTMAxisIterator iter) {
568            // TODO: gather nodes from all DOMs ?
569            return _main.makeNodeList(iter);
570        }
571    
572        public String getLanguage(int node) {
573            return _adapters[getDTMId(node)].getLanguage(node);
574        }
575    
576        public int getSize() {
577            int size = 0;
578            for (int i=0; i<_size; i++) {
579                size += _adapters[i].getSize();
580            }
581            return(size);
582        }
583    
584        public String getDocumentURI(int node) {
585            if (node == DTM.NULL) {
586                node = DOM.NULL;
587            }
588            return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getDocumentURI(0);
589        }
590    
591        public boolean isElement(final int node) {
592            if (node == DTM.NULL) {
593                return false;
594            }
595            return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isElement(node));
596        }
597    
598        public boolean isAttribute(final int node) {
599            if (node == DTM.NULL) {
600                return false;
601            }
602            return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isAttribute(node));
603        }
604        
605        public int getDTMId(int nodeHandle)
606        {
607            if (nodeHandle == DTM.NULL)
608                return 0;
609            
610            int id = nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS;
611            while (id >= 2 && _adapters[id] == _adapters[id-1]) {
612                id--;
613            }
614            return id;
615        }
616        
617        public int getNodeIdent(int nodeHandle)
618        {
619            return _adapters[nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeIdent(nodeHandle);
620        }
621        
622        public int getNodeHandle(int nodeId)
623        {
624            return _main.getNodeHandle(nodeId);
625        }
626        
627        public DOM getResultTreeFrag(int initSize, int rtfType)
628        {
629            return _main.getResultTreeFrag(initSize, rtfType);
630        }
631        
632        public DOM getResultTreeFrag(int initSize, int rtfType, boolean addToManager)
633        {
634            return _main.getResultTreeFrag(initSize, rtfType, addToManager);
635        }
636        
637        public DOM getMain()
638        {
639            return _main;
640        }
641        
642        /**
643         * Returns a DOMBuilder class wrapped in a SAX adapter.
644         */
645        public SerializationHandler getOutputDomBuilder()
646        {
647            return _main.getOutputDomBuilder();
648        }
649    
650        public String lookupNamespace(int node, String prefix) 
651            throws TransletException
652        {
653            return _main.lookupNamespace(node, prefix);
654        }
655    
656        // %HZ% Does this method make any sense here???
657        public String getUnparsedEntityURI(String entity) {
658            return _main.getUnparsedEntityURI(entity);
659        }
660    
661        // %HZ% Does this method make any sense here???
662        public Hashtable getElementsWithIDs() {
663            return _main.getElementsWithIDs();
664        }
665    }