1 /* 2 * $Id: ResolvingLocaleUrlDefinitionDAO.java 823662 2009-10-09 18:48:03Z apetrelli $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 package org.apache.tiles.definition.dao; 23 24 import java.util.HashSet; 25 import java.util.LinkedHashMap; 26 import java.util.Locale; 27 import java.util.Map; 28 import java.util.Set; 29 30 import org.apache.tiles.Definition; 31 import org.apache.tiles.definition.NoSuchDefinitionException; 32 import org.slf4j.Logger; 33 import org.slf4j.LoggerFactory; 34 35 /** 36 * <p> 37 * A definitions DAO (loading URLs and using Locale as a customization key) that 38 * caches definitions that have been loaded and resolves inheritances. 39 * </p> 40 * <p> 41 * It can check if the URLs change, but by default this feature is turned off. 42 * </p> 43 * 44 * @version $Rev: 823662 $ $Date: 2009-10-09 20:48:03 +0200 (ven, 09 ott 2009) $ 45 * @since 2.1.0 46 */ 47 public class ResolvingLocaleUrlDefinitionDAO extends 48 CachingLocaleUrlDefinitionDAO { 49 50 /** 51 * The logging object. 52 */ 53 private final Logger log = LoggerFactory.getLogger(ResolvingLocaleUrlDefinitionDAO.class); 54 55 /** {@inheritDoc} */ 56 @Override 57 protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) { 58 return loadDefinitionsFromURLs(parentLocale); 59 } 60 61 /** {@inheritDoc} */ 62 @Override 63 protected Map<String, Definition> loadDefinitionsFromURLs( 64 Locale customizationKey) { 65 Map<String, Definition> retValue = super.loadDefinitionsFromURLs(customizationKey); 66 Map<String, Definition> defsMap = locale2definitionMap.get(customizationKey); 67 resolveInheritances(defsMap, customizationKey); 68 return retValue; 69 } 70 71 /** {@inheritDoc} */ 72 @Override 73 protected Definition getDefinitionFromResolver(String name, 74 Locale customizationKey) { 75 Definition retValue = super.getDefinitionFromResolver(name, customizationKey); 76 if (retValue != null && retValue.getExtends() != null) { 77 Definition parent = getDefinition(retValue.getExtends(), customizationKey); 78 retValue.inherit(parent); 79 } 80 81 return retValue; 82 } 83 84 /** {@inheritDoc} */ 85 @Override 86 @Deprecated 87 protected void postDefinitionLoadOperations( 88 Map<String, Definition> localeDefsMap, Locale customizationKey) { 89 resolveInheritances(localeDefsMap, customizationKey); 90 super.postDefinitionLoadOperations(localeDefsMap, customizationKey); 91 } 92 93 /** 94 * Resolve locale-specific extended instances. 95 * 96 * @param map The definition map containing the definitions to resolve. 97 * @param locale The locale to use. 98 * @throws NoSuchDefinitionException If a parent definition is not found. 99 * @since 2.1.0 100 */ 101 protected void resolveInheritances(Map<String, Definition> map, Locale locale) { 102 if (map != null) { 103 Set<String> alreadyResolvedDefinitions = new HashSet<String>(); 104 for (Definition definition : map.values()) { 105 resolveInheritance(definition, map, locale, 106 alreadyResolvedDefinitions); 107 } // end loop 108 } 109 } 110 111 /** 112 * Resolve locale-specific inheritance. First, resolve parent's inheritance, 113 * then set template to the parent's template. Also copy attributes setted 114 * in parent, and not set in child If instance doesn't extend anything, do 115 * nothing. 116 * 117 * @param definition The definition to resolve 118 * @param definitions The definitions to take when obtaining a parent 119 * definition. 120 * @param locale The locale to use. 121 * @param alreadyResolvedDefinitions The set of the definitions that have 122 * been already resolved. 123 * @throws NoSuchDefinitionException If an inheritance can not be solved. 124 * @since 2.1.0 125 */ 126 protected void resolveInheritance(Definition definition, 127 Map<String, Definition> definitions, Locale locale, 128 Set<String> alreadyResolvedDefinitions) { 129 // Already done, or not needed ? 130 if (!definition.isExtending() 131 || alreadyResolvedDefinitions.contains(definition.getName())) { 132 return; 133 } 134 135 if (log.isDebugEnabled()) { 136 log.debug("Resolve definition for child name='" 137 + definition.getName() 138 + "' extends='" + definition.getExtends() + "'."); 139 } 140 141 // Set as visited to avoid endless recurisvity. 142 alreadyResolvedDefinitions.add(definition.getName()); 143 144 // Resolve parent before itself. 145 Definition parent = definitions.get(definition.getExtends()); 146 if (parent == null) { // error 147 String msg = "Error while resolving definition inheritance: child '" 148 + definition.getName() 149 + "' can't find its ancestor '" 150 + definition.getExtends() 151 + "'. Please check your description file."; 152 // to do : find better exception 153 throw new NoSuchDefinitionException(msg); 154 } 155 156 resolveInheritance(parent, definitions, locale, 157 alreadyResolvedDefinitions); 158 159 definition.inherit(parent); 160 } 161 162 /** 163 * Copies the definition map to be passed to a higher level of customization 164 * key. 165 * 166 * @param localeDefsMap The map of definition to be copied. 167 * @return The copy of the definition map. This particular implementation 168 * deep-copies the <code>localeDefsMap</code> into a {@link LinkedHashMap}. 169 * @since 2.1.4 170 */ 171 @Override 172 protected Map<String, Definition> copyDefinitionMap( 173 Map<String, Definition> localeDefsMap) { 174 Map<String, Definition> retValue = new LinkedHashMap<String, Definition>( 175 localeDefsMap.size()); 176 177 for (Map.Entry<String, Definition> entry : localeDefsMap.entrySet()) { 178 Definition definition = new Definition(entry.getValue()); 179 retValue.put(entry.getKey(), definition); 180 } 181 182 return retValue; 183 } 184 }