blob: 7c7b35279fc9f47a590df5320d7dadf2aaaf7316 [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.definition.dao;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.tiles.Definition;
import org.apache.tiles.definition.NoSuchDefinitionException;
import org.apache.tiles.request.ApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* A definitions DAO (loading URLs and using Locale as a customization key) that
* caches definitions that have been loaded and resolves inheritances.
* </p>
* <p>
* It can check if the URLs change, but by default this feature is turned off.
* </p>
*
* @version $Rev$ $Date$
* @since 2.1.0
*/
public class ResolvingLocaleUrlDefinitionDAO extends
CachingLocaleUrlDefinitionDAO {
/**
* The logging object.
*/
private final Logger log = LoggerFactory.getLogger(ResolvingLocaleUrlDefinitionDAO.class);
public ResolvingLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
super(applicationContext);
}
/** {@inheritDoc} */
@Override
protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) {
return loadRawDefinitionsFromResources(parentLocale);
}
@Override
protected Map<String, Definition> loadDefinitions(Locale customizationKey) {
Map<String, Definition> localeDefsMap = super.loadDefinitions(customizationKey);
Map<String, Definition> defsMap = definitionResolver
.storeDefinitionPatterns(copyDefinitionMap(localeDefsMap),
customizationKey);
resolveInheritances(defsMap, customizationKey);
locale2definitionMap.put(customizationKey, defsMap);
return defsMap;
}
/** {@inheritDoc} */
@Override
protected Definition getDefinitionFromResolver(String name,
Locale customizationKey) {
Definition retValue = super.getDefinitionFromResolver(name, customizationKey);
if (retValue != null && retValue.getExtends() != null) {
Definition parent = getDefinition(retValue.getExtends(), customizationKey);
retValue.inherit(parent);
}
return retValue;
}
/**
* Resolve locale-specific extended instances.
*
* @param map The definition map containing the definitions to resolve.
* @param locale The locale to use.
* @throws NoSuchDefinitionException If a parent definition is not found.
* @since 2.1.0
*/
protected void resolveInheritances(Map<String, Definition> map, Locale locale) {
if (map != null) {
Set<String> alreadyResolvedDefinitions = new HashSet<String>();
for (Definition definition : map.values()) {
resolveInheritance(definition, map, locale,
alreadyResolvedDefinitions);
} // end loop
}
}
/**
* Resolve locale-specific inheritance. First, resolve parent's inheritance,
* then set template to the parent's template. Also copy attributes setted
* in parent, and not set in child If instance doesn't extend anything, do
* nothing.
*
* @param definition The definition to resolve
* @param definitions The definitions to take when obtaining a parent
* definition.
* @param locale The locale to use.
* @param alreadyResolvedDefinitions The set of the definitions that have
* been already resolved.
* @throws NoSuchDefinitionException If an inheritance can not be solved.
* @since 2.1.0
*/
protected void resolveInheritance(Definition definition,
Map<String, Definition> definitions, Locale locale,
Set<String> alreadyResolvedDefinitions) {
// Already done, or not needed ?
if (!definition.isExtending()
|| alreadyResolvedDefinitions.contains(definition.getName())) {
return;
}
log.debug("Resolve definition for child name='{}' extends='{}.",
definition.getName(), definition.getExtends());
// Set as visited to avoid endless recursivity.
alreadyResolvedDefinitions.add(definition.getName());
// Resolve parent before itself.
Definition parent = definitions.get(definition.getExtends());
if (parent == null) { // error
String msg = "Error while resolving definition inheritance: child '"
+ definition.getName()
+ "' can't find its ancestor '"
+ definition.getExtends()
+ "'. Please check your description file.";
// to do : find better exception
throw new NoSuchDefinitionException(msg);
}
resolveInheritance(parent, definitions, locale,
alreadyResolvedDefinitions);
definition.inherit(parent);
}
/**
* Copies the definition map to be passed to a higher level of customization
* key.
*
* @param localeDefsMap The map of definition to be copied.
* @return The copy of the definition map. This particular implementation
* deep-copies the <code>localeDefsMap</code> into a {@link LinkedHashMap}.
* @since 2.1.4
*/
@Override
protected Map<String, Definition> copyDefinitionMap(
Map<String, Definition> localeDefsMap) {
Map<String, Definition> retValue = new LinkedHashMap<String, Definition>(
localeDefsMap.size());
for (Map.Entry<String, Definition> entry : localeDefsMap.entrySet()) {
Definition definition = new Definition(entry.getValue());
retValue.put(entry.getKey(), definition);
}
return retValue;
}
}