Java tutorial
// 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.spec; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.tapestry.ApplicationRuntimeException; import org.apache.tapestry.ILocation; import org.apache.tapestry.IResourceLocation; import org.apache.tapestry.IResourceResolver; import org.apache.tapestry.Tapestry; /** * Specification for a library. {@link org.apache.tapestry.spec.ApplicationSpecification} * is a specialized kind of library. * * @author Howard Lewis Ship * @version $Id: LibrarySpecification.java,v 1.10 2004/02/19 17:37:52 hlship Exp $ * @since 2.2 * **/ public class LibrarySpecification extends LocatablePropertyHolder implements ILibrarySpecification { /** * Resource resolver (used to instantiate extensions). * **/ private IResourceResolver _resolver; /** * Map of page name to page specification path. * **/ private Map _pages; /** * Map of component alias to component specification path. * **/ private Map _components; /** * Map of service name to service class name. * **/ private Map _services; /** * Map of library id to library specification path. * **/ private Map _libraries; private String _description; /** * Map of extension name to {@link IExtensionSpecification}. * **/ private Map _extensions; /** * Map of extension name to Object for instantiated extensions. * **/ private Map _instantiatedExtensions; /** * The XML Public Id used when the library specification was read * (if applicable). * * @since 2.2 * **/ private String _publicId; /** * The location of the specification. * **/ private IResourceLocation _specificationLocation; public String getLibrarySpecificationPath(String id) { return (String) get(_libraries, id); } /** * Sets the specification path for an embedded library. * * @throws IllegalArgumentException if a library with the given * id already exists * **/ public void setLibrarySpecificationPath(String id, String path) { if (_libraries == null) _libraries = new HashMap(); if (_libraries.containsKey(id)) throw new IllegalArgumentException( Tapestry.format("LibrarySpecification.duplicate-child-namespace-id", id)); _libraries.put(id, path); } public List getLibraryIds() { return sortedKeys(_libraries); } public String getPageSpecificationPath(String name) { return (String) get(_pages, name); } public void setPageSpecificationPath(String name, String path) { if (_pages == null) _pages = new HashMap(); if (_pages.containsKey(name)) throw new IllegalArgumentException(Tapestry.format("LibrarySpecification.duplicate-page-name", name)); _pages.put(name, path); } public List getPageNames() { return sortedKeys(_pages); } public void setComponentSpecificationPath(String alias, String path) { if (_components == null) _components = new HashMap(); if (_components.containsKey(alias)) throw new IllegalArgumentException( Tapestry.format("LibrarySpecification.duplicate-component-alias", alias)); _components.put(alias, path); } public String getComponentSpecificationPath(String alias) { return (String) get(_components, alias); } /** * @since 3.0 * **/ public List getComponentTypes() { return sortedKeys(_components); } public String getServiceClassName(String name) { return (String) get(_services, name); } public List getServiceNames() { return sortedKeys(_services); } public void setServiceClassName(String name, String className) { if (_services == null) _services = new HashMap(); if (_services.containsKey(name)) throw new IllegalArgumentException( Tapestry.format("LibrarySpecification.duplicate-service-name", name)); _services.put(name, className); } private List sortedKeys(Map map) { if (map == null) return Collections.EMPTY_LIST; List result = new ArrayList(map.keySet()); Collections.sort(result); return result; } private Object get(Map map, Object key) { if (map == null) return null; return map.get(key); } /** * * Returns the documentation for this library.. * * **/ public String getDescription() { return _description; } /** * * Sets the documentation for this library. * * **/ public void setDescription(String description) { _description = description; } /** * Returns a Map of extensions; key is extension name, value is * {@link org.apache.tapestry.spec.IExtensionSpecification}. * May return null. The returned Map is immutable. * **/ public Map getExtensionSpecifications() { if (_extensions == null) return null; return Collections.unmodifiableMap(_extensions); } /** * Adds another extension specification. * * @throws IllegalArgumentException if an extension with the given name already exists. * **/ public void addExtensionSpecification(String name, IExtensionSpecification extension) { if (_extensions == null) _extensions = new HashMap(); if (_extensions.containsKey(name)) throw new IllegalArgumentException( Tapestry.format("LibrarySpecification.duplicate-extension-name", this, name)); _extensions.put(name, extension); } /** * Returns a sorted List of the names of all extensions. May return the empty list, * but won't return null. * **/ public synchronized List getExtensionNames() { return sortedKeys(_instantiatedExtensions); } /** * Returns the named IExtensionSpecification, or null if it doesn't exist. * **/ public IExtensionSpecification getExtensionSpecification(String name) { if (_extensions == null) return null; return (IExtensionSpecification) _extensions.get(name); } /** * Returns true if this library specification has a specification * for the named extension. * **/ public boolean checkExtension(String name) { if (_extensions == null) return false; return _extensions.containsKey(name); } /** * Returns an instantiated extension. Extensions are created as needed and * cached for later use. * * @throws IllegalArgumentException if no extension specification exists for the * given name. * **/ public synchronized Object getExtension(String name) { return getExtension(name, null); } /** @since 3.0 **/ public synchronized Object getExtension(String name, Class typeConstraint) { if (_instantiatedExtensions == null) _instantiatedExtensions = new HashMap(); Object result = _instantiatedExtensions.get(name); IExtensionSpecification spec = getExtensionSpecification(name); if (spec == null) throw new IllegalArgumentException(Tapestry.format("LibrarySpecification.no-such-extension", name)); if (result == null) { result = spec.instantiateExtension(_resolver); _instantiatedExtensions.put(name, result); } if (typeConstraint != null) applyTypeConstraint(name, result, typeConstraint, spec.getLocation()); return result; } /** * Checks that an extension conforms to the supplied type constraint. * * @throws IllegalArgumentException if the extension fails the check. * * @since 3.0 * **/ protected void applyTypeConstraint(String name, Object extension, Class typeConstraint, ILocation location) { Class extensionClass = extension.getClass(); // Can you assign an instance of the extension to a variable // of type typeContraint legally? if (typeConstraint.isAssignableFrom(extensionClass)) return; String key = typeConstraint.isInterface() ? "LibrarySpecification.extension-does-not-implement-interface" : "LibrarySpecification.extension-not-a-subclass"; throw new ApplicationRuntimeException( Tapestry.format(key, name, extensionClass.getName(), typeConstraint.getName()), location, null); } /** * Invoked after the entire specification has been constructed * to instantiate any extensions marked immediate. * **/ public synchronized void instantiateImmediateExtensions() { if (_extensions == null) return; Iterator i = _extensions.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); IExtensionSpecification spec = (IExtensionSpecification) entry.getValue(); if (!spec.isImmediate()) continue; String name = (String) entry.getKey(); getExtension(name); } } public IResourceResolver getResourceResolver() { return _resolver; } public void setResourceResolver(IResourceResolver resolver) { _resolver = resolver; } /** * Returns the extensions map. * @return Map of objects. * **/ protected Map getExtensions() { return _extensions; } /** * Updates the extension map. * @param extension A Map of extension specification paths * keyed on extension id. * * <p>The map is retained, not copied. * **/ protected void setExtensions(Map extension) { _extensions = extension; } /** * Returns the libraries map. * @return Map of {@link LibrarySpecification}. * **/ protected Map getLibraries() { return _libraries; } /** * Updates the library map. * @param libraries A Map of library specification paths * keyed on library id. * * <p>The map is retained, not copied. * **/ protected void setLibraries(Map libraries) { _libraries = libraries; } /** * Returns the pages map. * @return Map of {@link IComponentSpecification}. * **/ protected Map getPages() { return _pages; } /** * Updates the page map. * @param pages A Map of page specification paths * keyed on page id. * * <p>The map is retained, not copied. * **/ protected void setPages(Map pages) { _pages = pages; } /** * Returns the services. * @return Map of service class names. * **/ protected Map getServices() { return _services; } /** * Updates the services map. * @param services A Map of the fully qualified names of classes * which implement * {@link org.apache.tapestry.engine.IEngineService} * keyed on service id. * * <p>The map is retained, not copied. * **/ protected void setServices(Map services) { _services = services; } /** * Returns the components map. * @return Map of {@link IContainedComponent}. * **/ protected Map getComponents() { return _components; } /** * Updates the components map. * @param components A Map of {@link IContainedComponent} keyed on component id. * The map is retained, not copied. * **/ protected void setComponents(Map components) { _components = components; } /** * Returns the XML Public Id for the library 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. * * **/ public String getPublicId() { return _publicId; } public void setPublicId(String publicId) { _publicId = publicId; } /** @since 3.0 **/ public IResourceLocation getSpecificationLocation() { return _specificationLocation; } /** @since 3.0 **/ public void setSpecificationLocation(IResourceLocation specificationLocation) { _specificationLocation = specificationLocation; } /** @since 3.0 **/ public synchronized String toString() { ToStringBuilder builder = new ToStringBuilder(this); builder.append("components", _components); builder.append("description", _description); builder.append("instantiatedExtensions", _instantiatedExtensions); builder.append("libraries", _libraries); builder.append("pages", _pages); builder.append("publicId", _publicId); builder.append("resolver", _resolver); builder.append("services", _services); builder.append("specificationLocation", _specificationLocation); extendDescription(builder); return builder.toString(); } /** * Does nothing, subclasses may override to add additional * description. * * @see #toString() * @since 3.0 * **/ protected void extendDescription(ToStringBuilder builder) { } }