Clover coverage report - Code Coverage for tapestry release 4.0-beta-10
Coverage timestamp: Sat Oct 8 2005 19:08:05 EDT
file stats: LOC: 1,771   Methods: 75
NCLOC: 1,097   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SpecificationParser.java 95.7% 99% 100% 98.5%
coverage coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.tapestry.parse;
 16   
 17    import java.io.BufferedInputStream;
 18    import java.io.IOException;
 19    import java.io.InputStream;
 20    import java.net.URL;
 21    import java.util.HashMap;
 22    import java.util.Iterator;
 23    import java.util.Map;
 24   
 25    import javax.xml.parsers.SAXParser;
 26    import javax.xml.parsers.SAXParserFactory;
 27   
 28    import org.apache.commons.logging.Log;
 29    import org.apache.commons.logging.LogFactory;
 30    import org.apache.hivemind.ClassResolver;
 31    import org.apache.hivemind.ErrorHandler;
 32    import org.apache.hivemind.HiveMind;
 33    import org.apache.hivemind.Location;
 34    import org.apache.hivemind.Resource;
 35    import org.apache.hivemind.impl.DefaultErrorHandler;
 36    import org.apache.hivemind.impl.LocationImpl;
 37    import org.apache.hivemind.parse.AbstractParser;
 38    import org.apache.tapestry.INamespace;
 39    import org.apache.tapestry.Tapestry;
 40    import org.apache.tapestry.bean.BindingBeanInitializer;
 41    import org.apache.tapestry.bean.LightweightBeanInitializer;
 42    import org.apache.tapestry.binding.BindingConstants;
 43    import org.apache.tapestry.binding.BindingSource;
 44    import org.apache.tapestry.coerce.ValueConverter;
 45    import org.apache.tapestry.spec.BeanLifecycle;
 46    import org.apache.tapestry.spec.BindingType;
 47    import org.apache.tapestry.spec.IApplicationSpecification;
 48    import org.apache.tapestry.spec.IAssetSpecification;
 49    import org.apache.tapestry.spec.IBeanSpecification;
 50    import org.apache.tapestry.spec.IBindingSpecification;
 51    import org.apache.tapestry.spec.IComponentSpecification;
 52    import org.apache.tapestry.spec.IContainedComponent;
 53    import org.apache.tapestry.spec.IExtensionSpecification;
 54    import org.apache.tapestry.spec.ILibrarySpecification;
 55    import org.apache.tapestry.spec.IParameterSpecification;
 56    import org.apache.tapestry.spec.IPropertySpecification;
 57    import org.apache.tapestry.spec.InjectSpecification;
 58    import org.apache.tapestry.spec.SpecFactory;
 59    import org.apache.tapestry.util.IPropertyHolder;
 60    import org.apache.tapestry.util.RegexpMatcher;
 61    import org.apache.tapestry.util.xml.DocumentParseException;
 62    import org.apache.tapestry.util.xml.InvalidStringException;
 63    import org.xml.sax.InputSource;
 64    import org.xml.sax.SAXException;
 65    import org.xml.sax.SAXParseException;
 66   
 67    /**
 68    * Parses the different types of Tapestry specifications.
 69    * <p>
 70    * Not threadsafe; it is the callers responsibility to ensure thread safety.
 71    *
 72    * @author Howard Lewis Ship
 73    */
 74    public class SpecificationParser extends AbstractParser implements ISpecificationParser
 75    {
 76    private static final String IDENTIFIER_PATTERN = "_?[a-zA-Z]\\w*";
 77   
 78    private static final String EXTENDED_IDENTIFIER_PATTERN = "_?[a-zA-Z](\\w|-)*";
 79   
 80    /**
 81    * Perl5 pattern for asset names. Letter, followed by letter, number or underscore. Also allows
 82    * the special "$template" value.
 83    *
 84    * @since 2.2
 85    */
 86   
 87    public static final String ASSET_NAME_PATTERN = "(\\$template)|("
 88    + Tapestry.SIMPLE_PROPERTY_NAME_PATTERN + ")";
 89   
 90    /**
 91    * Perl5 pattern for helper bean names. Letter, followed by letter, number or underscore.
 92    *
 93    * @since 2.2
 94    */
 95   
 96    public static final String BEAN_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN;
 97   
 98    /**
 99    * 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 &lt;component&gt;
 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 &lt;page&gt; 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 &lt;service&gt; 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  104 CONVERSION_MAP.put("true", Boolean.TRUE);
 328  104 CONVERSION_MAP.put("t", Boolean.TRUE);
 329  104 CONVERSION_MAP.put("1", Boolean.TRUE);
 330  104 CONVERSION_MAP.put("y", Boolean.TRUE);
 331  104 CONVERSION_MAP.put("yes", Boolean.TRUE);
 332  104 CONVERSION_MAP.put("on", Boolean.TRUE);
 333  104 CONVERSION_MAP.put("aye", Boolean.TRUE);
 334   
 335  104 CONVERSION_MAP.put("false", Boolean.FALSE);
 336  104 CONVERSION_MAP.put("f", Boolean.FALSE);
 337  104 CONVERSION_MAP.put("0", Boolean.FALSE);
 338  104 CONVERSION_MAP.put("off", Boolean.FALSE);
 339  104 CONVERSION_MAP.put("no", Boolean.FALSE);
 340  104 CONVERSION_MAP.put("n", Boolean.FALSE);
 341  104 CONVERSION_MAP.put("nay", Boolean.FALSE);
 342   
 343  104 CONVERSION_MAP.put("none", BeanLifecycle.NONE);
 344  104 CONVERSION_MAP.put("request", BeanLifecycle.REQUEST);
 345  104 CONVERSION_MAP.put("page", BeanLifecycle.PAGE);
 346  104 CONVERSION_MAP.put("render", BeanLifecycle.RENDER);
 347   
 348  104 _parserFactory.setNamespaceAware(false);
 349  104 _parserFactory.setValidating(true);
 350    }
 351   
 352    /**
 353    * This constructor is a convienience used by some tests.
 354    */
 355  62 public SpecificationParser(ClassResolver resolver)
 356    {
 357  62 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  62 public SpecificationParser(ClassResolver resolver, SpecFactory factory)
 366    {
 367  62 this(new DefaultErrorHandler(), LogFactory.getLog(SpecificationParser.class), resolver,
 368    factory);
 369    }
 370   
 371    /**
 372    * The full constructor, used within Tapestry.
 373    */
 374  104 public SpecificationParser(ErrorHandler errorHandler, Log log, ClassResolver resolver,
 375    SpecFactory factory)
 376    {
 377  104 _errorHandler = errorHandler;
 378  104 _log = log;
 379  104 _resolver = resolver;
 380  104 _factory = factory;
 381    }
 382   
 383  6519 protected void begin(String elementName, Map attributes)
 384    {
 385  6519 _elementName = elementName;
 386  6519 _attributes = attributes;
 387   
 388  6519 switch (getState())
 389    {
 390  327 case STATE_COMPONENT_SPECIFICATION_INITIAL:
 391   
 392  327 beginComponentSpecificationInitial();
 393  326 break;
 394   
 395  103 case STATE_PAGE_SPECIFICATION_INITIAL:
 396   
 397  103 beginPageSpecificationInitial();
 398  103 break;
 399   
 400  41 case STATE_APPLICATION_SPECIFICATION_INITIAL:
 401   
 402  41 beginApplicationSpecificationInitial();
 403  41 break;
 404   
 405  65 case STATE_LIBRARY_SPECIFICATION_INITIAL:
 406   
 407  65 beginLibrarySpecificationInitial();
 408  65 break;
 409   
 410  2114 case STATE_COMPONENT_SPECIFICATION:
 411   
 412  2114 beginComponentSpecification();
 413  2108 break;
 414   
 415  181 case STATE_PAGE_SPECIFICATION:
 416   
 417  181 beginPageSpecification();
 418  181 break;
 419   
 420  746 case STATE_ALLOW_DESCRIPTION:
 421   
 422  746 beginAllowDescription();
 423  746 break;
 424   
 425  3 case STATE_ALLOW_PROPERTY:
 426   
 427  3 allowMetaData();
 428  3 break;
 429   
 430  16 case STATE_BEAN:
 431   
 432  16 beginBean();
 433  16 break;
 434   
 435  346 case STATE_COMPONENT:
 436   
 437  346 beginComponent();
 438  346 break;
 439   
 440  2544 case STATE_LIBRARY_SPECIFICATION:
 441   
 442  2544 beginLibrarySpecification();
 443  2539 break;
 444   
 445  33 case STATE_EXTENSION:
 446   
 447  33 beginExtension();
 448  33 break;
 449   
 450  0 default:
 451   
 452  0 unexpectedElement(_elementName);
 453    }
 454    }
 455   
 456    /**
 457    * Special state for a number of specification types that can support the &lt;description&gt;
 458    * element.
 459    */
 460   
 461  746 private void beginAllowDescription()
 462    {
 463  746 if (_elementName.equals("description"))
 464    {
 465  746 enterDescription();
 466  746 return;
 467    }
 468   
 469  0 unexpectedElement(_elementName);
 470    }
 471   
 472    /**
 473    * Special state for a number of elements that can support the nested &lt;meta&gt; meta data
 474    * element (&lt;property&gt; in 3.0 DTD).
 475    */
 476   
 477  58 private void allowMetaData()
 478    {
 479  58 if (_DTD_4_0)
 480    {
 481  11 if (_elementName.equals("meta"))
 482    {
 483  11 enterMeta();
 484  11 return;
 485    }
 486    }
 487  47 else if (_elementName.equals("property"))
 488    {
 489  47 enterProperty_3_0();
 490  47 return;
 491    }
 492   
 493  0 unexpectedElement(_elementName);
 494    }
 495   
 496  41 private void beginApplicationSpecificationInitial()
 497    {
 498  41 expectElement("application");
 499   
 500  41 String name = getAttribute("name");
 501  41 String engineClassName = getAttribute("engine-class");
 502   
 503  41 IApplicationSpecification as = _factory.createApplicationSpecification();
 504   
 505  41 as.setName(name);
 506   
 507  41 if (HiveMind.isNonBlank(engineClassName))
 508  5 as.setEngineClassName(engineClassName);
 509   
 510  41 _rootObject = as;
 511   
 512  41 push(_elementName, as, STATE_LIBRARY_SPECIFICATION);
 513    }
 514   
 515  16 private void beginBean()
 516    {
 517  16 if (_elementName.equals("set"))
 518    {
 519  3 enterSet();
 520  3 return;
 521    }
 522   
 523  13 if (_elementName.equals("set-property"))
 524    {
 525  5 enterSetProperty_3_0();
 526  5 return;
 527    }
 528   
 529  8 if (_elementName.equals("set-message-property"))
 530    {
 531  1 enterSetMessage_3_0();
 532  1 return;
 533    }
 534   
 535  7 if (_elementName.equals("description"))
 536    {
 537  2 enterDescription();
 538  2 return;
 539    }
 540   
 541  5 allowMetaData();
 542    }
 543   
 544  346 private void beginComponent()
 545    {
 546    // <binding> has changed between 3.0 and 4.0
 547   
 548  346 if (_elementName.equals("binding"))
 549    {
 550  303 enterBinding();
 551  303 return;
 552    }
 553   
 554  43 if (_elementName.equals("static-binding"))
 555    {
 556  32 enterStaticBinding_3_0();
 557  32 return;
 558    }
 559   
 560  11 if (_elementName.equals("message-binding"))
 561    {
 562  4 enterMessageBinding_3_0();
 563  4 return;
 564    }
 565   
 566  7 if (_elementName.equals("inherited-binding"))
 567    {
 568  2 enterInheritedBinding_3_0();
 569  2 return;
 570    }
 571   
 572  5 if (_elementName.equals("listener-binding"))
 573    {
 574  1 enterListenerBinding();
 575  1 return;
 576    }
 577   
 578  4 allowMetaData();
 579    }
 580   
 581  2114 private void beginComponentSpecification()
 582    {
 583  2114 if (_elementName.equals("reserved-parameter"))
 584    {
 585  110 enterReservedParameter();
 586  110 return;
 587    }
 588   
 589  2004 if (_elementName.equals("parameter"))
 590    {
 591  1070 enterParameter();
 592  1069 return;
 593    }
 594   
 595    // The remainder are common to both <component-specification> and
 596    // <page-specification>
 597   
 598  934 beginPageSpecification();
 599    }
 600   
 601  327 private void beginComponentSpecificationInitial()
 602    {
 603  327 expectElement("component-specification");
 604   
 605  326 IComponentSpecification cs = _factory.createComponentSpecification();
 606   
 607  326 cs.setAllowBody(getBooleanAttribute("allow-body", true));
 608  326 cs.setAllowInformalParameters(getBooleanAttribute("allow-informal-parameters", true));
 609  326 cs.setDeprecated(getBooleanAttribute("deprecated", false));
 610   
 611  326 String className = getAttribute("class");
 612   
 613  326 if (className != null)
 614  292 cs.setComponentClassName(className);
 615   
 616  326 cs.setSpecificationLocation(getResource());
 617   
 618  326 _rootObject = cs;
 619   
 620  326 push(_elementName, cs, STATE_COMPONENT_SPECIFICATION);
 621    }
 622   
 623  33 private void beginExtension()
 624    {
 625  33 if (_elementName.equals("configure"))
 626    {
 627  30 enterConfigure();
 628  30 return;
 629    }
 630   
 631  3 allowMetaData();
 632    }
 633   
 634  2544 private void beginLibrarySpecification()
 635    {
 636  2544 if (_elementName.equals("description"))
 637    {
 638  1 enterDescription();
 639  1 return;
 640    }
 641   
 642  2543 if (_elementName.equals("page"))
 643    {
 644  495 enterPage();
 645  494 return;
 646    }
 647   
 648  2048 if (_elementName.equals("component-type"))
 649    {
 650  1994 enterComponentType();
 651  1993 return;
 652    }
 653   
 654    // Holdover from the 3.0 DTD, now ignored.
 655   
 656  54 if (_elementName.equals("service"))
 657    {
 658  1 enterService_3_0();
 659  1 return;
 660    }
 661   
 662  53 if (_elementName.equals("library"))
 663    {
 664  23 enterLibrary();
 665  21 return;
 666    }
 667   
 668  30 if (_elementName.equals("extension"))
 669    {
 670  11 enterExtension();
 671  10 return;
 672    }
 673   
 674  19 allowMetaData();
 675    }
 676   
 677  65 private void beginLibrarySpecificationInitial()
 678    {
 679  65 expectElement("library-specification");
 680   
 681  65 ILibrarySpecification ls = _factory.createLibrarySpecification();
 682   
 683  65 _rootObject = ls;
 684   
 685  65 push(_elementName, ls, STATE_LIBRARY_SPECIFICATION);
 686    }
 687   
 688  1115 private void beginPageSpecification()
 689    {
 690  1115 if (_elementName.equals("component"))
 691    {
 692  263 enterComponent();
 693  259 return;
 694    }
 695   
 696  852 if (_elementName.equals("bean"))
 697    {
 698  33 enterBean();
 699  33 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  819 if (_elementName.equals("property-specification")
 706    || (_DTD_4_0 && _elementName.equals("property")))
 707    {
 708  121 enterProperty();
 709  121 return;
 710    }
 711   
 712  698 if (_elementName.equals("inject"))
 713    {
 714  359 enterInject();
 715  359 return;
 716    }
 717   
 718    // <asset> is new in 4.0
 719   
 720  339 if (_elementName.equals("asset"))
 721    {
 722  26 enterAsset();
 723  26 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  313 if (_elementName.equals("context-asset"))
 731    {
 732  7 enterContextAsset_3_0();
 733  7 return;
 734    }
 735   
 736  306 if (_elementName.equals("private-asset"))
 737    {
 738  8 enterPrivateAsset_3_0();
 739  7 return;
 740    }
 741   
 742  298 if (_elementName.equals("external-asset"))
 743    {
 744  3 enterExternalAsset_3_0();
 745  3 return;
 746   
 747    }
 748   
 749  295 if (_elementName.equals("description"))
 750    {
 751  271 enterDescription();
 752  271 return;
 753    }
 754   
 755  24 allowMetaData();
 756    }
 757   
 758  103 private void beginPageSpecificationInitial()
 759    {
 760  103 expectElement("page-specification");
 761   
 762  103 IComponentSpecification cs = _factory.createComponentSpecification();
 763   
 764  103 String className = getAttribute("class");
 765   
 766  103 if (className != null)
 767  72 cs.setComponentClassName(className);
 768   
 769  103 cs.setSpecificationLocation(getResource());
 770  103 cs.setPageSpecification(true);
 771   
 772  103 _rootObject = cs;
 773   
 774  103 push(_elementName, cs, STATE_PAGE_SPECIFICATION);
 775    }
 776   
 777    /**
 778    * Close a stream (if not null), ignoring any errors.
 779    */
 780  538 private void close(InputStream stream)
 781    {
 782  538 try
 783    {
 784  538 if (stream != null)
 785  16 stream.close();
 786    }
 787    catch (IOException ex)
 788    {
 789    // ignore
 790    }
 791    }
 792   
 793  3 private void copyBindings(String sourceComponentId, IComponentSpecification cs,
 794    IContainedComponent target)
 795    {
 796  3 IContainedComponent source = cs.getComponent(sourceComponentId);
 797  3 if (source == null)
 798  1 throw new DocumentParseException(ParseMessages.unableToCopy(sourceComponentId),
 799    getLocation());
 800   
 801  2 Iterator i = source.getBindingNames().iterator();
 802  2 while (i.hasNext())
 803    {
 804  4 String bindingName = (String) i.next();
 805  4 IBindingSpecification binding = source.getBinding(bindingName);
 806  4 target.setBinding(bindingName, binding);
 807    }
 808   
 809  2 target.setType(source.getType());
 810    }
 811   
 812  6492 protected void end(String elementName)
 813    {
 814  6492 _elementName = elementName;
 815   
 816  6492 switch (getState())
 817    {
 818  1020 case STATE_DESCRIPTION:
 819   
 820  1020 endDescription();
 821  1020 break;
 822   
 823  58 case STATE_META:
 824   
 825  58 endProperty();
 826  58 break;
 827   
 828  8 case STATE_SET:
 829   
 830  8 endSetProperty();
 831  8 break;
 832   
 833  29 case STATE_BINDING_3_0:
 834   
 835  29 endBinding_3_0();
 836  28 break;
 837   
 838  274 case STATE_BINDING:
 839   
 840  274 endBinding();
 841  274 break;
 842   
 843  32 case STATE_STATIC_BINDING:
 844   
 845  32 endStaticBinding();
 846  31 break;
 847   
 848  121 case STATE_PROPERTY:
 849   
 850  121 endPropertySpecification();
 851  121 break;
 852   
 853  101 case STATE_LIBRARY_SPECIFICATION:
 854   
 855  101 endLibrarySpecification();
 856  101 break;
 857   
 858  30 case STATE_CONFIGURE:
 859   
 860  30 endConfigure();
 861  30 break;
 862   
 863  4819 default:
 864  4819 break;
 865    }
 866   
 867    // Pop the top element of the stack and continue processing from there.
 868   
 869  6490 pop();
 870    }
 871   
 872  29 private void endBinding_3_0()
 873    {
 874  29 BindingSetter bs = (BindingSetter) peekObject();
 875   
 876  29 String expression = getExtendedValue(bs.getValue(), "expression", true);
 877   
 878  28 IBindingSpecification spec = _factory.createBindingSpecification();
 879   
 880  28 spec.setType(BindingType.PREFIXED);
 881  28 spec.setValue(BindingConstants.OGNL_PREFIX + ":" + expression);
 882   
 883  28 bs.apply(spec);
 884    }
 885   
 886  30 private void endConfigure()
 887    {
 888  30 ExtensionConfigurationSetter setter = (ExtensionConfigurationSetter) peekObject();
 889   
 890  30 String finalValue = getExtendedValue(setter.getValue(), "value", true);
 891   
 892  30 setter.apply(finalValue);
 893    }
 894   
 895  1020 private void endDescription()
 896    {
 897  1020 DescriptionSetter setter = (DescriptionSetter) peekObject();
 898   
 899  1020 String description = peekContent();
 900   
 901  1020 setter.apply(description);
 902    }
 903   
 904  101 private void endLibrarySpecification()
 905    {
 906  101 ILibrarySpecification spec = (ILibrarySpecification) peekObject();
 907   
 908  101 spec.setSpecificationLocation(getResource());
 909   
 910  101 spec.instantiateImmediateExtensions();
 911    }
 912   
 913  58 private void endProperty()
 914    {
 915  58 PropertyValueSetter pvs = (PropertyValueSetter) peekObject();
 916   
 917  58 String finalValue = getExtendedValue(pvs.getPropertyValue(), "value", true);
 918   
 919  58 pvs.applyValue(finalValue);
 920    }
 921   
 922  121 private void endPropertySpecification()
 923    {
 924  121 IPropertySpecification ps = (IPropertySpecification) peekObject();
 925   
 926  121 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  121 if (initialValue != null && !_DTD_4_0)
 932  5 initialValue = BindingConstants.OGNL_PREFIX + ":" + initialValue;
 933   
 934  121 ps.setInitialValue(initialValue);
 935    }
 936   
 937  8 private void endSetProperty()
 938    {
 939  8 BeanSetPropertySetter bs = (BeanSetPropertySetter) peekObject();
 940   
 941  8 String finalValue = getExtendedValue(bs.getBindingReference(), "expression", true);
 942   
 943  8 bs.applyBindingReference(finalValue);
 944    }
 945   
 946  32 private void endStaticBinding()
 947    {
 948  32 BindingSetter bs = (BindingSetter) peekObject();
 949   
 950  32 String literalValue = getExtendedValue(bs.getValue(), "value", true);
 951   
 952  31 IBindingSpecification spec = _factory.createBindingSpecification();
 953   
 954  31 spec.setType(BindingType.PREFIXED);
 955  31 spec.setValue(BindingConstants.LITERAL_PREFIX + ":" + literalValue);
 956   
 957  31 bs.apply(spec);
 958    }
 959   
 960  44 private void enterAsset(String pathAttributeName, String prefix)
 961    {
 962  44 String name = getValidatedAttribute("name", ASSET_NAME_PATTERN, "invalid-asset-name");
 963  43 String path = getAttribute(pathAttributeName);
 964  43 String propertyName = getValidatedAttribute(
 965    "property",
 966    PROPERTY_NAME_PATTERN,
 967    "invalid-property-name");
 968   
 969  43 IAssetSpecification ia = _factory.createAssetSpecification();
 970   
 971  43 ia.setPath(prefix == null ? path : prefix + path);
 972  43 ia.setPropertyName(propertyName);
 973   
 974  43 IComponentSpecification cs = (IComponentSpecification) peekObject();
 975   
 976  43 cs.addAsset(name, ia);
 977   
 978  43 push(_elementName, ia, STATE_ALLOW_PROPERTY);
 979    }
 980   
 981  33 private void enterBean()
 982    {
 983  33 String name = getValidatedAttribute("name", BEAN_NAME_PATTERN, "invalid-bean-name");
 984   
 985  33 String classAttribute = getAttribute("class");
 986   
 987    // Look for the lightweight initialization
 988   
 989  33 int commax = classAttribute.indexOf(',');
 990   
 991  33 String className = commax < 0 ? classAttribute : classAttribute.substring(0, commax);
 992   
 993  33 BeanLifecycle lifecycle = (BeanLifecycle) getConvertedAttribute(
 994    "lifecycle",
 995    BeanLifecycle.REQUEST);
 996  33 String propertyName = getValidatedAttribute(
 997    "property",
 998    PROPERTY_NAME_PATTERN,
 999    "invalid-property-name");
 1000   
 1001  33 IBeanSpecification bs = _factory.createBeanSpecification();
 1002   
 1003  33 bs.setClassName(className);
 1004  33 bs.setLifecycle(lifecycle);
 1005  33 bs.setPropertyName(propertyName);
 1006   
 1007  33 if (commax > 0)
 1008    {
 1009  1 String initializer = classAttribute.substring(commax + 1);
 1010  1 bs.addInitializer(new LightweightBeanInitializer(initializer));
 1011    }
 1012   
 1013  33 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1014   
 1015  33 cs.addBeanSpecification(name, bs);
 1016   
 1017  33 push(_elementName, bs, STATE_BEAN);
 1018    }
 1019   
 1020  303 private void enterBinding()
 1021    {
 1022  303 if (!_DTD_4_0)
 1023    {
 1024  29 enterBinding_3_0();
 1025  29 return;
 1026    }
 1027   
 1028    // 4.0 stuff
 1029   
 1030  274 String name = getValidatedAttribute(
 1031    "name",
 1032    PARAMETER_NAME_PATTERN,
 1033    "invalid-parameter-name");
 1034  274 String value = getAttribute("value");
 1035   
 1036  274 IContainedComponent cc = (IContainedComponent) peekObject();
 1037   
 1038  274 BindingSetter bs = new BindingSetter(cc, name, value);
 1039   
 1040  274 push(_elementName, bs, STATE_BINDING, false);
 1041    }
 1042   
 1043  274 private void endBinding()
 1044    {
 1045  274 BindingSetter bs = (BindingSetter) peekObject();
 1046   
 1047  274 String value = getExtendedValue(bs.getValue(), "value", true);
 1048   
 1049  274 IBindingSpecification spec = _factory.createBindingSpecification();
 1050   
 1051  274 spec.setType(BindingType.PREFIXED);
 1052  274 spec.setValue(value);
 1053   
 1054  274 bs.apply(spec);
 1055    }
 1056   
 1057    /**
 1058    * Handles a binding in a 3.0 DTD.
 1059    */
 1060   
 1061  29 private void enterBinding_3_0()
 1062    {
 1063  29 String name = getAttribute("name");
 1064  29 String expression = getAttribute("expression");
 1065   
 1066  29 IContainedComponent cc = (IContainedComponent) peekObject();
 1067   
 1068  29 BindingSetter bs = new BindingSetter(cc, name, expression);
 1069   
 1070  29 push(_elementName, bs, STATE_BINDING_3_0, false);
 1071    }
 1072   
 1073  263 private void enterComponent()
 1074    {
 1075  263 String id = getValidatedAttribute("id", COMPONENT_ID_PATTERN, "invalid-component-id");
 1076   
 1077  262 String type = getValidatedAttribute(
 1078    "type",
 1079    COMPONENT_TYPE_PATTERN,
 1080    "invalid-component-type");
 1081  262 String copyOf = getAttribute("copy-of");
 1082  262 boolean inherit = getBooleanAttribute("inherit-informal-parameters", false);
 1083  262 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  262 boolean hasCopyOf = HiveMind.isNonBlank(copyOf);
 1091   
 1092  262 if (hasCopyOf)
 1093    {
 1094  4 if (HiveMind.isNonBlank(type))
 1095  1 throw new DocumentParseException(ParseMessages.bothTypeAndCopyOf(id), getLocation());
 1096    }
 1097    else
 1098    {
 1099  258 if (HiveMind.isBlank(type))
 1100  1 throw new DocumentParseException(ParseMessages.missingTypeOrCopyOf(id),
 1101    getLocation());
 1102    }
 1103   
 1104  260 IContainedComponent cc = _factory.createContainedComponent();
 1105  260 cc.setType(type);
 1106  260 cc.setCopyOf(copyOf);
 1107  260 cc.setInheritInformalParameters(inherit);
 1108  260 cc.setPropertyName(propertyName);
 1109   
 1110  260 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1111   
 1112  260 cs.addComponent(id, cc);
 1113   
 1114  260 if (hasCopyOf)
 1115  3 copyBindings(copyOf, cs, cc);
 1116   
 1117  259 push(_elementName, cc, STATE_COMPONENT);
 1118    }
 1119   
 1120  1994 private void enterComponentType()
 1121    {
 1122  1994 String type = getValidatedAttribute(
 1123    "type",
 1124    COMPONENT_ALIAS_PATTERN,
 1125    "invalid-component-type");
 1126  1993 String path = getAttribute("specification-path");
 1127   
 1128  1993 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
 1129   
 1130  1993 ls.setComponentSpecificationPath(type, path);
 1131   
 1132  1993 push(_elementName, null, STATE_NO_CONTENT);
 1133    }
 1134   
 1135  30 private void enterConfigure()
 1136    {
 1137  30 String attributeName = _DTD_4_0 ? "property" : "property-name";
 1138   
 1139  30 String propertyName = getValidatedAttribute(
 1140    attributeName,
 1141    PROPERTY_NAME_PATTERN,
 1142    "invalid-property-name");
 1143   
 1144  30 String value = getAttribute("value");
 1145   
 1146  30 IExtensionSpecification es = (IExtensionSpecification) peekObject();
 1147   
 1148  30 ExtensionConfigurationSetter setter = new ExtensionConfigurationSetter(es, propertyName,
 1149    value);
 1150   
 1151  30 push(_elementName, setter, STATE_CONFIGURE, false);
 1152    }
 1153   
 1154  7 private void enterContextAsset_3_0()
 1155    {
 1156  7 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  26 private void enterAsset()
 1167    {
 1168  26 enterAsset("path", null);
 1169    }
 1170   
 1171  1020 private void enterDescription()
 1172    {
 1173  1020 push(_elementName, new DescriptionSetter(peekObject()), STATE_DESCRIPTION, false);
 1174    }
 1175   
 1176  11 private void enterExtension()
 1177    {
 1178  11 String name = getValidatedAttribute(
 1179    "name",
 1180    EXTENSION_NAME_PATTERN,
 1181    "invalid-extension-name");
 1182   
 1183  10 boolean immediate = getBooleanAttribute("immediate", false);
 1184  10 String className = getAttribute("class");
 1185   
 1186  10 IExtensionSpecification es = _factory.createExtensionSpecification(
 1187    _resolver,
 1188    _valueConverter);
 1189   
 1190  10 es.setClassName(className);
 1191  10 es.setImmediate(immediate);
 1192   
 1193  10 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
 1194   
 1195  10 ls.addExtensionSpecification(name, es);
 1196   
 1197  10 push(_elementName, es, STATE_EXTENSION);
 1198    }
 1199   
 1200  3 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  3 enterAsset("URL", null);
 1206    }
 1207   
 1208    /** A throwback to the 3.0 DTD */
 1209   
 1210  2 private void enterInheritedBinding_3_0()
 1211    {
 1212  2 String name = getAttribute("name");
 1213  2 String parameterName = getAttribute("parameter-name");
 1214   
 1215  2 IBindingSpecification bs = _factory.createBindingSpecification();
 1216  2 bs.setType(BindingType.INHERITED);
 1217  2 bs.setValue(parameterName);
 1218   
 1219  2 IContainedComponent cc = (IContainedComponent) peekObject();
 1220   
 1221  2 cc.setBinding(name, bs);
 1222   
 1223  2 push(_elementName, null, STATE_NO_CONTENT);
 1224    }
 1225   
 1226  23 private void enterLibrary()
 1227    {
 1228  23 String libraryId = getValidatedAttribute("id", LIBRARY_ID_PATTERN, "invalid-library-id");
 1229  22 String path = getAttribute("specification-path");
 1230   
 1231  22 if (libraryId.equals(INamespace.FRAMEWORK_NAMESPACE)
 1232    || libraryId.equals(INamespace.APPLICATION_NAMESPACE))
 1233  1 throw new DocumentParseException(ParseMessages
 1234    .frameworkLibraryIdIsReserved(INamespace.FRAMEWORK_NAMESPACE), getLocation());
 1235   
 1236  21 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
 1237   
 1238  21 ls.setLibrarySpecificationPath(libraryId, path);
 1239   
 1240  21 push(_elementName, null, STATE_NO_CONTENT);
 1241    }
 1242   
 1243  1 private void enterListenerBinding()
 1244    {
 1245  1 _log.warn(ParseMessages.listenerBindingUnsupported(getLocation()));
 1246   
 1247  1 push(_elementName, null, STATE_LISTENER_BINDING, false);
 1248    }
 1249   
 1250  4 private void enterMessageBinding_3_0()
 1251    {
 1252  4 String name = getAttribute("name");
 1253  4 String key = getAttribute("key");
 1254   
 1255  4 IBindingSpecification bs = _factory.createBindingSpecification();
 1256  4 bs.setType(BindingType.PREFIXED);
 1257  4 bs.setValue(BindingConstants.MESSAGE_PREFIX + ":" + key);
 1258  4 bs.setLocation(getLocation());
 1259   
 1260  4 IContainedComponent cc = (IContainedComponent) peekObject();
 1261   
 1262  4 cc.setBinding(name, bs);
 1263   
 1264  4 push(_elementName, null, STATE_NO_CONTENT);
 1265    }
 1266   
 1267  495 private void enterPage()
 1268    {
 1269  495 String name = getValidatedAttribute("name", PAGE_NAME_PATTERN, "invalid-page-name");
 1270  494 String path = getAttribute("specification-path");
 1271   
 1272  494 ILibrarySpecification ls = (ILibrarySpecification) peekObject();
 1273   
 1274  494 ls.setPageSpecificationPath(name, path);
 1275   
 1276  494 push(_elementName, null, STATE_NO_CONTENT);
 1277    }
 1278   
 1279  1070 private void enterParameter()
 1280    {
 1281  1070 IParameterSpecification ps = _factory.createParameterSpecification();
 1282   
 1283  1070 String name = getValidatedAttribute(
 1284    "name",
 1285    PARAMETER_NAME_PATTERN,
 1286    "invalid-parameter-name");
 1287   
 1288  1069 String attributeName = _DTD_4_0 ? "property" : "property-name";
 1289   
 1290  1069 String propertyName = getValidatedAttribute(
 1291    attributeName,
 1292    PROPERTY_NAME_PATTERN,
 1293    "invalid-property-name");
 1294   
 1295  1069 if (propertyName == null)
 1296  976 propertyName = name;
 1297   
 1298  1069 ps.setParameterName(name);
 1299  1069 ps.setPropertyName(propertyName);
 1300   
 1301  1069 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  1069 String defaultValue = getAttribute("default-value");
 1308   
 1309  1069 if (defaultValue != null && !_DTD_4_0)
 1310  7 defaultValue = BindingConstants.OGNL_PREFIX + ":" + defaultValue;
 1311   
 1312  1069 ps.setDefaultValue(defaultValue);
 1313   
 1314  1069 if (!_DTD_4_0)
 1315    {
 1316    // When direction=auto (in a 3.0 DTD), turn caching off
 1317   
 1318  44 String direction = getAttribute("direction");
 1319  44 ps.setCache(!"auto".equals(direction));
 1320    }
 1321    else
 1322    {
 1323  1025 boolean cache = getBooleanAttribute("cache", true);
 1324  1025 ps.setCache(cache);
 1325    }
 1326   
 1327    // type will only be specified in a 3.0 DTD.
 1328   
 1329  1069 String type = getAttribute("type");
 1330   
 1331  1069 if (type != null)
 1332  38 ps.setType(type);
 1333   
 1334    // aliases is new in the 4.0 DTD
 1335   
 1336  1069 String aliases = getAttribute("aliases");
 1337   
 1338  1069 ps.setAliases(aliases);
 1339  1069 ps.setDeprecated(getBooleanAttribute("deprecated", false));
 1340   
 1341  1069 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1342   
 1343  1069 cs.addParameter(ps);
 1344   
 1345  1069 push(_elementName, ps, STATE_ALLOW_DESCRIPTION);
 1346    }
 1347   
 1348  8 private void enterPrivateAsset_3_0()
 1349    {
 1350  8 enterAsset("resource-path", "classpath:");
 1351    }
 1352   
 1353    /** @since 4.0 */
 1354  11 private void enterMeta()
 1355    {
 1356  11 String key = getAttribute("key");
 1357  11 String value = getAttribute("value");
 1358   
 1359    // Value may be null, in which case the value is set from the element content
 1360   
 1361  11 IPropertyHolder ph = (IPropertyHolder) peekObject();
 1362   
 1363  11 push(_elementName, new PropertyValueSetter(ph, key, value), STATE_META, false);
 1364    }
 1365   
 1366  47 private void enterProperty_3_0()
 1367    {
 1368  47 String name = getAttribute("name");
 1369  47 String value = getAttribute("value");
 1370   
 1371    // Value may be null, in which case the value is set from the element content
 1372   
 1373  47 IPropertyHolder ph = (IPropertyHolder) peekObject();
 1374   
 1375  47 push(_elementName, new PropertyValueSetter(ph, name, value), STATE_META, false);
 1376    }
 1377   
 1378    /**
 1379    * &tl;property&gt; in 4.0, or &lt;property-specification&gt; in 3.0
 1380    */
 1381   
 1382  121 private void enterProperty()
 1383    {
 1384  121 String name = getValidatedAttribute("name", PROPERTY_NAME_PATTERN, "invalid-property-name");
 1385  121 String type = getAttribute("type");
 1386   
 1387  121 String persistence = null;
 1388   
 1389  121 if (_DTD_4_0)
 1390  68 persistence = getAttribute("persist");
 1391    else
 1392  53 persistence = getBooleanAttribute("persistent", false) ? "session" : null;
 1393   
 1394  121 String initialValue = getAttribute("initial-value");
 1395   
 1396  121 IPropertySpecification ps = _factory.createPropertySpecification();
 1397  121 ps.setName(name);
 1398   
 1399  121 if (HiveMind.isNonBlank(type))
 1400  50 ps.setType(type);
 1401   
 1402  121 ps.setPersistence(persistence);
 1403  121 ps.setInitialValue(initialValue);
 1404   
 1405  121 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1406  121 cs.addPropertySpecification(ps);
 1407   
 1408  121 push(_elementName, ps, STATE_PROPERTY, false);
 1409    }
 1410   
 1411    /**
 1412    * @since 4.0
 1413    */
 1414   
 1415  359 private void enterInject()
 1416    {
 1417  359 String property = getValidatedAttribute(
 1418    "property",
 1419    PROPERTY_NAME_PATTERN,
 1420    "invalid-property-name");
 1421  359 String type = getAttribute("type");
 1422  359 String objectReference = getAttribute("object");
 1423   
 1424  359 InjectSpecification spec = _factory.createInjectSpecification();
 1425   
 1426  359 spec.setProperty(property);
 1427  359 spec.setType(type);
 1428  359 spec.setObject(objectReference);
 1429  359 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1430   
 1431  359 cs.addInjectSpecification(spec);
 1432   
 1433  359 push(_elementName, spec, STATE_NO_CONTENT);
 1434    }
 1435   
 1436  110 private void enterReservedParameter()
 1437    {
 1438  110 String name = getAttribute("name");
 1439  110 IComponentSpecification cs = (IComponentSpecification) peekObject();
 1440   
 1441  110 cs.addReservedParameterName(name);
 1442   
 1443  110 push(_elementName, null, STATE_NO_CONTENT);
 1444    }
 1445   
 1446  1 private void enterService_3_0()
 1447    {
 1448  1 _errorHandler.error(_log, ParseMessages.serviceElementNotSupported(), getLocation(), null);
 1449   
 1450  1 push(_elementName, null, STATE_NO_CONTENT);
 1451    }
 1452   
 1453  1 private void enterSetMessage_3_0()
 1454    {
 1455  1 String name = getAttribute("name");
 1456  1 String key = getAttribute("key");
 1457   
 1458  1 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
 1459   
 1460  1 bi.setPropertyName(name);
 1461  1 bi.setBindingReference(BindingConstants.MESSAGE_PREFIX + ":" + key);
 1462  1 bi.setLocation(getLocation());
 1463   
 1464  1 IBeanSpecification bs = (IBeanSpecification) peekObject();
 1465   
 1466  1 bs.addInitializer(bi);
 1467   
 1468  1 push(_elementName, null, STATE_NO_CONTENT);
 1469    }
 1470   
 1471  3 private void enterSet()
 1472    {
 1473  3 String name = getAttribute("name");
 1474  3 String reference = getAttribute("value");
 1475   
 1476  3 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
 1477   
 1478  3 bi.setPropertyName(name);
 1479   
 1480  3 IBeanSpecification bs = (IBeanSpecification) peekObject();
 1481   
 1482  3 push(_elementName, new BeanSetPropertySetter(bs, bi, null, reference), STATE_SET, false);
 1483    }
 1484   
 1485  5 private void enterSetProperty_3_0()
 1486    {
 1487  5 String name = getAttribute("name");
 1488  5 String expression = getAttribute("expression");
 1489   
 1490  5 BindingBeanInitializer bi = _factory.createBindingBeanInitializer(_bindingSource);
 1491   
 1492  5 bi.setPropertyName(name);
 1493   
 1494  5 IBeanSpecification bs = (IBeanSpecification) peekObject();
 1495   
 1496  5 push(_elementName, new BeanSetPropertySetter(bs, bi, BindingConstants.OGNL_PREFIX + ":",
 1497    expression), STATE_SET, false);
 1498    }
 1499   
 1500  32 private void enterStaticBinding_3_0()
 1501    {
 1502  32 String name = getAttribute("name");
 1503  32 String expression = getAttribute("value");
 1504   
 1505  32 IContainedComponent cc = (IContainedComponent) peekObject();
 1506   
 1507  32 BindingSetter bs = new BindingSetter(cc, name, expression);
 1508   
 1509  32 push(_elementName, bs, STATE_STATIC_BINDING, false);
 1510    }
 1511   
 1512  536 private void expectElement(String elementName)
 1513    {
 1514  536 if (_elementName.equals(elementName))
 1515  535 return;
 1516   
 1517  1 throw new DocumentParseException(ParseMessages.incorrectDocumentType(
 1518    _elementName,
 1519    elementName), getLocation(), null);
 1520   
 1521    }
 1522   
 1523  19214 private String getAttribute(String name)
 1524    {
 1525  19214 return (String) _attributes.get(name);
 1526    }
 1527   
 1528  4466 private boolean getBooleanAttribute(String name, boolean defaultValue)
 1529    {
 1530  4466 String value = getAttribute(name);
 1531   
 1532  4466 if (value == null)
 1533  85 return defaultValue;
 1534   
 1535  4381 Boolean b = (Boolean) CONVERSION_MAP.get(value);
 1536   
 1537  4381 return b.booleanValue();
 1538    }
 1539   
 1540  33 private Object getConvertedAttribute(String name, Object defaultValue)
 1541    {
 1542  33 String key = getAttribute(name);
 1543   
 1544  33 if (key == null)
 1545  0 return defaultValue;
 1546   
 1547  33 return CONVERSION_MAP.get(key);
 1548    }
 1549   
 1550  536 private InputSource getDTDInputSource(String name)
 1551    {
 1552  536 InputStream stream = getClass().getResourceAsStream(name);
 1553   
 1554  536 return new InputSource(stream);
 1555    }
 1556   
 1557  552 private String getExtendedValue(String attributeValue, String attributeName, boolean required)
 1558    {
 1559  552 String contentValue = peekContent();
 1560   
 1561  552 boolean asAttribute = HiveMind.isNonBlank(attributeValue);
 1562  552 boolean asContent = HiveMind.isNonBlank(contentValue);
 1563   
 1564  552 if (asAttribute && asContent)
 1565    {
 1566  1 throw new DocumentParseException(ParseMessages.noAttributeAndBody(
 1567    attributeName,
 1568    _elementName), getLocation(), null);
 1569    }
 1570   
 1571  551 if (required && !(asAttribute || asContent))
 1572    {
 1573  1 throw new DocumentParseException(ParseMessages.requiredExtendedAttribute(
 1574    _elementName,
 1575    attributeName), getLocation(), null);
 1576    }
 1577   
 1578  550 if (asAttribute)
 1579  378 return attributeValue;
 1580   
 1581  172 return contentValue;
 1582    }
 1583   
 1584  6386 private String getValidatedAttribute(String name, String pattern, String errorKey)
 1585    {
 1586  6386 String value = getAttribute(name);
 1587   
 1588  6386 if (value == null)
 1589  1311 return null;
 1590   
 1591  5075 if (_matcher.matches(pattern, value))
 1592  5068 return value;
 1593   
 1594  7 throw new InvalidStringException(ParseMessages.invalidAttribute(errorKey, value), value,
 1595    getLocation());
 1596    }
 1597   
 1598  538 protected void initializeParser(Resource resource, int startState)
 1599    {
 1600  538 super.initializeParser(resource, startState);
 1601   
 1602  538 _rootObject = null;
 1603  538 _attributes = new HashMap();
 1604    }
 1605   
 1606  43 public IApplicationSpecification parseApplicationSpecification(Resource resource)
 1607    {
 1608  43 initializeParser(resource, STATE_APPLICATION_SPECIFICATION_INITIAL);
 1609   
 1610  43 try
 1611    {
 1612  43 parseDocument();
 1613   
 1614  38 return (IApplicationSpecification) _rootObject;
 1615    }
 1616    finally
 1617    {
 1618  43 resetParser();
 1619    }
 1620    }
 1621   
 1622  327 public IComponentSpecification parseComponentSpecification(Resource resource)
 1623    {
 1624  327 initializeParser(resource, STATE_COMPONENT_SPECIFICATION_INITIAL);
 1625   
 1626  327 try
 1627    {
 1628  327 parseDocument();
 1629   
 1630  320 return (IComponentSpecification) _rootObject;
 1631    }
 1632    finally
 1633    {
 1634  327 resetParser();
 1635    }
 1636    }
 1637   
 1638  538 private void parseDocument()
 1639    {
 1640  538 InputStream stream = null;
 1641   
 1642  538 Resource resource = getResource();
 1643   
 1644  538 boolean success = false;
 1645   
 1646  538 try
 1647    {
 1648  538 if (_parser == null)
 1649  104 _parser = _parserFactory.newSAXParser();
 1650   
 1651  538 URL resourceURL = resource.getResourceURL();
 1652   
 1653  538 if (resourceURL == null)
 1654  0 throw new DocumentParseException(ParseMessages.missingResource(resource), resource);
 1655   
 1656  538 InputStream rawStream = resourceURL.openStream();
 1657  538 stream = new BufferedInputStream(rawStream);
 1658   
 1659  538 _parser.parse(stream, this, resourceURL.toExternalForm());
 1660   
 1661  522 stream.close();
 1662  522 stream = null;
 1663   
 1664  522 success = true;
 1665    }
 1666    catch (SAXParseException ex)
 1667    {
 1668  1 _parser = null;
 1669   
 1670  1 Location location = new LocationImpl(resource, ex.getLineNumber(), ex.getColumnNumber());
 1671   
 1672  1 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex),
 1673    location, ex);
 1674    }
 1675    catch (Exception ex)
 1676    {
 1677  15 _parser = null;
 1678   
 1679  15 throw new DocumentParseException(ParseMessages.errorReadingResource(resource, ex),
 1680    resource, ex);
 1681    }
 1682    finally
 1683    {
 1684  538 if (!success)
 1685  16 _parser = null;
 1686   
 1687  538 close(stream);
 1688    }
 1689    }
 1690   
 1691  65 public ILibrarySpecification parseLibrarySpecification(Resource resource)
 1692    {
 1693  65 initializeParser(resource, STATE_LIBRARY_SPECIFICATION_INITIAL);
 1694   
 1695  65 try
 1696    {
 1697  65 parseDocument();
 1698   
 1699  63 return (ILibrarySpecification) _rootObject;
 1700    }
 1701    finally
 1702    {
 1703  65 resetParser();
 1704    }
 1705    }
 1706   
 1707  103 public IComponentSpecification parsePageSpecification(Resource resource)
 1708    {
 1709  103 initializeParser(resource, STATE_PAGE_SPECIFICATION_INITIAL);
 1710   
 1711  103 try
 1712    {
 1713  103 parseDocument();
 1714   
 1715  101 return (IComponentSpecification) _rootObject;
 1716    }
 1717    finally
 1718    {
 1719  103 resetParser();
 1720    }
 1721    }
 1722   
 1723  1572 protected String peekContent()
 1724    {
 1725  1572 String content = super.peekContent();
 1726   
 1727  1572 if (content == null)
 1728  491 return null;
 1729   
 1730  1081 return content.trim();
 1731    }
 1732   
 1733  538 protected void resetParser()
 1734    {
 1735  538 _rootObject = null;
 1736  538 _DTD_4_0 = false;
 1737   
 1738  538 _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  537 public InputSource resolveEntity(String publicId, String systemId) throws SAXException
 1746    {
 1747  537 if (TAPESTRY_DTD_4_0_PUBLIC_ID.equals(publicId))
 1748    {
 1749  319 _DTD_4_0 = true;
 1750  319 return getDTDInputSource("Tapestry_4_0.dtd");
 1751    }
 1752   
 1753  218 if (TAPESTRY_DTD_3_0_PUBLIC_ID.equals(publicId))
 1754  217 return getDTDInputSource("Tapestry_3_0.dtd");
 1755   
 1756  1 throw new DocumentParseException(ParseMessages.unknownPublicId(getResource(), publicId),
 1757    new LocationImpl(getResource()), null);
 1758    }
 1759   
 1760    /** @since 4.0 */
 1761  55 public void setBindingSource(BindingSource bindingSource)
 1762    {
 1763  55 _bindingSource = bindingSource;
 1764    }
 1765   
 1766    /** @since 4.0 */
 1767  64 public void setValueConverter(ValueConverter valueConverter)
 1768    {
 1769  64 _valueConverter = valueConverter;
 1770    }
 1771    }