| // Copyright 2004 The Apache Software Foundation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package org.apache.tapestry.parse; |
| |
| import java.net.URL; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.commons.digester.Rule; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.tapestry.ILocationHolder; |
| import org.apache.tapestry.IResourceLocation; |
| import org.apache.tapestry.IResourceResolver; |
| import org.apache.tapestry.Tapestry; |
| import org.apache.tapestry.spec.AssetType; |
| import org.apache.tapestry.spec.BeanLifecycle; |
| import org.apache.tapestry.spec.BindingType; |
| import org.apache.tapestry.spec.Direction; |
| import org.apache.tapestry.spec.IApplicationSpecification; |
| import org.apache.tapestry.spec.IComponentSpecification; |
| import org.apache.tapestry.spec.IExtensionSpecification; |
| import org.apache.tapestry.spec.ILibrarySpecification; |
| import org.apache.tapestry.spec.SpecFactory; |
| import org.apache.tapestry.util.xml.DocumentParseException; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXParseException; |
| |
| /** |
| * Used to parse an application or component specification into a |
| * {@link org.apache.tapestry.spec.ApplicationSpecification} or {@link IComponentSpecification}. |
| * |
| * |
| * <table border=1 |
| * <tr> |
| * <th>Version</th> <th>PUBLIC ID</th> <th>SYSTEM ID</th> <th>Description</th> |
| * </tr> |
| * |
| * |
| * <tr valign="top"> |
| * <td>1.3</td> |
| * <td><code>-//Howard Lewis Ship//Tapestry Specification 1.3//EN</code></td> |
| * <td><code>http://tapestry.sf.net/dtd/Tapestry_1_3.dtd</code></td> |
| * <td> |
| * Version of specification introduced in release 2.2. |
| * </td> |
| * </tr> |
| * |
| * <tr valign="top"> |
| * <td>3.0</td> |
| * <td><code>-//Howard Lewis Ship//Tapestry Specification 3.0//EN</code></td> |
| * <td><code>http://tapestry.sf.net/dtd/Tapestry_3_0.dtd</code></td> |
| * <td> |
| * Version of specification introduced in release 3.0. |
| * <br/> |
| * Note: Future DTD versions will track Tapestry release numbers. |
| * </td> |
| * </tr> |
| * |
| * |
| * </table> |
| * |
| * @version $Id$ |
| * @author Howard Lewis Ship |
| * |
| **/ |
| |
| public class SpecificationParser |
| { |
| private static final Log LOG = LogFactory.getLog(SpecificationParser.class); |
| |
| /** @since 2.2 **/ |
| |
| public static final String TAPESTRY_DTD_1_3_PUBLIC_ID = |
| "-//Howard Lewis Ship//Tapestry Specification 1.3//EN"; |
| |
| /** @since 3.0 **/ |
| |
| public static final String TAPESTRY_DTD_3_0_PUBLIC_ID = |
| "-//Apache Software Foundation//Tapestry Specification 3.0//EN"; |
| |
| /** |
| * Like modified property name, but allows periods in the name as |
| * well. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String EXTENDED_PROPERTY_NAME_PATTERN = "^_?[a-zA-Z](\\w|-|\\.)*$"; |
| |
| /** |
| * Perl5 pattern that parameter names must conform to. |
| * Letter, followed by letter, number or underscore. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String PARAMETER_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern that property names (that can be connected to |
| * parameters) must conform to. |
| * Letter, followed by letter, number or underscore. |
| * |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String PROPERTY_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for page names. Letter |
| * followed by letter, number, dash, underscore or period. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String PAGE_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for component alias. |
| * Letter, followed by letter, number, or underscore. |
| * This is used to validate component types registered |
| * in the application or library specifications. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String COMPONENT_ALIAS_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for helper bean names. |
| * Letter, followed by letter, number or underscore. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String BEAN_NAME_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for component ids. Letter, followed by |
| * letter, number or underscore. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String COMPONENT_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for asset names. Letter, followed by |
| * letter, number or underscore. Also allows |
| * the special "$template" value. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String ASSET_NAME_PATTERN = |
| "(\\$template)|(" + Tapestry.SIMPLE_PROPERTY_NAME_PATTERN + ")"; |
| |
| /** |
| * Perl5 pattern for service names. Letter |
| * followed by letter, number, dash, underscore or period. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String SERVICE_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for library ids. Letter followed |
| * by letter, number or underscore. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String LIBRARY_ID_PATTERN = Tapestry.SIMPLE_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Per5 pattern for extension names. Letter followed |
| * by letter, number, dash, period or underscore. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static final String EXTENSION_NAME_PATTERN = EXTENDED_PROPERTY_NAME_PATTERN; |
| |
| /** |
| * Perl5 pattern for component types. Component types are an optional |
| * namespace prefix followed by a normal identifier. |
| * |
| * @since 2.2 |
| **/ |
| |
| public static final String COMPONENT_TYPE_PATTERN = "^(_?[a-zA-Z]\\w*:)?[a-zA-Z_](\\w)*$"; |
| |
| /** |
| * We can share a single map for all the XML attribute to object conversions, |
| * since the keys are unique. |
| * |
| **/ |
| |
| private static final Map CONVERSION_MAP = new HashMap(); |
| |
| /** @since 1.0.9 **/ |
| |
| private SpecFactory _factory; |
| |
| /** |
| * Digester used for component specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| private SpecificationDigester _componentDigester; |
| |
| /** |
| * Digestger used for page specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| private SpecificationDigester _pageDigester; |
| |
| /** |
| * Digester use for library specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| private SpecificationDigester _libraryDigester; |
| |
| /** |
| * @since 3.0 |
| * |
| **/ |
| |
| private IResourceResolver _resolver; |
| |
| private interface IConverter |
| { |
| public Object convert(String value) throws DocumentParseException; |
| } |
| |
| private static class BooleanConverter implements IConverter |
| |
| { |
| public Object convert(String value) throws DocumentParseException |
| { |
| Object result = CONVERSION_MAP.get(value.toLowerCase()); |
| |
| if (result == null || !(result instanceof Boolean)) |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.fail-convert-boolean", value)); |
| |
| return result; |
| } |
| } |
| |
| private static class IntConverter implements IConverter |
| { |
| public Object convert(String value) throws DocumentParseException |
| { |
| try |
| { |
| return new Integer(value); |
| } |
| catch (NumberFormatException ex) |
| { |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.fail-convert-int", value), |
| ex); |
| } |
| } |
| } |
| |
| private static class LongConverter implements IConverter |
| { |
| public Object convert(String value) throws DocumentParseException |
| { |
| try |
| { |
| return new Long(value); |
| } |
| catch (NumberFormatException ex) |
| { |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.fail-convert-long", value), |
| ex); |
| } |
| } |
| } |
| |
| private static class DoubleConverter implements IConverter |
| { |
| public Object convert(String value) throws DocumentParseException |
| { |
| try |
| { |
| return new Double(value); |
| } |
| catch (NumberFormatException ex) |
| { |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.fail-convert-double", value), |
| ex); |
| } |
| } |
| } |
| |
| private static class StringConverter implements IConverter |
| { |
| public Object convert(String value) |
| { |
| return value.trim(); |
| } |
| } |
| |
| /** |
| * Base class for creating locatable objects using the |
| * {@link SpecFactory}. |
| * |
| **/ |
| |
| private abstract static class SpecFactoryCreateRule extends AbstractSpecificationRule |
| { |
| /** |
| * Implement in subclass to create correct locatable object. |
| * |
| **/ |
| |
| public abstract ILocationHolder create(); |
| |
| public void begin(String namespace, String name, Attributes attributes) throws Exception |
| { |
| ILocationHolder holder = create(); |
| |
| holder.setLocation(getLocation()); |
| |
| digester.push(holder); |
| } |
| |
| public void end(String namespace, String name) throws Exception |
| { |
| digester.pop(); |
| } |
| |
| } |
| |
| private class CreateExpressionBeanInitializerRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createExpressionBeanInitializer(); |
| } |
| } |
| |
| private class CreateStringBeanInitializerRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createMessageBeanInitializer(); |
| } |
| } |
| |
| private class CreateContainedComponentRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createContainedComponent(); |
| } |
| } |
| |
| private class CreateParameterSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createParameterSpecification(); |
| } |
| } |
| |
| private class CreateComponentSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createComponentSpecification(); |
| } |
| } |
| |
| private class CreateBindingSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createBindingSpecification(); |
| } |
| } |
| |
| private class CreateBeanSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createBeanSpecification(); |
| } |
| } |
| |
| private class CreateListenerBindingSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createListenerBindingSpecification(); |
| } |
| } |
| |
| private class CreateAssetSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createAssetSpecification(); |
| } |
| } |
| |
| private class CreatePropertySpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createPropertySpecification(); |
| } |
| } |
| |
| private class CreateApplicationSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createApplicationSpecification(); |
| } |
| } |
| |
| private class CreateLibrarySpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createLibrarySpecification(); |
| } |
| } |
| |
| private class CreateExtensionSpecificationRule extends SpecFactoryCreateRule |
| { |
| public ILocationHolder create() |
| { |
| return _factory.createExtensionSpecification(); |
| } |
| } |
| |
| private static class ProcessExtensionConfigurationRule extends AbstractSpecificationRule |
| { |
| private String _value; |
| private String _propertyName; |
| private IConverter _converter; |
| |
| public void begin(String namespace, String name, Attributes attributes) throws Exception |
| { |
| _propertyName = getValue(attributes, "property-name"); |
| _value = getValue(attributes, "value"); |
| |
| String type = getValue(attributes, "type"); |
| |
| _converter = (IConverter) CONVERSION_MAP.get(type); |
| |
| if (_converter == null) |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.unknown-static-value-type", type), |
| getResourceLocation()); |
| |
| } |
| |
| public void body(String namespace, String name, String text) throws Exception |
| { |
| if (Tapestry.isBlank(text)) |
| return; |
| |
| if (_value != null) |
| throw new DocumentParseException( |
| Tapestry.format("SpecificationParser.no-attribute-and-body", "value", name), |
| getResourceLocation()); |
| |
| _value = text.trim(); |
| } |
| |
| public void end(String namespace, String name) throws Exception |
| { |
| if (_value == null) |
| throw new DocumentParseException( |
| Tapestry.format( |
| "SpecificationParser.required-extended-attribute", |
| name, |
| "value"), |
| getResourceLocation()); |
| |
| Object objectValue = _converter.convert(_value); |
| |
| IExtensionSpecification top = (IExtensionSpecification) digester.peek(); |
| |
| top.addConfiguration(_propertyName, objectValue); |
| |
| _converter = null; |
| _value = null; |
| _propertyName = null; |
| |
| } |
| |
| } |
| |
| // Identify all the different acceptible values. |
| // We continue to sneak by with a single map because |
| // there aren't conflicts; when we have 'foo' meaning |
| // different things in different places in the DTD, we'll |
| // need multiple maps. |
| |
| static { |
| |
| CONVERSION_MAP.put("true", Boolean.TRUE); |
| CONVERSION_MAP.put("t", Boolean.TRUE); |
| CONVERSION_MAP.put("1", Boolean.TRUE); |
| CONVERSION_MAP.put("y", Boolean.TRUE); |
| CONVERSION_MAP.put("yes", Boolean.TRUE); |
| CONVERSION_MAP.put("on", Boolean.TRUE); |
| |
| CONVERSION_MAP.put("false", Boolean.FALSE); |
| CONVERSION_MAP.put("f", Boolean.FALSE); |
| CONVERSION_MAP.put("0", Boolean.FALSE); |
| CONVERSION_MAP.put("off", Boolean.FALSE); |
| CONVERSION_MAP.put("no", Boolean.FALSE); |
| CONVERSION_MAP.put("n", Boolean.FALSE); |
| |
| CONVERSION_MAP.put("none", BeanLifecycle.NONE); |
| CONVERSION_MAP.put("request", BeanLifecycle.REQUEST); |
| CONVERSION_MAP.put("page", BeanLifecycle.PAGE); |
| CONVERSION_MAP.put("render", BeanLifecycle.RENDER); |
| |
| CONVERSION_MAP.put("boolean", new BooleanConverter()); |
| CONVERSION_MAP.put("int", new IntConverter()); |
| CONVERSION_MAP.put("double", new DoubleConverter()); |
| CONVERSION_MAP.put("String", new StringConverter()); |
| CONVERSION_MAP.put("long", new LongConverter()); |
| |
| CONVERSION_MAP.put("in", Direction.IN); |
| CONVERSION_MAP.put("form", Direction.FORM); |
| CONVERSION_MAP.put("custom", Direction.CUSTOM); |
| CONVERSION_MAP.put("auto", Direction.AUTO); |
| } |
| |
| public SpecificationParser(IResourceResolver resolver) |
| { |
| _resolver = resolver; |
| setFactory(new SpecFactory()); |
| } |
| |
| /** |
| * Parses an input stream containing a page or component specification and assembles |
| * an {@link IComponentSpecification} from it. |
| * |
| * @throws DocumentParseException if the input stream cannot be fully |
| * parsed or contains invalid data. |
| * |
| **/ |
| |
| public IComponentSpecification parseComponentSpecification(IResourceLocation resourceLocation) |
| throws DocumentParseException |
| { |
| if (_componentDigester == null) |
| _componentDigester = constructComponentDigester(); |
| |
| try |
| { |
| IComponentSpecification result = |
| (IComponentSpecification) parse(_componentDigester, resourceLocation); |
| |
| result.setSpecificationLocation(resourceLocation); |
| |
| return result; |
| } |
| catch (DocumentParseException ex) |
| { |
| _componentDigester = null; |
| |
| throw ex; |
| } |
| } |
| |
| /** |
| * Parses a resource using a particular digester. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected Object parse(SpecificationDigester digester, IResourceLocation location) |
| throws DocumentParseException |
| { |
| try |
| { |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Parsing " + location); |
| |
| URL url = location.getResourceURL(); |
| |
| if (url == null) |
| throw new DocumentParseException( |
| Tapestry.format("AbstractDocumentParser.missing-resource", location), |
| location); |
| |
| InputSource source = new InputSource(url.toExternalForm()); |
| |
| digester.setResourceLocation(location); |
| |
| Object result = digester.parse(source); |
| |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Result: " + result); |
| |
| return result; |
| } |
| catch (SAXParseException ex) |
| { |
| throw new DocumentParseException(ex); |
| } |
| catch (DocumentParseException ex) |
| { |
| throw ex; |
| } |
| catch (Exception ex) |
| { |
| throw new DocumentParseException( |
| Tapestry.format( |
| "SpecificationParser.error-reading-resource", |
| location, |
| ex.getMessage()), |
| location, |
| ex); |
| } |
| finally |
| { |
| digester.setResourceLocation(null); |
| } |
| } |
| |
| /** |
| * Parses an input stream containing a page specification and assembles |
| * an {@link IComponentSpecification} from it. |
| * |
| * @throws DocumentParseException if the input stream cannot be fully |
| * parsed or contains invalid data. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public IComponentSpecification parsePageSpecification(IResourceLocation resourceLocation) |
| throws DocumentParseException |
| { |
| if (_pageDigester == null) |
| _pageDigester = constructPageDigester(); |
| |
| try |
| { |
| IComponentSpecification result = |
| (IComponentSpecification) parse(_pageDigester, resourceLocation); |
| |
| result.setSpecificationLocation(resourceLocation); |
| |
| return result; |
| } |
| catch (DocumentParseException ex) |
| { |
| _pageDigester = null; |
| |
| throw ex; |
| } |
| } |
| |
| /** |
| * Parses an resource containing an application specification and assembles |
| * an {@link org.apache.tapestry.spec.ApplicationSpecification} from it. |
| * |
| * @throws DocumentParseException if the input stream cannot be fully |
| * parsed or contains invalid data. |
| * |
| **/ |
| |
| public IApplicationSpecification parseApplicationSpecification(IResourceLocation resourceLocation) |
| throws DocumentParseException |
| { |
| |
| // Use a one-shot digester, because you only parse the app spec |
| // once. |
| |
| IApplicationSpecification result = |
| (IApplicationSpecification) parse(constructApplicationDigester(), resourceLocation); |
| |
| result.setResourceResolver(_resolver); |
| result.setSpecificationLocation(resourceLocation); |
| result.instantiateImmediateExtensions(); |
| |
| return result; |
| } |
| |
| /** |
| * Parses an input stream containing a library specification and assembles |
| * a {@link org.apache.tapestry.spec.LibrarySpecification} from it. |
| * |
| * @throws DocumentParseException if the input stream cannot be fully |
| * parsed or contains invalid data. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public ILibrarySpecification parseLibrarySpecification(IResourceLocation resourceLocation) |
| throws DocumentParseException |
| { |
| if (_libraryDigester == null) |
| _libraryDigester = constructLibraryDigester(); |
| |
| try |
| { |
| ILibrarySpecification result = |
| (ILibrarySpecification) parse(_libraryDigester, resourceLocation); |
| |
| result.setResourceResolver(_resolver); |
| result.setSpecificationLocation(resourceLocation); |
| result.instantiateImmediateExtensions(); |
| |
| return result; |
| } |
| catch (DocumentParseException ex) |
| { |
| _libraryDigester = null; |
| |
| throw ex; |
| } |
| } |
| |
| /** |
| * Sets the SpecFactory which instantiates Tapestry spec objects. |
| * |
| * @since 1.0.9 |
| **/ |
| |
| public void setFactory(SpecFactory factory) |
| { |
| _factory = factory; |
| } |
| |
| /** |
| * Returns the current SpecFactory which instantiates Tapestry spec objects. |
| * |
| * @since 1.0.9 |
| * |
| **/ |
| |
| public SpecFactory getFactory() |
| { |
| return _factory; |
| } |
| |
| /** |
| * Constructs a digester, registerring the known DTDs and the |
| * global rules (for <property> and <description>). |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected SpecificationDigester constructBaseDigester(String rootElement) |
| { |
| SpecificationDigester result = new SpecificationDigester(); |
| |
| // <description> |
| |
| result.addBeanPropertySetter("*/description", "description"); |
| |
| // <property> |
| |
| result.addRule("*/property", new SetMetaPropertyRule()); |
| |
| result.register(TAPESTRY_DTD_1_3_PUBLIC_ID, getURL("Tapestry_1_3.dtd")); |
| result.register(TAPESTRY_DTD_3_0_PUBLIC_ID, getURL("Tapestry_3_0.dtd")); |
| |
| result.addDocumentRule( |
| new ValidatePublicIdRule( |
| new String[] { TAPESTRY_DTD_1_3_PUBLIC_ID, TAPESTRY_DTD_3_0_PUBLIC_ID }, |
| rootElement)); |
| |
| result.setValidating(true); |
| |
| return result; |
| |
| } |
| |
| /** |
| * Constructs a digester configued to parse application specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected SpecificationDigester constructApplicationDigester() |
| { |
| SpecificationDigester result = constructBaseDigester("application"); |
| |
| String pattern = "application"; |
| |
| result.addRule(pattern, new CreateApplicationSpecificationRule()); |
| result.addSetLimitedProperties( |
| pattern, |
| new String[] { "name", "engine-class" }, |
| new String[] { "name", "engineClassName" }); |
| result.addRule(pattern, new SetPublicIdRule()); |
| |
| configureLibraryCommon(result, "application"); |
| |
| return result; |
| } |
| |
| /** |
| * Constructs a digester configured to parse library specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected SpecificationDigester constructLibraryDigester() |
| { |
| SpecificationDigester result = constructBaseDigester("library-specification"); |
| |
| String pattern = "library-specification"; |
| |
| result.addRule(pattern, new CreateLibrarySpecificationRule()); |
| result.addRule(pattern, new SetPublicIdRule()); |
| |
| // Has no attributes |
| |
| configureLibraryCommon(result, "library-specification"); |
| |
| return result; |
| } |
| |
| /** |
| * Configures a digester to parse the common elements of |
| * a <application> or <library-specification>. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected void configureLibraryCommon(SpecificationDigester digester, String rootElementName) |
| { |
| String pattern = rootElementName + "/page"; |
| |
| // <page> |
| |
| digester.addValidate( |
| pattern, |
| "name", |
| PAGE_NAME_PATTERN, |
| "SpecificationParser.invalid-page-name"); |
| digester.addCallMethod(pattern, "setPageSpecificationPath", 2); |
| digester.addCallParam(pattern, 0, "name"); |
| digester.addCallParam(pattern, 1, "specification-path"); |
| |
| // <component-type> |
| |
| pattern = rootElementName + "/component-type"; |
| digester.addValidate( |
| pattern, |
| "type", |
| COMPONENT_ALIAS_PATTERN, |
| "SpecificationParser.invalid-component-type"); |
| digester.addCallMethod(pattern, "setComponentSpecificationPath", 2); |
| digester.addCallParam(pattern, 0, "type"); |
| digester.addCallParam(pattern, 1, "specification-path"); |
| |
| // <component-alias> |
| // From 1.3 DTD, replaced with <component-type> in 3.0 DTD |
| |
| pattern = rootElementName + "/component-alias"; |
| digester.addValidate( |
| pattern, |
| "type", |
| COMPONENT_ALIAS_PATTERN, |
| "SpecificationParser.invalid-component-type"); |
| digester.addCallMethod(pattern, "setComponentSpecificationPath", 2); |
| digester.addCallParam(pattern, 0, "type"); |
| digester.addCallParam(pattern, 1, "specification-path"); |
| |
| // <service> |
| |
| pattern = rootElementName + "/service"; |
| |
| digester.addValidate( |
| pattern, |
| "name", |
| SERVICE_NAME_PATTERN, |
| "SpecificationParser.invalid-service-name"); |
| digester.addCallMethod(pattern, "setServiceClassName", 2); |
| digester.addCallParam(pattern, 0, "name"); |
| digester.addCallParam(pattern, 1, "class"); |
| |
| // <library> |
| |
| pattern = rootElementName + "/library"; |
| |
| digester.addValidate( |
| pattern, |
| "id", |
| LIBRARY_ID_PATTERN, |
| "SpecificationParser.invalid-library-id"); |
| digester.addRule(pattern, new DisallowFrameworkNamespaceRule()); |
| digester.addCallMethod(pattern, "setLibrarySpecificationPath", 2); |
| digester.addCallParam(pattern, 0, "id"); |
| digester.addCallParam(pattern, 1, "specification-path"); |
| |
| // <extension> |
| |
| pattern = rootElementName + "/extension"; |
| |
| digester.addRule(pattern, new CreateExtensionSpecificationRule()); |
| digester.addValidate( |
| pattern, |
| "name", |
| EXTENSION_NAME_PATTERN, |
| "SpecificationParser.invalid-extension-name"); |
| digester.addSetBooleanProperty(pattern, "immediate", "immediate"); |
| digester.addSetLimitedProperties(pattern, "class", "className"); |
| digester.addConnectChild(pattern, "addExtensionSpecification", "name"); |
| |
| // <configure> within <extension> |
| |
| pattern = rootElementName + "/extension/configure"; |
| digester.addValidate( |
| pattern, |
| "property-name", |
| PROPERTY_NAME_PATTERN, |
| "SpecificationParser.invalid-property-name"); |
| digester.addRule(pattern, new ProcessExtensionConfigurationRule()); |
| |
| } |
| |
| /** |
| * Returns a digester configured to parse page specifications. |
| * |
| * @since 3.0 |
| * |
| **/ |
| |
| protected SpecificationDigester constructPageDigester() |
| { |
| SpecificationDigester result = constructBaseDigester("page-specification"); |
| |
| // <page-specification> |
| |
| String pattern = "page-specification"; |
| |
| result.addRule(pattern, new CreateComponentSpecificationRule()); |
| result.addRule(pattern, new SetPublicIdRule()); |
| result.addInitializeProperty(pattern, "pageSpecification", Boolean.TRUE); |
| result.addInitializeProperty(pattern, "allowBody", Boolean.TRUE); |
| result.addInitializeProperty(pattern, "allowInformalParameters", Boolean.FALSE); |
| result.addSetLimitedProperties(pattern, "class", "componentClassName"); |
| |
| configureCommon(result, "page-specification"); |
| |
| return result; |
| } |
| |
| /** |
| * Returns a digester configured to parse component specifications. |
| * |
| **/ |
| |
| protected SpecificationDigester constructComponentDigester() |
| { |
| SpecificationDigester result = constructBaseDigester("component-specification"); |
| |
| // <component-specification> |
| |
| String pattern = "component-specification"; |
| |
| result.addRule(pattern, new CreateComponentSpecificationRule()); |
| result.addRule(pattern, new SetPublicIdRule()); |
| |
| result.addSetBooleanProperty(pattern, "allow-body", "allowBody"); |
| result.addSetBooleanProperty( |
| pattern, |
| "allow-informal-parameters", |
| "allowInformalParameters"); |
| result.addSetLimitedProperties(pattern, "class", "componentClassName"); |
| |
| // TODO: publicId |
| |
| // <parameter> |
| |
| pattern = "component-specification/parameter"; |
| |
| result.addRule(pattern, new CreateParameterSpecificationRule()); |
| result.addValidate( |
| pattern, |
| "name", |
| PARAMETER_NAME_PATTERN, |
| "SpecificationParser.invalid-parameter-name"); |
| |
| result.addValidate( |
| pattern, |
| "property-name", |
| PROPERTY_NAME_PATTERN, |
| "SpecificationParser.invalid-property-name"); |
| |
| // We use a slight kludge to set the default propertyName from the |
| // name attribute. If the spec includes a property-name attribute, that |
| // will overwrite the default property name). |
| // Remember that digester rule order counts! |
| |
| result.addSetLimitedProperties(pattern, "name", "propertyName"); |
| |
| // java-type is a holdover from the 1.3 DTD and will eventually be removed. |
| |
| result.addSetLimitedProperties( |
| pattern, |
| new String[] { "property-name", "type", "java-type", "default-value" }, |
| new String[] { "propertyName", "type", "type", "defaultValue" }); |
| |
| result.addSetBooleanProperty(pattern, "required", "required"); |
| |
| result.addSetConvertedProperty(pattern, CONVERSION_MAP, "direction", "direction"); |
| |
| result.addConnectChild(pattern, "addParameter", "name"); |
| |
| // <reserved-parameter> |
| |
| pattern = "component-specification/reserved-parameter"; |
| |
| result.addCallMethod(pattern, "addReservedParameterName", 1); |
| result.addCallParam(pattern, 0, "name"); |
| |
| configureCommon(result, "component-specification"); |
| |
| return result; |
| } |
| |
| /** |
| * Configure the common elements shared by both <page-specification> |
| * and <component-specification>. |
| * |
| **/ |
| |
| protected void configureCommon(SpecificationDigester digester, String rootElementName) |
| { |
| // <bean> |
| |
| String pattern = rootElementName + "/bean"; |
| |
| digester.addRule(pattern, new CreateBeanSpecificationRule()); |
| digester.addValidate( |
| pattern, |
| "name", |
| BEAN_NAME_PATTERN, |
| "SpecificationParser.invalid-bean-name"); |
| digester.addSetConvertedProperty(pattern, CONVERSION_MAP, "lifecycle", "lifecycle"); |
| digester.addSetLimitedProperties(pattern, "class", "className"); |
| digester.addConnectChild(pattern, "addBeanSpecification", "name"); |
| |
| // <set-property> inside <bean> |
| |
| pattern = rootElementName + "/bean/set-property"; |
| |
| digester.addRule(pattern, new CreateExpressionBeanInitializerRule()); |
| digester.addSetLimitedProperties(pattern, "name", "propertyName"); |
| digester.addSetExtendedProperty(pattern, "expression", "expression", true); |
| digester.addSetNext(pattern, "addInitializer"); |
| |
| // <set-string-property> inside <bean> |
| // This is for compatibility with the 1.3 DTD |
| |
| pattern = rootElementName + "/bean/set-string-property"; |
| |
| digester.addRule(pattern, new CreateStringBeanInitializerRule()); |
| digester.addSetLimitedProperties( |
| pattern, |
| new String[] { "name", "key" }, |
| new String[] { "propertyName", "key" }); |
| |
| digester.addSetNext(pattern, "addInitializer"); |
| |
| // It's now set-message-property in the 3.0 DTD |
| |
| pattern = rootElementName + "/bean/set-message-property"; |
| |
| digester.addRule(pattern, new CreateStringBeanInitializerRule()); |
| digester.addSetLimitedProperties( |
| pattern, |
| new String[] { "name", "key" }, |
| new String[] { "propertyName", "key" }); |
| |
| digester.addSetNext(pattern, "addInitializer"); |
| |
| // <component> |
| |
| pattern = rootElementName + "/component"; |
| |
| digester.addRule(pattern, new CreateContainedComponentRule()); |
| digester.addValidate( |
| pattern, |
| "id", |
| COMPONENT_ID_PATTERN, |
| "SpecificationParser.invalid-component-id"); |
| digester.addValidate( |
| pattern, |
| "type", |
| COMPONENT_TYPE_PATTERN, |
| "SpecificationParser.invalid-component-type"); |
| digester.addSetLimitedProperties(pattern, "type", "type"); |
| digester.addRule(pattern, new ComponentCopyOfRule()); |
| digester.addConnectChild(pattern, "addComponent", "id"); |
| digester.addSetBooleanProperty( |
| pattern, |
| "inherit-informal-parameters", |
| "inheritInformalParameters"); |
| |
| // <binding> inside <component> |
| |
| pattern = rootElementName + "/component/binding"; |
| |
| Rule createBindingSpecificationRule = new CreateBindingSpecificationRule(); |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.DYNAMIC); |
| digester.addSetExtendedProperty(pattern, "expression", "value", true); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <field-binding> inside <component> |
| // For compatibility with 1.3 DTD only, removed in 3.0 DTD |
| |
| pattern = rootElementName + "/component/field-binding"; |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.FIELD); |
| digester.addSetExtendedProperty(pattern, "field-name", "value", true); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <inherited-binding> inside <component> |
| |
| pattern = rootElementName + "/component/inherited-binding"; |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.INHERITED); |
| digester.addSetLimitedProperties(pattern, "parameter-name", "value"); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <static-binding> inside <component> |
| |
| pattern = rootElementName + "/component/static-binding"; |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.STATIC); |
| digester.addSetExtendedProperty(pattern, "value", "value", true); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <string-binding> inside <component> |
| // Maintained just for 1.3 DTD compatibility |
| |
| pattern = rootElementName + "/component/string-binding"; |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.STRING); |
| digester.addSetLimitedProperties(pattern, "key", "value"); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // Renamed to <message-binding> in the 3.0 DTD |
| |
| pattern = rootElementName + "/component/message-binding"; |
| |
| digester.addRule(pattern, createBindingSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", BindingType.STRING); |
| digester.addSetLimitedProperties(pattern, "key", "value"); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <listener-binding> inside <component> |
| |
| pattern = rootElementName + "/component/listener-binding"; |
| |
| digester.addRule(pattern, new CreateListenerBindingSpecificationRule()); |
| digester.addSetLimitedProperties(pattern, "language", "language"); |
| digester.addBody(pattern, "value"); |
| digester.addConnectChild(pattern, "setBinding", "name"); |
| |
| // <external-asset> |
| |
| pattern = rootElementName + "/external-asset"; |
| |
| Rule createAssetSpecificationRule = new CreateAssetSpecificationRule(); |
| |
| digester.addRule(pattern, createAssetSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", AssetType.EXTERNAL); |
| digester.addValidate( |
| pattern, |
| "name", |
| ASSET_NAME_PATTERN, |
| "SpecificationParser.invalid-asset-name"); |
| digester.addSetLimitedProperties(pattern, "URL", "path"); |
| digester.addConnectChild(pattern, "addAsset", "name"); |
| |
| // <context-asset> |
| |
| pattern = rootElementName + "/context-asset"; |
| |
| digester.addRule(pattern, createAssetSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", AssetType.CONTEXT); |
| digester.addValidate( |
| pattern, |
| "name", |
| ASSET_NAME_PATTERN, |
| "SpecificationParser.invalid-asset-name"); |
| |
| // TODO: $template$ |
| |
| digester.addSetLimitedProperties(pattern, "path", "path"); |
| digester.addConnectChild(pattern, "addAsset", "name"); |
| |
| // <private-asset> |
| |
| pattern = rootElementName + "/private-asset"; |
| |
| digester.addRule(pattern, createAssetSpecificationRule); |
| digester.addInitializeProperty(pattern, "type", AssetType.PRIVATE); |
| digester.addValidate( |
| pattern, |
| "name", |
| ASSET_NAME_PATTERN, |
| "SpecificationParser.invalid-asset-name"); |
| |
| // TODO: $template$ |
| |
| digester.addSetLimitedProperties(pattern, "resource-path", "path"); |
| digester.addConnectChild(pattern, "addAsset", "name"); |
| |
| // <property-specification> |
| |
| pattern = rootElementName + "/property-specification"; |
| |
| digester.addRule(pattern, new CreatePropertySpecificationRule()); |
| digester.addValidate( |
| pattern, |
| "name", |
| PROPERTY_NAME_PATTERN, |
| "SpecificationParser.invalid-property-name"); |
| digester.addSetLimitedProperties( |
| pattern, |
| new String[] { "name", "type" }, |
| new String[] { "name", "type" }); |
| digester.addSetBooleanProperty(pattern, "persistent", "persistent"); |
| digester.addSetExtendedProperty(pattern, "initial-value", "initialValue", false); |
| digester.addSetNext(pattern, "addPropertySpecification"); |
| } |
| |
| private String getURL(String resource) |
| { |
| return getClass().getResource(resource).toExternalForm(); |
| } |
| } |