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