1 7 8 package org.cyberneko.html; 9 10 import java.lang.reflect.InvocationTargetException ; 11 import java.lang.reflect.Method ; 12 13 import org.apache.xerces.util.XMLAttributesImpl; 14 import org.apache.xerces.xni.Augmentations; 15 import org.apache.xerces.xni.NamespaceContext; 16 import org.apache.xerces.xni.QName; 17 import org.apache.xerces.xni.XMLAttributes; 18 import org.apache.xerces.xni.XMLDocumentHandler; 19 import org.apache.xerces.xni.XMLLocator; 20 import org.apache.xerces.xni.XMLResourceIdentifier; 21 import org.apache.xerces.xni.XMLString; 22 import org.apache.xerces.xni.XNIException; 23 import org.apache.xerces.xni.parser.XMLComponentManager; 24 import org.apache.xerces.xni.parser.XMLConfigurationException; 25 import org.apache.xerces.xni.parser.XMLDocumentFilter; 26 import org.apache.xerces.xni.parser.XMLDocumentSource; 27 28 59 public class HTMLTagBalancer 60 implements XMLDocumentFilter, HTMLComponent { 61 62 66 68 69 protected static final String NAMESPACES = "http://xml.org/sax/features/namespaces"; 70 71 72 protected static final String AUGMENTATIONS = "http://cyberneko.org/html/features/augmentations"; 73 74 75 protected static final String REPORT_ERRORS = "http://cyberneko.org/html/features/report-errors"; 76 77 78 protected static final String DOCUMENT_FRAGMENT_DEPRECATED = "http://cyberneko.org/html/features/document-fragment"; 79 80 81 protected static final String DOCUMENT_FRAGMENT = "http://cyberneko.org/html/features/balance-tags/document-fragment"; 82 83 84 protected static final String IGNORE_OUTSIDE_CONTENT = "http://cyberneko.org/html/features/balance-tags/ignore-outside-content"; 85 86 87 private static final String [] RECOGNIZED_FEATURES = { 88 NAMESPACES, 89 AUGMENTATIONS, 90 REPORT_ERRORS, 91 DOCUMENT_FRAGMENT_DEPRECATED, 92 DOCUMENT_FRAGMENT, 93 IGNORE_OUTSIDE_CONTENT, 94 }; 95 96 97 private static final Boolean [] RECOGNIZED_FEATURES_DEFAULTS = { 98 null, 99 null, 100 null, 101 null, 102 Boolean.FALSE, 103 Boolean.FALSE, 104 }; 105 106 108 109 protected static final String NAMES_ELEMS = "http://cyberneko.org/html/properties/names/elems"; 110 111 112 protected static final String NAMES_ATTRS = "http://cyberneko.org/html/properties/names/attrs"; 113 114 115 protected static final String ERROR_REPORTER = "http://cyberneko.org/html/properties/error-reporter"; 116 117 118 private static final String [] RECOGNIZED_PROPERTIES = { 119 NAMES_ELEMS, 120 NAMES_ATTRS, 121 ERROR_REPORTER, 122 }; 123 124 125 private static final Object [] RECOGNIZED_PROPERTIES_DEFAULTS = { 126 null, 127 null, 128 null, 129 }; 130 131 133 134 protected static final short NAMES_NO_CHANGE = 0; 135 136 137 protected static final short NAMES_MATCH = 0; 138 139 140 protected static final short NAMES_UPPERCASE = 1; 141 142 143 protected static final short NAMES_LOWERCASE = 2; 144 145 147 148 protected static final HTMLEventInfo SYNTHESIZED_ITEM = 149 new HTMLEventInfo.SynthesizedItem(); 150 151 155 157 158 protected boolean fNamespaces; 159 160 161 protected boolean fAugmentations; 162 163 164 protected boolean fReportErrors; 165 166 167 protected boolean fDocumentFragment; 168 169 170 protected boolean fIgnoreOutsideContent; 171 172 174 175 protected short fNamesElems; 176 177 178 protected short fNamesAttrs; 179 180 181 protected HTMLErrorReporter fErrorReporter; 182 183 185 186 protected XMLDocumentSource fDocumentSource; 187 188 189 protected XMLDocumentHandler fDocumentHandler; 190 191 193 194 protected final InfoStack fElementStack = new InfoStack(); 195 196 197 protected final InfoStack fInlineStack = new InfoStack(); 198 199 200 protected boolean fSeenAnything; 201 202 203 protected boolean fSeenDoctype; 204 205 206 protected boolean fSeenRootElement; 207 208 215 protected boolean fSeenRootElementEnd; 216 217 218 protected boolean fSeenHeadElement; 219 220 221 protected boolean fSeenBodyElement; 222 223 225 226 private final QName fQName = new QName(); 227 228 229 private final XMLAttributes fEmptyAttrs = new XMLAttributesImpl(); 230 231 232 private final HTMLAugmentations fInfosetAugs = new HTMLAugmentations(); 233 234 238 239 public Boolean getFeatureDefault(String featureId) { 240 int length = RECOGNIZED_FEATURES != null ? RECOGNIZED_FEATURES.length : 0; 241 for (int i = 0; i < length; i++) { 242 if (RECOGNIZED_FEATURES[i].equals(featureId)) { 243 return RECOGNIZED_FEATURES_DEFAULTS[i]; 244 } 245 } 246 return null; 247 } 249 250 public Object getPropertyDefault(String propertyId) { 251 int length = RECOGNIZED_PROPERTIES != null ? RECOGNIZED_PROPERTIES.length : 0; 252 for (int i = 0; i < length; i++) { 253 if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { 254 return RECOGNIZED_PROPERTIES_DEFAULTS[i]; 255 } 256 } 257 return null; 258 } 260 264 265 public String [] getRecognizedFeatures() { 266 return RECOGNIZED_FEATURES; 267 } 269 270 public String [] getRecognizedProperties() { 271 return RECOGNIZED_PROPERTIES; 272 } 274 275 public void reset(XMLComponentManager manager) 276 throws XMLConfigurationException { 277 278 fNamespaces = manager.getFeature(NAMESPACES); 280 fAugmentations = manager.getFeature(AUGMENTATIONS); 281 fReportErrors = manager.getFeature(REPORT_ERRORS); 282 fDocumentFragment = manager.getFeature(DOCUMENT_FRAGMENT) || 283 manager.getFeature(DOCUMENT_FRAGMENT_DEPRECATED); 284 fIgnoreOutsideContent = manager.getFeature(IGNORE_OUTSIDE_CONTENT); 285 286 fNamesElems = getNamesValue(String.valueOf(manager.getProperty(NAMES_ELEMS))); 288 fNamesAttrs = getNamesValue(String.valueOf(manager.getProperty(NAMES_ATTRS))); 289 fErrorReporter = (HTMLErrorReporter)manager.getProperty(ERROR_REPORTER); 290 291 } 293 294 public void setFeature(String featureId, boolean state) 295 throws XMLConfigurationException { 296 297 if (featureId.equals(AUGMENTATIONS)) { 298 fAugmentations = state; 299 return; 300 } 301 if (featureId.equals(REPORT_ERRORS)) { 302 fReportErrors = state; 303 return; 304 } 305 if (featureId.equals(IGNORE_OUTSIDE_CONTENT)) { 306 fIgnoreOutsideContent = state; 307 return; 308 } 309 310 } 312 313 public void setProperty(String propertyId, Object value) 314 throws XMLConfigurationException { 315 316 if (propertyId.equals(NAMES_ELEMS)) { 317 fNamesElems = getNamesValue(String.valueOf(value)); 318 return; 319 } 320 321 if (propertyId.equals(NAMES_ATTRS)) { 322 fNamesAttrs = getNamesValue(String.valueOf(value)); 323 return; 324 } 325 326 } 328 332 333 public void setDocumentHandler(XMLDocumentHandler handler) { 334 fDocumentHandler = handler; 335 } 337 339 340 public XMLDocumentHandler getDocumentHandler() { 341 return fDocumentHandler; 342 } 344 348 350 351 public void startDocument(XMLLocator locator, String encoding, 352 NamespaceContext nscontext, Augmentations augs) 353 throws XNIException { 354 355 fElementStack.top = 0; 357 fSeenAnything = false; 358 fSeenDoctype = false; 359 fSeenRootElement = false; 360 fSeenRootElementEnd = false; 361 fSeenHeadElement = false; 362 fSeenBodyElement = false; 363 364 if (fDocumentHandler != null) { 366 try { 367 Class cls = fDocumentHandler.getClass(); 371 Class [] types = { 372 XMLLocator.class, String .class, 373 NamespaceContext.class, Augmentations.class 374 }; 375 Method method = cls.getMethod("startDocument", types); 376 Object [] params = { 377 locator, encoding, 378 nscontext, augs 379 }; 380 method.invoke(fDocumentHandler, params); 381 } 382 catch (IllegalAccessException e) { 383 throw new XNIException(e); 384 } 385 catch (InvocationTargetException e) { 386 throw new XNIException(e); 387 } 388 catch (NoSuchMethodException e) { 389 try { 390 Class cls = fDocumentHandler.getClass(); 394 Class [] types = { 395 XMLLocator.class, String .class, Augmentations.class 396 }; 397 Method method = cls.getMethod("startDocument", types); 398 Object [] params = { 399 locator, encoding, augs 400 }; 401 method.invoke(fDocumentHandler, params); 402 } 403 catch (IllegalAccessException ex) { 404 throw new XNIException(ex); 406 } 407 catch (InvocationTargetException ex) { 408 throw new XNIException(ex); 410 } 411 catch (NoSuchMethodException ex) { 412 throw new XNIException(ex); 414 } 415 } 416 } 417 418 } 420 422 423 public void xmlDecl(String version, String encoding, String standalone, 424 Augmentations augs) throws XNIException { 425 if (!fSeenAnything && fDocumentHandler != null) { 426 fDocumentHandler.xmlDecl(version, encoding, standalone, augs); 427 } 428 } 430 431 public void doctypeDecl(String rootElementName, String publicId, String systemId, 432 Augmentations augs) throws XNIException { 433 fSeenAnything = true; 434 if (fReportErrors) { 435 if (fSeenRootElement) { 436 fErrorReporter.reportError("HTML2010", null); 437 } 438 else if (fSeenDoctype) { 439 fErrorReporter.reportError("HTML2011", null); 440 } 441 } 442 if (!fSeenRootElement && !fSeenDoctype) { 443 fSeenDoctype = true; 444 if (fDocumentHandler != null) { 445 fDocumentHandler.doctypeDecl(rootElementName, publicId, systemId, augs); 446 } 447 } 448 } 450 451 public void endDocument(Augmentations augs) throws XNIException { 452 453 if (!fSeenRootElement && !fDocumentFragment) { 455 if (fReportErrors) { 456 fErrorReporter.reportError("HTML2000", null); 457 } 458 String ename = modifyName("html", fNamesElems); 459 fQName.setValues(null, ename, ename, null); 460 if (fDocumentHandler != null) { 461 callStartElement(fQName, emptyAttributes(), synthesizedAugs()); 462 callEndElement(fQName, synthesizedAugs()); 463 } 464 } 465 466 else { 468 int length = fElementStack.top; 469 for (int i = 0; i < length; i++) { 470 Info info = fElementStack.pop(); 471 if (fReportErrors) { 472 String ename = info.qname.rawname; 473 fErrorReporter.reportWarning("HTML2001", new Object []{ename}); 474 } 475 if (fDocumentHandler != null) { 476 callEndElement(info.qname, synthesizedAugs()); 477 } 478 } 479 } 480 481 if (fDocumentHandler != null) { 483 fDocumentHandler.endDocument(augs); 484 } 485 486 } 488 489 public void comment(XMLString text, Augmentations augs) throws XNIException { 490 fSeenAnything = true; 491 if (fDocumentHandler != null) { 492 fDocumentHandler.comment(text, augs); 493 } 494 } 496 497 public void processingInstruction(String target, XMLString data, 498 Augmentations augs) throws XNIException { 499 fSeenAnything = true; 500 if (fDocumentHandler != null) { 501 fDocumentHandler.processingInstruction(target, data, augs); 502 } 503 } 505 506 public void startElement(QName elem, XMLAttributes attrs, Augmentations augs) 507 throws XNIException { 508 fSeenAnything = true; 509 510 if (fSeenRootElementEnd) { 512 return; 513 } 514 515 HTMLElements.Element element = getElement(elem.rawname); 517 518 if (fSeenRootElement && element.code == HTMLElements.HTML) { 520 return; 521 } 522 if (element.code == HTMLElements.HEAD) { 523 if (fSeenHeadElement) { 524 return; 525 } 526 fSeenHeadElement = true; 527 } 528 if (element.code == HTMLElements.BODY) { 529 if (fSeenBodyElement) { 530 return; 531 } 532 fSeenBodyElement = true; 533 } 534 535 if (element.parent != null) { 537 if (!fSeenRootElement && !fDocumentFragment) { 538 String pname = element.parent[0].name; 539 pname = modifyName(pname, fNamesElems); 540 if (fReportErrors) { 541 String ename = elem.rawname; 542 fErrorReporter.reportWarning("HTML2002", new Object []{ename,pname}); 543 } 544 QName qname = new QName(null, pname, pname, null); 545 startElement(qname, null, synthesizedAugs()); 546 } 547 else { 548 HTMLElements.Element pelement = element.parent[0]; 549 if (pelement.code != HTMLElements.HEAD || (!fSeenBodyElement && !fDocumentFragment)) { 550 int depth = getParentDepth(element.parent, element.bounds); 551 if (depth == -1) { 552 String pname = pelement.name; 553 pname = modifyName(pname, fNamesElems); 554 int pdepth = getParentDepth(pelement.parent, pelement.bounds); 555 if (pdepth != -1) { 556 QName qname = new QName(null, pname, pname, null); 557 if (fReportErrors) { 558 String ename = elem.rawname; 559 fErrorReporter.reportWarning("HTML2004", new Object []{ename,pname}); 560 } 561 startElement(qname, null, synthesizedAugs()); 562 } 563 } 564 } 565 } 566 } 567 568 int depth = 0; 570 if (element.flags == 0) { 571 int length = fElementStack.top; 572 fInlineStack.top = 0; 573 for (int i = length - 1; i >= 0; i--) { 574 Info info = fElementStack.data[i]; 575 if (!info.element.isInline()) { 576 break; 577 } 578 fInlineStack.push(info); 579 endElement(info.qname, synthesizedAugs()); 580 } 581 depth = fInlineStack.top; 582 } 583 584 if (element.closes != null) { 586 int length = fElementStack.top; 587 for (int i = length - 1; i >= 0; i--) { 588 Info info = fElementStack.data[i]; 589 590 if (element.closes(info.element.code)) { 592 if (fReportErrors) { 593 String ename = elem.rawname; 594 String iname = info.qname.rawname; 595 fErrorReporter.reportWarning("HTML2005", new Object []{ename,iname}); 596 } 597 for (int j = length - 1; j >= i; j--) { 598 info = fElementStack.pop(); 599 if (fDocumentHandler != null) { 600 callEndElement(info.qname, synthesizedAugs()); 602 } 603 } 604 length = i; 605 continue; 606 } 607 608 boolean container = info.element.isContainer(); 610 boolean parent = false; 611 if (!container) { 612 for (int j = 0; j < element.parent.length; j++) { 613 parent = parent || info.element.code == element.parent[j].code; 614 } 615 } 616 if (container || parent) { 617 break; 618 } 619 } 620 } 621 622 fSeenRootElement = true; 624 if (element != null && element.isEmpty()) { 625 if (attrs == null) { 626 attrs = emptyAttributes(); 627 } 628 if (fDocumentHandler != null) { 629 fDocumentHandler.emptyElement(elem, attrs, augs); 630 } 631 } 632 else { 633 boolean inline = element != null && element.isInline(); 634 fElementStack.push(new Info(element, elem, inline ? attrs : null)); 635 if (attrs == null) { 636 attrs = emptyAttributes(); 637 } 638 if (fDocumentHandler != null) { 639 callStartElement(elem, attrs, augs); 640 } 641 } 642 643 for (int i = 0; i < depth; i++) { 645 Info info = fInlineStack.pop(); 646 startElement(info.qname, info.attributes, synthesizedAugs()); 647 } 648 649 } 651 652 public void emptyElement(QName elem, XMLAttributes attrs, Augmentations augs) 653 throws XNIException { 654 startElement(elem, attrs, augs); 655 endElement(elem, augs); 656 } 658 659 public void startGeneralEntity(String name, 660 XMLResourceIdentifier id, 661 String encoding, 662 Augmentations augs) throws XNIException { 663 fSeenAnything = true; 664 665 if (fSeenRootElementEnd) { 667 return; 668 } 669 670 if (!fDocumentFragment) { 672 boolean insertBody = !fSeenRootElement; 673 if (!insertBody) { 674 Info info = fElementStack.peek(); 675 if (info.element.code == HTMLElements.HEAD || 676 info.element.code == HTMLElements.HTML) { 677 String hname = modifyName("head", fNamesElems); 678 String bname = modifyName("body", fNamesElems); 679 if (fReportErrors) { 680 fErrorReporter.reportWarning("HTML2009", new Object []{hname,bname}); 681 } 682 fQName.setValues(null, hname, hname, null); 683 endElement(fQName, synthesizedAugs()); 684 insertBody = true; 685 } 686 } 687 if (insertBody) { 688 String ename = modifyName("body", fNamesElems); 689 fQName.setValues(null, ename, ename, null); 690 if (fReportErrors) { 691 fErrorReporter.reportWarning("HTML2006", new Object []{ename}); 692 } 693 startElement(fQName, null, synthesizedAugs()); 694 } 695 } 696 697 if (fDocumentHandler != null) { 699 fDocumentHandler.startGeneralEntity(name, id, encoding, augs); 700 } 701 702 } 704 705 public void textDecl(String version, String encoding, Augmentations augs) 706 throws XNIException { 707 fSeenAnything = true; 708 709 if (fSeenRootElementEnd) { 711 return; 712 } 713 714 if (fDocumentHandler != null) { 716 fDocumentHandler.textDecl(version, encoding, augs); 717 } 718 719 } 721 722 public void endGeneralEntity(String name, Augmentations augs) throws XNIException { 723 724 if (fSeenRootElementEnd) { 726 return; 727 } 728 729 if (fDocumentHandler != null) { 731 fDocumentHandler.endGeneralEntity(name, augs); 732 } 733 734 } 736 737 public void startCDATA(Augmentations augs) throws XNIException { 738 fSeenAnything = true; 739 740 if (fSeenRootElementEnd) { 742 return; 743 } 744 745 if (fDocumentHandler != null) { 747 fDocumentHandler.startCDATA(augs); 748 } 749 750 } 752 753 public void endCDATA(Augmentations augs) throws XNIException { 754 755 if (fSeenRootElementEnd) { 757 return; 758 } 759 760 if (fDocumentHandler != null) { 762 fDocumentHandler.endCDATA(augs); 763 } 764 765 } 767 768 public void characters(XMLString text, Augmentations augs) throws XNIException { 769 770 if (fSeenRootElementEnd) { 772 return; 773 } 774 775 boolean whitespace = true; 777 for (int i = 0; i < text.length; i++) { 778 if (!Character.isWhitespace(text.ch[text.offset + i])) { 779 whitespace = false; 780 break; 781 } 782 } 783 784 if (!fDocumentFragment) { 785 if (!fSeenRootElement) { 787 if (whitespace) { 788 return; 789 } 790 String ename = modifyName("body", fNamesElems); 791 fQName.setValues(null, ename, ename, null); 792 if (fReportErrors) { 793 fErrorReporter.reportWarning("HTML2006", new Object []{ename}); 794 } 795 startElement(fQName, null, synthesizedAugs()); 796 } 797 798 else if (!whitespace) { 803 Info info = fElementStack.peek(); 804 if (info.element.code == HTMLElements.HEAD || 805 info.element.code == HTMLElements.HTML) { 806 String hname = modifyName("head", fNamesElems); 807 String bname = modifyName("body", fNamesElems); 808 if (fReportErrors) { 809 fErrorReporter.reportWarning("HTML2009", new Object []{hname,bname}); 810 } 811 fQName.setValues(null, hname, hname, null); 812 endElement(fQName, synthesizedAugs()); 813 fQName.setValues(null, bname, bname, null); 814 startElement(fQName, null, synthesizedAugs()); 815 } 816 } 817 } 818 819 if (fDocumentHandler != null) { 821 fDocumentHandler.characters(text, augs); 822 } 823 824 } 826 827 public void ignorableWhitespace(XMLString text, Augmentations augs) 828 throws XNIException { 829 characters(text, augs); 830 } 832 833 public void endElement(QName element, Augmentations augs) throws XNIException { 834 835 if (fSeenRootElementEnd) { 837 return; 838 } 839 840 HTMLElements.Element elem = getElement(element.rawname); 842 843 if (!fIgnoreOutsideContent && 845 (elem.code == HTMLElements.BODY || elem.code == HTMLElements.HTML)) { 846 return; 847 } 848 849 if (elem.code == HTMLElements.HTML) { 851 fSeenRootElementEnd = true; 852 } 853 854 int depth = getElementDepth(elem); 856 if (depth == -1 && elem.code == HTMLElements.P) { 857 startElement(element, emptyAttributes(), synthesizedAugs()); 858 endElement(element, augs); 859 return; 860 } 861 862 if (depth > 1 && elem.isInline()) { 864 int size = fElementStack.top; 865 fInlineStack.top = 0; 866 for (int i = 0; i < depth - 1; i++) { 867 Info info = fElementStack.data[size - i - 1]; 868 HTMLElements.Element pelem = info.element; 869 if (pelem.isInline()) { 870 fInlineStack.push(info); 874 } 875 } 876 } 877 878 for (int i = 0; i < depth; i++) { 880 Info info = fElementStack.pop(); 881 if (fReportErrors && i < depth - 1) { 882 String ename = modifyName(element.rawname, fNamesElems); 883 String iname = info.qname.rawname; 884 fErrorReporter.reportWarning("HTML2007", new Object []{ename,iname}); 885 } 886 if (fDocumentHandler != null) { 887 callEndElement(info.qname, i < depth - 1 ? synthesizedAugs() : augs); 889 } 890 } 891 892 if (depth > 1) { 894 int size = fInlineStack.top; 895 for (int i = 0; i < size; i++) { 896 Info info = (Info)fInlineStack.pop(); 897 XMLAttributes attributes = info.attributes; 898 if (fReportErrors) { 899 String iname = info.qname.rawname; 900 fErrorReporter.reportWarning("HTML2008", new Object []{iname}); 901 } 902 startElement(info.qname, attributes, synthesizedAugs()); 903 } 904 } 905 906 } 908 910 911 public void setDocumentSource(XMLDocumentSource source) { 912 fDocumentSource = source; 913 } 915 916 public XMLDocumentSource getDocumentSource() { 917 return fDocumentSource; 918 } 920 922 923 public void startDocument(XMLLocator locator, String encoding, Augmentations augs) 924 throws XNIException { 925 startDocument(locator, encoding, null, augs); 926 } 928 929 public void startPrefixMapping(String prefix, String uri, Augmentations augs) 930 throws XNIException { 931 932 if (fSeenRootElementEnd) { 934 return; 935 } 936 937 if (fDocumentHandler != null) { 939 Class cls = fDocumentHandler.getClass(); 940 Class [] types = { String .class, String .class, Augmentations.class }; 941 try { 942 Method method = cls.getMethod("startPrefixMapping", types); 943 Object [] args = { prefix, uri, augs }; 944 method.invoke(fDocumentHandler, args); 945 } 946 catch (NoSuchMethodException e) { 947 } 949 catch (IllegalAccessException e) { 950 } 952 catch (InvocationTargetException e) { 953 } 955 } 956 957 } 959 960 public void endPrefixMapping(String prefix, Augmentations augs) 961 throws XNIException { 962 963 if (fSeenRootElementEnd) { 965 return; 966 } 967 968 if (fDocumentHandler != null) { 970 Class cls = fDocumentHandler.getClass(); 971 Class [] types = { String .class, Augmentations.class }; 972 try { 973 Method method = cls.getMethod("endPrefixMapping", types); 974 Object [] args = { prefix, augs }; 975 method.invoke(fDocumentHandler, args); 976 } 977 catch (NoSuchMethodException e) { 978 } 980 catch (IllegalAccessException e) { 981 } 983 catch (InvocationTargetException e) { 984 } 986 } 987 988 } 990 994 995 protected HTMLElements.Element getElement(String name) { 996 if (fNamespaces) { 997 int index = name.indexOf(':'); 998 if (index != -1) { 999 name = name.substring(index+1); 1000 } 1001 } 1002 return HTMLElements.getElement(name); 1003 } 1005 1006 protected final void callStartElement(QName element, XMLAttributes attrs, 1007 Augmentations augs) 1008 throws XNIException { 1009 fDocumentHandler.startElement(element, attrs, augs); 1010 } 1012 1013 protected final void callEndElement(QName element, Augmentations augs) 1014 throws XNIException { 1015 fDocumentHandler.endElement(element, augs); 1016 } 1018 1024 protected final int getElementDepth(HTMLElements.Element element) { 1025 final boolean container = element.isContainer(); 1026 int depth = -1; 1027 for (int i = fElementStack.top - 1; i >= 0; i--) { 1028 Info info = fElementStack.data[i]; 1029 if (info.element.code == element.code) { 1030 depth = fElementStack.top - i; 1031 break; 1032 } 1033 if (!container && info.element.isBlock()) { 1034 break; 1035 } 1036 } 1037 return depth; 1038 } 1040 1046 protected int getParentDepth(HTMLElements.Element[] parents, short bounds) { 1047 if (parents != null) { 1048 for (int i = fElementStack.top - 1; i >= 0; i--) { 1049 Info info = fElementStack.data[i]; 1050 if (info.element.code == bounds) { 1051 break; 1052 } 1053 for (int j = 0; j < parents.length; j++) { 1054 if (info.element.code == parents[j].code) { 1055 return fElementStack.top - i; 1056 } 1057 } 1058 } 1059 } 1060 return -1; 1061 } 1063 1064 protected final XMLAttributes emptyAttributes() { 1065 fEmptyAttrs.removeAllAttributes(); 1066 return fEmptyAttrs; 1067 } 1069 1070 protected final Augmentations synthesizedAugs() { 1071 HTMLAugmentations augs = null; 1072 if (fAugmentations) { 1073 augs = fInfosetAugs; 1074 augs.removeAllItems(); 1075 augs.putItem(AUGMENTATIONS, SYNTHESIZED_ITEM); 1076 } 1077 return augs; 1078 } 1080 1084 1085 protected static final String modifyName(String name, short mode) { 1086 switch (mode) { 1087 case NAMES_UPPERCASE: return name.toUpperCase(); 1088 case NAMES_LOWERCASE: return name.toLowerCase(); 1089 } 1090 return name; 1091 } 1093 1100 protected static final short getNamesValue(String value) { 1101 if (value.equals("lower")) { 1102 return NAMES_LOWERCASE; 1103 } 1104 if (value.equals("upper")) { 1105 return NAMES_UPPERCASE; 1106 } 1107 return NAMES_NO_CHANGE; 1108 } 1110 1114 1129 public static class Info { 1130 1131 1135 1136 public HTMLElements.Element element; 1137 1138 1139 public QName qname; 1140 1141 1142 public XMLAttributes attributes; 1143 1144 1148 1156 public Info(HTMLElements.Element element, QName qname) { 1157 this(element, qname, null); 1158 } 1160 1169 public Info(HTMLElements.Element element, 1170 QName qname, XMLAttributes attributes) { 1171 this.element = element; 1172 this.qname = new QName(qname); 1173 if (attributes != null) { 1174 int length = attributes.getLength(); 1175 if (length > 0) { 1176 QName aqname = new QName(); 1177 XMLAttributes newattrs = new XMLAttributesImpl(); 1178 for (int i = 0; i < length; i++) { 1179 attributes.getName(i, aqname); 1180 String type = attributes.getType(i); 1181 String value = attributes.getValue(i); 1182 String nonNormalizedValue = attributes.getNonNormalizedValue(i); 1183 boolean specified = attributes.isSpecified(i); 1184 newattrs.addAttribute(aqname, type, value); 1185 newattrs.setNonNormalizedValue(i, nonNormalizedValue); 1186 newattrs.setSpecified(i, specified); 1187 } 1188 this.attributes = newattrs; 1189 } 1190 } 1191 } 1193 } 1195 1196 public static class InfoStack { 1197 1198 1202 1203 public int top; 1204 1205 1206 public Info[] data = new Info[10]; 1207 1208 1212 1213 public void push(Info info) { 1214 if (top == data.length) { 1215 Info[] newarray = new Info[top + 10]; 1216 System.arraycopy(data, 0, newarray, 0, top); 1217 data = newarray; 1218 } 1219 data[top++] = info; 1220 } 1222 1223 public Info peek() { 1224 return data[top-1]; 1225 } 1227 1228 public Info pop() { 1229 return data[--top]; 1230 } 1232 } 1234} | Popular Tags |