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 }