1/*2 * $Id: DefinitionManager.java 616890 2008-01-30 20:16:51Z 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 org.apache.tiles.Attribute;
24import org.apache.tiles.Definition;
25import org.apache.tiles.context.TilesRequestContext;
26import org.apache.tiles.definition.DefinitionsFactory;
27import org.apache.tiles.definition.DefinitionsFactoryException;
28import org.apache.tiles.definition.NoSuchDefinitionException;
29import org.apache.commons.logging.Log;
30import org.apache.commons.logging.LogFactory;
3132import java.util.HashMap;
33import java.util.Map;
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: 616890 $ $Date: 2008-01-30 21:16:51 +0100 (Wed, 30 Jan 2008) $40 */41publicclassDefinitionManager {
4243/***44 * The logging object.45 */46privatestaticfinal Log LOG =
47 LogFactory.getLog(DefinitionManager.class);
4849/***50 * The default name of the attribute in which storing custom definitions.51 */52privatestaticfinal String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
53"org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
5455/***56 * The definitions factory to use to get main definitions.57 */58privateDefinitionsFactory factory;
5960/***61 * The name of the attribute in which storing custom definitions.62 */63private String definitionsAttributeName;
6465/***66 * Constructor.67 */68publicDefinitionManager() {
69 definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
70 }
7172/***73 * Constructor.74 *75 * @param definitionsAttributeName The name of the attribute in which76 * storing custom definitions.77 */78publicDefinitionManager(String definitionsAttributeName) {
79this.definitionsAttributeName = definitionsAttributeName;
80if (this.definitionsAttributeName == null) {
81this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
82 }
83 }
8485/***86 * Returns the used definitions factory.87 *88 * @return The used definitions factory.89 */90publicDefinitionsFactory getFactory() {
91return factory;
92 }
9394/***95 * Sets the definitions factory to use.96 *97 * @param factory The definitions factory.98 */99publicvoid setFactory(DefinitionsFactory factory) {
100this.factory = factory;
101 }
102103/***104 * Returns a definition by name.105 *106 * @param definition The name of the definition.107 * @param request The current request.108 * @return The requested definition, either main or custom.109 * @throws DefinitionsFactoryException If something goes wrong when110 * obtaining a main definition.111 */112public Definition getDefinition(String definition, TilesRequestContext request)
113 throws DefinitionsFactoryException {
114 Map<String, Definition> definitions =
115 getDefinitions(request);
116if (definitions != null && definitions.containsKey(definition)) {
117return definitions.get(definition);
118 }
119return getFactory().getDefinition(definition, request);
120 }
121122/***123 * Adds a definition to the set of custom ones.124 *125 * @param definition The definition to add.126 * @param request The current request.127 * @throws DefinitionsFactoryException If something goes wrong during the128 * addition.129 */130publicvoid addDefinition(Definition definition,
131TilesRequestContext request)
132 throws DefinitionsFactoryException {
133 validate(definition);
134135if (definition.isExtending()) {
136this.resolveInheritance(definition, request);
137 }
138139 getOrCreateDefinitions(request).put(definition.getName(), definition);
140 }
141142/***143 * Validates a custom definition.144 *145 * @param definition The definition to validate.146 */147privatevoid validate(Definition definition) {
148 Map<String, Attribute> attrs = definition.getAttributes();
149for (Attribute attribute : attrs.values()) {
150if (attribute.getValue() == null) {
151thrownew IllegalArgumentException("Attribute value not defined");
152 }
153 }
154 }
155156/***157 * Resolve inheritance.158 * First, resolve parent's inheritance, then set template to the parent's159 * template.160 * Also copy attributes setted in parent, and not set in child161 * If instance doesn't extend anything, do nothing.162 *163 * @param definition The definition that needs to have its inheritances164 * resolved.165 * @param request The current request.166 * @throws DefinitionsFactoryException If an inheritance can not be solved.167 */168protectedvoid resolveInheritance(Definition definition,
169TilesRequestContext request)
170 throws DefinitionsFactoryException {
171// Already done, or not needed ?172if (!definition.isExtending()) {
173return;
174 }
175176if (LOG.isDebugEnabled()) {
177 LOG.debug("Resolve definition for child name='"178 + definition.getName()
179 + "' extends='" + definition.getExtends() + "'.");
180 }
181182// TODO Factories our factory implementations will be context agnostic,183// however, this may cause errors for other implementations.184// we should probably make all factories agnostic and allow the manager to185// utilize the correct factory based on the context.186 Definition parent = getDefinition(definition.getExtends(), request);
187188if (parent == null) { // error189 String msg = "Error while resolving definition inheritance: child '"190 + definition.getName()
191 + "' can't find its ancestor '"192 + definition.getExtends()
193 + "'. Please check your description file.";
194 LOG.error(msg);
195// to do : find better exception196thrownewNoSuchDefinitionException(msg);
197 }
198199// Resolve parent before itself.200 resolveInheritance(parent, request);
201 overload(parent, definition);
202 }
203204/***205 * Overloads a child definition with a given parent.206 * All attributes present in child are kept. All missing attributes are207 * copied from the parent.208 * Special attribute 'template','role' and 'extends' are overloaded in child209 * if not defined210 *211 * @param parent The parent definition.212 * @param child The child that will be overloaded.213 */214// FIXME This is the same as DefinitionsImpl.overload.215protectedvoid overload(Definition parent, Definition child) {
216// Iterate on each parent's attribute and add it if not defined in child.217for (Map.Entry<String, Attribute> entry : parent.getAttributes().entrySet()) {
218if (!child.hasAttributeValue(entry.getKey())) {
219 child.putAttribute(entry.getKey(), new Attribute(entry.getValue()));
220 }
221 }
222223if (child.getTemplate() == null) {
224 child.setTemplate(parent.getTemplate());
225 }
226227if (child.getRoles() == null) {
228 child.setRoles(parent.getRoles());
229 }
230231if (child.getPreparer() == null) {
232 child.setPreparer(parent.getPreparer());
233 }
234 }
235236/***237 * Returns the map with custom definitions for the current request.238 *239 * @param request The current request.240 * @return A map that connects a definition name to a definition.241 */242 @SuppressWarnings("unchecked")
243protected Map<String, Definition> getDefinitions(
244TilesRequestContext request) {
245return (Map<String, Definition>) request.getRequestScope()
246 .get(definitionsAttributeName);
247 }
248249/***250 * Returns a map of type "definition name -> definition" and, if it has not251 * been defined before, creates one.252 *253 * @param request The current request.254 * @return A map that connects a definition name to a definition.255 */256 @SuppressWarnings("unchecked")
257protected Map<String, Definition> getOrCreateDefinitions(
258TilesRequestContext request) {
259 Map<String, Definition> definitions =
260 (Map<String, Definition>) request
261 .getRequestScope().get(definitionsAttributeName);
262if (definitions == null) {
263 definitions = new HashMap<String, Definition>();
264 request.getRequestScope()
265 .put(definitionsAttributeName, definitions);
266 }
267268return definitions;
269 }
270 }