1/*2 * $Id: DefinitionManager.java 791161 2009-07-04 18:53:36Z 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;
25import java.util.Set;
2627import org.apache.tiles.Attribute;
28import org.apache.tiles.Definition;
29import org.apache.tiles.context.TilesRequestContext;
30import org.apache.tiles.definition.DefinitionsFactory;
31import org.apache.tiles.definition.NoSuchDefinitionException;
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
3435/***36 * Manages custom and configured definitions, so they can be used by the37 * container, instead of using a simple {@link DefinitionsFactory}.38 *39 * @version $Rev: 791161 $ $Date: 2009-07-04 20:53:36 +0200 (sab, 04 lug 2009) $40 */41publicclassDefinitionManager {
4243/***44 * The logging object.45 */46privatefinal Logger log = LoggerFactory.getLogger(DefinitionManager.class);
4748/***49 * The default name of the attribute in which storing custom definitions.50 */51privatestaticfinal String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
52"org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
5354/***55 * The definitions factory to use to get main definitions.56 */57privateDefinitionsFactory factory;
5859/***60 * The name of the attribute in which storing custom definitions.61 */62private String definitionsAttributeName;
6364/***65 * Constructor.66 */67publicDefinitionManager() {
68 definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
69 }
7071/***72 * Constructor.73 *74 * @param definitionsAttributeName The name of the attribute in which75 * storing custom definitions.76 */77publicDefinitionManager(String definitionsAttributeName) {
78this.definitionsAttributeName = definitionsAttributeName;
79if (this.definitionsAttributeName == null) {
80this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
81 }
82 }
8384/***85 * Returns the used definitions factory.86 *87 * @return The used definitions factory.88 */89publicDefinitionsFactory getFactory() {
90return factory;
91 }
9293/***94 * Sets the definitions factory to use.95 *96 * @param factory The definitions factory.97 */98publicvoid setFactory(DefinitionsFactory factory) {
99this.factory = factory;
100 }
101102/***103 * Returns a definition by name.104 *105 * @param definition The name of the definition.106 * @param request The current request.107 * @return The requested definition, either main or custom.108 * @throws org.apache.tiles.definition.DefinitionsFactoryException If109 * something goes wrong when obtaining a main definition.110 */111public Definition getDefinition(String definition,
112TilesRequestContext request) {
113 Map<String, Definition> definitions =
114 getDefinitions(request);
115if (definitions != null && definitions.containsKey(definition)) {
116return definitions.get(definition);
117 }
118return getFactory().getDefinition(definition, request);
119 }
120121/***122 * Adds a definition to the set of custom ones.123 *124 * @param definition The definition to add.125 * @param request The current request.126 * @throws org.apache.tiles.definition.DefinitionsFactoryException If127 * something goes wrong during the addition.128 */129publicvoid addDefinition(Definition definition,
130TilesRequestContext request) {
131 Map<String, Definition> definitions = getOrCreateDefinitions(request);
132if (definition.getName() == null) {
133 definition.setName(getNextUniqueDefinitionName(definitions));
134 }
135 validate(definition);
136137if (definition.isExtending()) {
138this.resolveInheritance(definition, request);
139 }
140141 definitions.put(definition.getName(), definition);
142 }
143144/***145 * Validates a custom definition.146 *147 * @param definition The definition to validate.148 */149privatevoid validate(Definition definition) {
150 Set<String> names = definition.getLocalAttributeNames();
151if (names != null) {
152for (String name : names) {
153 Attribute attribute = definition.getLocalAttribute(name);
154if (attribute.getValue() == null) {
155thrownew IllegalArgumentException(
156"Attribute '" + name + "' value not defined");
157 }
158 }
159 }
160 names = definition.getCascadedAttributeNames();
161if (names != null) {
162for (String name : names) {
163 Attribute attribute = definition.getCascadedAttribute(name);
164if (attribute.getValue() == null) {
165thrownew IllegalArgumentException(
166"Attribute '" + name + "' value not defined");
167 }
168 }
169 }
170 }
171172/***173 * Resolve inheritance.174 * First, resolve parent's inheritance, then set template to the parent's175 * template.176 * Also copy attributes setted in parent, and not set in child177 * If instance doesn't extend anything, do nothing.178 *179 * @param definition The definition that needs to have its inheritances180 * resolved.181 * @param request The current request.182 * @throws org.apache.tiles.definition.DefinitionsFactoryException If an183 * inheritance can not be solved.184 */185protectedvoid resolveInheritance(Definition definition,
186TilesRequestContext request) {
187// Already done, or not needed ?188if (!definition.isExtending()) {
189return;
190 }
191192if (log.isDebugEnabled()) {
193 log.debug("Resolve definition for child name='"194 + definition.getName()
195 + "' extends='" + definition.getExtends() + "'.");
196 }
197198// TODO Factories our factory implementations will be context agnostic,199// however, this may cause errors for other implementations.200// we should probably make all factories agnostic and allow the manager to201// utilize the correct factory based on the context.202 Definition parent = getDefinition(definition.getExtends(), request);
203204if (parent == null) { // error205 String msg = "Error while resolving definition inheritance: child '"206 + definition.getName()
207 + "' can't find its ancestor '"208 + definition.getExtends()
209 + "'. Please check your description file.";
210 log.error(msg);
211// to do : find better exception212thrownewNoSuchDefinitionException(msg);
213 }
214215// Resolve parent before itself.216 resolveInheritance(parent, request);
217 definition.inherit(parent);
218 }
219220/***221 * Overloads a child definition with a given parent.222 * All attributes present in child are kept. All missing attributes are223 * copied from the parent.224 * Special attribute 'template','role' and 'extends' are overloaded in child225 * if not defined226 *227 * @param parent The parent definition.228 * @param child The child that will be overloaded.229 * @deprecated Use {@link Definition#inherit(org.apache.tiles.BasicAttributeContext)}.230 */231protectedvoid overload(Definition parent, Definition child) {
232 child.inherit(parent);
233 }
234235/***236 * Returns the map with custom definitions for the current request.237 *238 * @param request The current request.239 * @return A map that connects a definition name to a definition.240 */241 @SuppressWarnings("unchecked")
242protected Map<String, Definition> getDefinitions(
243TilesRequestContext request) {
244return (Map<String, Definition>) request.getRequestScope()
245 .get(definitionsAttributeName);
246 }
247248/***249 * Returns a map of type "definition name -> definition" and, if it has not250 * been defined before, creates one.251 *252 * @param request The current request.253 * @return A map that connects a definition name to a definition.254 */255 @SuppressWarnings("unchecked")
256protected Map<String, Definition> getOrCreateDefinitions(
257TilesRequestContext request) {
258 Map<String, Definition> definitions =
259 (Map<String, Definition>) request
260 .getRequestScope().get(definitionsAttributeName);
261if (definitions == null) {
262 definitions = new HashMap<String, Definition>();
263 request.getRequestScope()
264 .put(definitionsAttributeName, definitions);
265 }
266267return definitions;
268 }
269270/***271 * Create a unique definition name usable to store anonymous definitions.272 *273 * @param definitions The already created definitions.274 * @return The unique definition name to be used to store the definition.275 * @since 2.1.0276 */277protected String getNextUniqueDefinitionName(
278 Map<String, Definition> definitions) {
279 String candidate;
280int anonymousDefinitionIndex = 1;
281282do {
283 candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
284 anonymousDefinitionIndex++;
285 } while (definitions.containsKey(candidate));
286287return candidate;
288 }
289 }