1 4 package gnu.xml; 5 import gnu.lists.*; 6 import gnu.text.Char; 7 import gnu.text.SourceLocator; 8 import gnu.text.SourceMessages; 9 import gnu.mapping.Symbol; 10 import gnu.expr.Keyword; 12 24 25 public class XMLFilter implements XConsumer, PositionConsumer 26 { 27 29 TreeList tlist; 30 31 33 public Consumer out; 34 35 Consumer base; 36 37 public static final int COPY_NAMESPACES_PRESERVE = 1; 38 public static final int COPY_NAMESPACES_INHERIT = 2; 39 public transient int copyNamespacesMode = COPY_NAMESPACES_PRESERVE; 40 41 49 Object [] workStack; 50 NamespaceBinding namespaceBindings; 51 52 private SourceMessages messages; 53 SourceLocator locator; 54 55 public void setSourceLocator (SourceLocator locator) 56 { this.locator = locator; } 57 public void setMessages (SourceMessages messages) 58 { this.messages = messages; } 59 60 61 protected int nesting; 62 63 int previous = 0; 64 private static final int SAW_CR = 1; 65 private static final int SAW_KEYWORD = 2; 66 private static final int SAW_WORD = 3; 67 68 72 protected int stringizingLevel; 73 78 protected int stringizingElementNesting = -1; 79 83 protected int ignoringLevel; 84 85 int[] startIndexes = null; 87 88 91 int attrCount = -1; 92 93 boolean inStartTag; 94 95 96 String attrLocalName; 97 String attrPrefix; 98 99 102 String currentNamespacePrefix; 103 104 106 public boolean namespacePrefixes = false; 107 108 117 MappingInfo[] mappingTable = new MappingInfo[128]; 118 int mappingTableMask = mappingTable.length - 1; 119 120 boolean mismatchReported; 121 122 public void setLocator (SourceLocator locator) 123 { 124 this.locator = locator; 125 } 126 127 131 public NamespaceBinding 132 findNamespaceBinding (String prefix, String uri, NamespaceBinding oldBindings) 133 { 134 int hash = uri == null ? 0 : uri.hashCode(); 135 if (prefix != null) 136 hash ^= prefix.hashCode(); 137 int bucket = hash & mappingTableMask; 138 MappingInfo info = mappingTable[bucket]; 139 for (;; info = info.nextInBucket) 140 { 141 if (info == null) 142 { 143 info = new MappingInfo(); 144 info.nextInBucket = mappingTable[bucket]; 145 mappingTable[bucket] = info; 146 info.tagHash = hash; 147 info.prefix = prefix; 148 info.local = uri; 149 info.uri = uri; 150 if (uri == "") 151 uri = null; 152 NamespaceBinding namespaces 153 = new NamespaceBinding(prefix, uri, oldBindings); 154 info.namespaces = namespaces; 155 return info.namespaces; 156 } 157 NamespaceBinding namespaces; 158 if (info.tagHash == hash 159 && info.prefix == prefix 160 && (namespaces = info.namespaces) != null 161 && namespaces.getNext() == namespaceBindings 162 && namespaces.getPrefix() == prefix 163 && info.uri == uri) 164 { 165 return info.namespaces; 166 } 167 } 168 } 169 170 176 public MappingInfo lookupNamespaceBinding (String prefix, 177 char[] uriChars, 178 int uriStart, int uriLength, 179 int uriHash, 180 NamespaceBinding oldBindings) 181 { 182 int hash = prefix == null ? uriHash : prefix.hashCode() ^ uriHash; 183 int bucket = hash & mappingTableMask; 189 MappingInfo info = mappingTable[bucket]; 190 for (;; info = info.nextInBucket) 191 { 192 if (info == null) 193 { 194 info = new MappingInfo(); 195 info.nextInBucket = mappingTable[bucket]; 196 mappingTable[bucket] = info; 197 String uri = new String (uriChars, uriStart, uriLength).intern(); 198 info.tagHash = hash; 203 info.prefix = prefix; 204 info.local = uri; 205 info.uri = uri; 206 if (uri == "") 207 uri = null; 208 NamespaceBinding namespaces 209 = new NamespaceBinding(prefix, uri, oldBindings); 210 info.namespaces = namespaces; 211 return info; 212 } 213 NamespaceBinding namespaces; 214 if (info.tagHash == hash 215 && info.prefix == prefix 216 && (namespaces = info.namespaces) != null 217 && namespaces.getNext() == namespaceBindings 218 && namespaces.getPrefix() == prefix 219 && MappingInfo.equals(info.uri, uriChars, uriStart, uriLength)) 220 { 221 return info; 222 } 223 } 224 } 225 226 public void endAttribute() 227 { 228 if (attrLocalName == null) 229 return; 230 if (previous == SAW_KEYWORD) 231 { 232 previous = 0; 233 return; 234 } 235 if (stringizingElementNesting >= 0) 236 ignoringLevel--; 237 if (--stringizingLevel == 0) 238 { 239 if (attrLocalName == "id" && attrPrefix == "xml") 240 { 241 int valStart 243 = startIndexes[attrCount-1] + TreeList.BEGIN_ATTRIBUTE_LONG_SIZE; 244 int valEnd = tlist.gapStart; 245 char[] data = tlist.data; 246 for (int i = valStart; ; ) 247 { 248 if (i >= valEnd) 249 { 250 break; 252 } 253 char datum = data[i++]; 254 if (((datum & 0xFFFF) > TreeList.MAX_CHAR_SHORT) 255 || datum == '\t' || datum == '\r' || datum == '\n' 256 || (datum == ' ' && (i == valEnd || data[i] == ' '))) 257 { 258 StringBuffer sbuf = new StringBuffer (); 265 tlist.stringValue(valStart, valEnd, sbuf); 266 tlist.gapStart = valStart; 267 tlist.write(TextUtils 268 .replaceWhitespace(sbuf.toString(), true)); 269 break; 270 } 271 } 272 } 273 274 attrLocalName = null; 275 attrPrefix = null; 276 if (currentNamespacePrefix == null || namespacePrefixes) 277 tlist.endAttribute(); 278 if (currentNamespacePrefix != null) 279 { 280 int attrStart = startIndexes[attrCount-1]; 282 int uriStart = attrStart; 283 int uriEnd = tlist.gapStart; 284 int uriLength = uriEnd - uriStart; 285 char[] data = tlist.data; 286 287 int uriHash = 0; 291 for (int i = uriStart; i < uriEnd; i++) 292 { 293 char datum = data[i]; 294 if ((datum & 0xFFFF) > TreeList.MAX_CHAR_SHORT) 295 { 296 StringBuffer sbuf = new StringBuffer (); 297 tlist.stringValue(uriStart, uriEnd, sbuf); 298 uriHash = sbuf.hashCode(); 299 uriStart = 0; 300 uriEnd = uriLength = sbuf.length(); 301 data = new char[sbuf.length()]; 302 sbuf.getChars(0, uriEnd, data, 0); 303 break; 304 } 305 uriHash = 31 * uriHash + datum; 306 } 307 tlist.gapStart = attrStart; 308 309 String prefix = currentNamespacePrefix == "" ? null 310 : currentNamespacePrefix; 311 MappingInfo info 312 = lookupNamespaceBinding(prefix, data, uriStart, uriLength, 313 uriHash, namespaceBindings); 314 namespaceBindings = info.namespaces; 315 316 currentNamespacePrefix = null; 317 } 318 } 319 } 320 321 private String resolve(String prefix, boolean isAttribute) 322 { 323 if (isAttribute && prefix == null) 324 return ""; 325 String uri = namespaceBindings.resolve(prefix); 326 if (uri != null) 327 return uri; 328 if (prefix != null) 329 { 330 error('e', "unknown namespace prefix '" + prefix + '\''); 331 } 332 return ""; 333 } 334 335 void closeStartTag () 336 { 337 if (attrCount < 0 || stringizingLevel > 0) 338 return; 339 inStartTag = false; 340 previous = 0; 341 342 if (attrLocalName != null) endAttribute(); 344 NamespaceBinding outer = nesting == 0 ? NamespaceBinding.predefinedXML 345 : (NamespaceBinding) workStack[nesting-2]; 346 347 NamespaceBinding bindings = namespaceBindings; 348 349 for (int i = 0; i <= attrCount; i++) 352 { 353 Object saved = workStack[nesting+i-1]; 354 if (saved instanceof Symbol) 355 { 356 Symbol sym = (Symbol) saved; 357 String prefix = sym.getPrefix(); 358 if (prefix == "") 359 prefix = null; 360 String uri = sym.getNamespaceURI(); 361 if (uri == "") 362 uri = null; 363 if (i > 0 && prefix == null && uri == null) 364 continue; 365 boolean isOuter = false; 366 for (NamespaceBinding ns = bindings; ; ns = ns.next) 367 { 368 if (ns == outer) 369 isOuter = true; 370 if (ns == null) 371 { 372 if (prefix != null || uri != null) 373 bindings = findNamespaceBinding(prefix, uri, bindings); 374 break; 375 } 376 if (ns.prefix == prefix) 377 { 378 if (ns.uri != uri) 379 { 380 if (isOuter) 381 bindings = findNamespaceBinding(prefix, uri, bindings); 382 else 383 { 384 String nprefix; 386 for (NamespaceBinding ns2 = bindings; 387 ; ns2 = ns2.next) 388 { 389 if (ns2 == null) 390 { 391 for (int j = 1; ; j++) 393 { 394 nprefix = ("_ns_"+j).intern(); 395 if (bindings.resolve(nprefix) == null) 396 break; 397 } 398 break; 399 } 400 if (ns2.uri == uri) 401 { 402 nprefix = ns2.prefix; 403 if (bindings.resolve(nprefix) == uri) 404 break; 405 } 406 } 407 bindings = findNamespaceBinding(nprefix, uri, bindings); 408 String local = sym.getLocalName(); 409 if (uri == null) 410 uri = ""; 411 workStack[nesting+i-1] 412 = Symbol.make(uri, local, nprefix); 413 } 414 } 415 break; 416 } 417 } 418 } 419 420 } 421 422 for (int i = 0; i <= attrCount; i++) 423 { 424 Object saved = workStack[nesting+i-1]; 425 MappingInfo info; 426 boolean isNsNode = false; 427 String prefix, uri, local; 428 if (saved instanceof MappingInfo || out == tlist) 429 { 430 if (saved instanceof MappingInfo) 431 { 432 info = (MappingInfo) saved; 433 prefix = info.prefix; 434 local = info.local; 435 if (i > 0 436 && ((prefix == null && local == "xmlns") 437 || prefix == "xmlns")) 438 { 439 isNsNode = true; 440 uri = "(namespace-node)"; 441 } 442 else 443 uri = resolve(prefix, i > 0); 444 } 445 else 446 { 447 Symbol symbol = (Symbol) saved; 448 info = lookupTag(symbol); 449 prefix = info.prefix; 450 local = info.local; 451 uri = symbol.getNamespaceURI(); 452 } 453 int hash = info.tagHash; 454 int bucket = hash & mappingTableMask; 455 456 info = mappingTable[bucket]; 457 MappingInfo tagMatch = null; 458 Object type; 459 for (;; info = info.nextInBucket) 460 { 461 if (info == null) 462 { 463 info = tagMatch; 464 info = new MappingInfo(); 465 info.tagHash = hash; 466 info.prefix = prefix; 467 info.local = local; 468 info.nextInBucket = mappingTable[bucket]; 469 mappingTable[bucket] = info; 470 info.uri = uri; 471 info.qname = Symbol.make(uri, local, prefix); 472 if (i == 0) 473 { 474 XName xname = new XName(info.qname, bindings); 475 type = xname; 476 info.type = xname; 477 info.namespaces = bindings; 478 } 479 break; 480 } 481 if (info.tagHash == hash 482 && info.local == local 483 && info.prefix == prefix) 484 { 485 if (info.uri == null) 486 { 487 info.uri = uri; 488 info.qname = Symbol.make(uri, local, prefix); 489 } 490 else if (info.uri != uri) 491 continue; 492 else if (info.qname == null) 493 info.qname = Symbol.make(uri, local, prefix); 494 if (i == 0) 495 { 496 if (info.namespaces == bindings 497 || info.namespaces == null) 498 { 499 type = info.type; 500 info.namespaces = bindings; 501 if (type == null) 502 { 503 XName xname = new XName(info.qname, bindings); 504 type = xname; 505 info.type = xname; 506 } 507 break; 508 } 509 } 510 else 511 { 512 type = info.qname; 513 break; 514 } 515 } 516 } 517 workStack[nesting+i-1] = info; 518 } 519 else 520 { 521 Symbol sym = (Symbol) saved; 522 uri = sym.getNamespaceURI(); 523 local = sym.getLocalName(); 524 info = null; 525 } 526 527 for (int j = 1; j < i; j++) 529 { 530 Object other = workStack[nesting+j-1]; 531 Symbol osym; 532 if (other instanceof Symbol) 533 osym = (Symbol) other; 534 else if (other instanceof MappingInfo) 535 osym = ((MappingInfo) other).qname; 536 else 537 continue; 538 if (local == osym.getLocalPart() 539 && uri == osym.getNamespaceURI()) 540 { 541 Object tag = workStack[nesting-1]; 542 if (tag instanceof MappingInfo) 543 tag = ((MappingInfo) tag).qname; 544 error('e', XMLFilter.duplicateAttributeMessage(osym, tag)); 545 } 546 } 547 548 if (out == tlist) 549 { 550 Object type = i == 0 ? info.type : info.qname; 551 int index = info.index; 552 if (index <= 0 553 || tlist.objects[index] != type) 554 { 555 index = tlist.find(type); 556 info.index = index; 557 } 558 if (i == 0) 559 tlist.setGroupName(tlist.gapEnd, index); 560 else if (! isNsNode || namespacePrefixes) 561 tlist.setAttributeName(startIndexes[i-1], index); 562 } 563 else 564 { 565 Object type = info == null ? saved 566 : i == 0 ? info.type : info.qname; 567 if (i == 0) 568 out.beginGroup(type); 569 else if (! isNsNode || namespacePrefixes) 570 { 571 out.beginAttribute(type); 572 int start = startIndexes[i-1]; 573 int end = i < attrCount ? startIndexes[i] : tlist.gapStart; 574 tlist.consumeIRange(start + TreeList.BEGIN_ATTRIBUTE_LONG_SIZE, 575 end - TreeList.END_ATTRIBUTE_SIZE, 576 out); 577 out.endAttribute(); 578 } 579 } 580 } 581 for (int i = 1; i <= attrCount; i++) 582 workStack[nesting+i-1] = null; if (out != tlist) 584 { 585 base = out; 586 tlist.clear(); 588 } 589 attrCount = -1; 590 } 591 592 protected boolean checkWriteAtomic () 593 { 594 previous = 0; 595 if (ignoringLevel > 0) 596 return false; 597 closeStartTag(); 598 return true; 599 } 600 601 public void write (int v) 602 { 603 if (checkWriteAtomic()) 604 base.write(v); 605 } 606 607 public void writeBoolean (boolean v) 608 { 609 if (checkWriteAtomic()) 610 base.writeBoolean(v); 611 } 612 613 public void writeFloat (float v) 614 { 615 if (checkWriteAtomic()) 616 base.writeFloat(v); 617 } 618 619 public void writeDouble (double v) 620 { 621 if (checkWriteAtomic()) 622 base.writeDouble(v); 623 } 624 625 public void writeInt(int v) 626 { 627 if (checkWriteAtomic()) 628 base.writeInt(v); 629 } 630 631 public void writeLong (long v) 632 { 633 if (checkWriteAtomic()) 634 base.writeLong(v); 635 } 636 637 public void writeDocumentUri (Object uri) 638 { 639 if (nesting == 2 && base instanceof TreeList) 640 ((TreeList) base).writeDocumentUri(uri); 641 } 642 643 public void consume (SeqPosition position) 644 { 645 writePosition(position.sequence, position.ipos); 646 } 647 648 public void writePosition(AbstractSequence seq, int ipos) 649 { 650 if (ignoringLevel > 0) 651 return; 652 if (stringizingLevel > 0 && previous == SAW_WORD) 653 { 654 if (stringizingElementNesting < 0) 655 write(' '); 656 previous = 0; 657 } 658 seq.consumeNext(ipos, this); 659 if (stringizingLevel > 0 && stringizingElementNesting < 0) 660 previous = SAW_WORD; 661 } 662 663 664 public void writeObject(Object v) 665 { 666 if (ignoringLevel > 0) 667 return; 668 if (v instanceof SeqPosition) 669 { 670 SeqPosition pos = (SeqPosition) v; 671 writePosition(pos.sequence, pos.getPos()); 672 } 673 else if (v instanceof TreeList) 674 ((TreeList) v).consume(this); 675 else if (v instanceof Keyword) 676 { 677 Keyword k = (Keyword) v; 678 beginAttribute(k.asSymbol()); 679 previous = SAW_KEYWORD; 680 } 681 else 682 { 683 closeStartTag(); 684 if (v instanceof UnescapedData) 685 { 686 base.writeObject(v); 687 previous = 0; 688 } 689 else 690 { 691 if (previous == SAW_WORD) 692 write(' '); 693 TextUtils.textValue(v, this); previous = SAW_WORD; 695 } 696 } 697 } 698 699 public XMLFilter (Consumer out) 700 { 701 this.base = out; 702 this.out = out; 703 if (out instanceof NodeTree) 704 this.tlist = (NodeTree) out; 705 else 706 tlist = new TreeList(); 708 namespaceBindings = NamespaceBinding.predefinedXML; 709 } 710 711 712 public void write (char[] data, int start, int length) 713 { 714 if (length == 0) 715 writeJoiner(); 716 else if (checkWriteAtomic()) 717 base.write(data, start, length); 718 } 719 720 public void write(String str) 721 { 722 write(str, 0, str.length()); 723 } 724 725 726 public void write(CharSequence str, int start, int length) 727 728 730 { 731 if (length == 0) 732 writeJoiner(); 733 else if (checkWriteAtomic()) 734 base.write(str, start, length); 735 } 736 737 public void textFromParser (char[] data, int start, int length) 738 { 739 if (nesting == 0) 744 { 745 for (int i = 0; ; i++) 746 { 747 if (i == length) 748 return; 749 if (! Character.isWhitespace(data[start+i])) 750 break; 751 } 752 } 753 else if (length > 0) 754 { 755 if (previous == SAW_CR) 756 { 757 char ch = data[start]; 758 previous = 0; 759 if (ch == '\n' || ch == 0x85) 760 { 761 start++; 762 length--; 763 } 764 } 765 if (! checkWriteAtomic()) 766 return; 767 768 int limit = start + length; 771 TreeList blist = base instanceof TreeList ? (TreeList) base : null; 772 outerLoop: 773 for (int i = start; ; i++) 774 { 775 char ch; 776 if (blist != null) 778 { 779 blist.ensureSpace(limit-i); 780 char[] bdata = blist.data; 781 int gapStart = blist.gapStart; 782 for (;; i++) 783 { 784 if (i >= limit) 785 { 786 blist.gapStart = gapStart; 787 break outerLoop; 788 } 789 ch = data[i]; 790 if (ch != '\r' && ch < 0x85) bdata[gapStart++] = ch; 792 else if (ch > TreeList.MAX_CHAR_SHORT) 793 { 794 blist.gapStart = gapStart; 795 blist.write(ch); 796 continue outerLoop; 797 } 798 else if (ch == '\r' || ch == 0x85 || ch == 0x2028) 799 { 800 blist.gapStart = gapStart; 801 start = i+1; 802 break; 803 } 804 else 805 bdata[gapStart++] = ch; 806 } 807 } 808 else if (i >= limit) 809 ch = 0; 810 else 811 { 812 ch = data[i]; 813 if (ch >= ' ' && ch < 0x85) continue; 815 if (ch != '\r' && ch != 0x85 && ch != 0x2028) 816 continue; 817 } 818 if (i > start) 819 { 820 base.write(data, start, i-start); 821 } 822 if (i >= limit) 823 break; 824 start = i+1; 825 if (ch == '\r') 826 { 827 if (start < limit) 828 { 829 ch = data[i+1]; 830 if (ch == '\n') 831 continue; if (ch == 0x85) 833 i++; 834 } 835 else 836 previous = SAW_CR; 837 } 838 base.write('\n'); 839 } 840 } 841 } 842 843 public void write (String str, int start, int length) 844 { 845 if (length == 0) 846 writeJoiner(); 847 else if (checkWriteAtomic()) 848 base.write(str, start, length); 849 } 850 851 protected void writeJoiner () 852 { 853 previous = 0; 854 if (ignoringLevel == 0) 855 ((TreeList) base).writeJoiner(); 856 } 857 858 862 public void writeCDATA(char[] data, int start, int length) 863 { 864 if (checkWriteAtomic()) 865 { 866 if (base instanceof XConsumer) 867 ((XConsumer) base).writeCDATA(data, start, length); 868 else 869 write(data, start, length); 870 } 871 } 872 873 protected void beginGroupCommon () 874 { 875 closeStartTag(); 876 if (stringizingLevel == 0) 877 { 878 ensureSpaceInWorkStack(nesting); 879 workStack[nesting] = namespaceBindings; 880 tlist.beginGroup(0); 881 base = tlist; 882 attrCount = 0; 883 } 884 else 885 { 886 if (previous == SAW_WORD && stringizingElementNesting < 0) 887 write(' '); 888 previous = 0; 889 if (stringizingElementNesting < 0) 890 stringizingElementNesting = nesting; 891 } 892 nesting += 2; 893 } 894 895 896 public void emitBeginElement(char[] data, int start, int count) 897 { 898 closeStartTag(); 899 MappingInfo info = lookupTag(data, start, count); 900 beginGroupCommon(); 901 ensureSpaceInWorkStack(nesting-1); 902 workStack[nesting-1] = info; 903 } 904 905 public void beginGroup(Object type) 906 { 907 beginGroupCommon(); 908 if (stringizingLevel == 0) 909 { 910 ensureSpaceInWorkStack(nesting-1); 911 workStack[nesting-1] = type; 912 if (copyNamespacesMode == 0) 913 namespaceBindings = NamespaceBinding.predefinedXML; 914 else if (copyNamespacesMode == COPY_NAMESPACES_PRESERVE 915 || nesting == 2) 916 namespaceBindings 917 = (type instanceof XName ? ((XName) type).getNamespaceNodes() 918 : NamespaceBinding.predefinedXML); 919 else 920 { 921 NamespaceBinding inherited; 922 for (int i = 2; ; i += 2) 924 { 925 if (i == nesting) 926 { 927 inherited = null; 928 break; 929 } 930 if (workStack[i+1] != null) 931 { inherited = (NamespaceBinding) workStack[i]; 933 break; 934 } 935 } 936 if (inherited == null) 937 { 938 namespaceBindings 940 = (type instanceof XName ? ((XName) type).getNamespaceNodes() 941 : NamespaceBinding.predefinedXML); 942 } 943 else if (copyNamespacesMode == COPY_NAMESPACES_INHERIT) 944 namespaceBindings = inherited; 945 else if (type instanceof XName) 946 { 947 NamespaceBinding preserved = ((XName) type).getNamespaceNodes(); 948 NamespaceBinding join = NamespaceBinding.commonAncestor(inherited, preserved); 949 if (join == inherited) 950 namespaceBindings = preserved; 951 else 952 namespaceBindings = mergeHelper(inherited, preserved); 953 } 954 else 955 namespaceBindings = inherited; 956 } 957 } 958 } 959 960 private NamespaceBinding mergeHelper (NamespaceBinding list, 961 NamespaceBinding node) 962 { 963 if (node == NamespaceBinding.predefinedXML) 964 return list; 965 list = mergeHelper(list, node.next); 966 String uri = node.uri; 967 if (list == null) 968 { 969 if (uri == null) 970 return list; 971 list = NamespaceBinding.predefinedXML; 972 } 973 String prefix = node.prefix; 974 String found = list.resolve(prefix); 975 if (found == null ? uri == null : found.equals(uri)) 976 return list; 977 return findNamespaceBinding(prefix, uri, list); 978 } 979 980 private boolean beginAttributeCommon() 981 { 982 if (stringizingElementNesting >= 0) 983 ignoringLevel++; 984 if (stringizingLevel++ > 0) 985 return false; 986 987 if (attrCount < 0) attrCount = 0; 989 ensureSpaceInWorkStack(nesting+attrCount); 990 ensureSpaceInStartIndexes(attrCount); 991 startIndexes[attrCount] = tlist.gapStart; 992 attrCount++; 993 return true; 994 } 995 996 public void beginAttribute (Object attrType) 997 { 998 previous = 0; 999 if (attrType instanceof Symbol) 1000 { 1001 Symbol sym = (Symbol) attrType; 1002 String local = sym.getLocalPart(); 1003 attrLocalName = local; 1004 attrPrefix = sym.getPrefix(); 1005 String uri = sym.getNamespaceURI(); 1006 if (uri == "http://www.w3.org/2000/xmlns/" 1007 || (uri == "" && local == "xmlns")) 1008 error('e', "arttribute name cannot be 'xmlns' or in xmlns namespace"); 1009 } 1010 if (nesting == 2 && workStack[1] == null) 1011 error('e', "attribute not allowed at document level"); 1012 if (attrCount < 0 && nesting > 0) 1013 error('e', "attribute '"+attrType+"' follows non-attribute content"); 1014 if (! beginAttributeCommon()) 1015 return; 1016 workStack[nesting+attrCount-1] = attrType; 1017 if (nesting == 0) 1018 base.beginAttribute(attrType); 1019 else 1020 tlist.beginAttribute(0); 1021 } 1022 1023 1028 public void emitBeginAttribute(char[] data, int start, int count) 1029 { 1030 if (attrLocalName != null) 1031 endAttribute(); 1032 if (! beginAttributeCommon()) 1033 return; 1034 1035 MappingInfo info = lookupTag(data, start, count); 1036 workStack[nesting+attrCount-1] = info; 1037 String prefix = info.prefix; 1038 String local = info.local; 1039 attrLocalName = local; 1040 attrPrefix = prefix; 1041 if (prefix != null) 1042 { 1043 if (prefix == "xmlns") 1044 { 1045 currentNamespacePrefix = local; 1046 } 1047 } 1048 else 1049 { 1050 if (local == "xmlns" && prefix == null) 1051 { 1052 currentNamespacePrefix = ""; 1053 } 1054 } 1055 if (currentNamespacePrefix == null || namespacePrefixes) 1056 tlist.beginAttribute(0); 1057 } 1058 1059 1061 public void emitEndAttributes() 1062 { 1063 if (attrLocalName != null) 1064 endAttribute(); 1065 } 1066 1067 1070 public void emitEndElement(char[] data, int start, int length) 1071 { 1072 if (attrLocalName != null) 1073 { 1074 error('e', "unclosed attribute"); endAttribute(); 1076 } 1077 if (nesting == 0) 1078 { 1079 error('e', "unmatched end element"); return; 1081 } 1082 if (data != null) 1083 { 1084 MappingInfo info = lookupTag(data, start, length); 1085 Object old = workStack[nesting-1]; 1086 if (old instanceof MappingInfo && ! mismatchReported) 1087 { 1088 MappingInfo mold = (MappingInfo) old; 1089 if (info.local != mold.local || info.prefix != mold.prefix) 1090 { 1091 StringBuffer sbuf = new StringBuffer ("</"); 1092 sbuf.append(data, start, length); 1093 sbuf.append("> matching <"); 1094 String oldPrefix = mold.prefix; 1095 if (oldPrefix != null) 1096 { 1097 sbuf.append(oldPrefix); 1098 sbuf.append(':'); 1099 } 1100 sbuf.append(mold.local); 1101 sbuf.append('>'); 1102 error('e', sbuf.toString()); 1103 mismatchReported = true; 1104 } 1105 } 1106 } 1107 closeStartTag(); 1108 if (nesting <= 0) 1109 return; endGroup(); 1111 } 1112 1113 public void endGroup () 1114 { 1115 closeStartTag(); 1116 nesting -= 2; 1117 previous = 0; 1118 if (stringizingLevel == 0) 1119 { 1120 namespaceBindings = (NamespaceBinding) workStack[nesting]; 1121 workStack[nesting] = null; 1122 workStack[nesting+1] = null; 1123 base.endGroup(); 1124 } 1125 else if (stringizingElementNesting == nesting) 1126 { 1127 stringizingElementNesting = -1; 1128 previous = SAW_WORD; 1129 } 1130 1137 } 1138 1139 1143 public void emitEntityReference(char[] name, int start, int length) 1144 { 1145 char c0 = name[start]; 1146 char ch = '?'; 1147 if (length == 2 && name[start+1] == 't') 1148 { 1149 1150 if (c0 == 'l') 1151 ch = '<'; 1152 else if (c0 == 'g') 1153 ch = '>'; 1154 } 1155 else if (length == 3) 1156 { 1157 if (c0 == 'a' && name[start+1] == 'm' && name[start+2] == 'p') 1158 ch = '&'; 1159 } 1160 else if (length == 4) 1161 { 1162 char c1 = name[start+1]; 1163 char c2 = name[start+2]; 1164 char c3 = name[start+3]; 1165 if (c0 == 'q' && c1 == 'u' && c2 == 'o' && c3 == 't') 1166 ch = '"'; 1167 else if (c0 == 'a' && c1 == 'p' && c2 == 'o' && c3 == 's') 1168 ch = '\''; 1169 } 1170 write(ch); 1171 } 1172 1173 1176 public void emitCharacterReference(int value, char[] name, int start, int length) 1177 { 1178 if (value >= 0x10000) 1179 Char.print(value, this); 1180 else 1181 write(value); 1182 } 1183 1184 protected void checkValidComment (char[] chars, int offset, int length) 1185 { 1186 int i = length; 1187 boolean sawHyphen = true; 1188 while (--i >= 0) 1189 { 1190 boolean curHyphen = chars[offset+i] == '-'; 1191 if (sawHyphen && curHyphen) 1192 { 1193 error('e', "consecutive or final hyphen in XML comment"); 1194 break; 1195 } 1196 sawHyphen = curHyphen; 1197 } 1198 } 1199 1200 1203 public void writeComment (char[] chars, int start, int length) 1204 { 1205 checkValidComment(chars, start, length); 1206 commentFromParser(chars, start, length); 1207 } 1208 1209 1212 public void commentFromParser (char[] chars, int start, int length) 1213 { 1214 if (stringizingLevel == 0) 1215 { 1216 closeStartTag(); 1217 if (base instanceof XConsumer) 1218 ((XConsumer) base).writeComment(chars, start, length); 1219 } 1220 else if (stringizingElementNesting < 0) 1221 base.write(chars, start, length); 1222 } 1223 1224 public void writeProcessingInstruction(String target, char[] content, 1225 int offset, int length) 1226 { 1227 for (int i = offset+length; --i >= offset; ) 1228 { 1229 char ch = content[i]; 1230 while (ch == '>' && --i >= offset) 1231 { 1232 ch = content[i]; 1233 if (ch == '?') 1234 { 1235 error('e', "'?>' is not allowed in a processing-instruction"); 1236 break; 1237 } 1238 } 1239 } 1240 1241 if ("xml".equalsIgnoreCase(target)) 1242 error('e', 1243 "processing-instruction target may not be 'xml' (ignoring case)"); 1244 if (! XName.isName(target, true)) 1245 error('e', 1246 "processing-instruction target '"+target+"' is not a valid Name"); 1247 1248 processingInstructionCommon(target, content, offset, length); 1249 } 1250 1251 void processingInstructionCommon (String target, char[] content, 1252 int offset, int length) 1253 { 1254 if (stringizingLevel == 0) 1255 { 1256 closeStartTag(); 1257 if (base instanceof XConsumer) 1258 ((XConsumer) base) 1259 .writeProcessingInstruction(target, content, offset, length); 1260 } 1261 else if (stringizingElementNesting < 0) 1262 base.write(content, offset, length); 1263 } 1264 1265 1266 public void processingInstructionFromParser(char[] buffer, 1267 int tstart, int tlength, 1268 int dstart, int dlength) 1269 { 1270 if (nesting == 0 && tlength == 3 1272 && buffer[tstart] == 'x' 1273 && buffer[tstart+1] == 'm' 1274 && buffer[tstart+2] == 'l') 1275 return; 1276 String target = new String (buffer, tstart, tlength); 1277 processingInstructionCommon(target, buffer, dstart, dlength); 1278 } 1279 1280 public void beginDocument() 1281 { 1282 closeStartTag(); 1283 if (stringizingLevel > 0) 1284 writeJoiner(); 1285 else 1287 { 1288 if (nesting == 0) 1289 base.beginDocument(); 1290 else 1291 writeJoiner(); 1292 ensureSpaceInWorkStack(nesting); 1293 workStack[nesting] = namespaceBindings; 1294 workStack[nesting+1] = null; 1298 nesting += 2; 1299 } 1300 } 1301 1302 public void endDocument () 1303 { 1304 if (stringizingLevel > 0) 1305 { 1306 writeJoiner(); 1307 return; 1308 } 1309 nesting -= 2; 1310 namespaceBindings = (NamespaceBinding) workStack[nesting]; 1311 workStack[nesting] = null; 1312 workStack[nesting+1] = null; 1313 if (nesting == 0) 1314 base.endDocument(); 1315 else 1316 writeJoiner(); 1317 1324 } 1325 1326 1327 public void emitDoctypeDecl(char[] buffer, 1328 int target, int tlength, 1329 int data, int dlength) 1330 { 1331 } 1333 1334 public void beginEntity (Object baseUri) 1335 { 1336 if (base instanceof XConsumer) 1337 ((XConsumer) base).beginEntity(baseUri); 1338 } 1339 1340 public void endEntity () 1341 { 1342 if (base instanceof XConsumer) 1343 ((XConsumer) base).endEntity(); 1344 } 1345 1346 1347 1353 1361 1369 1370 MappingInfo lookupTag (Symbol qname) 1371 { 1372 String local = qname.getLocalPart(); 1373 String prefix = qname.getPrefix(); 1374 if (prefix == "") 1375 prefix = null; 1376 String uri = qname.getNamespaceURI(); 1377 int hash = MappingInfo.hash(prefix, local); 1378 int index = hash & mappingTableMask; 1379 MappingInfo first = mappingTable[index]; 1380 MappingInfo info = first; 1381 for (;;) 1382 { 1383 if (info == null) 1384 { 1385 info = new MappingInfo(); 1387 info.qname = qname; 1388 info.prefix = prefix; 1389 info.uri = uri; 1390 info.local = local; 1391 info.tagHash = hash; 1392 info.nextInBucket = first; 1393 mappingTable[index] = first; 1394 return info; 1395 } 1396 if (qname == info.qname) 1397 return info; 1398 if (local == info.local && info.qname == null 1399 && (uri == info.uri || info.uri == null) 1400 && prefix == info.prefix) 1401 { 1402 info.uri = uri; 1403 info.qname = qname; 1404 return info; 1405 } 1406 info = info.nextInBucket; 1407 } 1408 } 1409 1410 1418 MappingInfo lookupTag (char[] data, int start, int length) 1419 { 1420 int hash = 0; 1422 int prefixHash = 0; 1423 int colon = -1; 1424 for (int i = 0; i < length; i++) 1425 { 1426 char ch = data[start+i]; 1427 if (ch == ':' && colon < 0) 1428 { 1429 colon = i; 1430 prefixHash = hash; 1431 hash = 0; 1432 } 1433 else 1434 hash = 31 * hash + ch; 1435 } 1436 hash = prefixHash ^ hash; 1437 int index = hash & mappingTableMask; 1438 MappingInfo first = mappingTable[index]; 1439 MappingInfo info = first; 1440 for (;;) 1441 { 1442 if (info == null) 1443 { 1444 info = new MappingInfo(); 1446 info.tagHash = hash; 1447 if (colon >= 0) 1448 { 1449 info.prefix = new String (data, start, colon).intern(); 1450 colon++; 1451 int lstart = start+colon; 1452 info.local = new String (data, lstart, length-colon).intern(); 1453 } 1454 else 1455 { 1456 info.prefix = null; 1457 info.local = new String (data, start, length).intern(); 1458 } 1459 info.nextInBucket = first; 1460 mappingTable[index] = first; 1461 return info; 1462 } 1463 if (hash == info.tagHash 1464 && info.match(data, start, length)) 1465 return info; 1466 info = info.nextInBucket; 1467 } 1468 } 1469 1470 private void ensureSpaceInWorkStack (int oldSize) 1471 { 1472 if (workStack == null) 1473 { 1474 workStack = new Object [20]; 1475 } 1476 else if (oldSize >= workStack.length) 1477 { 1478 Object [] tmpn = new Object [2*workStack.length]; 1479 System.arraycopy(workStack, 0, tmpn, 0, oldSize); 1480 workStack = tmpn; 1481 } 1482 } 1483 1484 private void ensureSpaceInStartIndexes (int oldSize) 1485 { 1486 if (startIndexes == null) 1487 { 1488 startIndexes = new int[20]; 1489 } 1490 else if (oldSize >= startIndexes.length) 1491 { 1492 int[] tmpn = new int[2*startIndexes.length]; 1493 System.arraycopy(startIndexes, 0, tmpn, 0, oldSize); 1494 startIndexes = tmpn; 1495 } 1496 } 1497 1498 public static String 1499 duplicateAttributeMessage (Symbol attrSymbol, Object groupName) 1500 { 1501 StringBuffer sbuf = new StringBuffer ("duplicate attribute: "); 1502 String uri = attrSymbol.getNamespaceURI(); 1503 if (uri != null && uri.length() > 0) 1504 { 1505 sbuf.append('{'); 1506 sbuf.append('}'); 1507 sbuf.append(uri); 1508 } 1509 sbuf.append(attrSymbol.getLocalPart()); 1510 if (groupName != null) 1511 { 1512 sbuf.append(" in <"); 1513 sbuf.append(groupName); 1514 sbuf.append('>'); 1515 } 1516 return sbuf.toString(); 1517 } 1518 1519 public void error(char severity, String message) 1520 { 1521 if (messages == null) 1522 throw new RuntimeException (message); 1523 else if (locator != null) 1524 messages.error(severity, locator, message); 1525 else 1526 messages.error(severity, message); 1527 } 1528 1529 public boolean ignoring() 1530 { 1531 return ignoringLevel > 0; 1532 } 1533} 1534 1535final class MappingInfo 1536{ 1537 1538 MappingInfo nextInBucket; 1539 1540 1543 1544 int tagHash; 1545 1546 1548 String prefix; 1549 1550 1552 String local; 1553 1554 1556 String uri; 1557 1558 1562 Symbol qname; 1563 1564 NamespaceBinding namespaces; 1565 1566 1570 XName type; 1571 1572 1573 int index = -1; 1574 1575 static int hash (String prefix, String local) 1576 { 1577 int hash = local.hashCode(); 1578 if (prefix != null) 1579 hash ^= prefix.hashCode(); 1580 return hash; 1581 } 1582 1583 1584 static int hash (char[] data, int start, int length) 1585 { 1586 int hash = 0; 1587 int prefixHash = 0; 1588 int colonPos = -1; 1589 for (int i = 0; i < length; i++) 1590 { 1591 char ch = data[start+i]; 1592 if (ch == ':' && colonPos < 0) 1593 { 1594 colonPos = i; 1595 prefixHash = hash; 1596 hash = 0; 1597 } 1598 else 1599 hash = 31 * hash + ch; 1600 } 1601 return prefixHash ^ hash; 1602 } 1603 1604 1605 boolean match (char[] data, int start, int length) 1606 { 1607 if (prefix != null) 1608 { 1609 int localLength = local.length(); 1610 int prefixLength = prefix.length(); 1611 return length == prefixLength + 1 + localLength 1612 && data[prefixLength] == ':' 1613 && equals(prefix, data, start, prefixLength) 1614 && equals(local, data, start+prefixLength+1, localLength); 1615 } 1616 else 1617 return equals(local, data, start, length); 1618 } 1619 1620 1622 static boolean equals (String tag, StringBuffer sbuf) 1623 { 1624 int length = sbuf.length(); 1625 if (tag.length () != length) 1626 return false; 1627 for (int i = 0; i < length; i++) 1628 if (sbuf.charAt(i) != tag.charAt(i)) 1629 return false; 1630 return true; 1631 } 1632 1633 static boolean equals (String tag, char[] data, int start, int length) 1634 { 1635 if (tag.length () != length) 1636 return false; 1637 for (int i = 0; i < length; i++) 1638 if (data[start+i] != tag.charAt(i)) 1639 return false; 1640 return true; 1641 } 1642} 1643 | Popular Tags |