1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xalan.internal.xsltc.dom;
22
23import com.sun.org.apache.xalan.internal.xsltc.DOM;
24import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
25import com.sun.org.apache.xalan.internal.xsltc.TransletException;
26import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
27import com.sun.org.apache.xml.internal.dtm.Axis;
28import com.sun.org.apache.xml.internal.dtm.DTM;
29import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
30import com.sun.org.apache.xml.internal.dtm.DTMManager;
31import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIteratorBase;
32import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIterNodeList;
33import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase;
34import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
35import com.sun.org.apache.xml.internal.utils.SuballocatedIntVector;
36import java.util.HashMap;
37import java.util.Map;
38import org.w3c.dom.Node;
39import org.w3c.dom.NodeList;
40
41/**
42 * @author Jacek Ambroziak
43 * @author Morten Jorgensen
44 * @author Erwin Bolwidt <ejb@klomp.org>
45 */
46public final class MultiDOM implements DOM {
47
48    private static final int NO_TYPE = DOM.FIRST_TYPE - 2;
49    private static final int INITIAL_SIZE = 4;
50
51    private DOM[] _adapters;
52    private DOMAdapter _main;
53    private DTMManager _dtmManager;
54    private int _free;
55    private int _size;
56
57    private Map<String, Integer> _documents = new HashMap<>();
58
59    private final class AxisIterator extends DTMAxisIteratorBase {
60        // constitutive data
61        private final int _axis;
62        private final int _type;
63        // implementation mechanism
64        private DTMAxisIterator _source;
65        private int _dtmId = -1;
66
67        public AxisIterator(final int axis, final int type) {
68            _axis = axis;
69            _type = type;
70        }
71
72        public int next() {
73            if (_source == null) {
74                return(END);
75            }
76            return _source.next();
77        }
78
79
80        public void setRestartable(boolean flag) {
81            if (_source != null) {
82                _source.setRestartable(flag);
83            }
84        }
85
86        public DTMAxisIterator setStartNode(final int node) {
87            if (node == DTM.NULL) {
88                return this;
89            }
90
91            int dom = node >>> DTMManager.IDENT_DTM_NODE_BITS;
92
93            // Get a new source first time and when mask changes
94            if (_source == null || _dtmId != dom) {
95                if (_type == NO_TYPE) {
96                    _source = _adapters[dom].getAxisIterator(_axis);
97                } else if (_axis == Axis.CHILD) {
98                    _source = _adapters[dom].getTypedChildren(_type);
99                } 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 the Map
328        if (indexByURI) {
329            String uri = adapter.getDocumentURI(0);
330            _documents.put(uri, 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 = _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 = _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        int index = iter.next();
569        if (index == DTM.NULL) {
570            return new DTMAxisIterNodeList(null, null);
571        }
572        iter.reset();
573        return _adapters[getDTMId(index)].makeNodeList(iter);
574    }
575
576    public String getLanguage(int node) {
577        return _adapters[getDTMId(node)].getLanguage(node);
578    }
579
580    public int getSize() {
581        int size = 0;
582        for (int i=0; i<_size; i++) {
583            size += _adapters[i].getSize();
584        }
585        return(size);
586    }
587
588    public String getDocumentURI(int node) {
589        if (node == DTM.NULL) {
590            node = DOM.NULL;
591        }
592        return _adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].getDocumentURI(0);
593    }
594
595    public boolean isElement(final int node) {
596        if (node == DTM.NULL) {
597            return false;
598        }
599        return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isElement(node));
600    }
601
602    public boolean isAttribute(final int node) {
603        if (node == DTM.NULL) {
604            return false;
605        }
606        return(_adapters[node >>> DTMManager.IDENT_DTM_NODE_BITS].isAttribute(node));
607    }
608
609    public int getDTMId(int nodeHandle)
610    {
611        if (nodeHandle == DTM.NULL)
612            return 0;
613
614        int id = nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS;
615        while (id >= 2 && _adapters[id] == _adapters[id-1]) {
616            id--;
617        }
618        return id;
619    }
620
621    public DOM getDTM(int nodeHandle) {
622        return _adapters[getDTMId(nodeHandle)];
623    }
624
625    public int getNodeIdent(int nodeHandle)
626    {
627        return _adapters[nodeHandle >>> DTMManager.IDENT_DTM_NODE_BITS].getNodeIdent(nodeHandle);
628    }
629
630    public int getNodeHandle(int nodeId)
631    {
632        return _main.getNodeHandle(nodeId);
633    }
634
635    public DOM getResultTreeFrag(int initSize, int rtfType)
636    {
637        return _main.getResultTreeFrag(initSize, rtfType);
638    }
639
640    public DOM getResultTreeFrag(int initSize, int rtfType, boolean addToManager)
641    {
642        return _main.getResultTreeFrag(initSize, rtfType, addToManager);
643    }
644
645    public DOM getMain()
646    {
647        return _main;
648    }
649
650    /**
651     * Returns a DOMBuilder class wrapped in a SAX adapter.
652     */
653    public SerializationHandler getOutputDomBuilder()
654    {
655        return _main.getOutputDomBuilder();
656    }
657
658    public String lookupNamespace(int node, String prefix)
659        throws TransletException
660    {
661        return _main.lookupNamespace(node, prefix);
662    }
663
664    // %HZ% Does this method make any sense here???
665    public String getUnparsedEntityURI(String entity) {
666        return _main.getUnparsedEntityURI(entity);
667    }
668
669    // %HZ% Does this method make any sense here???
670    public Map<String, Integer> getElementsWithIDs() {
671        return _main.getElementsWithIDs();
672    }
673
674    public void release() {
675        _main.release();
676    }
677
678    private boolean isMatchingAdapterEntry(DOM entry, DOMAdapter adapter) {
679        DOM dom = adapter.getDOMImpl();
680
681        return (entry == adapter) || (
682            /*
683             * Method addDOMAdapter overwrites for AdaptiveResultTreeImpl
684             * objects the usual entry with an adapter to the nested
685             * DOM, so we must check this here. See last 'if' statement
686             * of addDOMAdapter.
687             */
688            (dom instanceof AdaptiveResultTreeImpl) &&
689            (entry instanceof DOMAdapter) &&
690            (((AdaptiveResultTreeImpl)dom).getNestedDOM() == ((DOMAdapter)entry).getDOMImpl())
691        );
692    }
693
694    public void removeDOMAdapter(DOMAdapter adapter) {
695        _documents.remove(adapter.getDocumentURI(0));
696        DOM dom = adapter.getDOMImpl();
697
698        if (dom instanceof DTMDefaultBase) {
699            SuballocatedIntVector ids = ((DTMDefaultBase) dom).getDTMIDs();
700            int idsSize = ids.size();
701            for (int i = 0; i < idsSize; i++) {
702                _adapters[ids.elementAt(i) >>> DTMManager.IDENT_DTM_NODE_BITS] = null;
703            }
704        } else {
705            int id = dom.getDocument() >>> DTMManager.IDENT_DTM_NODE_BITS;
706            if ((id > 0) && (id < _adapters.length) && isMatchingAdapterEntry(_adapters[id], adapter)) {
707                _adapters[id] = null;
708            } else {
709                boolean found = false;
710                for (int i = 0; i < _adapters.length; i++) {
711                    if (isMatchingAdapterEntry(_adapters[id], adapter)) {
712                        _adapters[i] = null;
713                        found = true;
714                        break;
715                    }
716                }
717            }
718        }
719    }
720}
721