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