blob: 2344cf28fed34796eaadd05bf07e69e15290ee87 [file] [log] [blame]
// 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.engine;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.Location;
import org.apache.hivemind.Resource;
import org.apache.tapestry.INamespace;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.services.NamespaceResources;
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 org.apache.tapestry.services.NamespaceResources} to obtain page and
* component specifications as needed.
*
* @author Howard Lewis Ship
* @since 2.2
*/
public class Namespace implements INamespace
{
private final ILibrarySpecification _specification;
private final String _id;
private String _extendedId;
private final INamespace _parent;
private final boolean _frameworkNamespace;
private final boolean _applicationNamespace;
/** @since 4.0 */
private final NamespaceResources _resources;
/**
* 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 final Map _pages = new ConcurrentHashMap();
/**
* Map of {@link org.apache.tapestry.spec.ComponentSpecification}keyed on
* component alias.
*/
private final Map _components = new ConcurrentHashMap();
/**
* Map, keyed on id, of {@link INamespace}.
*/
private final Map _children = new ConcurrentHashMap();
public Namespace(String id, INamespace parent, ILibrarySpecification specification,
NamespaceResources resources)
{
_id = id;
_parent = parent;
_specification = specification;
_resources = resources;
_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 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()));
// We don't record line-precise data about <page> elements
// so use the location for the specification as a whole (at least
// identifying
// the right file)
return _resources.getPageSpecification(getSpecificationLocation(), path, getLocation());
}
private IComponentSpecification locateComponentSpecification(String type)
{
String path = _specification.getComponentSpecificationPath(type);
if (path == null)
throw new ApplicationRuntimeException(Tapestry.format("Namespace.no-such-alias", type, getNamespaceId()));
// We don't record line-precise data about <component-type> elements
// so use the location for the specification as a whole (at least
// identifying
// the right file)
return _resources.getComponentSpecification(getSpecificationLocation(), path, getLocation());
}
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()));
// We don't record line-precise data about <library> elements
// so use the location for the specification as a whole (at least
// identifying
// the right file)
ILibrarySpecification ls =
_resources.findChildLibrarySpecification(getSpecificationLocation(), path, getLocation());
return new Namespace(id, this, ls, _resources);
}
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 Resource getSpecificationLocation()
{
return _specification.getSpecificationLocation();
}
/** @since 3.0 * */
public boolean isApplicationNamespace()
{
return _applicationNamespace;
}
/** @since 3.0 * */
public void installPageSpecification(String pageName, IComponentSpecification specification)
{
_pages.put(pageName, specification);
}
/** @since 3.0 * */
public 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 Location getLocation()
{
if (_specification == null)
return null;
return _specification.getLocation();
}
/**
* Returns property values defined in the namespace's library specification.
*
* @return the property, or null if not provided in the specification.
* @since 4.0
*/
public String getPropertyValue(String propertyName)
{
String ret = _specification.getProperty(propertyName);
if (ret == null && _parent != null)
{
return _parent.getPropertyValue(propertyName);
}
return ret;
}
}