blob: 608ba4740fc42d4b246977146132ab3b05865a97 [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.tiles.impl.mgmt;
import java.util.HashMap;
import java.util.Map;
import org.apache.tiles.Definition;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesContainerWrapper;
import org.apache.tiles.definition.NoSuchDefinitionException;
import org.apache.tiles.mgmt.MutableTilesContainer;
import org.apache.tiles.request.Request;
/**
* Manages custom and configured definitions, so they can be used by the
* container, instead of using a simple {@link org.apache.tiles.definition.DefinitionsFactory}.
*
* @version $Rev$ $Date$
*/
public class CachingTilesContainer extends TilesContainerWrapper implements MutableTilesContainer {
/**
* The default name of the attribute in which storing custom definitions.
*/
private static final String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
"org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
/**
* The name of the attribute in which storing custom definitions.
*/
private String definitionsAttributeName;
/**
* Constructor.
* @param originalContainer The original container to wrap.
*/
public CachingTilesContainer(TilesContainer originalContainer) {
super(originalContainer);
definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
}
/**
* Constructor.
* @param originalContainer The original container to wrap.
* @param definitionsAttributeName The name of the attribute in which
* storing custom definitions.
*/
public CachingTilesContainer(TilesContainer originalContainer, String definitionsAttributeName) {
super(originalContainer);
this.definitionsAttributeName = definitionsAttributeName;
if (this.definitionsAttributeName == null) {
this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
}
}
/**
* Returns a definition by name.
*
* @param definition The name of the definition.
* @param request The current request.
* @return The requested definition, either main or custom.
* @throws org.apache.tiles.definition.DefinitionsFactoryException If
* something goes wrong when obtaining a main definition.
*/
public Definition getDefinition(String definition,
Request request) {
Definition retValue = null;
retValue = getCustomDefinition(definition, request);
if (retValue == null) {
retValue = super.getDefinition(definition, request);
}
return retValue;
}
/** {@inheritDoc} */
@Override
public boolean isValidDefinition(String definition, Request request) {
if (getCustomDefinition(definition, request) != null) {
return true;
}
return super.isValidDefinition(definition, request);
}
/** {@inheritDoc} */
@Override
public void register(Definition definition, Request request) {
Map<String, Definition> definitions = getOrCreateDefinitions(request);
if (definition.getName() == null) {
definition.setName(getNextUniqueDefinitionName(definitions));
}
if (definition.isExtending()) {
this.resolveInheritance(definition, request);
}
definitions.put(definition.getName(), definition);
}
/** {@inheritDoc} */
@Override
public void render(String definition, Request request) {
Definition toRender = getDefinition(definition, request);
if (toRender == null) {
throw new NoSuchDefinitionException(
"Cannot find definition named '" + definition + "'");
}
super.render(toRender, request);
}
/**
* Resolve inheritance.
* First, resolve parent's inheritance, then set template to the parent's
* template.
* Also copy attributes set in parent, and not set in child
* If instance doesn't extend anything, do nothing.
*
* @param definition The definition that needs to have its inheritances
* resolved.
* @param request The current request.
* @throws org.apache.tiles.definition.DefinitionsFactoryException If an
* inheritance can not be solved.
*/
private void resolveInheritance(Definition definition,
Request request) {
// Already done, or not needed ?
if (!definition.isExtending()) {
return;
}
String parentDefinitionName = definition.getExtends();
boolean recurse = true;
Definition parent = getCustomDefinition(parentDefinitionName, request);
if (parent == null) {
parent = container.getDefinition(parentDefinitionName, request);
recurse = false;
}
if (parent == null) {
throw new NoSuchDefinitionException(
"Error while resolving definition inheritance: child '"
+ definition.getName()
+ "' can't find its ancestor '"
+ parentDefinitionName
+ "'. Please check your description file.");
}
// Resolve parent before itself.
if (recurse) {
resolveInheritance(parent, request);
}
definition.inherit(parent);
}
/**
* Returns the map with custom definitions for the current request.
*
* @param request The current request.
* @return A map that connects a definition name to a definition.
*/
@SuppressWarnings("unchecked")
private Map<String, Definition> getDefinitions(
Request request) {
return (Map<String, Definition>) request.getContext("request")
.get(definitionsAttributeName);
}
/**
* Returns a map of type "definition name -> definition" and, if it has not
* been defined before, creates one.
*
* @param request The current request.
* @return A map that connects a definition name to a definition.
*/
@SuppressWarnings("unchecked")
private Map<String, Definition> getOrCreateDefinitions(
Request request) {
Map<String, Definition> definitions =
(Map<String, Definition>) request.getContext("request").get(definitionsAttributeName);
if (definitions == null) {
definitions = new HashMap<String, Definition>();
request.getContext("request")
.put(definitionsAttributeName, definitions);
}
return definitions;
}
/**
* Create a unique definition name usable to store anonymous definitions.
*
* @param definitions The already created definitions.
* @return The unique definition name to be used to store the definition.
* @since 2.1.0
*/
private String getNextUniqueDefinitionName(
Map<String, Definition> definitions) {
String candidate;
int anonymousDefinitionIndex = 1;
do {
candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
anonymousDefinitionIndex++;
} while (definitions.containsKey(candidate));
return candidate;
}
/**
* Returns a custom definition from the cache.
*
* @param definition The definition to search.
* @param request The request.
* @return The requested definition.
*/
private Definition getCustomDefinition(String definition, Request request) {
Map<String, Definition> definitions = getDefinitions(request);
if (definitions != null) {
return definitions.get(definition);
}
return null;
}
}