1/*2 * $Id: DefinitionsImpl.java 605994 2007-12-20 18:41:47Z 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 */2122package org.apache.tiles.definition;
2324import org.apache.commons.logging.Log;
25import org.apache.commons.logging.LogFactory;
26import org.apache.tiles.Attribute;
27import org.apache.tiles.Definition;
2829import java.util.HashMap;
30import java.util.HashSet;
31import java.util.Locale;
32import java.util.Map;
33import java.util.Set;
3435/***36 * @version $Rev: 605994 $ $Date: 2007-12-20 19:41:47 +0100 (Thu, 20 Dec 2007) $37 */38publicclassDefinitionsImpl implements Definitions {
3940/***41 * Commons Logging instance.42 */43privatestatic Log log = LogFactory.getLog(DefinitionsImpl.class);
4445/***46 * The base set of Definition objects not discriminated by locale.47 */48private Map<String, Definition> baseDefinitions;
49/***50 * The locale-specific set of definitions objects.51 */52private Map<Locale, Map<String, Definition>> localeSpecificDefinitions;
5354/***55 * Creates a new instance of DefinitionsImpl.56 */57publicDefinitionsImpl() {
58 baseDefinitions = new HashMap<String, Definition>();
59 localeSpecificDefinitions =
60new HashMap<Locale, Map<String, Definition>>();
61 }
6263/***64 * Returns a Definition object that matches the given name.65 *66 * @param name The name of the Definition to return.67 * @return the Definition matching the given name or null if none68 * is found.69 */70public Definition getDefinition(String name) {
71return baseDefinitions.get(name);
72 }
7374/***75 * Adds new Definition objects to the internal collection and76 * resolves inheritance attraibutes.77 *78 * @param defsMap The new definitions to add.79 * @throws NoSuchDefinitionException If something goes wrong during80 * addition.81 */82publicvoid addDefinitions(Map<String, Definition> defsMap)
83 throws NoSuchDefinitionException {
84this.baseDefinitions.putAll(defsMap);
85 resolveInheritances();
86 }
8788/***89 * Adds new locale-specific Definition objects to the internal90 * collection and resolves inheritance attraibutes.91 *92 * @param defsMap The new definitions to add.93 * @param locale The locale to add the definitions to.94 * @throws NoSuchDefinitionException If something goes wrong during95 * inheritance resolution.96 */97publicvoid addDefinitions(Map<String, Definition> defsMap,
98 Locale locale) throws NoSuchDefinitionException {
99 localeSpecificDefinitions.put(locale, defsMap);
100 resolveInheritances(locale);
101 }
102103/***104 * Returns a Definition object that matches the given name and locale.105 *106 * @param name The name of the Definition to return.107 * @param locale The locale to use to resolve the definition.108 * @return the Definition matching the given name or null if none109 * is found.110 */111public Definition getDefinition(String name, Locale locale) {
112 Definition definition = null;
113114if (locale != null) {
115 Map<String, Definition> localeSpecificMap =
116 localeSpecificDefinitions.get(locale);
117if (localeSpecificMap != null) {
118 definition = localeSpecificMap.get(name);
119 }
120 }
121122if (definition == null) {
123 definition = getDefinition(name);
124 }
125126return definition;
127 }
128129/***130 * Resolve extended instances.131 *132 * @throws NoSuchDefinitionException If a parent definition is not found.133 */134publicvoid resolveInheritances() throws NoSuchDefinitionException {
135 Set<String> alreadyResolvedDefinitions = new HashSet<String>();
136137for (Definition definition : baseDefinitions.values()) {
138 resolveInheritance(definition, null, alreadyResolvedDefinitions);
139 } // end loop140 }
141142/***143 * Resolve locale-specific extended instances.144 *145 * @param locale The locale to use.146 * @throws NoSuchDefinitionException If a parent definition is not found.147 */148publicvoid resolveInheritances(Locale locale) throws NoSuchDefinitionException {
149 resolveInheritances();
150151 Map<String, Definition> map = localeSpecificDefinitions.get(locale);
152if (map != null) {
153 Set<String> alreadyResolvedDefinitions = new HashSet<String>();
154for (Definition definition : map.values()) {
155 resolveInheritance(definition, locale,
156 alreadyResolvedDefinitions);
157 } // end loop158 }
159 }
160161/***162 * Clears definitions.163 */164publicvoid reset() {
165this.baseDefinitions = new HashMap<String, Definition>();
166this.localeSpecificDefinitions =
167new HashMap<Locale, Map<String, Definition>>();
168 }
169170/***171 * Returns base definitions collection.172 *173 * @return The base (i.e. not depending on any locale) definitions map.174 */175public Map<String, Definition> getBaseDefinitions() {
176return baseDefinitions;
177 }
178179/***180 * Searches for a definition specified as an attribute.181 *182 * @param attr The attribute to use.183 * @param locale The locale to search into.184 * @return The required definition if found, otherwise it returns185 * <code>null</code>.186 */187protected Definition getDefinitionByAttribute(
188 Attribute attr, Locale locale) {
189 Definition retValue = null;
190191 Object attrValue = attr.getValue();
192if (attrValue instanceof String) {
193 retValue = this.getDefinition((String) attr
194 .getValue(), locale);
195 }
196197return retValue;
198 }
199200/***201 * Resolve locale-specific inheritance.202 * First, resolve parent's inheritance, then set template to the parent's203 * template.204 * Also copy attributes setted in parent, and not set in child205 * If instance doesn't extend anything, do nothing.206 *207 * @param definition The definition to resolve208 * @param locale The locale to use.209 * @param alreadyResolvedDefinitions The set of the definitions that have210 * been already resolved.211 * @throws NoSuchDefinitionException If an inheritance can not be solved.212 */213protectedvoid resolveInheritance(Definition definition, Locale locale,
214 Set<String> alreadyResolvedDefinitions)
215 throws NoSuchDefinitionException {
216// Already done, or not needed ?217if (!definition.isExtending()
218 || alreadyResolvedDefinitions.contains(definition.getName())) {
219return;
220 }
221222if (log.isDebugEnabled()) {
223 log.debug("Resolve definition for child name='"224 + definition.getName()
225 + "' extends='" + definition.getExtends() + "'.");
226 }
227228// Set as visited to avoid endless recurisvity.229 alreadyResolvedDefinitions.add(definition.getName());
230231// Resolve parent before itself.232 Definition parent = getDefinition(definition.getExtends(),
233 locale);
234if (parent == null) { // error235 String msg = "Error while resolving definition inheritance: child '"236 + definition.getName()
237 + "' can't find its ancestor '"238 + definition.getExtends()
239 + "'. Please check your description file.";
240 log.error(msg);
241// to do : find better exception242thrownewNoSuchDefinitionException(msg);
243 }
244245 resolveInheritance(parent, locale, alreadyResolvedDefinitions);
246247 overload(parent, definition);
248 }
249250/***251 * Overloads a child definition with a given parent.252 * All attributes present in child are kept. All missing attributes are253 * copied from the parent.254 * Special attribute 'template','role' and 'extends' are overloaded in child255 * if not defined256 *257 * @param parent The parent definition.258 * @param child The child that will be overloaded.259 */260// FIXME This is the same as DefinitionManager.overload.261protectedvoid overload(Definition parent, Definition child) {
262// Iterate on each parent's attribute and add it if not defined in child.263for (Map.Entry<String, Attribute> entry : parent.getAttributes().entrySet()) {
264if (!child.hasAttributeValue(entry.getKey())) {
265 child.putAttribute(entry.getKey(), new Attribute(entry.getValue()));
266 }
267 }
268// Set template and role if not setted269if (child.getTemplate() == null) {
270 child.setTemplate(parent.getTemplate());
271 }
272if (child.getRoles() == null) {
273 child.setRoles(parent.getRoles());
274 }
275if (child.getPreparer() == null) {
276 child.setPreparer(parent.getPreparer());
277 }
278 }
279 }