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