1 /*
   2  * Copyright (c) 2015, 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 
  21 package com.sun.org.apache.xalan.internal.xsltc.dom;
  22 
  23 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  24 import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
  25 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  26 import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
  27 import com.sun.org.apache.xml.internal.dtm.Axis;
  28 import com.sun.org.apache.xml.internal.dtm.DTM;
  29 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  30 import com.sun.org.apache.xml.internal.dtm.DTMManager;
  31 import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIteratorBase;
  32 import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIterNodeList;
  33 import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase;
  34 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  35 import com.sun.org.apache.xml.internal.utils.SuballocatedIntVector;
  36 import java.util.HashMap;
  37 import java.util.Map;
  38 import org.w3c.dom.Node;
  39 import org.w3c.dom.NodeList;
  40 
  41 /**
  42  * @author Jacek Ambroziak
  43  * @author Morten Jorgensen
  44  * @author Erwin Bolwidt <ejb@klomp.org>
  45  */
  46 public 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 }