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