001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.parse; 016 017 import org.apache.commons.logging.Log; 018 import org.apache.commons.logging.LogFactory; 019 import org.apache.hivemind.*; 020 import org.apache.hivemind.impl.DefaultErrorHandler; 021 import org.apache.hivemind.impl.LocationImpl; 022 import org.apache.hivemind.parse.AbstractParser; 023 import org.apache.tapestry.INamespace; 024 import org.apache.tapestry.Tapestry; 025 import org.apache.tapestry.bean.BindingBeanInitializer; 026 import org.apache.tapestry.bean.LightweightBeanInitializer; 027 import org.apache.tapestry.binding.BindingConstants; 028 import org.apache.tapestry.binding.BindingSource; 029 import org.apache.tapestry.coerce.ValueConverter; 030 import org.apache.tapestry.spec.*; 031 import org.apache.tapestry.util.IPropertyHolder; 032 import org.apache.tapestry.util.RegexpMatcher; 033 import org.apache.tapestry.util.xml.DocumentParseException; 034 import org.apache.tapestry.util.xml.InvalidStringException; 035 import org.xml.sax.InputSource; 036 import org.xml.sax.SAXException; 037 import org.xml.sax.SAXParseException; 038 039 import javax.xml.parsers.SAXParser; 040 import javax.xml.parsers.SAXParserFactory; 041 import java.io.BufferedInputStream; 042 import java.io.IOException; 043 import java.io.InputStream; 044 import java.net.URL; 045 import java.util.HashMap; 046 import java.util.Iterator; 047 import java.util.Map; 048 049 /** 050 * Parses the different types of Tapestry specifications. 051 * <p> 052 * Not threadsafe; it is the callers responsibility to ensure thread safety. 053 * 054 * @author Howard Lewis Ship 055 */ 056 public class SpecificationParser extends AbstractParser implements ISpecificationParser 057 { 058 /** 059 * Perl5 pattern for asset names. Letter, followed by letter, number or underscore. Also allows 060 * the special "$template" value. 061 * 062 * @since 2.2 063 */ 064 065 public static final String ASSET_NAME_PATTERN = "(\\$template)|(" + Tapestry.SIMPLE_PROPERTY_NAME_PATTERN + ")"; 066 067 /** 068 * Perl5 pattern for helper bean names. Letter, followed by letter, number or underscore. 069 * 070 * @since 2.2 071 */ 072 073 public static final String BEAN_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; 074 075 public static final String IDENTIFIER_PATTERN = "_?[a-zA-Z]\\w*"; 076 077 public static final String EXTENDED_IDENTIFIER_PATTERN = "_?[a-zA-Z](\\w|-)*"; 078 079 /** 080 * Perl5 pattern for component type (which was known as an "alias" in earlier versions of 081 * Tapestry). This is either a simple property name, or a series of property names seperated by 082 * slashes (the latter being new in Tapestry 4.0). This defines a literal that can appear in a 083 * library or application specification. 084 * 085 * @since 2.2 086 */ 087 088 public static final String COMPONENT_ALIAS_PATTERN = "^(" + IDENTIFIER_PATTERN + "/)*" 089 + IDENTIFIER_PATTERN + "$"; 090 091 /** 092 * Perl5 pattern for component ids. Letter, followed by letter, number or underscore. 093 * 094 * @since 2.2 095 */ 096 097 public static final String COMPONENT_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; 098 099 /** 100 * Perl5 pattern for component types (i.e., the type attribute of the <component> 101 * element). Component types are an optional namespace prefix followed by a component type 102 * (within the library defined by the namespace). Starting in 4.0, the type portion is actually 103 * a series of identifiers seperated by slashes. 104 * 105 * @since 2.2 106 */ 107 108 public static final String COMPONENT_TYPE_PATTERN = "^(" + IDENTIFIER_PATTERN + ":)?" + "(" 109 + IDENTIFIER_PATTERN + "/)*" + IDENTIFIER_PATTERN + "$"; 110 111 /** 112 * Extended version of {@link Tapestry#SIMPLE_PROPERTY_NAME_PATTERN}, but allows a series of 113 * individual property names, seperated by periods. In addition, each name within the dotted 114 * sequence is allowed to contain dashes. 115 * 116 * @since 2.2 117 */ 118 119 public static final String EXTENDED_PROPERTY_NAME_PATTERN = "^" + EXTENDED_IDENTIFIER_PATTERN 120 + "(\\." + EXTENDED_IDENTIFIER_PATTERN + ")*$"; 121 122 /** 123 * Per5 pattern for extension names. Letter followed by letter, number, dash, period or 124 * underscore. 125 * 126 * @since 2.2 127 */ 128 129 public static final String EXTENSION_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN; 130 131 /** 132 * Perl5 pattern for library ids. Letter followed by letter, number or underscore. 133 * 134 * @since 2.2 135 */ 136 137 public static final String LIBRARY_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; 138 139 /** 140 * Perl5 pattern for page names. Page names appear in library and application specifications, in 141 * the <page> element. Starting with 4.0, the page name may look more like a path name, 142 * consisting of a number of ids seperated by slashes. This is used to determine the folder 143 * which contains the page specification or the page's template. 144 * 145 * @since 2.2 146 */ 147 148 public static final String PAGE_NAME_PATTERN = "^" + IDENTIFIER_PATTERN + "(/" + EXTENDED_IDENTIFIER_PATTERN + ")*$"; 149 150 /** 151 * Perl5 pattern that parameter names must conform to. Letter, followed by letter, number or 152 * underscore. 153 * 154 * @since 2.2 155 */ 156 157 public static final String PARAMETER_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; 158 159 /** 160 * Perl5 pattern that property names (that can be connected to parameters) must conform to. 161 * Letter, followed by letter, number or underscore. 162 * 163 * @since 2.2 164 */ 165 166 public static final String PROPERTY_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; 167 168 /** 169 * Perl5 pattern for service names. Letter followed by letter, number, dash, underscore or 170 * period. 171 * 172 * @since 2.2 173 * @deprecated As of release 4.0, the <service> element (in 3.0 DTDs) is no longer 174 * supported. 175 */ 176 177 public static final String SERVICE_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN; 178 179 /** @since 3.0 */ 180 181 public static final String TAPESTRY_DTD_3_0_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 3.0//EN"; 182 183 /** @since 4.0 */ 184 185 public static final String TAPESTRY_DTD_4_0_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 4.0//EN"; 186 187 /** @since 4.1 */ 188 189 public static final String TAPESTRY_DTD_4_1_PUBLIC_ID = "-//Apache Software Foundation//Tapestry Specification 4.1//EN"; 190 191 private static final int STATE_ALLOW_DESCRIPTION = 2000; 192 193 private static final int STATE_ALLOW_PROPERTY = 2001; 194 195 private static final int STATE_APPLICATION_SPECIFICATION_INITIAL = 1002; 196 197 private static final int STATE_BEAN = 4; 198 199 /** Very different between 3.0 and 4.0 DTD. */ 200 201 private static final int STATE_BINDING_3_0 = 7; 202 203 /** @since 4.0 */ 204 205 private static final int STATE_BINDING = 100; 206 207 private static final int STATE_COMPONENT = 6; 208 209 private static final int STATE_COMPONENT_SPECIFICATION = 1; 210 211 private static final int STATE_COMPONENT_SPECIFICATION_INITIAL = 1000; 212 213 private static final int STATE_CONFIGURE = 14; 214 215 private static final int STATE_DESCRIPTION = 2; 216 217 private static final int STATE_EXTENSION = 13; 218 219 private static final int STATE_LIBRARY_SPECIFICATION = 12; 220 221 private static final int STATE_LIBRARY_SPECIFICATION_INITIAL = 1003; 222 223 private static final int STATE_LISTENER_BINDING = 8; 224 225 private static final int STATE_NO_CONTENT = 3000; 226 227 private static final int STATE_PAGE_SPECIFICATION = 11; 228 229 private static final int STATE_PAGE_SPECIFICATION_INITIAL = 1001; 230 231 private static final int STATE_META = 3; 232 233 private static final int STATE_PROPERTY = 10; 234 235 private static final int STATE_SET = 5; 236 237 /** 3.0 DTD only. */ 238 private static final int STATE_STATIC_BINDING = 9; 239 240 /** 241 * We can share a single map for all the XML attribute to object conversions, since the keys are 242 * unique. 243 */ 244 245 private final Map _conversionMap = new HashMap(); 246 247 /** @since 4.0 */ 248 private final Log _log; 249 250 /** @since 4.0 */ 251 private final ErrorHandler _errorHandler; 252 253 /** 254 * Set to true if parsing the 4.0 DTD. 255 * 256 * @since 4.0 257 */ 258 259 private boolean _dtd40; 260 261 /** 262 * The attributes of the current element, as a map (string keyed on string). 263 */ 264 265 private Map _attributes; 266 267 /** 268 * The name of the current element. 269 */ 270 271 private String _elementName; 272 273 /** @since 1.0.9 */ 274 275 private final SpecFactory _factory; 276 277 private RegexpMatcher _matcher = new RegexpMatcher(); 278 279 private SAXParser _parser; 280 281 private SAXParserFactory _parserFactory = SAXParserFactory.newInstance(); 282 283 /** 284 * @since 3.0 285 */ 286 287 private final ClassResolver _resolver; 288 289 /** @since 4.0 */ 290 291 private BindingSource _bindingSource; 292 293 /** 294 * The root object parsed: a component or page specification, a library specification, or an 295 * application specification. 296 */ 297 private Object _rootObject; 298 299 /** @since 4.0 */ 300 301 private ValueConverter _valueConverter; 302 303 // Identify all the different acceptible values. 304 // We continue to sneak by with a single map because 305 // there aren't conflicts; when we have 'foo' meaning 306 // different things in different places in the DTD, we'll 307 // need multiple maps. 308 309 { 310 311 _conversionMap.put("true", Boolean.TRUE); 312 _conversionMap.put("t", Boolean.TRUE); 313 _conversionMap.put("1", Boolean.TRUE); 314 _conversionMap.put("y", Boolean.TRUE); 315 _conversionMap.put("yes", Boolean.TRUE); 316 _conversionMap.put("on", Boolean.TRUE); 317 _conversionMap.put("aye", Boolean.TRUE); 318 319 _conversionMap.put("false", Boolean.FALSE); 320 _conversionMap.put("f", Boolean.FALSE); 321 _conversionMap.put("0", Boolean.FALSE); 322 _conversionMap.put("off", Boolean.FALSE); 323 _conversionMap.put("no", Boolean.FALSE); 324 _conversionMap.put("n", Boolean.FALSE); 325 _conversionMap.put("nay", Boolean.FALSE); 326 327 _conversionMap.put("none", BeanLifecycle.NONE); 328 _conversionMap.put("request", BeanLifecycle.REQUEST); 329 _conversionMap.put("page", BeanLifecycle.PAGE); 330 _conversionMap.put("render", BeanLifecycle.RENDER); 331 332 _parserFactory.setNamespaceAware(false); 333 _parserFactory.setValidating(true); 334 } 335 336 /** 337 * This constructor is a convienience used by some tests. 338 */ 339 public SpecificationParser(ClassResolver resolver) 340 { 341 this(new DefaultErrorHandler(), LogFactory.getLog(SpecificationParser.class), 342 resolver, new SpecFactory()); 343 } 344 345 /** 346 * The full constructor, used within Tapestry. 347 */ 348 public SpecificationParser(ErrorHandler errorHandler, Log log, ClassResolver resolver, 349 SpecFactory factory) 350 { 351 _errorHandler = errorHandler; 352 _log = log; 353 _resolver = resolver; 354 _factory = factory; 355 } 356 357 protected void begin(String elementName, Map attributes) 358 { 359 _elementName = elementName; 360 _attributes = attributes; 361 362 switch (getState()) 363 { 364 case STATE_COMPONENT_SPECIFICATION_INITIAL: 365 366 beginComponentSpecificationInitial(); 367 break; 368 369 case STATE_PAGE_SPECIFICATION_INITIAL: 370 371 beginPageSpecificationInitial(); 372 break; 373 374 case STATE_APPLICATION_SPECIFICATION_INITIAL: 375 376 beginApplicationSpecificationInitial(); 377 break; 378 379 case STATE_LIBRARY_SPECIFICATION_INITIAL: 380 381 beginLibrarySpecificationInitial(); 382 break; 383 384 case STATE_COMPONENT_SPECIFICATION: 385 386 beginComponentSpecification(); 387 break; 388 389 case STATE_PAGE_SPECIFICATION: 390 391 beginPageSpecification(); 392 break; 393 394 case STATE_ALLOW_DESCRIPTION: 395 396 beginAllowDescription(); 397 break; 398 399 case STATE_ALLOW_PROPERTY: 400 401 allowMetaData(); 402 break; 403 404 case STATE_BEAN: 405 406 beginBean(); 407 break; 408 409 case STATE_COMPONENT: 410 411 beginComponent(); 412 break; 413 414 case STATE_LIBRARY_SPECIFICATION: 415 416 beginLibrarySpecification(); 417 break; 418 419 case STATE_EXTENSION: 420 421 beginExtension(); 422 break; 423 424 default: 425 426 unexpectedElement(_elementName); 427 } 428 } 429 430 /** 431 * Special state for a number of specification types that can support the <description> 432 * element. 433 */ 434 435 private void beginAllowDescription() 436 { 437 if (_elementName.equals("description")) 438 { 439 enterDescription(); 440 return; 441 } 442 443 unexpectedElement(_elementName); 444 } 445 446 /** 447 * Special state for a number of elements that can support the nested <meta> meta data 448 * element (<property> in 3.0 DTD). 449 */ 450 451 private void allowMetaData() 452 { 453 if (_dtd40) 454 { 455 if (_elementName.equals("meta")) 456 { 457 enterMeta(); 458 return; 459 } 460 } 461 else if (_elementName.equals("property")) 462 { 463 enterProperty30(); 464 return; 465 } 466 467 unexpectedElement(_elementName); 468 } 469 470 private void beginApplicationSpecificationInitial() 471 { 472 expectElement("application"); 473 474 String name = getAttribute("name"); 475 String engineClassName = getAttribute("engine-class"); 476 477 IApplicationSpecification as = _factory.createApplicationSpecification(); 478 479 as.setName(name); 480 481 if (HiveMind.isNonBlank(engineClassName)) 482 as.setEngineClassName(engineClassName); 483 484 _rootObject = as; 485 486 push(_elementName, as, STATE_LIBRARY_SPECIFICATION); 487 } 488 489 private void beginBean() 490 { 491 if (_elementName.equals("set")) 492 { 493 enterSet(); 494 return; 495 } 496 497 if (_elementName.equals("set-property")) 498 { 499 enterSetProperty30(); 500 return; 501 } 502 503 if (_elementName.equals("set-message-property")) 504 { 505 enterSetMessage30(); 506 return; 507 } 508 509 if (_elementName.equals("description")) 510 { 511 enterDescription(); 512 return; 513 } 514 515 allowMetaData(); 516 } 517 518 private void beginComponent() 519 { 520 // <binding> has changed between 3.0 and 4.0 521 522 if (_elementName.equals("binding")) 523 { 524 enterBinding(); 525 return; 526 } 527 528 if (_elementName.equals("static-binding")) 529 { 530 enterStaticBinding30(); 531 return; 532 } 533 534 if (_elementName.equals("message-binding")) 535 { 536 enterMessageBinding30(); 537 return; 538 } 539 540 if (_elementName.equals("inherited-binding")) 541 { 542 enterInheritedBinding30(); 543 return; 544 } 545 546 if (_elementName.equals("listener-binding")) 547 { 548 enterListenerBinding(); 549 return; 550 } 551 552 allowMetaData(); 553 } 554 555 private void beginComponentSpecification() 556 { 557 if (_elementName.equals("reserved-parameter")) 558 { 559 enterReservedParameter(); 560 return; 561 } 562 563 if (_elementName.equals("parameter")) 564 { 565 enterParameter(); 566 return; 567 } 568 569 // The remainder are common to both <component-specification> and 570 // <page-specification> 571 572 beginPageSpecification(); 573 } 574 575 private void beginComponentSpecificationInitial() 576 { 577 expectElement("component-specification"); 578 579 IComponentSpecification cs = _factory.createComponentSpecification(); 580 581 cs.setAllowBody(getBooleanAttribute("allow-body", true)); 582 cs.setAllowInformalParameters(getBooleanAttribute("allow-informal-parameters", true)); 583 cs.setDeprecated(getBooleanAttribute("deprecated", false)); 584 585 String className = getAttribute("class"); 586 587 if (className != null) 588 cs.setComponentClassName(className); 589 590 cs.setSpecificationLocation(getResource()); 591 592 _rootObject = cs; 593 594 push(_elementName, cs, STATE_COMPONENT_SPECIFICATION); 595 } 596 597 private void beginExtension() 598 { 599 if (_elementName.equals("configure")) 600 { 601 enterConfigure(); 602 return; 603 } 604 605 allowMetaData(); 606 } 607 608 private void beginLibrarySpecification() 609 { 610 if (_elementName.equals("description")) 611 { 612 enterDescription(); 613 return; 614 } 615 616 if (_elementName.equals("page")) 617 { 618 enterPage(); 619 return; 620 } 621 622 if (_elementName.equals("component-type")) 623 { 624 enterComponentType(); 625 return; 626 } 627 628 // Holdover from the 3.0 DTD, now ignored. 629 630 if (_elementName.equals("service")) 631 { 632 enterService30(); 633 return; 634 } 635 636 if (_elementName.equals("library")) 637 { 638 enterLibrary(); 639 return; 640 } 641 642 if (_elementName.equals("extension")) 643 { 644 enterExtension(); 645 return; 646 } 647 648 allowMetaData(); 649 } 650 651 private void beginLibrarySpecificationInitial() 652 { 653 expectElement("library-specification"); 654 655 ILibrarySpecification ls = _factory.createLibrarySpecification(); 656 657 _rootObject = ls; 658 659 push(_elementName, ls, STATE_LIBRARY_SPECIFICATION); 660 } 661 662 private void beginPageSpecification() 663 { 664 if (_elementName.equals("component")) 665 { 666 enterComponent(); 667 return; 668 } 669 670 if (_elementName.equals("bean")) 671 { 672 enterBean(); 673 return; 674 } 675 676 // <property-specification> in 3.0, <property> in 4.0 677 // Have to be careful, because <meta> in 4.0 was <property> in 3.0 678 679 if (_elementName.equals("property-specification") 680 || (_dtd40 && _elementName.equals("property"))) 681 { 682 enterProperty(); 683 return; 684 } 685 686 if (_elementName.equals("inject")) 687 { 688 enterInject(); 689 return; 690 } 691 692 // <asset> is new in 4.0 693 694 if (_elementName.equals("asset")) 695 { 696 enterAsset(); 697 return; 698 } 699 700 // <context-asset>, <external-asset>, and <private-asset> 701 // are all throwbacks to the 3.0 DTD and don't exist 702 // in the 4.0 DTD. 703 704 if (_elementName.equals("context-asset")) 705 { 706 enterContextAsset30(); 707 return; 708 } 709 710 if (_elementName.equals("private-asset")) 711 { 712 enterPrivateAsset30(); 713 return; 714 } 715 716 if (_elementName.equals("external-asset")) 717 { 718 enterExternalAsset30(); 719 return; 720 721 } 722 723 if (_elementName.equals("description")) 724 { 725 enterDescription(); 726 return; 727 } 728 729 allowMetaData(); 730 } 731 732 private void beginPageSpecificationInitial() 733 { 734 expectElement("page-specification"); 735 736 IComponentSpecification cs = _factory.createComponentSpecification(); 737 738 String className = getAttribute("class"); 739 740 if (className != null) 741 cs.setComponentClassName(className); 742 743 cs.setSpecificationLocation(getResource()); 744 cs.setPageSpecification(true); 745 746 _rootObject = cs; 747 748 push(_elementName, cs, STATE_PAGE_SPECIFICATION); 749 } 750 751 /** 752 * Close a stream (if not null), ignoring any errors. 753 */ 754 private void close(InputStream stream) 755 { 756 try 757 { 758 if (stream != null) 759 stream.close(); 760 } 761 catch (IOException ex) 762 { 763 // ignore 764 } 765 } 766 767 private void copyBindings(String sourceComponentId, IComponentSpecification cs, 768 IContainedComponent target) 769 { 770 IContainedComponent source = cs.getComponent(sourceComponentId); 771 if (source == null) 772 throw new DocumentParseException(ParseMessages.unableToCopy(sourceComponentId), 773 getLocation()); 774 775 Iterator i = source.getBindingNames().iterator(); 776 while (i.hasNext()) 777 { 778 String bindingName = (String) i.next(); 779 IBindingSpecification binding = source.getBinding(bindingName); 780 target.setBinding(bindingName, binding); 781 } 782 783 target.setType(source.getType()); 784 } 785 786 protected void end(String elementName) 787 { 788 _elementName = elementName; 789 790 switch (getState()) 791 { 792 case STATE_DESCRIPTION: 793 794 endDescription(); 795 break; 796 797 case STATE_META: 798 799 endProperty(); 800 break; 801 802 case STATE_SET: 803 804 endSetProperty(); 805 break; 806 807 case STATE_BINDING_3_0: 808 809 endBinding30(); 810 break; 811 812 case STATE_BINDING: 813 814 endBinding(); 815 break; 816 817 case STATE_STATIC_BINDING: 818 819 endStaticBinding(); 820 break; 821 822 case STATE_PROPERTY: 823 824 endPropertySpecification(); 825 break; 826 827 case STATE_LIBRARY_SPECIFICATION: 828 829 endLibrarySpecification(); 830 break; 831 832 case STATE_CONFIGURE: 833 834 endConfigure(); 835 break; 836 837 default: 838 break; 839 } 840 841 // Pop the top element of the stack and continue processing from there. 842 843 pop(); 844 } 845 846 private void endBinding30() 847 { 848 BindingSetter bs = (BindingSetter) peekObject(); 849 850 String expression = getExtendedValue(bs.getValue(), "expression", true); 851 852 IBindingSpecification spec = _factory.createBindingSpecification(); 853 854 spec.setType(BindingType.PREFIXED); 855 spec.setValue(BindingConstants.OGNL_PREFIX + ":" + expression); 856 857 bs.apply(spec); 858 } 859 860 private void endConfigure() 861 { 862 ExtensionConfigurationSetter setter = (ExtensionConfigurationSetter) peekObject(); 863 864 String finalValue = getExtendedValue(setter.getValue(), "value", true); 865 866 setter.apply(finalValue); 867 } 868 869 private void endDescription() 870 { 871 DescriptionSetter setter = (DescriptionSetter) peekObject(); 872 873 String description = peekContent(); 874 875 setter.apply(description); 876 } 877 878 private void endLibrarySpecification() 879 { 880 ILibrarySpecification spec = (ILibrarySpecification) peekObject(); 881 882 spec.setSpecificationLocation(getResource()); 883 884 spec.instantiateImmediateExtensions(); 885 } 886 887 private void endProperty() 888 { 889 PropertyValueSetter pvs = (PropertyValueSetter) peekObject(); 890 891 String finalValue = getExtendedValue(pvs.getPropertyValue(), "value", true); 892 893 pvs.applyValue(finalValue); 894 } 895 896 private void endPropertySpecification() 897 { 898 IPropertySpecification ps = (IPropertySpecification) peekObject(); 899 900 String initialValue = getExtendedValue(ps.getInitialValue(), "initial-value", false); 901 902 // In the 3.0 DTD, the initial value was always an OGNL expression. 903 // In the 4.0 DTD, it is a binding reference, qualified with a prefix. 904 905 if (initialValue != null && !_dtd40) 906 initialValue = BindingConstants.OGNL_PREFIX + ":" + initialValue; 907 908 ps.setInitialValue(initialValue); 909 } 910 911 private void endSetProperty() 912 { 913 BeanSetPropertySetter bs = (BeanSetPropertySetter) peekObject(); 914 915 String finalValue = getExtendedValue(bs.getBindingReference(), "expression", true); 916 917 bs.applyBindingReference(finalValue); 918 } 919 920 private void endStaticBinding() 921 { 922 BindingSetter bs = (BindingSetter) peekObject(); 923 924 String literalValue = getExtendedValue(bs.getValue(), "value", true); 925 926 IBindingSpecification spec = _factory.createBindingSpecification(); 927 928 spec.setType(BindingType.PREFIXED); 929 spec.setValue(BindingConstants.LITERAL_PREFIX + ":" + literalValue); 930 931 bs.apply(spec); 932 } 933 934 private void enterAsset(String pathAttributeName, String prefix) 935 { 936 String name = getValidatedAttribute("name", ASSET_NAME_PATTERN, "invalid-asset-name"); 937 String path = getAttribute(pathAttributeName); 938 String propertyName = getValidatedAttribute( 939 "property", 940 PROPERTY_NAME_PATTERN, 941 "invalid-property-name"); 942 943 IAssetSpecification ia = _factory.createAssetSpecification(); 944 945 ia.setPath(prefix == null ? path : prefix + path); 946 ia.setPropertyName(propertyName); 947 948 IComponentSpecification cs = (IComponentSpecification) peekObject(); 949 950 cs.addAsset(name, ia); 951 952 push(_elementName, ia, STATE_ALLOW_PROPERTY); 953 } 954 955 private void enterBean() 956 { 957 String name = getValidatedAttribute("name", BEAN_NAME_PATTERN, "invalid-bean-name"); 958 959 String classAttribute = getAttribute("class"); 960 961 // Look for the lightweight initialization 962 963 int commax = classAttribute.indexOf(','); 964 965 String className = commax < 0 ? classAttribute : classAttribute.substring(0, commax); 966 967 BeanLifecycle lifecycle = (BeanLifecycle) getConvertedAttribute( 968 "lifecycle", 969 BeanLifecycle.REQUEST); 970 String propertyName = getValidatedAttribute( 971 "property", 972 PROPERTY_NAME_PATTERN, 973 "invalid-property-name"); 974 975 IBeanSpecification bs = _factory.createBeanSpecification(); 976 977 bs.setClassName(className); 978 bs.setLifecycle(lifecycle); 979 bs.setPropertyName(propertyName); 980 981 if (commax > 0) 982 { 983 String initializer = classAttribute.substring(commax + 1); 984 bs.addInitializer(new LightweightBeanInitializer(initializer)); 985 } 986 987 IComponentSpecification cs = (IComponentSpecification) peekObject(); 988 989 cs.addBeanSpecification(name, bs); 990 991 push(_elementName, bs, STATE_BEAN); 992 } 993 994 private void enterBinding() 995 { 996 if (!_dtd40) 997 { 998 enterBinding30(); 999 return; 1000 } 1001 1002 // 4.0 stuff 1003 1004 String name = getValidatedAttribute( 1005 "name", 1006 PARAMETER_NAME_PATTERN, 1007 "invalid-parameter-name"); 1008 String value = getAttribute("value"); 1009 1010 IContainedComponent cc = (IContainedComponent) peekObject(); 1011 1012 BindingSetter bs = new BindingSetter(cc, name, value); 1013 1014 push(_elementName, bs, STATE_BINDING, false); 1015 } 1016 1017 private void endBinding() 1018 { 1019 BindingSetter bs = (BindingSetter) peekObject(); 1020 1021 String value = getExtendedValue(bs.getValue(), "value", true); 1022 1023 IBindingSpecification spec = _factory.createBindingSpecification(); 1024 1025 spec.setType(BindingType.PREFIXED); 1026 spec.setValue(value); 1027 1028 bs.apply(spec); 1029 } 1030 1031 /** 1032 * Handles a binding in a 3.0 DTD. 1033 */ 1034 1035 private void enterBinding30() 1036 { 1037 String name = getAttribute("name"); 1038 String expression = getAttribute("expression"); 1039 1040 IContainedComponent cc = (IContainedComponent) peekObject(); 1041 1042 BindingSetter bs = new BindingSetter(cc, name, expression); 1043 1044 push(_elementName, bs, STATE_BINDING_3_0, false); 1045 } 1046 1047 private void enterComponent() 1048 { 1049 String id = getValidatedAttribute("id", COMPONENT_ID_PATTERN, "invalid-component-id"); 1050 1051 String type = getValidatedAttribute( 1052 "type", 1053 COMPONENT_TYPE_PATTERN, 1054 "invalid-component-type"); 1055 String copyOf = getAttribute("copy-of"); 1056 boolean inherit = getBooleanAttribute("inherit-informal-parameters", false); 1057 String propertyName = getValidatedAttribute( 1058 "property", 1059 PROPERTY_NAME_PATTERN, 1060 "invalid-property-name"); 1061 1062 // Check that either copy-of or type, but not both 1063 1064 boolean hasCopyOf = HiveMind.isNonBlank(copyOf); 1065 1066 if (hasCopyOf) 1067 { 1068 if (HiveMind.isNonBlank(type)) 1069 throw new DocumentParseException(ParseMessages.bothTypeAndCopyOf(id), getLocation()); 1070 } 1071 else 1072 { 1073 if (HiveMind.isBlank(type)) 1074 throw new DocumentParseException(ParseMessages.missingTypeOrCopyOf(id), 1075 getLocation()); 1076 } 1077 1078 IContainedComponent cc = _factory.createContainedComponent(); 1079 cc.setType(type); 1080 cc.setCopyOf(copyOf); 1081 cc.setInheritInformalParameters(inherit); 1082 cc.setPropertyName(propertyName); 1083 1084 IComponentSpecification cs = (IComponentSpecification) peekObject(); 1085 1086 cs.addComponent(id, cc); 1087 1088 if (hasCopyOf) 1089 copyBindings(copyOf, cs, cc); 1090 1091 push(_elementName, cc, STATE_COMPONENT); 1092 } 1093 1094 private void enterComponentType() 1095 { 1096 String type = getValidatedAttribute( 1097 "type", 1098 COMPONENT_ALIAS_PATTERN, 1099 "invalid-component-type"); 1100 String path = getAttribute("specification-path"); 1101 1102 ILibrarySpecification ls = (ILibrarySpecification) peekObject(); 1103 1104 ls.setComponentSpecificationPath(type, path); 1105 1106 push(_elementName, null, STATE_NO_CONTENT); 1107 } 1108 1109 private void enterConfigure() 1110 { 1111 String attributeName = _dtd40 ? "property" : "property-name"; 1112 1113 String propertyName = getValidatedAttribute( 1114 attributeName, 1115 PROPERTY_NAME_PATTERN, 1116 "invalid-property-name"); 1117 1118 String value = getAttribute("value"); 1119 1120 IExtensionSpecification es = (IExtensionSpecification) peekObject(); 1121 1122 ExtensionConfigurationSetter setter = new ExtensionConfigurationSetter(es, propertyName, 1123 value); 1124 1125 push(_elementName, setter, STATE_CONFIGURE, false); 1126 } 1127 1128 private void enterContextAsset30() 1129 { 1130 enterAsset("path", "context:"); 1131 } 1132 1133 /** 1134 * New in the 4.0 DTD. When using the 4.0 DTD, you must explicitly specify prefix if the asset 1135 * is not stored in the same domain as the specification file. 1136 * 1137 * @since 4.0 1138 */ 1139 1140 private void enterAsset() 1141 { 1142 enterAsset("path", null); 1143 } 1144 1145 private void enterDescription() 1146 { 1147 push(_elementName, new DescriptionSetter(peekObject()), STATE_DESCRIPTION, false); 1148 } 1149 1150 private void enterExtension() 1151 { 1152 String name = getValidatedAttribute( 1153 "name", 1154 EXTENSION_NAME_PATTERN, 1155 "invalid-extension-name"); 1156 1157 boolean immediate = getBooleanAttribute("immediate", false); 1158 String className = getAttribute("class"); 1159 1160 IExtensionSpecification es = _factory.createExtensionSpecification( 1161 _resolver, 1162 _valueConverter); 1163 1164 es.setClassName(className); 1165 es.setImmediate(immediate); 1166 1167 ILibrarySpecification ls = (ILibrarySpecification) peekObject(); 1168 1169 ls.addExtensionSpecification(name, es); 1170 1171 push(_elementName, es, STATE_EXTENSION); 1172 } 1173 1174 private void enterExternalAsset30() 1175 { 1176 // External URLs get no prefix, but will have a scheme (i.e., "http:") that 1177 // fulfils much the same purpose. 1178 1179 enterAsset("URL", null); 1180 } 1181 1182 /** A throwback to the 3.0 DTD. */ 1183 1184 private void enterInheritedBinding30() 1185 { 1186 String name = getAttribute("name"); 1187 String parameterName = getAttribute("parameter-name"); 1188 1189 IBindingSpecification bs = _factory.createBindingSpecification(); 1190 bs.setType(BindingType.INHERITED); 1191 bs.setValue(parameterName); 1192 1193 IContainedComponent cc = (IContainedComponent) peekObject(); 1194 1195 cc.setBinding(name, bs); 1196 1197 push(_elementName, null, STATE_NO_CONTENT); 1198 } 1199 1200 private void enterLibrary() 1201 { 1202 String libraryId = getValidatedAttribute("id", LIBRARY_ID_PATTERN, "invalid-library-id"); 1203 String path = getAttribute("specification-path"); 1204 1205 if (libraryId.equals(INamespace.FRAMEWORK_NAMESPACE) 1206 || libraryId.equals(INamespace.APPLICATION_NAMESPACE)) 1207 throw new DocumentParseException(ParseMessages 1208 .frameworkLibraryIdIsReserved(INamespace.FRAMEWORK_NAMESPACE), getLocation()); 1209 1210 ILibrarySpecification ls = (ILibrarySpecification) peekObject(); 1211 1212 ls.setLibrarySpecificationPath(libraryId, path); 1213 1214 push(_elementName, null, STATE_NO_CONTENT); 1215 } 1216 1217 private void enterListenerBinding() 1218 { 1219 _log.warn(ParseMessages.listenerBindingUnsupported(getLocation())); 1220 1221 push(_elementName, null, STATE_LISTENER_BINDING, false); 1222 } 1223 1224 private void enterMessageBinding30() 1225 { 1226 String name = getAttribute("name"); 1227 String key = getAttribute("key"); 1228 1229 IBindingSpecification bs = _factory.createBindingSpecification(); 1230 bs.setType(BindingType.PREFIXED); 1231 bs.setValue(BindingConstants.MESSAGE_PREFIX + ":" + key); 1232 bs.setLocation(getLocation()); 1233 1234 IContainedComponent cc = (IContainedComponent) peekObject(); 1235 1236 cc.setBinding(name, bs); 1237 1238 push(_elementName, null, STATE_NO_CONTENT); 1239 } 1240 1241 private void enterPage() 1242 { 1243 String name = getValidatedAttribute("name", PAGE_NAME_PATTERN, "invalid-page-name"); 1244 String path = getAttribute("specification-path"); 1245 1246 ILibrarySpecification ls = (ILibrarySpecification) peekObject(); 1247 1248 ls.setPageSpecificationPath(name, path); 1249 1250 push(_elementName, null, STATE_NO_CONTENT); 1251 } 1252 1253 private void enterParameter() 1254 { 1255 IParameterSpecification ps = _factory.createParameterSpecification(); 1256 1257 String name = getValidatedAttribute( 1258 "name", 1259 PARAMETER_NAME_PATTERN, 1260 "invalid-parameter-name"); 1261 1262 String attributeName = _dtd40 ? "property" : "property-name"; 1263 1264 String propertyName = getValidatedAttribute( 1265 attributeName, 1266 PROPERTY_NAME_PATTERN, 1267 "invalid-property-name"); 1268 1269 if (propertyName == null) 1270 propertyName = name; 1271 1272 ps.setParameterName(name); 1273 ps.setPropertyName(propertyName); 1274 1275 ps.setRequired(getBooleanAttribute("required", false)); 1276 1277 // In the 3.0 DTD, default-value was always an OGNL expression. 1278 // Starting with 4.0, it's like a binding (prefixed). For a 3.0 1279 // DTD, we supply the "ognl:" prefix. 1280 1281 String defaultValue = getAttribute("default-value"); 1282 1283 if (defaultValue != null && !_dtd40) 1284 defaultValue = BindingConstants.OGNL_PREFIX + ":" + defaultValue; 1285 1286 ps.setDefaultValue(defaultValue); 1287 1288 if (!_dtd40) 1289 { 1290 // When direction=auto (in a 3.0 DTD), turn caching off 1291 1292 String direction = getAttribute("direction"); 1293 ps.setCache(!"auto".equals(direction)); 1294 } 1295 else 1296 { 1297 boolean cache = getBooleanAttribute("cache", true); 1298 ps.setCache(cache); 1299 } 1300 1301 // type will only be specified in a 3.0 DTD. 1302 1303 String type = getAttribute("type"); 1304 1305 if (type != null) 1306 ps.setType(type); 1307 1308 // aliases is new in the 4.0 DTD 1309 1310 String aliases = getAttribute("aliases"); 1311 1312 ps.setAliases(aliases); 1313 ps.setDeprecated(getBooleanAttribute("deprecated", false)); 1314 1315 IComponentSpecification cs = (IComponentSpecification) peekObject(); 1316 1317 cs.addParameter(ps); 1318 1319 push(_elementName, ps, STATE_ALLOW_DESCRIPTION); 1320 } 1321 1322 private void enterPrivateAsset30() 1323 { 1324 enterAsset("resource-path", "classpath:"); 1325 } 1326 1327 /** @since 4.0 */ 1328 private void enterMeta() 1329 { 1330 String key = getAttribute("key"); 1331 String value = getAttribute("value"); 1332 1333 // Value may be null, in which case the value is set from the element content 1334 1335 IPropertyHolder ph = (IPropertyHolder) peekObject(); 1336 1337 push(_elementName, new PropertyValueSetter(ph, key, value), STATE_META, false); 1338 } 1339 1340 private void enterProperty30() 1341 { 1342 String name = getAttribute("name"); 1343 String value = getAttribute("value"); 1344 1345 // Value may be null, in which case the value is set from the element content 1346 1347 IPropertyHolder ph = (IPropertyHolder) peekObject(); 1348 1349 push(_elementName, new PropertyValueSetter(ph, name, value), STATE_META, false); 1350 } 1351 1352 /** 1353 * &tl;property> in 4.0, or <property-specification> in 3.0. 1354 */ 1355 1356 private void enterProperty() 1357 { 1358 String name = getValidatedAttribute("name", PROPERTY_NAME_PATTERN, "invalid-property-name"); 1359 String type = getAttribute("type"); 1360 1361 String persistence = null; 1362 1363 if (_dtd40) 1364 persistence = getAttribute("persist"); 1365 else 1366 persistence = getBooleanAttribute("persistent", false) ? "session" : null; 1367 1368 String initialValue = getAttribute("initial-value"); 1369 1370 IPropertySpecification ps = _factory.createPropertySpecification(); 1371 ps.setName(name); 1372 1373 if (HiveMind.isNonBlank(type)) 1374 ps.setType(type); 1375 1376 ps.setPersistence(persistence); 1377 ps.setInitialValue(initialValue); 1378 1379 IComponentSpecification cs = (IComponentSpecification) peekObject(); 1380 cs.addPropertySpecification(ps); 1381 1382 push(_elementName, ps, STATE_PROPERTY, false); 1383 } 1384 1385 /** 1386 * @since 4.0 1387 */ 1388 1389 private void enterInject() 1390 { 1391 String property = getValidatedAttribute( 1392 "property", 1393 PROPERTY_NAME_PATTERN, 1394 "invalid-property-name"); 1395 String type = getAttribute("type"); 1396 String objectReference = getAttribute("object"); 1397 1398 InjectSpecification spec = _factory.createInjectSpecification(); 1399 1400 spec.setProperty(property); 1401 spec.setType(type); 1402 spec.setObject(objectReference); 1403 IComponentSpecification cs = (IComponentSpecification) peekObject(); 1404 1405 cs.addInjectSpecification(spec); 1406 1407 push(_elementName, spec, STATE_NO_CONTENT); 1408 } 1409 1410 private void enterReservedParameter() 1411 { 1412 String name = getAttribute("name"); 1413 IComponentSpecification cs = (IComponentSpecification) peekObject(); 1414 1415 cs.addReservedParameterName(name); 1416 1417 push(_elementName, null, STATE_NO_CONTENT); 1418 } 1419 1420 private void enterService30() 1421 { 1422 _errorHandler.error(_log, ParseMessages.serviceElementNotSupported(), getLocation(), null); 1423 1424 push(_elementName, null, STATE_NO_CONTENT); 1425 } 1426 1427 private void enterSetMessage30() 1428 { 1429 String name = getAttribute("name"); 1430 String key = getAttribute("key"); 1431 1432 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource); 1433 1434 bi.setPropertyName(name); 1435 bi.setBindingReference(BindingConstants.MESSAGE_PREFIX + ":" + key); 1436 bi.setLocation(getLocation()); 1437 1438 IBeanSpecification bs = (IBeanSpecification) peekObject(); 1439 1440 bs.addInitializer(bi); 1441 1442 push(_elementName, null, STATE_NO_CONTENT); 1443 } 1444 1445 private void enterSet() 1446 { 1447 String name = getAttribute("name"); 1448 String reference = getAttribute("value"); 1449 1450 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource); 1451 1452 bi.setPropertyName(name); 1453 1454 IBeanSpecification bs = (IBeanSpecification) peekObject(); 1455 1456 push(_elementName, new BeanSetPropertySetter(bs, bi, null, reference), STATE_SET, false); 1457 } 1458 1459 private void enterSetProperty30() 1460 { 1461 String name = getAttribute("name"); 1462 String expression = getAttribute("expression"); 1463 1464 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource); 1465 1466 bi.setPropertyName(name); 1467 1468 IBeanSpecification bs = (IBeanSpecification) peekObject(); 1469 1470 push(_elementName, new BeanSetPropertySetter(bs, bi, BindingConstants.OGNL_PREFIX + ":", 1471 expression), STATE_SET, false); 1472 } 1473 1474 private void enterStaticBinding30() 1475 { 1476 String name = getAttribute("name"); 1477 String expression = getAttribute("value"); 1478 1479 IContainedComponent cc = (IContainedComponent) peekObject(); 1480 1481 BindingSetter bs = new BindingSetter(cc, name, expression); 1482 1483 push(_elementName, bs, STATE_STATIC_BINDING, false); 1484 } 1485 1486 private void expectElement(String elementName) 1487 { 1488 if (_elementName.equals(elementName)) 1489 return; 1490 1491 throw new DocumentParseException(ParseMessages.incorrectDocumentType( 1492 _elementName, 1493 elementName), getLocation(), null); 1494 1495 } 1496 1497 private String getAttribute(String name) 1498 { 1499 return (String) _attributes.get(name); 1500 } 1501 1502 private boolean getBooleanAttribute(String name, boolean defaultValue) 1503 { 1504 String value = getAttribute(name); 1505 1506 if (value == null) 1507 return defaultValue; 1508 1509 Boolean b = (Boolean) _conversionMap.get(value); 1510 1511 return b.booleanValue(); 1512 } 1513 1514 private Object getConvertedAttribute(String name, Object defaultValue) 1515 { 1516 String key = getAttribute(name); 1517 1518 if (key == null) 1519 return defaultValue; 1520 1521 return _conversionMap.get(key); 1522 } 1523 1524 private InputSource getDTDInputSource(String name) 1525 { 1526 InputStream stream = getClass().getResourceAsStream(name); 1527 1528 return new InputSource(stream); 1529 } 1530 1531 private String getExtendedValue(String attributeValue, String attributeName, boolean required) 1532 { 1533 String contentValue = peekContent(); 1534 1535 boolean asAttribute = HiveMind.isNonBlank(attributeValue); 1536 boolean asContent = HiveMind.isNonBlank(contentValue); 1537 1538 if (asAttribute && asContent) 1539 { 1540 throw new DocumentParseException(ParseMessages.noAttributeAndBody( 1541 attributeName, 1542 _elementName), getLocation(), null); 1543 } 1544 1545 if (required && !(asAttribute || asContent)) 1546 { 1547 throw new DocumentParseException(ParseMessages.requiredExtendedAttribute( 1548 _elementName, 1549 attributeName), getLocation(), null); 1550 } 1551 1552 if (asAttribute) 1553 return attributeValue; 1554 1555 return contentValue; 1556 } 1557 1558 private String getValidatedAttribute(String name, String pattern, String errorKey) 1559 { 1560 String value = getAttribute(name); 1561 1562 if (value == null) 1563 return null; 1564 1565 if (_matcher.matches(pattern, value)) 1566 return value; 1567 1568 throw new InvalidStringException(ParseMessages.invalidAttribute(errorKey, value), value, 1569 getLocation()); 1570 } 1571 1572 protected void initializeParser(Resource resource, int startState) 1573 { 1574 super.initializeParser(resource, startState); 1575 1576 _rootObject = null; 1577 _attributes = new HashMap(); 1578 } 1579 1580 public IApplicationSpecification parseApplicationSpecification(Resource resource) 1581 { 1582 initializeParser(resource, STATE_APPLICATION_SPECIFICATION_INITIAL); 1583 1584 try 1585 { 1586 parseDocument(); 1587 1588 return (IApplicationSpecification) _rootObject; 1589 } 1590 finally 1591 { 1592 resetParser(); 1593 } 1594 } 1595 1596 public IComponentSpecification parseComponentSpecification(Resource resource) 1597 { 1598 initializeParser(resource, STATE_COMPONENT_SPECIFICATION_INITIAL); 1599 1600 try 1601 { 1602 parseDocument(); 1603 1604 return (IComponentSpecification) _rootObject; 1605 } 1606 finally 1607 { 1608 resetParser(); 1609 } 1610 } 1611 1612 private void parseDocument() 1613 { 1614 InputStream stream = null; 1615 1616 Resource resource = getResource(); 1617 1618 boolean success = false; 1619 1620 try 1621 { 1622 if (_parser == null) 1623 _parser = _parserFactory.newSAXParser(); 1624 1625 URL resourceURL = resource.getResourceURL(); 1626 1627 if (resourceURL == null) 1628 throw new DocumentParseException(ParseMessages.missingResource(resource), resource); 1629 1630 InputStream rawStream = resourceURL.openStream(); 1631 stream = new BufferedInputStream(rawStream); 1632 1633 _parser.parse(stream, this, resourceURL.toExternalForm()); 1634 1635 stream.close(); 1636 stream = null; 1637 1638 success = true; 1639 } 1640 catch (SAXParseException ex) 1641 { 1642 _parser = null; 1643 1644 Location location = new LocationImpl(resource, ex.getLineNumber(), ex.getColumnNumber()); 1645 1646 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex), 1647 location, ex); 1648 } 1649 catch (Exception ex) 1650 { 1651 _parser = null; 1652 1653 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex), 1654 resource, ex); 1655 } 1656 finally 1657 { 1658 if (!success) 1659 _parser = null; 1660 1661 close(stream); 1662 } 1663 } 1664 1665 public ILibrarySpecification parseLibrarySpecification(Resource resource) 1666 { 1667 initializeParser(resource, STATE_LIBRARY_SPECIFICATION_INITIAL); 1668 1669 try 1670 { 1671 parseDocument(); 1672 1673 return (ILibrarySpecification) _rootObject; 1674 } 1675 finally 1676 { 1677 resetParser(); 1678 } 1679 } 1680 1681 public IComponentSpecification parsePageSpecification(Resource resource) 1682 { 1683 initializeParser(resource, STATE_PAGE_SPECIFICATION_INITIAL); 1684 1685 try 1686 { 1687 parseDocument(); 1688 1689 return (IComponentSpecification) _rootObject; 1690 } 1691 finally 1692 { 1693 resetParser(); 1694 } 1695 } 1696 1697 protected String peekContent() 1698 { 1699 String content = super.peekContent(); 1700 1701 if (content == null) 1702 return null; 1703 1704 return content.trim(); 1705 } 1706 1707 protected void resetParser() 1708 { 1709 _rootObject = null; 1710 _dtd40 = false; 1711 1712 _attributes.clear(); 1713 } 1714 1715 /** 1716 * Resolved an external entity, which is assumed to be the doctype. Might need a check to ensure 1717 * that specs without a doctype fail. 1718 */ 1719 public InputSource resolveEntity(String publicId, String systemId) throws SAXException 1720 { 1721 if (TAPESTRY_DTD_4_0_PUBLIC_ID.equals(publicId)) 1722 { 1723 _dtd40 = true; 1724 return getDTDInputSource("Tapestry_4_0.dtd"); 1725 } 1726 1727 if (TAPESTRY_DTD_4_1_PUBLIC_ID.equals(publicId)) 1728 { 1729 _dtd40 = true; 1730 return getDTDInputSource("Tapestry_4_1.dtd"); 1731 } 1732 1733 if (TAPESTRY_DTD_3_0_PUBLIC_ID.equals(publicId)) 1734 return getDTDInputSource("Tapestry_3_0.dtd"); 1735 1736 throw new DocumentParseException(ParseMessages.unknownPublicId(getResource(), publicId), 1737 new LocationImpl(getResource()), null); 1738 } 1739 1740 /** @since 4.0 */ 1741 public void setBindingSource(BindingSource bindingSource) 1742 { 1743 _bindingSource = bindingSource; 1744 } 1745 1746 /** @since 4.0 */ 1747 public void setValueConverter(ValueConverter valueConverter) 1748 { 1749 _valueConverter = valueConverter; 1750 } 1751 }