1/* 2 * Copyright (c) 2015, 2017, 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.xerces.internal.dom; 22 23import java.util.ArrayList; 24import java.util.HashMap; 25import org.w3c.dom.DOMImplementation; 26import org.w3c.dom.Element; 27import org.w3c.dom.Node; 28 29/** 30 * The Document interface represents the entire HTML or XML document. 31 * Conceptually, it is the root of the document tree, and provides the 32 * primary access to the document's data. 33 * <P> 34 * Since elements, text nodes, comments, processing instructions, 35 * etc. cannot exist outside the context of a Document, the Document 36 * interface also contains the factory methods needed to create these 37 * objects. The Node objects created have a ownerDocument attribute 38 * which associates them with the Document within whose context they 39 * were created. 40 * 41 * @xerces.internal 42 * 43 * @since PR-DOM-Level-1-19980818. 44 */ 45public class DeferredDocumentImpl 46 extends DocumentImpl 47 implements DeferredNode { 48 49 // 50 // Constants 51 // 52 53 /** Serialization version. */ 54 static final long serialVersionUID = 5186323580749626857L; 55 56 // debugging 57 58 /** To include code for printing the ref count tables. */ 59 private static final boolean DEBUG_PRINT_REF_COUNTS = false; 60 61 /** To include code for printing the internal tables. */ 62 private static final boolean DEBUG_PRINT_TABLES = false; 63 64 /** To debug identifiers set to true and recompile. */ 65 private static final boolean DEBUG_IDS = false; 66 67 // protected 68 69 /** Chunk shift. */ 70 protected static final int CHUNK_SHIFT = 8; // 2^8 = 256 71 72 /** Chunk size. */ 73 protected static final int CHUNK_SIZE = (1 << CHUNK_SHIFT); 74 75 /** Chunk mask. */ 76 protected static final int CHUNK_MASK = CHUNK_SIZE - 1; 77 78 /** Initial chunk size. */ 79 protected static final int INITIAL_CHUNK_COUNT = (1 << (13 - CHUNK_SHIFT)); // 32 80 81 // 82 // Data 83 // 84 85 // lazy-eval information 86 // To maximize memory consumption the actual semantic of these fields vary 87 // depending on the node type. 88 89 /** Node count. */ 90 protected transient int fNodeCount = 0; 91 92 /** Node types. */ 93 protected transient int fNodeType[][]; 94 95 /** Node names. */ 96 protected transient Object fNodeName[][]; 97 98 /** Node values. */ 99 protected transient Object fNodeValue[][]; 100 101 /** Node parents. */ 102 protected transient int fNodeParent[][]; 103 104 /** Node first children. */ 105 protected transient int fNodeLastChild[][]; 106 107 /** Node prev siblings. */ 108 protected transient int fNodePrevSib[][]; 109 110 /** Node namespace URI. */ 111 protected transient Object fNodeURI[][]; 112 113 /** Extra data. */ 114 protected transient int fNodeExtra[][]; 115 116 /** Identifier count. */ 117 protected transient int fIdCount; 118 119 /** Identifier name indexes. */ 120 protected transient String fIdName[]; 121 122 /** Identifier element indexes. */ 123 protected transient int fIdElement[]; 124 125 /** DOM2: For namespace support in the deferred case. 126 */ 127 // Implementation Note: The deferred element and attribute must know how to 128 // interpret the int representing the qname. 129 protected boolean fNamespacesEnabled = false; 130 131 // 132 // private data 133 // 134 private transient final StringBuilder fBufferStr = new StringBuilder(); 135 private transient final ArrayList fStrChunks = new ArrayList(); 136 137 // 138 // Constructors 139 // 140 141 /** 142 * NON-DOM: Actually creating a Document is outside the DOM's spec, 143 * since it has to operate in terms of a particular implementation. 144 */ 145 public DeferredDocumentImpl() { 146 this(false); 147 } // <init>() 148 149 /** 150 * NON-DOM: Actually creating a Document is outside the DOM's spec, 151 * since it has to operate in terms of a particular implementation. 152 */ 153 public DeferredDocumentImpl(boolean namespacesEnabled) { 154 this(namespacesEnabled, false); 155 } // <init>(boolean) 156 157 /** Experimental constructor. */ 158 public DeferredDocumentImpl(boolean namespaces, boolean grammarAccess) { 159 super(grammarAccess); 160 161 needsSyncData(true); 162 needsSyncChildren(true); 163 164 fNamespacesEnabled = namespaces; 165 166 } // <init>(boolean,boolean) 167 168 // 169 // Public methods 170 // 171 172 /** 173 * Retrieve information describing the abilities of this particular 174 * DOM implementation. Intended to support applications that may be 175 * using DOMs retrieved from several different sources, potentially 176 * with different underlying representations. 177 */ 178 public DOMImplementation getImplementation() { 179 // Currently implemented as a singleton, since it's hardcoded 180 // information anyway. 181 return DeferredDOMImplementationImpl.getDOMImplementation(); 182 } 183 184 /** Returns the cached parser.getNamespaces() value.*/ 185 boolean getNamespacesEnabled() { 186 return fNamespacesEnabled; 187 } 188 189 void setNamespacesEnabled(boolean enable) { 190 fNamespacesEnabled = enable; 191 } 192 193 // internal factory methods 194 195 /** Creates a document node in the table. */ 196 public int createDeferredDocument() { 197 int nodeIndex = createNode(Node.DOCUMENT_NODE); 198 return nodeIndex; 199 } 200 201 /** Creates a doctype. */ 202 public int createDeferredDocumentType(String rootElementName, 203 String publicId, String systemId) { 204 205 // create node 206 int nodeIndex = createNode(Node.DOCUMENT_TYPE_NODE); 207 int chunk = nodeIndex >> CHUNK_SHIFT; 208 int index = nodeIndex & CHUNK_MASK; 209 210 // save name, public id, system id 211 setChunkValue(fNodeName, rootElementName, chunk, index); 212 setChunkValue(fNodeValue, publicId, chunk, index); 213 setChunkValue(fNodeURI, systemId, chunk, index); 214 215 // return node index 216 return nodeIndex; 217 218 } // createDeferredDocumentType(String,String,String):int 219 220 public void setInternalSubset(int doctypeIndex, String subset) { 221 int chunk = doctypeIndex >> CHUNK_SHIFT; 222 int index = doctypeIndex & CHUNK_MASK; 223 224 // create extra data node to store internal subset 225 int extraDataIndex = createNode(Node.DOCUMENT_TYPE_NODE); 226 int echunk = extraDataIndex >> CHUNK_SHIFT; 227 int eindex = extraDataIndex & CHUNK_MASK; 228 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 229 setChunkValue(fNodeValue, subset, echunk, eindex); 230 } 231 232 /** Creates a notation in the table. */ 233 public int createDeferredNotation(String notationName, 234 String publicId, String systemId, String baseURI) { 235 236 // create node 237 int nodeIndex = createNode(Node.NOTATION_NODE); 238 int chunk = nodeIndex >> CHUNK_SHIFT; 239 int index = nodeIndex & CHUNK_MASK; 240 241 242 // create extra data node 243 int extraDataIndex = createNode(Node.NOTATION_NODE); 244 int echunk = extraDataIndex >> CHUNK_SHIFT; 245 int eindex = extraDataIndex & CHUNK_MASK; 246 247 // save name, public id, system id, and notation name 248 setChunkValue(fNodeName, notationName, chunk, index); 249 setChunkValue(fNodeValue, publicId, chunk, index); 250 setChunkValue(fNodeURI, systemId, chunk, index); 251 252 // in extra data node set baseURI value 253 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 254 setChunkValue(fNodeName, baseURI, echunk, eindex); 255 256 // return node index 257 return nodeIndex; 258 259 } // createDeferredNotation(String,String,String):int 260 261 /** Creates an entity in the table. */ 262 public int createDeferredEntity(String entityName, String publicId, 263 String systemId, String notationName, 264 String baseURI) { 265 // create node 266 int nodeIndex = createNode(Node.ENTITY_NODE); 267 int chunk = nodeIndex >> CHUNK_SHIFT; 268 int index = nodeIndex & CHUNK_MASK; 269 270 // create extra data node 271 int extraDataIndex = createNode(Node.ENTITY_NODE); 272 int echunk = extraDataIndex >> CHUNK_SHIFT; 273 int eindex = extraDataIndex & CHUNK_MASK; 274 275 // save name, public id, system id, and notation name 276 setChunkValue(fNodeName, entityName, chunk, index); 277 setChunkValue(fNodeValue, publicId, chunk, index); 278 setChunkValue(fNodeURI, systemId, chunk, index); 279 setChunkIndex(fNodeExtra, extraDataIndex, chunk, index); 280 // set other values in the extra chunk 281 // notation 282 setChunkValue(fNodeName, notationName, echunk, eindex); 283 // version L3 284 setChunkValue(fNodeValue, null, echunk, eindex); 285 // encoding L3 286 setChunkValue(fNodeURI, null, echunk, eindex); 287 288 289 int extraDataIndex2 = createNode(Node.ENTITY_NODE); 290 int echunk2 = extraDataIndex2 >> CHUNK_SHIFT; 291 int eindex2 = extraDataIndex2 & CHUNK_MASK; 292 293 setChunkIndex(fNodeExtra, extraDataIndex2, echunk, eindex); 294 295 // baseURI 296 setChunkValue(fNodeName, baseURI, echunk2, eindex2); 297 298 // return node index 299 return nodeIndex; 300 301 } // createDeferredEntity(String,String,String,String):int 302 303 public String getDeferredEntityBaseURI (int entityIndex){ 304 if (entityIndex != -1) { 305 int extraDataIndex = getNodeExtra(entityIndex, false); 306 extraDataIndex = getNodeExtra(extraDataIndex, false); 307 return getNodeName (extraDataIndex, false); 308 } 309 return null; 310 } 311 312 // DOM Level 3: setting encoding and version 313 public void setEntityInfo(int currentEntityDecl, 314 String version, String encoding){ 315 int eNodeIndex = getNodeExtra(currentEntityDecl, false); 316 if (eNodeIndex !=-1) { 317 int echunk = eNodeIndex >> CHUNK_SHIFT; 318 int eindex = eNodeIndex & CHUNK_MASK; 319 setChunkValue(fNodeValue, version, echunk, eindex); 320 setChunkValue(fNodeURI, encoding, echunk, eindex); 321 } 322 } 323 324 // DOM Level 3: sets element TypeInfo 325 public void setTypeInfo(int elementNodeIndex, Object type) { 326 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 327 int elementIndex = elementNodeIndex & CHUNK_MASK; 328 setChunkValue(fNodeValue, type, elementChunk, elementIndex); 329 } 330 331 /** 332 * DOM Internal 333 * 334 * An attribute specifying the actual encoding of this document. This is 335 * <code>null</code> otherwise. 336 * <br> This attribute represents the property [character encoding scheme] 337 * defined in . 338 */ 339 public void setInputEncoding(int currentEntityDecl, String value){ 340 // get first extra data chunk 341 int nodeIndex = getNodeExtra(currentEntityDecl, false); 342 // get second extra data chunk 343 int extraDataIndex = getNodeExtra(nodeIndex, false); 344 345 int echunk = extraDataIndex >> CHUNK_SHIFT; 346 int eindex = extraDataIndex & CHUNK_MASK; 347 348 setChunkValue(fNodeValue, value, echunk, eindex); 349 350 } 351 352 /** Creates an entity reference node in the table. */ 353 public int createDeferredEntityReference(String name, String baseURI) { 354 355 // create node 356 int nodeIndex = createNode(Node.ENTITY_REFERENCE_NODE); 357 int chunk = nodeIndex >> CHUNK_SHIFT; 358 int index = nodeIndex & CHUNK_MASK; 359 setChunkValue(fNodeName, name, chunk, index); 360 setChunkValue(fNodeValue, baseURI, chunk, index); 361 362 // return node index 363 return nodeIndex; 364 365 } // createDeferredEntityReference(String):int 366 367 368 /** 369 * Creates an element node with a URI in the table and type information. 370 * @deprecated 371 */ 372 @Deprecated 373 public int createDeferredElement(String elementURI, String elementName, 374 Object type) { 375 376 // create node 377 int elementNodeIndex = createNode(Node.ELEMENT_NODE); 378 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 379 int elementIndex = elementNodeIndex & CHUNK_MASK; 380 setChunkValue(fNodeName, elementName, elementChunk, elementIndex); 381 setChunkValue(fNodeURI, elementURI, elementChunk, elementIndex); 382 setChunkValue(fNodeValue, type, elementChunk, elementIndex); 383 384 // return node index 385 return elementNodeIndex; 386 387 } // createDeferredElement(String,String,Object):int 388 389 /** 390 * Creates an element node in the table. 391 * @deprecated 392 */ 393 @Deprecated 394 public int createDeferredElement(String elementName) { 395 return createDeferredElement(null, elementName); 396 } 397 398 /** 399 * Creates an element node with a URI in the table. 400 */ 401 public int createDeferredElement(String elementURI, String elementName) { 402 403 // create node 404 int elementNodeIndex = createNode(Node.ELEMENT_NODE); 405 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 406 int elementIndex = elementNodeIndex & CHUNK_MASK; 407 setChunkValue(fNodeName, elementName, elementChunk, elementIndex); 408 setChunkValue(fNodeURI, elementURI, elementChunk, elementIndex); 409 410 // return node index 411 return elementNodeIndex; 412 413 } // createDeferredElement(String,String):int 414 415 416 /** 417 * This method is used by the DOMParser to create attributes. 418 * @param elementNodeIndex 419 * @param attrName 420 * @param attrURI 421 * @param attrValue 422 * @param specified 423 * @param id 424 * @param type 425 * @return int 426 */ 427 public int setDeferredAttribute(int elementNodeIndex, 428 String attrName, 429 String attrURI, 430 String attrValue, 431 boolean specified, 432 boolean id, 433 Object type) { 434 435 // create attribute 436 int attrNodeIndex = createDeferredAttribute(attrName, attrURI, attrValue, specified); 437 int attrChunk = attrNodeIndex >> CHUNK_SHIFT; 438 int attrIndex = attrNodeIndex & CHUNK_MASK; 439 // set attribute's parent to element 440 setChunkIndex(fNodeParent, elementNodeIndex, attrChunk, attrIndex); 441 442 int elementChunk = elementNodeIndex >> CHUNK_SHIFT; 443 int elementIndex = elementNodeIndex & CHUNK_MASK; 444 445 // get element's last attribute 446 int lastAttrNodeIndex = getChunkIndex(fNodeExtra, elementChunk, elementIndex); 447 if (lastAttrNodeIndex != 0) { 448 // add link from new attribute to last attribute 449 setChunkIndex(fNodePrevSib, lastAttrNodeIndex, attrChunk, attrIndex); 450 } 451 // add link from element to new last attribute 452 setChunkIndex(fNodeExtra, attrNodeIndex, elementChunk, elementIndex); 453 454 int extra = getChunkIndex(fNodeExtra, attrChunk, attrIndex); 455 if (id) { 456 extra = extra | ID; 457 setChunkIndex(fNodeExtra, extra, attrChunk, attrIndex); 458 String value = getChunkValue(fNodeValue, attrChunk, attrIndex); 459 putIdentifier(value, elementNodeIndex); 460 } 461 // store type information 462 if (type != null) { 463 int extraDataIndex = createNode(DeferredNode.TYPE_NODE); 464 int echunk = extraDataIndex >> CHUNK_SHIFT; 465 int eindex = extraDataIndex & CHUNK_MASK; 466 467 setChunkIndex(fNodeLastChild, extraDataIndex, attrChunk, attrIndex); 468 setChunkValue(fNodeValue, type, echunk, eindex); 469 } 470 471 // return node index 472 return attrNodeIndex; 473 } 474 475 /** Creates an attribute in the table. */ 476 public int createDeferredAttribute(String attrName, String attrValue, 477 boolean specified) { 478 return createDeferredAttribute(attrName, null, attrValue, specified); 479 } 480 481 /** Creates an attribute with a URI in the table. */ 482 public int createDeferredAttribute(String attrName, String attrURI, 483 String attrValue, boolean specified) { 484 485 // create node 486 int nodeIndex = createNode(NodeImpl.ATTRIBUTE_NODE); 487 int chunk = nodeIndex >> CHUNK_SHIFT; 488 int index = nodeIndex & CHUNK_MASK; 489 setChunkValue(fNodeName, attrName, chunk, index); 490 setChunkValue(fNodeURI, attrURI, chunk, index); 491 setChunkValue(fNodeValue, attrValue, chunk, index); 492 int extra = specified ? SPECIFIED : 0; 493 setChunkIndex(fNodeExtra, extra, chunk, index); 494 495 // return node index 496 return nodeIndex; 497 498 } // createDeferredAttribute(String,String,String,boolean):int 499 500 /** Creates an element definition in the table.*/ 501 public int createDeferredElementDefinition(String elementName) { 502 503 // create node 504 int nodeIndex = createNode(NodeImpl.ELEMENT_DEFINITION_NODE); 505 int chunk = nodeIndex >> CHUNK_SHIFT; 506 int index = nodeIndex & CHUNK_MASK; 507 setChunkValue(fNodeName, elementName, chunk, index); 508 509 // return node index 510 return nodeIndex; 511 512 } // createDeferredElementDefinition(String):int 513 514 /** Creates a text node in the table. */ 515 public int createDeferredTextNode(String data, 516 boolean ignorableWhitespace) { 517 518 // create node 519 int nodeIndex = createNode(Node.TEXT_NODE); 520 int chunk = nodeIndex >> CHUNK_SHIFT; 521 int index = nodeIndex & CHUNK_MASK; 522 setChunkValue(fNodeValue, data, chunk, index); 523 // use extra to store ignorableWhitespace info 524 setChunkIndex(fNodeExtra, ignorableWhitespace ? 1 : 0, chunk, index); 525 526 // return node index 527 return nodeIndex; 528 529 } // createDeferredTextNode(String,boolean):int 530 531 /** Creates a CDATA section node in the table. */ 532 public int createDeferredCDATASection(String data) { 533 534 // create node 535 int nodeIndex = createNode(Node.CDATA_SECTION_NODE); 536 int chunk = nodeIndex >> CHUNK_SHIFT; 537 int index = nodeIndex & CHUNK_MASK; 538 setChunkValue(fNodeValue, data, chunk, index); 539 540 // return node index 541 return nodeIndex; 542 543 } // createDeferredCDATASection(String):int 544 545 /** Creates a processing instruction node in the table. */ 546 public int createDeferredProcessingInstruction(String target, 547 String data) { 548 // create node 549 int nodeIndex = createNode(Node.PROCESSING_INSTRUCTION_NODE); 550 int chunk = nodeIndex >> CHUNK_SHIFT; 551 int index = nodeIndex & CHUNK_MASK; 552 setChunkValue(fNodeName, target, chunk, index); 553 setChunkValue(fNodeValue, data, chunk, index); 554 // return node index 555 return nodeIndex; 556 557 } // createDeferredProcessingInstruction(String,String):int 558 559 /** Creates a comment node in the table. */ 560 public int createDeferredComment(String data) { 561 562 // create node 563 int nodeIndex = createNode(Node.COMMENT_NODE); 564 int chunk = nodeIndex >> CHUNK_SHIFT; 565 int index = nodeIndex & CHUNK_MASK; 566 setChunkValue(fNodeValue, data, chunk, index); 567 568 // return node index 569 return nodeIndex; 570 571 } // createDeferredComment(String):int 572 573 /** Creates a clone of the specified node. */ 574 public int cloneNode(int nodeIndex, boolean deep) { 575 576 // clone immediate node 577 578 int nchunk = nodeIndex >> CHUNK_SHIFT; 579 int nindex = nodeIndex & CHUNK_MASK; 580 int nodeType = fNodeType[nchunk][nindex]; 581 int cloneIndex = createNode((short)nodeType); 582 int cchunk = cloneIndex >> CHUNK_SHIFT; 583 int cindex = cloneIndex & CHUNK_MASK; 584 setChunkValue(fNodeName, fNodeName[nchunk][nindex], cchunk, cindex); 585 setChunkValue(fNodeValue, fNodeValue[nchunk][nindex], cchunk, cindex); 586 setChunkValue(fNodeURI, fNodeURI[nchunk][nindex], cchunk, cindex); 587 int extraIndex = fNodeExtra[nchunk][nindex]; 588 if (extraIndex != -1) { 589 if (nodeType != Node.ATTRIBUTE_NODE && nodeType != Node.TEXT_NODE) { 590 extraIndex = cloneNode(extraIndex, false); 591 } 592 setChunkIndex(fNodeExtra, extraIndex, cchunk, cindex); 593 } 594 595 // clone and attach children 596 if (deep) { 597 int prevIndex = -1; 598 int childIndex = getLastChild(nodeIndex, false); 599 while (childIndex != -1) { 600 int clonedChildIndex = cloneNode(childIndex, deep); 601 insertBefore(cloneIndex, clonedChildIndex, prevIndex); 602 prevIndex = clonedChildIndex; 603 childIndex = getRealPrevSibling(childIndex, false); 604 } 605 606 607 } 608 609 // return cloned node index 610 return cloneIndex; 611 612 } // cloneNode(int,boolean):int 613 614 /** Appends a child to the specified parent in the table. */ 615 public void appendChild(int parentIndex, int childIndex) { 616 617 // append parent index 618 int pchunk = parentIndex >> CHUNK_SHIFT; 619 int pindex = parentIndex & CHUNK_MASK; 620 int cchunk = childIndex >> CHUNK_SHIFT; 621 int cindex = childIndex & CHUNK_MASK; 622 setChunkIndex(fNodeParent, parentIndex, cchunk, cindex); 623 624 // set previous sibling of new child 625 int olast = getChunkIndex(fNodeLastChild, pchunk, pindex); 626 setChunkIndex(fNodePrevSib, olast, cchunk, cindex); 627 628 // update parent's last child 629 setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex); 630 631 } // appendChild(int,int) 632 633 /** Adds an attribute node to the specified element. */ 634 public int setAttributeNode(int elemIndex, int attrIndex) { 635 636 int echunk = elemIndex >> CHUNK_SHIFT; 637 int eindex = elemIndex & CHUNK_MASK; 638 int achunk = attrIndex >> CHUNK_SHIFT; 639 int aindex = attrIndex & CHUNK_MASK; 640 641 // see if this attribute is already here 642 String attrName = getChunkValue(fNodeName, achunk, aindex); 643 int oldAttrIndex = getChunkIndex(fNodeExtra, echunk, eindex); 644 int nextIndex = -1; 645 int oachunk = -1; 646 int oaindex = -1; 647 while (oldAttrIndex != -1) { 648 oachunk = oldAttrIndex >> CHUNK_SHIFT; 649 oaindex = oldAttrIndex & CHUNK_MASK; 650 String oldAttrName = getChunkValue(fNodeName, oachunk, oaindex); 651 if (oldAttrName.equals(attrName)) { 652 break; 653 } 654 nextIndex = oldAttrIndex; 655 oldAttrIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex); 656 } 657 658 // remove old attribute 659 if (oldAttrIndex != -1) { 660 661 // patch links 662 int prevIndex = getChunkIndex(fNodePrevSib, oachunk, oaindex); 663 if (nextIndex == -1) { 664 setChunkIndex(fNodeExtra, prevIndex, echunk, eindex); 665 } 666 else { 667 int pchunk = nextIndex >> CHUNK_SHIFT; 668 int pindex = nextIndex & CHUNK_MASK; 669 setChunkIndex(fNodePrevSib, prevIndex, pchunk, pindex); 670 } 671 672 // remove connections to siblings 673 clearChunkIndex(fNodeType, oachunk, oaindex); 674 clearChunkValue(fNodeName, oachunk, oaindex); 675 clearChunkValue(fNodeValue, oachunk, oaindex); 676 clearChunkIndex(fNodeParent, oachunk, oaindex); 677 clearChunkIndex(fNodePrevSib, oachunk, oaindex); 678 int attrTextIndex = 679 clearChunkIndex(fNodeLastChild, oachunk, oaindex); 680 int atchunk = attrTextIndex >> CHUNK_SHIFT; 681 int atindex = attrTextIndex & CHUNK_MASK; 682 clearChunkIndex(fNodeType, atchunk, atindex); 683 clearChunkValue(fNodeValue, atchunk, atindex); 684 clearChunkIndex(fNodeParent, atchunk, atindex); 685 clearChunkIndex(fNodeLastChild, atchunk, atindex); 686 } 687 688 // add new attribute 689 int prevIndex = getChunkIndex(fNodeExtra, echunk, eindex); 690 setChunkIndex(fNodeExtra, attrIndex, echunk, eindex); 691 setChunkIndex(fNodePrevSib, prevIndex, achunk, aindex); 692 693 // return 694 return oldAttrIndex; 695 696 } // setAttributeNode(int,int):int 697 698 699 /** Adds an attribute node to the specified element. */ 700 public void setIdAttributeNode(int elemIndex, int attrIndex) { 701 702 int chunk = attrIndex >> CHUNK_SHIFT; 703 int index = attrIndex & CHUNK_MASK; 704 int extra = getChunkIndex(fNodeExtra, chunk, index); 705 extra = extra | ID; 706 setChunkIndex(fNodeExtra, extra, chunk, index); 707 708 String value = getChunkValue(fNodeValue, chunk, index); 709 putIdentifier(value, elemIndex); 710 } 711 712 713 /** Sets type of attribute */ 714 public void setIdAttribute(int attrIndex) { 715 716 int chunk = attrIndex >> CHUNK_SHIFT; 717 int index = attrIndex & CHUNK_MASK; 718 int extra = getChunkIndex(fNodeExtra, chunk, index); 719 extra = extra | ID; 720 setChunkIndex(fNodeExtra, extra, chunk, index); 721 } 722 723 /** Inserts a child before the specified node in the table. */ 724 public int insertBefore(int parentIndex, int newChildIndex, int refChildIndex) { 725 726 if (refChildIndex == -1) { 727 appendChild(parentIndex, newChildIndex); 728 return newChildIndex; 729 } 730 731 int nchunk = newChildIndex >> CHUNK_SHIFT; 732 int nindex = newChildIndex & CHUNK_MASK; 733 int rchunk = refChildIndex >> CHUNK_SHIFT; 734 int rindex = refChildIndex & CHUNK_MASK; 735 int previousIndex = getChunkIndex(fNodePrevSib, rchunk, rindex); 736 setChunkIndex(fNodePrevSib, newChildIndex, rchunk, rindex); 737 setChunkIndex(fNodePrevSib, previousIndex, nchunk, nindex); 738 739 return newChildIndex; 740 741 } // insertBefore(int,int,int):int 742 743 /** Sets the last child of the parentIndex to childIndex. */ 744 public void setAsLastChild(int parentIndex, int childIndex) { 745 int pchunk = parentIndex >> CHUNK_SHIFT; 746 int pindex = parentIndex & CHUNK_MASK; 747 setChunkIndex(fNodeLastChild, childIndex, pchunk, pindex); 748 } // setAsLastChild(int,int) 749 750 /** 751 * Returns the parent node of the given node. 752 * <em>Calling this method does not free the parent index.</em> 753 */ 754 public int getParentNode(int nodeIndex) { 755 return getParentNode(nodeIndex, false); 756 } 757 758 /** 759 * Returns the parent node of the given node. 760 * @param free True to free parent node. 761 */ 762 public int getParentNode(int nodeIndex, boolean free) { 763 764 if (nodeIndex == -1) { 765 return -1; 766 } 767 768 int chunk = nodeIndex >> CHUNK_SHIFT; 769 int index = nodeIndex & CHUNK_MASK; 770 return free ? clearChunkIndex(fNodeParent, chunk, index) 771 : getChunkIndex(fNodeParent, chunk, index); 772 773 } // getParentNode(int):int 774 775 /** Returns the last child of the given node. */ 776 public int getLastChild(int nodeIndex) { 777 return getLastChild(nodeIndex, true); 778 } 779 780 /** 781 * Returns the last child of the given node. 782 * @param free True to free child index. 783 */ 784 public int getLastChild(int nodeIndex, boolean free) { 785 786 if (nodeIndex == -1) { 787 return -1; 788 } 789 790 int chunk = nodeIndex >> CHUNK_SHIFT; 791 int index = nodeIndex & CHUNK_MASK; 792 return free ? clearChunkIndex(fNodeLastChild, chunk, index) 793 : getChunkIndex(fNodeLastChild, chunk, index); 794 795 } // getLastChild(int,boolean):int 796 797 /** 798 * Returns the prev sibling of the given node. 799 * This is post-normalization of Text Nodes. 800 */ 801 public int getPrevSibling(int nodeIndex) { 802 return getPrevSibling(nodeIndex, true); 803 } 804 805 /** 806 * Returns the prev sibling of the given node. 807 * @param free True to free sibling index. 808 */ 809 public int getPrevSibling(int nodeIndex, boolean free) { 810 811 if (nodeIndex == -1) { 812 return -1; 813 } 814 815 int chunk = nodeIndex >> CHUNK_SHIFT; 816 int index = nodeIndex & CHUNK_MASK; 817 int type = getChunkIndex(fNodeType, chunk, index); 818 if (type == Node.TEXT_NODE) { 819 do { 820 nodeIndex = getChunkIndex(fNodePrevSib, chunk, index); 821 if (nodeIndex == -1) { 822 break; 823 } 824 chunk = nodeIndex >> CHUNK_SHIFT; 825 index = nodeIndex & CHUNK_MASK; 826 type = getChunkIndex(fNodeType, chunk, index); 827 } while (type == Node.TEXT_NODE); 828 } 829 else { 830 nodeIndex = getChunkIndex(fNodePrevSib, chunk, index); 831 } 832 833 return nodeIndex; 834 835 } // getPrevSibling(int,boolean):int 836 837 /** 838 * Returns the <i>real</i> prev sibling of the given node, 839 * directly from the data structures. Used by TextImpl#getNodeValue() 840 * to normalize values. 841 */ 842 public int getRealPrevSibling(int nodeIndex) { 843 return getRealPrevSibling(nodeIndex, true); 844 } 845 846 /** 847 * Returns the <i>real</i> prev sibling of the given node. 848 * @param free True to free sibling index. 849 */ 850 public int getRealPrevSibling(int nodeIndex, boolean free) { 851 852 if (nodeIndex == -1) { 853 return -1; 854 } 855 856 int chunk = nodeIndex >> CHUNK_SHIFT; 857 int index = nodeIndex & CHUNK_MASK; 858 return free ? clearChunkIndex(fNodePrevSib, chunk, index) 859 : getChunkIndex(fNodePrevSib, chunk, index); 860 861 } // getReadPrevSibling(int,boolean):int 862 863 /** 864 * Returns the index of the element definition in the table 865 * with the specified name index, or -1 if no such definition 866 * exists. 867 */ 868 public int lookupElementDefinition(String elementName) { 869 870 if (fNodeCount > 1) { 871 872 // find doctype 873 int docTypeIndex = -1; 874 int nchunk = 0; 875 int nindex = 0; 876 for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex); 877 index != -1; 878 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) { 879 880 nchunk = index >> CHUNK_SHIFT; 881 nindex = index & CHUNK_MASK; 882 if (getChunkIndex(fNodeType, nchunk, nindex) == Node.DOCUMENT_TYPE_NODE) { 883 docTypeIndex = index; 884 break; 885 } 886 } 887 888 // find element definition 889 if (docTypeIndex == -1) { 890 return -1; 891 } 892 nchunk = docTypeIndex >> CHUNK_SHIFT; 893 nindex = docTypeIndex & CHUNK_MASK; 894 for (int index = getChunkIndex(fNodeLastChild, nchunk, nindex); 895 index != -1; 896 index = getChunkIndex(fNodePrevSib, nchunk, nindex)) { 897 898 nchunk = index >> CHUNK_SHIFT; 899 nindex = index & CHUNK_MASK; 900 if (getChunkIndex(fNodeType, nchunk, nindex) == 901 NodeImpl.ELEMENT_DEFINITION_NODE 902 && getChunkValue(fNodeName, nchunk, nindex) == elementName) { 903 return index; 904 } 905 } 906 } 907 908 return -1; 909 910 } // lookupElementDefinition(String):int 911 912 /** Instantiates the requested node object. */ 913 public DeferredNode getNodeObject(int nodeIndex) { 914 915 // is there anything to do? 916 if (nodeIndex == -1) { 917 return null; 918 } 919 920 // get node type 921 int chunk = nodeIndex >> CHUNK_SHIFT; 922 int index = nodeIndex & CHUNK_MASK; 923 int type = getChunkIndex(fNodeType, chunk, index); 924 if (type != Node.TEXT_NODE && type != Node.CDATA_SECTION_NODE) { 925 clearChunkIndex(fNodeType, chunk, index); 926 } 927 928 // create new node 929 DeferredNode node = null; 930 switch (type) { 931 932 // 933 // Standard DOM node types 934 // 935 936 case Node.ATTRIBUTE_NODE: { 937 if (fNamespacesEnabled) { 938 node = new DeferredAttrNSImpl(this, nodeIndex); 939 } else { 940 node = new DeferredAttrImpl(this, nodeIndex); 941 } 942 break; 943 } 944 945 case Node.CDATA_SECTION_NODE: { 946 node = new DeferredCDATASectionImpl(this, nodeIndex); 947 break; 948 } 949 950 case Node.COMMENT_NODE: { 951 node = new DeferredCommentImpl(this, nodeIndex); 952 break; 953 } 954 955 // NOTE: Document fragments can never be "fast". 956 // 957 // The parser will never ask to create a document 958 // fragment during the parse. Document fragments 959 // are used by the application *after* the parse. 960 // 961 // case Node.DOCUMENT_FRAGMENT_NODE: { break; } 962 case Node.DOCUMENT_NODE: { 963 // this node is never "fast" 964 node = this; 965 break; 966 } 967 968 case Node.DOCUMENT_TYPE_NODE: { 969 node = new DeferredDocumentTypeImpl(this, nodeIndex); 970 // save the doctype node 971 docType = (DocumentTypeImpl)node; 972 break; 973 } 974 975 case Node.ELEMENT_NODE: { 976 977 if (DEBUG_IDS) { 978 System.out.println("getNodeObject(ELEMENT_NODE): "+nodeIndex); 979 } 980 981 // create node 982 if (fNamespacesEnabled) { 983 node = new DeferredElementNSImpl(this, nodeIndex); 984 } else { 985 node = new DeferredElementImpl(this, nodeIndex); 986 } 987 988 // check to see if this element needs to be 989 // registered for its ID attributes 990 if (fIdElement != null) { 991 int idIndex = binarySearch(fIdElement, 0, 992 fIdCount-1, nodeIndex); 993 while (idIndex != -1) { 994 995 if (DEBUG_IDS) { 996 System.out.println(" id index: "+idIndex); 997 System.out.println(" fIdName["+idIndex+ 998 "]: "+fIdName[idIndex]); 999 } 1000 1001 // register ID 1002 String name = fIdName[idIndex]; 1003 if (name != null) { 1004 if (DEBUG_IDS) { 1005 System.out.println(" name: "+name); 1006 System.out.print("getNodeObject()#"); 1007 } 1008 putIdentifier0(name, (Element)node); 1009 fIdName[idIndex] = null; 1010 } 1011 1012 // continue if there are more IDs for 1013 // this element 1014 if (idIndex + 1 < fIdCount && 1015 fIdElement[idIndex + 1] == nodeIndex) { 1016 idIndex++; 1017 } 1018 else { 1019 idIndex = -1; 1020 } 1021 } 1022 } 1023 break; 1024 } 1025 1026 case Node.ENTITY_NODE: { 1027 node = new DeferredEntityImpl(this, nodeIndex); 1028 break; 1029 } 1030 1031 case Node.ENTITY_REFERENCE_NODE: { 1032 node = new DeferredEntityReferenceImpl(this, nodeIndex); 1033 break; 1034 } 1035 1036 case Node.NOTATION_NODE: { 1037 node = new DeferredNotationImpl(this, nodeIndex); 1038 break; 1039 } 1040 1041 case Node.PROCESSING_INSTRUCTION_NODE: { 1042 node = new DeferredProcessingInstructionImpl(this, nodeIndex); 1043 break; 1044 } 1045 1046 case Node.TEXT_NODE: { 1047 node = new DeferredTextImpl(this, nodeIndex); 1048 break; 1049 } 1050 1051 // 1052 // non-standard DOM node types 1053 // 1054 1055 case NodeImpl.ELEMENT_DEFINITION_NODE: { 1056 node = new DeferredElementDefinitionImpl(this, nodeIndex); 1057 break; 1058 } 1059 1060 default: { 1061 throw new IllegalArgumentException("type: "+type); 1062 } 1063 1064 } // switch node type 1065 1066 // store and return 1067 if (node != null) { 1068 return node; 1069 } 1070 1071 // error 1072 throw new IllegalArgumentException(); 1073 1074 } // createNodeObject(int):Node 1075 1076 /** Returns the name of the given node. */ 1077 public String getNodeName(int nodeIndex) { 1078 return getNodeName(nodeIndex, true); 1079 } // getNodeNameString(int):String 1080 1081 /** 1082 * Returns the name of the given node. 1083 * @param free True to free the string index. 1084 */ 1085 public String getNodeName(int nodeIndex, boolean free) { 1086 1087 if (nodeIndex == -1) { 1088 return null; 1089 } 1090 1091 int chunk = nodeIndex >> CHUNK_SHIFT; 1092 int index = nodeIndex & CHUNK_MASK; 1093 return free ? clearChunkValue(fNodeName, chunk, index) 1094 : getChunkValue(fNodeName, chunk, index); 1095 1096 } // getNodeName(int,boolean):String 1097 1098 /** Returns the real value of the given node. */ 1099 public String getNodeValueString(int nodeIndex) { 1100 return getNodeValueString(nodeIndex, true); 1101 } // getNodeValueString(int):String 1102 1103 /** 1104 * Returns the real value of the given node. 1105 * @param free True to free the string index. 1106 */ 1107 public String getNodeValueString(int nodeIndex, boolean free) { 1108 1109 if (nodeIndex == -1) { 1110 return null; 1111 } 1112 1113 int chunk = nodeIndex >> CHUNK_SHIFT; 1114 int index = nodeIndex & CHUNK_MASK; 1115 String value = free ? clearChunkValue(fNodeValue, chunk, index) 1116 : getChunkValue(fNodeValue, chunk, index); 1117 if (value == null) { 1118 return null; 1119 } 1120 1121 int type = getChunkIndex(fNodeType, chunk, index); 1122 if (type == Node.TEXT_NODE) { 1123 int prevSib = getRealPrevSibling(nodeIndex); 1124 if (prevSib != -1 && 1125 getNodeType(prevSib, false) == Node.TEXT_NODE) { 1126 // append data that is stored in fNodeValue 1127 // REVISIT: for text nodes it works differently than for CDATA 1128 // nodes. 1129 fStrChunks.add(value); 1130 do { 1131 // go in reverse order: find last child, then 1132 // its previous sibling, etc 1133 chunk = prevSib >> CHUNK_SHIFT; 1134 index = prevSib & CHUNK_MASK; 1135 value = getChunkValue(fNodeValue, chunk, index); 1136 fStrChunks.add(value); 1137 prevSib = getChunkIndex(fNodePrevSib, chunk, index); 1138 if (prevSib == -1) { 1139 break; 1140 } 1141 } while (getNodeType(prevSib, false) == Node.TEXT_NODE); 1142 1143 int chunkCount = fStrChunks.size(); 1144 1145 // add to the buffer in the correct order. 1146 for (int i = chunkCount - 1; i >= 0; i--) { 1147 fBufferStr.append((String)fStrChunks.get(i)); 1148 } 1149 1150 value = fBufferStr.toString(); 1151 fStrChunks.clear(); 1152 fBufferStr.setLength(0); 1153 return value; 1154 } 1155 } 1156 else if (type == Node.CDATA_SECTION_NODE) { 1157 // find if any other data stored in children 1158 int child = getLastChild(nodeIndex, false); 1159 if (child !=-1) { 1160 // append data that is stored in fNodeValue 1161 fBufferStr.append(value); 1162 while (child !=-1) { 1163 // go in reverse order: find last child, then 1164 // its previous sibling, etc 1165 chunk = child >> CHUNK_SHIFT; 1166 index = child & CHUNK_MASK; 1167 value = getChunkValue(fNodeValue, chunk, index); 1168 fStrChunks.add(value); 1169 child = getChunkIndex(fNodePrevSib, chunk, index); 1170 } 1171 // add to the buffer in the correct order. 1172 for (int i=fStrChunks.size()-1; i>=0; i--) { 1173 fBufferStr.append((String)fStrChunks.get(i)); 1174 } 1175 1176 value = fBufferStr.toString(); 1177 fStrChunks.clear(); 1178 fBufferStr.setLength(0); 1179 return value; 1180 } 1181 } 1182 1183 return value; 1184 1185 } // getNodeValueString(int,boolean):String 1186 1187 /** 1188 * Returns the value of the given node. 1189 */ 1190 public String getNodeValue(int nodeIndex) { 1191 return getNodeValue(nodeIndex, true); 1192 } 1193 1194 /** 1195 * Clears the type info that is stored in the fNodeValue array 1196 * @param nodeIndex 1197 * @return Object - type information for the attribute/element node 1198 */ 1199 public Object getTypeInfo(int nodeIndex) { 1200 if (nodeIndex == -1) { 1201 return null; 1202 } 1203 1204 int chunk = nodeIndex >> CHUNK_SHIFT; 1205 int index = nodeIndex & CHUNK_MASK; 1206 1207 1208 Object value = fNodeValue[chunk] != null ? fNodeValue[chunk][index] : null; 1209 if (value != null) { 1210 fNodeValue[chunk][index] = null; 1211 RefCount c = (RefCount) fNodeValue[chunk][CHUNK_SIZE]; 1212 c.fCount--; 1213 if (c.fCount == 0) { 1214 fNodeValue[chunk] = null; 1215 } 1216 } 1217 return value; 1218 } 1219 1220 /** 1221 * Returns the value of the given node. 1222 * @param free True to free the value index. 1223 */ 1224 public String getNodeValue(int nodeIndex, boolean free) { 1225 1226 if (nodeIndex == -1) { 1227 return null; 1228 } 1229 1230 int chunk = nodeIndex >> CHUNK_SHIFT; 1231 int index = nodeIndex & CHUNK_MASK; 1232 return free ? clearChunkValue(fNodeValue, chunk, index) 1233 : getChunkValue(fNodeValue, chunk, index); 1234 1235 } // getNodeValue(int,boolean):String 1236 1237 /** 1238 * Returns the extra info of the given node. 1239 * Used by AttrImpl to store specified value (1 == true). 1240 */ 1241 public int getNodeExtra(int nodeIndex) { 1242 return getNodeExtra(nodeIndex, true); 1243 } 1244 1245 /** 1246 * Returns the extra info of the given node. 1247 * @param free True to free the value index. 1248 */ 1249 public int getNodeExtra(int nodeIndex, boolean free) { 1250 1251 if (nodeIndex == -1) { 1252 return -1; 1253 } 1254 1255 int chunk = nodeIndex >> CHUNK_SHIFT; 1256 int index = nodeIndex & CHUNK_MASK; 1257 return free ? clearChunkIndex(fNodeExtra, chunk, index) 1258 : getChunkIndex(fNodeExtra, chunk, index); 1259 1260 } // getNodeExtra(int,boolean):int 1261 1262 /** Returns the type of the given node. */ 1263 public short getNodeType(int nodeIndex) { 1264 return getNodeType(nodeIndex, true); 1265 } 1266 1267 /** 1268 * Returns the type of the given node. 1269 * @param free True to free type index. 1270 */ 1271 public short getNodeType(int nodeIndex, boolean free) { 1272 1273 if (nodeIndex == -1) { 1274 return -1; 1275 } 1276 1277 int chunk = nodeIndex >> CHUNK_SHIFT; 1278 int index = nodeIndex & CHUNK_MASK; 1279 return free ? (short)clearChunkIndex(fNodeType, chunk, index) 1280 : (short)getChunkIndex(fNodeType, chunk, index); 1281 1282 } // getNodeType(int):int 1283 1284 /** Returns the attribute value of the given name. */ 1285 public String getAttribute(int elemIndex, String name) { 1286 if (elemIndex == -1 || name == null) { 1287 return null; 1288 } 1289 int echunk = elemIndex >> CHUNK_SHIFT; 1290 int eindex = elemIndex & CHUNK_MASK; 1291 int attrIndex = getChunkIndex(fNodeExtra, echunk, eindex); 1292 while (attrIndex != -1) { 1293 int achunk = attrIndex >> CHUNK_SHIFT; 1294 int aindex = attrIndex & CHUNK_MASK; 1295 if (getChunkValue(fNodeName, achunk, aindex) == name) { 1296 return getChunkValue(fNodeValue, achunk, aindex); 1297 } 1298 attrIndex = getChunkIndex(fNodePrevSib, achunk, aindex); 1299 } 1300 return null; 1301 } 1302 1303 /** Returns the URI of the given node. */ 1304 public String getNodeURI(int nodeIndex) { 1305 return getNodeURI(nodeIndex, true); 1306 } 1307 1308 /** 1309 * Returns the URI of the given node. 1310 * @param free True to free URI index. 1311 */ 1312 public String getNodeURI(int nodeIndex, boolean free) { 1313 1314 if (nodeIndex == -1) { 1315 return null; 1316 } 1317 1318 int chunk = nodeIndex >> CHUNK_SHIFT; 1319 int index = nodeIndex & CHUNK_MASK; 1320 return free ? clearChunkValue(fNodeURI, chunk, index) 1321 : getChunkValue(fNodeURI, chunk, index); 1322 1323 } // getNodeURI(int,int):String 1324 1325 // identifier maintenance 1326 1327 /** Registers an identifier name with a specified element node. */ 1328 public void putIdentifier(String name, int elementNodeIndex) { 1329 1330 if (DEBUG_IDS) { 1331 System.out.println("putIdentifier(" + name + ", " 1332 + elementNodeIndex + ')' + " // " + 1333 getChunkValue(fNodeName, 1334 elementNodeIndex >> CHUNK_SHIFT, 1335 elementNodeIndex & CHUNK_MASK)); 1336 } 1337 1338 // initialize arrays 1339 if (fIdName == null) { 1340 fIdName = new String[64]; 1341 fIdElement = new int[64]; 1342 } 1343 1344 // resize arrays 1345 if (fIdCount == fIdName.length) { 1346 String idName[] = new String[fIdCount * 2]; 1347 System.arraycopy(fIdName, 0, idName, 0, fIdCount); 1348 fIdName = idName; 1349 1350 int idElement[] = new int[idName.length]; 1351 System.arraycopy(fIdElement, 0, idElement, 0, fIdCount); 1352 fIdElement = idElement; 1353 } 1354 1355 // store identifier 1356 fIdName[fIdCount] = name; 1357 fIdElement[fIdCount] = elementNodeIndex; 1358 fIdCount++; 1359 1360 } // putIdentifier(String,int) 1361 1362 // 1363 // DEBUG 1364 // 1365 1366 /** Prints out the tables. */ 1367 public void print() { 1368 1369 if (DEBUG_PRINT_REF_COUNTS) { 1370 System.out.print("num\t"); 1371 System.out.print("type\t"); 1372 System.out.print("name\t"); 1373 System.out.print("val\t"); 1374 System.out.print("par\t"); 1375 System.out.print("lch\t"); 1376 System.out.print("psib"); 1377 System.out.println(); 1378 for (int i = 0; i < fNodeType.length; i++) { 1379 if (fNodeType[i] != null) { 1380 // separator 1381 System.out.print("--------"); 1382 System.out.print("--------"); 1383 System.out.print("--------"); 1384 System.out.print("--------"); 1385 System.out.print("--------"); 1386 System.out.print("--------"); 1387 System.out.print("--------"); 1388 System.out.println(); 1389 1390 // ref count 1391 System.out.print(i); 1392 System.out.print('\t'); 1393 switch (fNodeType[i][CHUNK_SIZE]) { 1394 case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; } 1395 case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; } 1396 case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; } 1397 case Node.COMMENT_NODE: { System.out.print("Com"); break; } 1398 case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; } 1399 case Node.ELEMENT_NODE: { System.out.print("Elem"); break; } 1400 case Node.ENTITY_NODE: { System.out.print("Ent"); break; } 1401 case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; } 1402 case Node.TEXT_NODE: { System.out.print("Text"); break; } 1403 case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; } 1404 case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; } 1405 default: { System.out.print("?"+fNodeType[i][CHUNK_SIZE]); } 1406 } 1407 System.out.print('\t'); 1408 System.out.print(fNodeName[i][CHUNK_SIZE]); 1409 System.out.print('\t'); 1410 System.out.print(fNodeValue[i][CHUNK_SIZE]); 1411 System.out.print('\t'); 1412 System.out.print(fNodeURI[i][CHUNK_SIZE]); 1413 System.out.print('\t'); 1414 System.out.print(fNodeParent[i][CHUNK_SIZE]); 1415 System.out.print('\t'); 1416 System.out.print(fNodeLastChild[i][CHUNK_SIZE]); 1417 System.out.print('\t'); 1418 System.out.print(fNodePrevSib[i][CHUNK_SIZE]); 1419 System.out.print('\t'); 1420 System.out.print(fNodeExtra[i][CHUNK_SIZE]); 1421 System.out.println(); 1422 } 1423 } 1424 } 1425 1426 if (DEBUG_PRINT_TABLES) { 1427 // This assumes that the document is small 1428 System.out.println("# start table"); 1429 for (int i = 0; i < fNodeCount; i++) { 1430 int chunk = i >> CHUNK_SHIFT; 1431 int index = i & CHUNK_MASK; 1432 if (i % 10 == 0) { 1433 System.out.print("num\t"); 1434 System.out.print("type\t"); 1435 System.out.print("name\t"); 1436 System.out.print("val\t"); 1437 System.out.print("uri\t"); 1438 System.out.print("par\t"); 1439 System.out.print("lch\t"); 1440 System.out.print("psib\t"); 1441 System.out.print("xtra"); 1442 System.out.println(); 1443 } 1444 System.out.print(i); 1445 System.out.print('\t'); 1446 switch (getChunkIndex(fNodeType, chunk, index)) { 1447 case DocumentImpl.ELEMENT_DEFINITION_NODE: { System.out.print("EDef"); break; } 1448 case Node.DOCUMENT_NODE: { System.out.print("Doc"); break; } 1449 case Node.DOCUMENT_TYPE_NODE: { System.out.print("DType"); break; } 1450 case Node.COMMENT_NODE: { System.out.print("Com"); break; } 1451 case Node.PROCESSING_INSTRUCTION_NODE: { System.out.print("PI"); break; } 1452 case Node.ELEMENT_NODE: { System.out.print("Elem"); break; } 1453 case Node.ENTITY_NODE: { System.out.print("Ent"); break; } 1454 case Node.ENTITY_REFERENCE_NODE: { System.out.print("ERef"); break; } 1455 case Node.TEXT_NODE: { System.out.print("Text"); break; } 1456 case Node.ATTRIBUTE_NODE: { System.out.print("Attr"); break; } 1457 case DeferredNode.TYPE_NODE: { System.out.print("TypeInfo"); break; } 1458 default: { System.out.print("?"+getChunkIndex(fNodeType, chunk, index)); } 1459 } 1460 System.out.print('\t'); 1461 System.out.print(getChunkValue(fNodeName, chunk, index)); 1462 System.out.print('\t'); 1463 System.out.print(getNodeValue(chunk, index)); 1464 System.out.print('\t'); 1465 System.out.print(getChunkValue(fNodeURI, chunk, index)); 1466 System.out.print('\t'); 1467 System.out.print(getChunkIndex(fNodeParent, chunk, index)); 1468 System.out.print('\t'); 1469 System.out.print(getChunkIndex(fNodeLastChild, chunk, index)); 1470 System.out.print('\t'); 1471 System.out.print(getChunkIndex(fNodePrevSib, chunk, index)); 1472 System.out.print('\t'); 1473 System.out.print(getChunkIndex(fNodeExtra, chunk, index)); 1474 System.out.println(); 1475 } 1476 System.out.println("# end table"); 1477 } 1478 1479 } // print() 1480 1481 // 1482 // DeferredNode methods 1483 // 1484 1485 /** Returns the node index. */ 1486 public int getNodeIndex() { 1487 return 0; 1488 } 1489 1490 // 1491 // Protected methods 1492 // 1493 1494 /** Synchronizes the node's data. */ 1495 protected void synchronizeData() { 1496 1497 // no need to sync in the future 1498 needsSyncData(false); 1499 1500 // fluff up enough nodes to fill identifiers hash 1501 if (fIdElement != null) { 1502 1503 // REVISIT: There has to be a more efficient way of 1504 // doing this. But keep in mind that the 1505 // tree can have been altered and re-ordered 1506 // before all of the element nodes with ID 1507 // attributes have been registered. For now 1508 // this is reasonable and safe. -Ac 1509 1510 IntVector path = new IntVector(); 1511 for (int i = 0; i < fIdCount; i++) { 1512 1513 // ignore if it's already been registered 1514 int elementNodeIndex = fIdElement[i]; 1515 String idName = fIdName[i]; 1516 if (idName == null) { 1517 continue; 1518 } 1519 1520 // find path from this element to the root 1521 path.removeAllElements(); 1522 int index = elementNodeIndex; 1523 do { 1524 path.addElement(index); 1525 int pchunk = index >> CHUNK_SHIFT; 1526 int pindex = index & CHUNK_MASK; 1527 index = getChunkIndex(fNodeParent, pchunk, pindex); 1528 } while (index != -1); 1529 1530 // Traverse path (backwards), fluffing the elements 1531 // along the way. When this loop finishes, "place" 1532 // will contain the reference to the element node 1533 // we're interested in. -Ac 1534 Node place = this; 1535 for (int j = path.size() - 2; j >= 0; j--) { 1536 index = path.elementAt(j); 1537 Node child = place.getLastChild(); 1538 while (child != null) { 1539 if (child instanceof DeferredNode) { 1540 int nodeIndex = 1541 ((DeferredNode)child).getNodeIndex(); 1542 if (nodeIndex == index) { 1543 place = child; 1544 break; 1545 } 1546 } 1547 child = child.getPreviousSibling(); 1548 } 1549 } 1550 1551 // register the element 1552 Element element = (Element)place; 1553 putIdentifier0(idName, element); 1554 fIdName[i] = null; 1555 1556 // see if there are more IDs on this element 1557 while (i + 1 < fIdCount && 1558 fIdElement[i + 1] == elementNodeIndex) { 1559 idName = fIdName[++i]; 1560 if (idName == null) { 1561 continue; 1562 } 1563 putIdentifier0(idName, element); 1564 } 1565 } 1566 1567 } // if identifiers 1568 1569 } // synchronizeData() 1570 1571 /** 1572 * Synchronizes the node's children with the internal structure. 1573 * Fluffing the children at once solves a lot of work to keep 1574 * the two structures in sync. The problem gets worse when 1575 * editing the tree -- this makes it a lot easier. 1576 */ 1577 protected void synchronizeChildren() { 1578 1579 if (needsSyncData()) { 1580 synchronizeData(); 1581 /* 1582 * when we have elements with IDs this method is being recursively 1583 * called from synchronizeData, in which case we've already gone 1584 * through the following and we can now simply stop here. 1585 */ 1586 if (!needsSyncChildren()) { 1587 return; 1588 } 1589 } 1590 1591 // we don't want to generate any event for this so turn them off 1592 boolean orig = mutationEvents; 1593 mutationEvents = false; 1594 1595 // no need to sync in the future 1596 needsSyncChildren(false); 1597 1598 getNodeType(0); 1599 1600 // create children and link them as siblings 1601 ChildNode first = null; 1602 ChildNode last = null; 1603 for (int index = getLastChild(0); 1604 index != -1; 1605 index = getPrevSibling(index)) { 1606 1607 ChildNode node = (ChildNode)getNodeObject(index); 1608 if (last == null) { 1609 last = node; 1610 } 1611 else { 1612 first.previousSibling = node; 1613 } 1614 node.ownerNode = this; 1615 node.isOwned(true); 1616 node.nextSibling = first; 1617 first = node; 1618 1619 // save doctype and document type 1620 int type = node.getNodeType(); 1621 if (type == Node.ELEMENT_NODE) { 1622 docElement = (ElementImpl)node; 1623 } 1624 else if (type == Node.DOCUMENT_TYPE_NODE) { 1625 docType = (DocumentTypeImpl)node; 1626 } 1627 } 1628 1629 if (first != null) { 1630 firstChild = first; 1631 first.isFirstChild(true); 1632 lastChild(last); 1633 } 1634 1635 // set mutation events flag back to its original value 1636 mutationEvents = orig; 1637 1638 } // synchronizeChildren() 1639 1640 /** 1641 * Synchronizes the node's children with the internal structure. 1642 * Fluffing the children at once solves a lot of work to keep 1643 * the two structures in sync. The problem gets worse when 1644 * editing the tree -- this makes it a lot easier. 1645 * This is not directly used in this class but this method is 1646 * here so that it can be shared by all deferred subclasses of AttrImpl. 1647 */ 1648 protected final void synchronizeChildren(AttrImpl a, int nodeIndex) { 1649 1650 // we don't want to generate any event for this so turn them off 1651 boolean orig = getMutationEvents(); 1652 setMutationEvents(false); 1653 1654 // no need to sync in the future 1655 a.needsSyncChildren(false); 1656 1657 // create children and link them as siblings or simply store the value 1658 // as a String if all we have is one piece of text 1659 int last = getLastChild(nodeIndex); 1660 int prev = getPrevSibling(last); 1661 if (prev == -1) { 1662 a.value = getNodeValueString(nodeIndex); 1663 a.hasStringValue(true); 1664 } 1665 else { 1666 ChildNode firstNode = null; 1667 ChildNode lastNode = null; 1668 for (int index = last; index != -1; 1669 index = getPrevSibling(index)) { 1670 1671 ChildNode node = (ChildNode) getNodeObject(index); 1672 if (lastNode == null) { 1673 lastNode = node; 1674 } 1675 else { 1676 firstNode.previousSibling = node; 1677 } 1678 node.ownerNode = a; 1679 node.isOwned(true); 1680 node.nextSibling = firstNode; 1681 firstNode = node; 1682 } 1683 if (lastNode != null) { 1684 a.value = firstNode; // firstChild = firstNode 1685 firstNode.isFirstChild(true); 1686 a.lastChild(lastNode); 1687 } 1688 a.hasStringValue(false); 1689 } 1690 1691 // set mutation events flag back to its original value 1692 setMutationEvents(orig); 1693 1694 } // synchronizeChildren(AttrImpl,int):void 1695 1696 1697 /** 1698 * Synchronizes the node's children with the internal structure. 1699 * Fluffing the children at once solves a lot of work to keep 1700 * the two structures in sync. The problem gets worse when 1701 * editing the tree -- this makes it a lot easier. 1702 * This is not directly used in this class but this method is 1703 * here so that it can be shared by all deferred subclasses of ParentNode. 1704 */ 1705 protected final void synchronizeChildren(ParentNode p, int nodeIndex) { 1706 1707 // we don't want to generate any event for this so turn them off 1708 boolean orig = getMutationEvents(); 1709 setMutationEvents(false); 1710 1711 // no need to sync in the future 1712 p.needsSyncChildren(false); 1713 1714 // create children and link them as siblings 1715 ChildNode firstNode = null; 1716 ChildNode lastNode = null; 1717 for (int index = getLastChild(nodeIndex); 1718 index != -1; 1719 index = getPrevSibling(index)) { 1720 1721 ChildNode node = (ChildNode) getNodeObject(index); 1722 if (lastNode == null) { 1723 lastNode = node; 1724 } 1725 else { 1726 firstNode.previousSibling = node; 1727 } 1728 node.ownerNode = p; 1729 node.isOwned(true); 1730 node.nextSibling = firstNode; 1731 firstNode = node; 1732 } 1733 if (lastNode != null) { 1734 p.firstChild = firstNode; 1735 firstNode.isFirstChild(true); 1736 p.lastChild(lastNode); 1737 } 1738 1739 // set mutation events flag back to its original value 1740 setMutationEvents(orig); 1741 1742 } // synchronizeChildren(ParentNode,int):void 1743 1744 // utility methods 1745 1746 /** Ensures that the internal tables are large enough. */ 1747 protected void ensureCapacity(int chunk) { 1748 if (fNodeType == null) { 1749 // create buffers 1750 fNodeType = new int[INITIAL_CHUNK_COUNT][]; 1751 fNodeName = new Object[INITIAL_CHUNK_COUNT][]; 1752 fNodeValue = new Object[INITIAL_CHUNK_COUNT][]; 1753 fNodeParent = new int[INITIAL_CHUNK_COUNT][]; 1754 fNodeLastChild = new int[INITIAL_CHUNK_COUNT][]; 1755 fNodePrevSib = new int[INITIAL_CHUNK_COUNT][]; 1756 fNodeURI = new Object[INITIAL_CHUNK_COUNT][]; 1757 fNodeExtra = new int[INITIAL_CHUNK_COUNT][]; 1758 } 1759 else if (fNodeType.length <= chunk) { 1760 // resize the tables 1761 int newsize = chunk * 2; 1762 1763 int[][] newArray = new int[newsize][]; 1764 System.arraycopy(fNodeType, 0, newArray, 0, chunk); 1765 fNodeType = newArray; 1766 1767 Object[][] newStrArray = new Object[newsize][]; 1768 System.arraycopy(fNodeName, 0, newStrArray, 0, chunk); 1769 fNodeName = newStrArray; 1770 1771 newStrArray = new Object[newsize][]; 1772 System.arraycopy(fNodeValue, 0, newStrArray, 0, chunk); 1773 fNodeValue = newStrArray; 1774 1775 newArray = new int[newsize][]; 1776 System.arraycopy(fNodeParent, 0, newArray, 0, chunk); 1777 fNodeParent = newArray; 1778 1779 newArray = new int[newsize][]; 1780 System.arraycopy(fNodeLastChild, 0, newArray, 0, chunk); 1781 fNodeLastChild = newArray; 1782 1783 newArray = new int[newsize][]; 1784 System.arraycopy(fNodePrevSib, 0, newArray, 0, chunk); 1785 fNodePrevSib = newArray; 1786 1787 newStrArray = new Object[newsize][]; 1788 System.arraycopy(fNodeURI, 0, newStrArray, 0, chunk); 1789 fNodeURI = newStrArray; 1790 1791 newArray = new int[newsize][]; 1792 System.arraycopy(fNodeExtra, 0, newArray, 0, chunk); 1793 fNodeExtra = newArray; 1794 } 1795 else if (fNodeType[chunk] != null) { 1796 // Done - there's sufficient capacity 1797 return; 1798 } 1799 1800 // create new chunks 1801 createChunk(fNodeType, chunk); 1802 createChunk(fNodeName, chunk); 1803 createChunk(fNodeValue, chunk); 1804 createChunk(fNodeParent, chunk); 1805 createChunk(fNodeLastChild, chunk); 1806 createChunk(fNodePrevSib, chunk); 1807 createChunk(fNodeURI, chunk); 1808 createChunk(fNodeExtra, chunk); 1809 1810 // Done 1811 return; 1812 1813 } // ensureCapacity(int,int) 1814 1815 /** Creates a node of the specified type. */ 1816 protected int createNode(short nodeType) { 1817 // ensure tables are large enough 1818 int chunk = fNodeCount >> CHUNK_SHIFT; 1819 int index = fNodeCount & CHUNK_MASK; 1820 ensureCapacity(chunk); 1821 1822 // initialize node 1823 setChunkIndex(fNodeType, nodeType, chunk, index); 1824 1825 // return node index number 1826 return fNodeCount++; 1827 1828 } // createNode(short):int 1829 1830 /** 1831 * Performs a binary search for a target value in an array of 1832 * values. The array of values must be in ascending sorted order 1833 * before calling this method and all array values must be 1834 * non-negative. 1835 * 1836 * @param values The array of values to search. 1837 * @param start The starting offset of the search. 1838 * @param end The ending offset of the search. 1839 * @param target The target value. 1840 * 1841 * @return This function will return the <i>first</i> occurrence 1842 * of the target value, or -1 if the target value cannot 1843 * be found. 1844 */ 1845 protected static int binarySearch(final int values[], 1846 int start, int end, int target) { 1847 1848 if (DEBUG_IDS) { 1849 System.out.println("binarySearch(), target: "+target); 1850 } 1851 1852 // look for target value 1853 while (start <= end) { 1854 1855 // is this the one we're looking for? 1856 int middle = (start + end) >>> 1; 1857 int value = values[middle]; 1858 if (DEBUG_IDS) { 1859 System.out.print(" value: "+value+", target: "+target+" // "); 1860 print(values, start, end, middle, target); 1861 } 1862 if (value == target) { 1863 while (middle > 0 && values[middle - 1] == target) { 1864 middle--; 1865 } 1866 if (DEBUG_IDS) { 1867 System.out.println("FOUND AT "+middle); 1868 } 1869 return middle; 1870 } 1871 1872 // is this point higher or lower? 1873 if (value > target) { 1874 end = middle - 1; 1875 } 1876 else { 1877 start = middle + 1; 1878 } 1879 1880 } // while 1881 1882 // not found 1883 if (DEBUG_IDS) { 1884 System.out.println("NOT FOUND!"); 1885 } 1886 return -1; 1887 1888 } // binarySearch(int[],int,int,int):int 1889 1890 // 1891 // Private methods 1892 // 1893 private static final int[] INIT_ARRAY = new int[CHUNK_SIZE + 1]; 1894 static { 1895 for (int i = 0; i < CHUNK_SIZE; i++) { 1896 INIT_ARRAY[i] = -1; 1897 } 1898 } 1899 /** Creates the specified chunk in the given array of chunks. */ 1900 private final void createChunk(int data[][], int chunk) { 1901 data[chunk] = new int[CHUNK_SIZE + 1]; 1902 System.arraycopy(INIT_ARRAY, 0, data[chunk], 0, CHUNK_SIZE); 1903 } 1904 1905 static final class RefCount { 1906 int fCount; 1907 } 1908 1909 private final void createChunk(Object data[][], int chunk) { 1910 data[chunk] = new Object[CHUNK_SIZE + 1]; 1911 data[chunk][CHUNK_SIZE] = new RefCount(); 1912 } 1913 1914 /** 1915 * Sets the specified value in the given of data at the chunk and index. 1916 * 1917 * @return Returns the old value. 1918 */ 1919 private final int setChunkIndex(int data[][], int value, 1920 int chunk, int index) { 1921 if (value == -1) { 1922 return clearChunkIndex(data, chunk, index); 1923 } 1924 int [] dataChunk = data[chunk]; 1925 // Re-create chunk if it was deleted. 1926 if (dataChunk == null) { 1927 createChunk(data, chunk); 1928 dataChunk = data[chunk]; 1929 } 1930 int ovalue = dataChunk[index]; 1931 if (ovalue == -1) { 1932 dataChunk[CHUNK_SIZE]++; 1933 } 1934 dataChunk[index] = value; 1935 return ovalue; 1936 } 1937 private final String setChunkValue(Object data[][], Object value, 1938 int chunk, int index) { 1939 if (value == null) { 1940 return clearChunkValue(data, chunk, index); 1941 } 1942 Object [] dataChunk = data[chunk]; 1943 // Re-create chunk if it was deleted. 1944 if (dataChunk == null) { 1945 createChunk(data, chunk); 1946 dataChunk = data[chunk]; 1947 } 1948 String ovalue = (String) dataChunk[index]; 1949 if (ovalue == null) { 1950 RefCount c = (RefCount) dataChunk[CHUNK_SIZE]; 1951 c.fCount++; 1952 } 1953 dataChunk[index] = value; 1954 return ovalue; 1955 } 1956 1957 /** 1958 * Returns the specified value in the given data at the chunk and index. 1959 */ 1960 private final int getChunkIndex(int data[][], int chunk, int index) { 1961 return data[chunk] != null ? data[chunk][index] : -1; 1962 } 1963 private final String getChunkValue(Object data[][], int chunk, int index) { 1964 return data[chunk] != null ? (String) data[chunk][index] : null; 1965 } 1966 private final String getNodeValue(int chunk, int index) { 1967 Object data = fNodeValue[chunk][index]; 1968 if (data == null){ 1969 return null; 1970 } 1971 else if (data instanceof String){ 1972 return (String)data; 1973 } 1974 else { 1975 // type information 1976 return data.toString(); 1977 } 1978 } 1979 1980 1981 /** 1982 * Clears the specified value in the given data at the chunk and index. 1983 * Note that this method will clear the given chunk if the reference 1984 * count becomes zero. 1985 * 1986 * @return Returns the old value. 1987 */ 1988 private final int clearChunkIndex(int data[][], int chunk, int index) { 1989 int value = data[chunk] != null ? data[chunk][index] : -1; 1990 if (value != -1) { 1991 data[chunk][CHUNK_SIZE]--; 1992 data[chunk][index] = -1; 1993 if (data[chunk][CHUNK_SIZE] == 0) { 1994 data[chunk] = null; 1995 } 1996 } 1997 return value; 1998 } 1999 private final String clearChunkValue(Object data[][], 2000 int chunk, int index) { 2001 String value = data[chunk] != null ? (String)data[chunk][index] : null; 2002 if (value != null) { 2003 data[chunk][index] = null; 2004 RefCount c = (RefCount) data[chunk][CHUNK_SIZE]; 2005 c.fCount--; 2006 if (c.fCount == 0) { 2007 data[chunk] = null; 2008 } 2009 } 2010 return value; 2011 } 2012 2013 /** 2014 * This version of putIdentifier is needed to avoid fluffing 2015 * all of the paths to ID attributes when a node object is 2016 * created that contains an ID attribute. 2017 */ 2018 private final void putIdentifier0(String idName, Element element) { 2019 2020 if (DEBUG_IDS) { 2021 System.out.println("putIdentifier0("+ 2022 idName+", "+ 2023 element+')'); 2024 } 2025 2026 // create Map 2027 if (identifiers == null) { 2028 identifiers = new HashMap<>(); 2029 } 2030 2031 // save ID and its associated element 2032 identifiers.put(idName, element); 2033 2034 } // putIdentifier0(String,Element) 2035 2036 /** Prints the ID array. */ 2037 private static void print(int values[], int start, int end, 2038 int middle, int target) { 2039 2040 if (DEBUG_IDS) { 2041 System.out.print(start); 2042 System.out.print(" ["); 2043 for (int i = start; i < end; i++) { 2044 if (middle == i) { 2045 System.out.print("!"); 2046 } 2047 System.out.print(values[i]); 2048 if (values[i] == target) { 2049 System.out.print("*"); 2050 } 2051 if (i < end - 1) { 2052 System.out.print(" "); 2053 } 2054 } 2055 System.out.println("] "+end); 2056 } 2057 2058 } // print(int[],int,int,int,int) 2059 2060 // 2061 // Classes 2062 // 2063 2064 /** 2065 * A simple integer vector. 2066 */ 2067 static final class IntVector { 2068 2069 // 2070 // Data 2071 // 2072 2073 /** Data. */ 2074 private int data[]; 2075 2076 /** Size. */ 2077 private int size; 2078 2079 // 2080 // Public methods 2081 // 2082 2083 /** Returns the length of this vector. */ 2084 public int size() { 2085 return size; 2086 } 2087 2088 /** Returns the element at the specified index. */ 2089 public int elementAt(int index) { 2090 return data[index]; 2091 } 2092 2093 /** Appends an element to the end of the vector. */ 2094 public void addElement(int element) { 2095 ensureCapacity(size + 1); 2096 data[size++] = element; 2097 } 2098 2099 /** Clears the vector. */ 2100 public void removeAllElements() { 2101 size = 0; 2102 } 2103 2104 // 2105 // Private methods 2106 // 2107 2108 /** Makes sure that there is enough storage. */ 2109 private void ensureCapacity(int newsize) { 2110 2111 if (data == null) { 2112 data = new int[newsize + 15]; 2113 } 2114 else if (newsize > data.length) { 2115 int newdata[] = new int[newsize + 15]; 2116 System.arraycopy(data, 0, newdata, 0, data.length); 2117 data = newdata; 2118 } 2119 2120 } // ensureCapacity(int) 2121 2122 } // class IntVector 2123 2124} // class DeferredDocumentImpl 2125