Package org.apache.tapestry.spec

Source Code of org.apache.tapestry.spec.ComponentSpecification

// Copyright 2004, 2005 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.spec;

import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.HiveMind;
import org.apache.hivemind.Resource;
import org.apache.hivemind.util.Defense;
import org.apache.hivemind.util.ToStringBuilder;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IForm;
import org.apache.tapestry.event.BrowserEvent;
import org.apache.tapestry.internal.event.ComponentEventProperty;
import org.apache.tapestry.internal.event.EventBoundListener;

import java.util.*;

/**
* A specification for a component, as read from an XML specification file.
* <p>
* A specification consists of
* <ul>
* <li>An implementing class
* <li>An optional description
* <li>A set of contained components
* <li>Bindings for the properties of each contained component
* <li>A set of named assets
* <li>Definitions for managed beans
* <li>Any reserved names (used for HTML attributes)
* <li>Declared properties
* <li>Property injections
* </ul>
* <p>
* From this information, an actual component may be instantiated and initialized. Instantiating a
* component is usually a recursive process, since to initialize a container component, it is
* necessary to instantiate and initialize its contained components as well.
*
* @see org.apache.tapestry.IComponent
* @see IContainedComponent
* @see org.apache.tapestry.engine.IPageLoader
* @author Howard Lewis Ship
*/

public class ComponentSpecification extends LocatablePropertyHolder implements IComponentSpecification
{
    /**
     * Keyed on component id, value is {@link IContainedComponent}.
     */

    protected Map _components;

    /**
     * Keyed on asset name, value is {@link IAssetSpecification}.
     */

    protected Map _assets;

    /**
     * Defines all formal parameters. Keyed on parameter name, value is
     * {@link IParameterSpecification}.
     */

    protected Map _parameters;

    /**
     * Defines all helper beans. Keyed on name, value is {@link IBeanSpecification}.
     *
     * @since 1.0.4
     */

    protected Map _beans;

    /**
     * The names of all reserved informal parameter names (as lower-case). This allows the page
     * loader to filter out any informal parameters during page load, rather than during render.
     *
     * @since 1.0.5
     */

    protected Set _reservedParameterNames;

    private String _componentClassName;

    /** @since 1.0.9 * */

    private String _description;
   
    /**
     * Is the component allowed to have a body (that is, wrap other elements?).
     */

    private boolean _allowBody = true;

    /**
     * Is the component allow to have informal parameter specified.
     */

    private boolean _allowInformalParameters = true;

    /**
     * The XML Public Id used when the page or component specification was read (if applicable).
     *
     * @since 2.2
     */

    private String _publicId;

    /**
     * Indicates that the specification is for a page, not a component.
     *
     * @since 2.2
     */

    private boolean _pageSpecification;

    /**
     * The location from which the specification was obtained.
     *
     * @since 3.0
     */

    private Resource _specificationLocation;

    /**
     * A Map of {@link IPropertySpecification}keyed on the name of the property.
     *
     * @since 3.0
     */

    private Map _propertySpecifications;

    /**
     * List of {@link InjectSpecification}.
     *
     * @since 4.0
     */

    private List _injectSpecifications;

    /**
     * Keyed on property name, value is some other object (such as an IAssetSpecification) that has
     * claimed a property of the page.
     *
     * @since 4.0
     */

    private Map _claimedProperties;

    /**
     * @since 4.0
     */

    private boolean _deprecated = false;
   
    private Map _componentEvents = new HashMap();
    private Map _elementEvents = new HashMap();

    /**
     * @throws ApplicationRuntimeException
     *             if the name already exists.
     */

    public void addAsset(String name, IAssetSpecification asset)
    {
        if (_assets == null)
            _assets = new HashMap();

        IAssetSpecification existing = (IAssetSpecification) _assets.get(name);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.duplicateAsset(name, existing),
                    asset.getLocation(), null);

        claimProperty(asset.getPropertyName(), asset);

        _assets.put(name, asset);
    }

    /**
     * @throws ApplicationRuntimeException if the id is already defined.
     */

    public void addComponent(String id, IContainedComponent component)
    {
        if (_components == null)
            _components = new HashMap();

        IContainedComponent existing = (IContainedComponent) _components.get(id);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.duplicateComponent(id, existing),
                    component.getLocation(), null);

        _components.put(id, component);

        claimProperty(component.getPropertyName(), component);
    }

    /**
     * Adds the parameter. The name is added as a reserved name.
     *
     * @throws ApplicationRuntimeException if the name already exists.
     */

    public void addParameter(IParameterSpecification spec)
    {
        if (_parameters == null)
            _parameters = new HashMap();

        String name = spec.getParameterName();

        addParameterByName(name, spec);

        Iterator i = spec.getAliasNames().iterator();
        while (i.hasNext())
        {
            String alias = (String) i.next();

            addParameterByName(alias, spec);
        }

        claimProperty(spec.getPropertyName(), spec);
    }

    private void addParameterByName(String name, IParameterSpecification spec)
    {
        IParameterSpecification existing = (IParameterSpecification) _parameters.get(name);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.duplicateParameter(name, existing),
                    spec.getLocation(), null);

        _parameters.put(name, spec);

        addReservedParameterName(name);
    }

    /**
     * Returns true if the component is allowed to wrap other elements (static HTML or other
     * components). The default is true.
     *
     * @see #setAllowBody(boolean)
     */

    public boolean getAllowBody()
    {
        return _allowBody;
    }

    /**
     * Returns true if the component allows informal parameters (parameters not formally defined).
     * Informal parameters are generally used to create additional HTML attributes for an HTML tag
     * rendered by the component. This is often used to specify JavaScript event handlers or the
     * class of the component (for Cascarding Style Sheets).
     * <p>
     * The default value is true.
     *
     * @see #setAllowInformalParameters(boolean)
     */

    public boolean getAllowInformalParameters()
    {
        return _allowInformalParameters;
    }

    /**
     * Returns the {@link IAssetSpecification}with the given name, or null if no such specification
     * exists.
     *
     * @see #addAsset(String,IAssetSpecification)
     */

    public IAssetSpecification getAsset(String name)
    {
        return (IAssetSpecification) get(_assets, name);
    }

    /**
     * Returns a <code>List</code> of the String names of all assets, in alphabetical order.
     */

    public List getAssetNames()
    {
        return sortedKeys(_assets);
    }

    /**
     * Returns the specification of a contained component with the given id, or null if no such
     * contained component exists.
     *
     * @see #addComponent(String, IContainedComponent)
     */

    public IContainedComponent getComponent(String id)
    {
        return (IContainedComponent) get(_components, id);
    }

   
    public String getComponentClassName()
    {
        return _componentClassName;
    }

    /**
     * Returns an <code>List</code> of the String names of the {@link IContainedComponent}s for
     * this component.
     *
     * @see #addComponent(String, IContainedComponent)
     */

    public List getComponentIds()
    {
        return sortedKeys(_components);
    }

    /**
     * Returns the specification of a parameter with the given name, or null if no such parameter
     * exists.
     *
     * @see #addParameterByName(String, IParameterSpecification)
     */

    public IParameterSpecification getParameter(String name)
    {
        return (IParameterSpecification) get(_parameters, name);
    }

    public Collection getRequiredParameters()
    {
        if (_parameters == null)
            return Collections.EMPTY_LIST;

        Collection result = new ArrayList();

        Iterator i = _parameters.entrySet().iterator();
        while (i.hasNext())
        {
            Map.Entry entry = (Map.Entry) i.next();
            String name = (String) entry.getKey();
            IParameterSpecification spec = (IParameterSpecification) entry.getValue();

            if (!spec.isRequired())
                continue;

            if (!name.equals(spec.getParameterName()))
                continue;

            result.add(spec);
        }

        return result;
    }

    /**
     * Returns a List of of String names of all parameters. This list is in alphabetical order.
     *
     * @see #addParameterByName(String, IParameterSpecification)
     */

    public List getParameterNames()
    {
        return sortedKeys(_parameters);
    }

    public void setAllowBody(boolean value)
    {
        _allowBody = value;
    }

    public void setAllowInformalParameters(boolean value)
    {
        _allowInformalParameters = value;
    }

    public void setComponentClassName(String value)
    {
        _componentClassName = value;
    }

    /**
     * @since 1.0.4
     * @throws ApplicationRuntimeException
     *             if the bean already has a specification.
     */

    public void addBeanSpecification(String name, IBeanSpecification specification)
    {
        if (_beans == null)
            _beans = new HashMap();

        IBeanSpecification existing = (IBeanSpecification) _beans.get(name);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.duplicateBean(name, existing),
                    specification.getLocation(), null);

        claimProperty(specification.getPropertyName(), specification);

        _beans.put(name, specification);
    }

    /**
     * Returns the {@link IBeanSpecification}for the given name, or null if not such specification
     * exists.
     *
     * @since 1.0.4
     */

    public IBeanSpecification getBeanSpecification(String name)
    {
        return (IBeanSpecification) get(_beans, name);
    }

    /**
     * Returns an unmodifiable collection of the names of all beans.
     */

    public Collection getBeanNames()
    {
        if (_beans == null)
            return Collections.EMPTY_LIST;

        return Collections.unmodifiableCollection(_beans.keySet());
    }

    /**
     * Adds the value as a reserved name. Reserved names are not allowed as the names of informal
     * parameters. Since the comparison is caseless, the value is converted to lowercase before
     * being stored.
     *
     * @since 1.0.5
     */

    public void addReservedParameterName(String value)
    {
        if (_reservedParameterNames == null)
            _reservedParameterNames = new HashSet();

        _reservedParameterNames.add(value.toLowerCase());
    }

    /**
     * Returns true if the value specified is in the reserved name list. The comparison is caseless.
     * All formal parameters are automatically in the reserved name list, as well as any additional
     * reserved names specified in the component specification. The latter refer to HTML attributes
     * generated directly by the component.
     *
     * @since 1.0.5
     */

    public boolean isReservedParameterName(String value)
    {
        if (_reservedParameterNames == null)
            return false;

        return _reservedParameterNames.contains(value.toLowerCase());
    }

    public String toString()
    {
        ToStringBuilder builder = new ToStringBuilder(this);

        builder.append("componentClassName", _componentClassName);
        builder.append("pageSpecification", _pageSpecification);
        builder.append("specificationLocation", _specificationLocation);
        builder.append("allowBody", _allowBody);
        builder.append("allowInformalParameter", _allowInformalParameters);

        return builder.toString();
    }

    /**
     * Returns the documentation for this component.
     *
     * @since 1.0.9
     */

    public String getDescription()
    {
        return _description;
    }

    /**
     * Sets the documentation for this component.
     *
     * @since 1.0.9
     */

    public void setDescription(String description)
    {
        _description = description;
    }

    /**
     * Returns the XML Public Id for the specification file, or null if not applicable.
     * <p>
     * This method exists as a convienience for the Spindle plugin. A previous method used an
     * arbitrary version string, the public id is more useful and less ambiguous.
     *
     * @since 2.2
     */

    public String getPublicId()
    {
        return _publicId;
    }

    /** @since 2.2 * */

    public void setPublicId(String publicId)
    {
        _publicId = publicId;
    }

    /**
     * Returns true if the specification is known to be a page specification and not a component
     * specification. Earlier versions of the framework did not distinguish between the two, but
     * starting in 2.2, there are seperate XML entities for pages and components. Pages omit several
     * attributes and entities related to parameters, as parameters only make sense for components.
     *
     * @since 2.2
     */

    public boolean isPageSpecification()
    {
        return _pageSpecification;
    }

    /** @since 2.2 * */

    public void setPageSpecification(boolean pageSpecification)
    {
        _pageSpecification = pageSpecification;
    }

    /** @since 2.2 * */

    private List sortedKeys(Map input)
    {
        if (input == null)
            return Collections.EMPTY_LIST;

        List result = new ArrayList(input.keySet());

        Collections.sort(result);

        return result;
    }

    /** @since 2.2 * */

    private Object get(Map map, Object key)
    {
        if (map == null)
            return null;

        return map.get(key);
    }

    /** @since 3.0 * */

    public Resource getSpecificationLocation()
    {
        return _specificationLocation;
    }

    /** @since 3.0 * */

    public void setSpecificationLocation(Resource specificationLocation)
    {
        _specificationLocation = specificationLocation;
    }

    /**
     * Adds a new property specification. The name of the property must not already be defined (and
     * must not change after being added).
     *
     * @since 3.0
     */

    public void addPropertySpecification(IPropertySpecification spec)
    {
        if (_propertySpecifications == null)
            _propertySpecifications = new HashMap();

        String name = spec.getName();
        IPropertySpecification existing = (IPropertySpecification) _propertySpecifications
                .get(name);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.duplicateProperty(name, existing), spec.getLocation(), null);

        claimProperty(name, spec);

        _propertySpecifications.put(name, spec);
    }

    /**
     * Returns a sorted, immutable list of the names of all
     * {@link org.apache.tapestry.spec.IPropertySpecification}s.
     *
     * @since 3.0
     */

    public List getPropertySpecificationNames()
    {
        return sortedKeys(_propertySpecifications);
    }

    /**
     * Returns the named {@link org.apache.tapestry.spec.IPropertySpecification}, or null if no
     * such specification exist.
     *
     * @since 3.0
     * @see #addPropertySpecification(IPropertySpecification)
     */

    public IPropertySpecification getPropertySpecification(String name)
    {
        return (IPropertySpecification) get(_propertySpecifications, name);
    }

    public void addInjectSpecification(InjectSpecification spec)
    {
        if (_injectSpecifications == null)
            _injectSpecifications = new ArrayList();

        claimProperty(spec.getProperty(), spec);

        _injectSpecifications.add(spec);
    }

    public List getInjectSpecifications()
    {
        return safeList(_injectSpecifications);
    }

    private List safeList(List input)
    {
        if (input == null)
            return Collections.EMPTY_LIST;

        return Collections.unmodifiableList(input);
    }

    private void claimProperty(String propertyName, Object subSpecification)
    {
        if (propertyName == null)
            return;

        if (_claimedProperties == null)
            _claimedProperties = new HashMap();

        Object existing = _claimedProperties.get(propertyName);

        if (existing != null)
            throw new ApplicationRuntimeException(SpecMessages.claimedProperty(
                    propertyName,
                    existing), HiveMind.getLocation(subSpecification), null);

        _claimedProperties.put(propertyName, subSpecification);
    }

    /** @since 4.0 */
    public boolean isDeprecated()
    {
        return _deprecated;
    }

    /** @since 4.0 */
    public void setDeprecated(boolean deprecated)
    {
        _deprecated = deprecated;
    }

    public Set getReservedParameterNames()
    {
        if (_reservedParameterNames == null)
            return Collections.EMPTY_SET;

        return Collections.unmodifiableSet(_reservedParameterNames);
    }
   
    /**
     * {@inheritDoc}
     */
    public void addEventListener(String componentId, String[] events,
            String methodName, String formId, boolean validateForm, boolean async, boolean focus, boolean autoSubmit)
    {
        ComponentEventProperty property = getComponentEvents(componentId);
        if (property == null) {
            property = new ComponentEventProperty(componentId);
            _componentEvents.put(componentId, property);
        }

        property.addListener(events, methodName, formId, validateForm, async, focus, autoSubmit);
    }
   
    /**
     * {@inheritDoc}
     */
    public void addElementEventListener(String elementId, String[] events,
            String methodName, String formId, boolean validateForm, boolean async, boolean focus)
    {
        ComponentEventProperty property = getElementEvents(elementId);
        if (property == null) {
            property = new ComponentEventProperty(elementId);
            _elementEvents.put(elementId, property);
        }

        property.addListener(events, methodName, formId, validateForm, async, focus, true);
    }

    public void connectAutoSubmitEvents(IComponent component, IForm form)
    {
        Defense.notNull(form, "form");
       
        ComponentEventProperty property = getComponentEvents(component.getExtendedId());
       
        if (property == null)
            return;

        property.connectAutoSubmitEvents(form.getExtendedId());
    }

    public void rewireComponentId(String componentId, String extendedId, String idPath)
    {
        ComponentEventProperty prop = getComponentEvents(componentId);
        if (prop == null)
            return;

        if (_componentEvents.containsKey(extendedId))
            return;

        try {

            ComponentEventProperty clone = (ComponentEventProperty) prop.clone();

            clone.rewireComponentId(extendedId, idPath);

            _componentEvents.put(extendedId, clone);

        } catch (CloneNotSupportedException e) {

            throw new ApplicationRuntimeException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public ComponentEventProperty getComponentEvents(String id)
    {
        return (ComponentEventProperty)_componentEvents.get(id);
    }

    public Map getComponentEvents()
    {
        return _componentEvents;
    }

    /**
     * {@inheritDoc}
     */
    public ComponentEventProperty getElementEvents(String id)
    {
        return (ComponentEventProperty)_elementEvents.get(id);
    }
   
    /**
     * {@inheritDoc}
     */
    public Map getElementEvents()
    {
        return _elementEvents;
    }
   
    /**
     * {@inheritDoc}
     */
    public EventBoundListener[] getFormEvents(String formId, BrowserEvent event)
    {
        List ret = new ArrayList();
       
        Iterator it = _componentEvents.keySet().iterator();
        while (it.hasNext()) {
           
            String compId = (String)it.next();
            ComponentEventProperty prop = (ComponentEventProperty)_componentEvents.get(compId);

            ret.addAll(prop.getFormEventListeners(formId, event, null));
        }
       
        it = _elementEvents.keySet().iterator();
        while (it.hasNext()) {
           
            String compId = (String)it.next();
            ComponentEventProperty prop = (ComponentEventProperty)_elementEvents.get(compId);

            ret.addAll(prop.getFormEventListeners(formId, event, null));
        }
       
        return (EventBoundListener[])ret.toArray(new EventBoundListener[ret.size()]);
    }
   
    /**
     * {@inheritDoc}
     */
    public boolean hasElementEvents()
    {
        return _elementEvents.size() > 0;
    }

    /**
     * {@inheritDoc}
     */
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((_componentClassName == null) ? 0 : _componentClassName.hashCode());
        result = prime * result + ((_description == null) ? 0 : _description.hashCode());
        result = prime * result + (_pageSpecification ? 1231 : 1237);
        result = prime * result + ((_publicId == null) ? 0 : _publicId.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(Object obj)
    {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        final ComponentSpecification other = (ComponentSpecification) obj;
        if (_componentClassName == null) {
            if (other._componentClassName != null) return false;
        } else if (!_componentClassName.equals(other._componentClassName)) return false;
        if (_description == null) {
            if (other._description != null) return false;
        } else if (!_description.equals(other._description)) return false;
        if (_pageSpecification != other._pageSpecification) return false;
        if (_publicId == null) {
            if (other._publicId != null) return false;
        } else if (!_publicId.equals(other._publicId)) return false;
        if (_specificationLocation == null) {
            if (other._specificationLocation != null) return false;
        } else if (!_specificationLocation.getPath().equals(other._specificationLocation.getPath())) return false;
        return true;
    }
   
   
}
TOP

Related Classes of org.apache.tapestry.spec.ComponentSpecification

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.