blob: 611dce66abe193e6e7c0e92bf0f3a6c298bb843e [file] [log] [blame]
// Copyright 2004, 2008 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.engine;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import org.apache.tapestry.*;
import org.apache.tapestry.resource.ClasspathResourceLocation;
import org.apache.tapestry.spec.IComponentSpecification;
import org.apache.tapestry.spec.ILibrarySpecification;
import java.util.*;
/**
* Implementation of {@link org.apache.tapestry.INamespace}
* that works with a {@link ISpecificationSource} to
* obtain page and component specifications as needed.
*
* @author Howard Lewis Ship
* @version $Id$
* @since 2.2
*
**/
public class Namespace implements INamespace
{
private ILibrarySpecification _specification;
private ISpecificationSource _specificationSource;
private String _id;
private String _extendedId;
private INamespace _parent;
private boolean _frameworkNamespace;
private boolean _applicationNamespace;
/**
* Map of {@link org.apache.tapestry.spec.ComponentSpecification} keyed on page name.
* The map is synchronized because different threads may
* try to update it simultaneously (due to dynamic page
* discovery in the application namespace).
*
**/
private Map _pages = new ConcurrentHashMap();
/**
* Map of {@link org.apache.tapestry.spec.ComponentSpecification} keyed on
* component alias.
*
**/
private Map _components = new ConcurrentHashMap();
/**
* Map, keyed on id, of {@link INamespace}.
*
**/
private Map _children = new ConcurrentHashMap();
public Namespace(
String id,
INamespace parent,
ILibrarySpecification specification,
ISpecificationSource specificationSource)
{
_id = id;
_parent = parent;
_specification = specification;
_specificationSource = specificationSource;
_applicationNamespace = (_id == null);
_frameworkNamespace = FRAMEWORK_NAMESPACE.equals(_id);
}
public String toString()
{
StringBuffer buffer = new StringBuffer("Namespace@");
buffer.append(Integer.toHexString(hashCode()));
buffer.append('[');
if (_applicationNamespace)
buffer.append("<application>");
else
buffer.append(getExtendedId());
buffer.append(']');
return buffer.toString();
}
public String getId()
{
return _id;
}
public String getExtendedId()
{
if (_applicationNamespace)
return null;
if (_extendedId == null)
_extendedId = buildExtendedId();
return _extendedId;
}
public INamespace getParentNamespace()
{
return _parent;
}
public INamespace getChildNamespace(String id)
{
String firstId = id;
String nextIds = null;
// Split the id into first and next if it is a dot separated sequence
int index = id.indexOf('.');
if (index >= 0)
{
firstId = id.substring(0, index);
nextIds = id.substring(index + 1);
}
// Get the first namespace
INamespace result = (INamespace) _children.get(firstId);
if (result == null)
{
result = createNamespace(firstId);
_children.put(firstId, result);
}
// If the id is a dot separated sequence, recurse to find
// the needed namespace
if (result != null && nextIds != null)
result = result.getChildNamespace(nextIds);
return result;
}
public List getChildIds()
{
return _specification.getLibraryIds();
}
public IComponentSpecification getPageSpecification(String name)
{
IComponentSpecification result = (IComponentSpecification) _pages.get(name);
if (result == null)
{
result = locatePageSpecification(name);
_pages.put(name, result);
}
return result;
}
public List getPageNames()
{
Set names = new HashSet();
names.addAll(_pages.keySet());
names.addAll(_specification.getPageNames());
List result = new ArrayList(names);
Collections.sort(result);
return result;
}
public IComponentSpecification getComponentSpecification(String alias)
{
IComponentSpecification result = (IComponentSpecification) _components.get(alias);
if (result == null)
{
result = locateComponentSpecification(alias);
_components.put(alias, result);
}
return result;
}
public String getServiceClassName(String name)
{
return _specification.getServiceClassName(name);
}
public List getServiceNames()
{
return _specification.getServiceNames();
}
public ILibrarySpecification getSpecification()
{
return _specification;
}
private String buildExtendedId()
{
if (_parent == null)
return _id;
String parentId = _parent.getExtendedId();
// If immediate child of application namespace
if (parentId == null)
return _id;
return parentId + "." + _id;
}
/**
* Returns a string identifying the namespace, for use in
* error messages. I.e., "Application namespace" or "namespace 'foo'".
*
**/
public String getNamespaceId()
{
if (_frameworkNamespace)
return Tapestry.getMessage("Namespace.framework-namespace");
if (_applicationNamespace)
return Tapestry.getMessage("Namespace.application-namespace");
return Tapestry.format("Namespace.nested-namespace", getExtendedId());
}
/**
* Gets the specification from the specification source.
*
* @throws ApplicationRuntimeException if the named page is not defined.
*
**/
private IComponentSpecification locatePageSpecification(String name)
{
String path = _specification.getPageSpecificationPath(name);
if (path == null)
throw new ApplicationRuntimeException(
Tapestry.format("Namespace.no-such-page", name, getNamespaceId()));
IResourceLocation location = getSpecificationLocation().getRelativeLocation(path);
return _specificationSource.getPageSpecification(location);
}
private IComponentSpecification locateComponentSpecification(String type)
{
String path = _specification.getComponentSpecificationPath(type);
if (path == null)
throw new ApplicationRuntimeException(
Tapestry.format("Namespace.no-such-alias", type, getNamespaceId()));
IResourceLocation location = getSpecificationLocation().getRelativeLocation(path);
return _specificationSource.getComponentSpecification(location);
}
private INamespace createNamespace(String id)
{
String path = _specification.getLibrarySpecificationPath(id);
if (path == null)
throw new ApplicationRuntimeException(
Tapestry.format("Namespace.library-id-not-found", id, getNamespaceId()));
IResourceLocation location = getSpecificationLocation().getRelativeLocation(path);
// Ok, an absolute path to a library for an application whose specification
// is in the context root is problematic, cause getRelativeLocation()
// will still be looking in the context. Handle this case with the
// following little kludge:
if (location.getResourceURL() == null && path.startsWith("/"))
location = new ClasspathResourceLocation(_specification.getResourceResolver(), path);
ILibrarySpecification ls = _specificationSource.getLibrarySpecification(location);
return new Namespace(id, this, ls, _specificationSource);
}
public boolean containsPage(String name)
{
return _pages.containsKey(name) || (_specification.getPageSpecificationPath(name) != null);
}
/** @since 2.3 **/
public String constructQualifiedName(String pageName)
{
String prefix = getExtendedId();
if (prefix == null)
return pageName;
return prefix + SEPARATOR + pageName;
}
/** @since 3.0 **/
public IResourceLocation getSpecificationLocation()
{
return _specification.getSpecificationLocation();
}
/** @since 3.0 **/
public boolean isApplicationNamespace()
{
return _applicationNamespace;
}
/** @since 3.0 **/
public synchronized void installPageSpecification(
String pageName,
IComponentSpecification specification)
{
_pages.put(pageName, specification);
}
/** @since 3.0 **/
public synchronized void installComponentSpecification(
String type,
IComponentSpecification specification)
{
_components.put(type, specification);
}
/** @since 3.0 **/
public boolean containsComponentType(String type)
{
return _components.containsKey(type)
|| (_specification.getComponentSpecificationPath(type) != null);
}
/** @since 3.0 **/
public List getComponentTypes()
{
Set types = new HashSet();
types.addAll(_components.keySet());
types.addAll(_specification.getComponentTypes());
List result = new ArrayList(types);
Collections.sort(result);
return result;
}
/** @since 3.0 **/
public ILocation getLocation()
{
if (_specification == null)
return null;
return _specification.getLocation();
}
}