1/*2 * $Id: CachingTilesContainer.java 1045365 2010-12-13 20:46:46Z apetrelli $3 *4 * Licensed to the Apache Software Foundation (ASF) under one5 * or more contributor license agreements. See the NOTICE file6 * distributed with this work for additional information7 * regarding copyright ownership. The ASF licenses this file8 * to you under the Apache License, Version 2.0 (the9 * "License"); you may not use this file except in compliance10 * with the License. You may obtain a copy of the License at11 *12 * http://www.apache.org/licenses/LICENSE-2.013 *14 * Unless required by applicable law or agreed to in writing,15 * software distributed under the License is distributed on an16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY17 * KIND, either express or implied. See the License for the18 * specific language governing permissions and limitations19 * under the License.20 */21package org.apache.tiles.impl.mgmt;
2223import java.util.HashMap;
24import java.util.Map;
2526import org.apache.tiles.Definition;
27import org.apache.tiles.TilesContainer;
28import org.apache.tiles.TilesContainerWrapper;
29import org.apache.tiles.definition.NoSuchDefinitionException;
30import org.apache.tiles.mgmt.MutableTilesContainer;
31import org.apache.tiles.request.Request;
3233/**34 * Manages custom and configured definitions, so they can be used by the35 * container, instead of using a simple {@link org.apache.tiles.definition.DefinitionsFactory}.36 *37 * @version $Rev: 1045365 $ $Date: 2010-12-14 07:46:46 +1100 (Tue, 14 Dec 2010) $38 */39publicclassCachingTilesContainerextendsTilesContainerWrapperimplementsMutableTilesContainer {
4041/**42 * The default name of the attribute in which storing custom definitions.43 */44privatestaticfinal String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
45"org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
4647/**48 * The name of the attribute in which storing custom definitions.49 */50private String definitionsAttributeName;
5152/**53 * Constructor.54 * @param originalContainer The original container to wrap.55 */56publicCachingTilesContainer(TilesContainer originalContainer) {
57super(originalContainer);
58 definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
59 }
6061/**62 * Constructor.63 * @param originalContainer The original container to wrap.64 * @param definitionsAttributeName The name of the attribute in which65 * storing custom definitions.66 */67publicCachingTilesContainer(TilesContainer originalContainer, String definitionsAttributeName) {
68super(originalContainer);
69this.definitionsAttributeName = definitionsAttributeName;
70if (this.definitionsAttributeName == null) {
71this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
72 }
73 }
7475/**76 * Returns a definition by name.77 *78 * @param definition The name of the definition.79 * @param request The current request.80 * @return The requested definition, either main or custom.81 * @throws org.apache.tiles.definition.DefinitionsFactoryException If82 * something goes wrong when obtaining a main definition.83 */84publicDefinition getDefinition(String definition,
85 Request request) {
86Definition retValue = null;
87 retValue = getCustomDefinition(definition, request);
88if (retValue == null) {
89 retValue = super.getDefinition(definition, request);
90 }
91return retValue;
92 }
9394/** {@inheritDoc} */95 @Override
96publicboolean isValidDefinition(String definition, Request request) {
97if (getCustomDefinition(definition, request) != null) {
98returntrue;
99 }
100returnsuper.isValidDefinition(definition, request);
101 }
102103/** {@inheritDoc} */104 @Override
105publicvoid register(Definition definition, Request request) {
106 Map<String, Definition> definitions = getOrCreateDefinitions(request);
107if (definition.getName() == null) {
108 definition.setName(getNextUniqueDefinitionName(definitions));
109 }
110111if (definition.isExtending()) {
112this.resolveInheritance(definition, request);
113 }
114115 definitions.put(definition.getName(), definition);
116 }
117118/** {@inheritDoc} */119 @Override
120publicvoid render(String definition, Request request) {
121Definition toRender = getDefinition(definition, request);
122if (toRender == null) {
123thrownewNoSuchDefinitionException(
124"Cannot find definition named '" + definition + "'");
125 }
126super.render(toRender, request);
127 }
128129/**130 * Resolve inheritance.131 * First, resolve parent's inheritance, then set template to the parent's132 * template.133 * Also copy attributes set in parent, and not set in child134 * If instance doesn't extend anything, do nothing.135 *136 * @param definition The definition that needs to have its inheritances137 * resolved.138 * @param request The current request.139 * @throws org.apache.tiles.definition.DefinitionsFactoryException If an140 * inheritance can not be solved.141 */142privatevoid resolveInheritance(Definition definition,
143 Request request) {
144// Already done, or not needed ?145if (!definition.isExtending()) {
146return;
147 }
148149 String parentDefinitionName = definition.getExtends();
150151boolean recurse = true;
152Definition parent = getCustomDefinition(parentDefinitionName, request);
153if (parent == null) {
154 parent = container.getDefinition(parentDefinitionName, request);
155 recurse = false;
156 }
157158if (parent == null) {
159thrownewNoSuchDefinitionException(
160"Error while resolving definition inheritance: child '"161 + definition.getName()
162 + "' can't find its ancestor '"163 + parentDefinitionName
164 + "'. Please check your description file.");
165 }
166167// Resolve parent before itself.168if (recurse) {
169 resolveInheritance(parent, request);
170 }
171 definition.inherit(parent);
172 }
173174/**175 * Returns the map with custom definitions for the current request.176 *177 * @param request The current request.178 * @return A map that connects a definition name to a definition.179 */180 @SuppressWarnings("unchecked")
181private Map<String, Definition> getDefinitions(
182 Request request) {
183return (Map<String, Definition>) request.getContext("request")
184 .get(definitionsAttributeName);
185 }
186187/**188 * Returns a map of type "definition name -> definition" and, if it has not189 * been defined before, creates one.190 *191 * @param request The current request.192 * @return A map that connects a definition name to a definition.193 */194 @SuppressWarnings("unchecked")
195private Map<String, Definition> getOrCreateDefinitions(
196 Request request) {
197 Map<String, Definition> definitions =
198 (Map<String, Definition>) request.getContext("request").get(definitionsAttributeName);
199if (definitions == null) {
200 definitions = new HashMap<String, Definition>();
201 request.getContext("request")
202 .put(definitionsAttributeName, definitions);
203 }
204205return definitions;
206 }
207208/**209 * Create a unique definition name usable to store anonymous definitions.210 *211 * @param definitions The already created definitions.212 * @return The unique definition name to be used to store the definition.213 * @since 2.1.0214 */215private String getNextUniqueDefinitionName(
216 Map<String, Definition> definitions) {
217 String candidate;
218int anonymousDefinitionIndex = 1;
219220do {
221 candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
222 anonymousDefinitionIndex++;
223 } while (definitions.containsKey(candidate));
224225return candidate;
226 }
227228/**229 * Returns a custom definition from the cache.230 *231 * @param definition The definition to search.232 * @param request The request.233 * @return The requested definition.234 */235privateDefinition getCustomDefinition(String definition, Request request) {
236 Map<String, Definition> definitions = getDefinitions(request);
237if (definitions != null) {
238return definitions.get(definition);
239 }
240returnnull;
241 }
242 }