Project Tiles has retired. For details please refer to its Attic page.
ResolvingLocaleUrlDefinitionDAO xref
View Javadoc

1   /*
2    * $Id: ResolvingLocaleUrlDefinitionDAO.java 1297705 2012-03-06 20:44:30Z nlebas $
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.apache.tiles.request.ApplicationContext;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * <p>
38   * A definitions DAO (loading URLs and using Locale as a customization key) that
39   * caches definitions that have been loaded and resolves inheritances.
40   * </p>
41   * <p>
42   * It can check if the URLs change, but by default this feature is turned off.
43   * </p>
44   *
45   * @version $Rev: 1297705 $ $Date: 2012-03-07 07:44:30 +1100 (Wed, 07 Mar 2012) $
46   * @since 2.1.0
47   */
48  public class ResolvingLocaleUrlDefinitionDAO extends
49          CachingLocaleUrlDefinitionDAO {
50  
51      /**
52       * The logging object.
53       */
54      private final Logger log = LoggerFactory.getLogger(ResolvingLocaleUrlDefinitionDAO.class);
55  
56      public ResolvingLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
57          super(applicationContext);
58      }
59  
60      /** {@inheritDoc} */
61      @Override
62      protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) {
63          return loadRawDefinitionsFromResources(parentLocale);
64      }
65  
66      @Override
67      protected Map<String, Definition> loadDefinitions(Locale customizationKey) {
68          Map<String, Definition> localeDefsMap = super.loadDefinitions(customizationKey);
69          Map<String, Definition> defsMap = definitionResolver
70                  .storeDefinitionPatterns(copyDefinitionMap(localeDefsMap),
71                          customizationKey);
72          resolveInheritances(defsMap, customizationKey);
73          locale2definitionMap.put(customizationKey, defsMap);
74          return defsMap;
75      }
76  
77      /** {@inheritDoc} */
78      @Override
79      protected Definition getDefinitionFromResolver(String name,
80              Locale customizationKey) {
81          Definition retValue = super.getDefinitionFromResolver(name, customizationKey);
82          if (retValue != null && retValue.getExtends() != null) {
83              Definition parent = getDefinition(retValue.getExtends(), customizationKey);
84              retValue.inherit(parent);
85          }
86  
87          return retValue;
88      }
89  
90      /**
91       * Resolve locale-specific extended instances.
92       *
93       * @param map The definition map containing the definitions to resolve.
94       * @param locale The locale to use.
95       * @throws NoSuchDefinitionException If a parent definition is not found.
96       * @since 2.1.0
97       */
98      protected void resolveInheritances(Map<String, Definition> map, Locale locale) {
99          if (map != null) {
100             Set<String> alreadyResolvedDefinitions = new HashSet<String>();
101             for (Definition definition : map.values()) {
102                 resolveInheritance(definition, map, locale,
103                         alreadyResolvedDefinitions);
104             }  // end loop
105         }
106     }
107 
108     /**
109      * Resolve locale-specific inheritance. First, resolve parent's inheritance,
110      * then set template to the parent's template. Also copy attributes setted
111      * in parent, and not set in child If instance doesn't extend anything, do
112      * nothing.
113      *
114      * @param definition The definition to resolve
115      * @param definitions The definitions to take when obtaining a parent
116      * definition.
117      * @param locale The locale to use.
118      * @param alreadyResolvedDefinitions The set of the definitions that have
119      * been already resolved.
120      * @throws NoSuchDefinitionException If an inheritance can not be solved.
121      * @since 2.1.0
122      */
123     protected void resolveInheritance(Definition definition,
124             Map<String, Definition> definitions, Locale locale,
125             Set<String> alreadyResolvedDefinitions) {
126         // Already done, or not needed ?
127         if (!definition.isExtending()
128                 || alreadyResolvedDefinitions.contains(definition.getName())) {
129             return;
130         }
131 
132         log.debug("Resolve definition for child name='{}' extends='{}.",
133                 definition.getName(), definition.getExtends());
134 
135         // Set as visited to avoid endless recursivity.
136         alreadyResolvedDefinitions.add(definition.getName());
137 
138         // Resolve parent before itself.
139         Definition parent = definitions.get(definition.getExtends());
140         if (parent == null) { // error
141             String msg = "Error while resolving definition inheritance: child '"
142                 + definition.getName()
143                 + "' can't find its ancestor '"
144                 + definition.getExtends()
145                 + "'. Please check your description file.";
146             // to do : find better exception
147             throw new NoSuchDefinitionException(msg);
148         }
149 
150         resolveInheritance(parent, definitions, locale,
151                 alreadyResolvedDefinitions);
152 
153         definition.inherit(parent);
154     }
155 
156     /**
157      * Copies the definition map to be passed to a higher level of customization
158      * key.
159      *
160      * @param localeDefsMap The map of definition to be copied.
161      * @return The copy of the definition map. This particular implementation
162      * deep-copies the <code>localeDefsMap</code> into a {@link LinkedHashMap}.
163      * @since 2.1.4
164      */
165     @Override
166     protected Map<String, Definition> copyDefinitionMap(
167             Map<String, Definition> localeDefsMap) {
168         Map<String, Definition> retValue = new LinkedHashMap<String, Definition>(
169                 localeDefsMap.size());
170 
171         for (Map.Entry<String, Definition> entry : localeDefsMap.entrySet()) {
172             Definition definition = new Definition(entry.getValue());
173             retValue.put(entry.getKey(), definition);
174         }
175 
176         return retValue;
177     }
178 }