This project has retired. For details please refer to its Attic page.
DefinitionsImpl xref
View Javadoc

1   /*
2    * $Id: DefinitionsImpl.java 605994 2007-12-20 18:41:47Z 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;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.tiles.Attribute;
27  import org.apache.tiles.Definition;
28  
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  
35  /***
36   * @version $Rev: 605994 $ $Date: 2007-12-20 19:41:47 +0100 (Thu, 20 Dec 2007) $
37   */
38  public class DefinitionsImpl implements Definitions {
39  
40      /***
41       * Commons Logging instance.
42       */
43      private static Log log = LogFactory.getLog(DefinitionsImpl.class);
44  
45      /***
46       * The base set of Definition objects not discriminated by locale.
47       */
48      private Map<String, Definition> baseDefinitions;
49      /***
50       * The locale-specific set of definitions objects.
51       */
52      private Map<Locale, Map<String, Definition>> localeSpecificDefinitions;
53  
54      /***
55       * Creates a new instance of DefinitionsImpl.
56       */
57      public DefinitionsImpl() {
58          baseDefinitions = new HashMap<String, Definition>();
59          localeSpecificDefinitions =
60              new HashMap<Locale, Map<String, Definition>>();
61      }
62  
63      /***
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 none
68       *         is found.
69       */
70      public Definition getDefinition(String name) {
71          return baseDefinitions.get(name);
72      }
73  
74      /***
75       * Adds new Definition objects to the internal collection and
76       * resolves inheritance attraibutes.
77       *
78       * @param defsMap The new definitions to add.
79       * @throws NoSuchDefinitionException If something goes wrong during
80       * addition.
81       */
82      public void addDefinitions(Map<String, Definition> defsMap)
83              throws NoSuchDefinitionException {
84          this.baseDefinitions.putAll(defsMap);
85          resolveInheritances();
86      }
87  
88      /***
89       * Adds new locale-specific Definition objects to the internal
90       * 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 during
95       * inheritance resolution.
96       */
97      public void addDefinitions(Map<String, Definition> defsMap,
98              Locale locale) throws NoSuchDefinitionException {
99          localeSpecificDefinitions.put(locale, defsMap);
100         resolveInheritances(locale);
101     }
102 
103     /***
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 none
109      *         is found.
110      */
111     public Definition getDefinition(String name, Locale locale) {
112         Definition definition = null;
113 
114         if (locale != null) {
115             Map<String, Definition> localeSpecificMap =
116                 localeSpecificDefinitions.get(locale);
117             if (localeSpecificMap != null) {
118                 definition = localeSpecificMap.get(name);
119             }
120         }
121 
122         if (definition == null) {
123             definition = getDefinition(name);
124         }
125 
126         return definition;
127     }
128 
129     /***
130      * Resolve extended instances.
131      *
132      * @throws NoSuchDefinitionException If a parent definition is not found.
133      */
134     public void resolveInheritances() throws NoSuchDefinitionException {
135         Set<String> alreadyResolvedDefinitions = new HashSet<String>();
136 
137         for (Definition definition : baseDefinitions.values()) {
138             resolveInheritance(definition, null, alreadyResolvedDefinitions);
139         }  // end loop
140     }
141 
142     /***
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      */
148     public void resolveInheritances(Locale locale) throws NoSuchDefinitionException {
149         resolveInheritances();
150 
151         Map<String, Definition> map = localeSpecificDefinitions.get(locale);
152         if (map != null) {
153             Set<String> alreadyResolvedDefinitions = new HashSet<String>();
154             for (Definition definition : map.values()) {
155                 resolveInheritance(definition, locale,
156                         alreadyResolvedDefinitions);
157             }  // end loop
158         }
159     }
160 
161     /***
162      * Clears definitions.
163      */
164     public void reset() {
165         this.baseDefinitions = new HashMap<String, Definition>();
166         this.localeSpecificDefinitions =
167             new HashMap<Locale, Map<String, Definition>>();
168     }
169 
170     /***
171      * Returns base definitions collection.
172      *
173      * @return The base (i.e. not depending on any locale) definitions map.
174      */
175     public Map<String, Definition> getBaseDefinitions() {
176         return baseDefinitions;
177     }
178 
179     /***
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 returns
185      *         <code>null</code>.
186      */
187     protected Definition getDefinitionByAttribute(
188         Attribute attr, Locale locale) {
189         Definition retValue = null;
190 
191         Object attrValue = attr.getValue();
192         if (attrValue instanceof String) {
193             retValue = this.getDefinition((String) attr
194                 .getValue(), locale);
195         }
196 
197         return retValue;
198     }
199 
200     /***
201      * Resolve locale-specific inheritance.
202      * First, resolve parent's inheritance, then set template to the parent's
203      * template.
204      * Also copy attributes setted in parent, and not set in child
205      * If instance doesn't extend anything, do nothing.
206      *
207      * @param definition The definition to resolve
208      * @param locale The locale to use.
209      * @param alreadyResolvedDefinitions The set of the definitions that have
210      * been already resolved.
211      * @throws NoSuchDefinitionException If an inheritance can not be solved.
212      */
213     protected void resolveInheritance(Definition definition, Locale locale,
214             Set<String> alreadyResolvedDefinitions)
215             throws NoSuchDefinitionException {
216         // Already done, or not needed ?
217         if (!definition.isExtending()
218                 || alreadyResolvedDefinitions.contains(definition.getName())) {
219             return;
220         }
221 
222         if (log.isDebugEnabled()) {
223             log.debug("Resolve definition for child name='"
224                 + definition.getName()
225                 + "' extends='" + definition.getExtends() + "'.");
226         }
227 
228         // Set as visited to avoid endless recurisvity.
229         alreadyResolvedDefinitions.add(definition.getName());
230 
231         // Resolve parent before itself.
232         Definition parent = getDefinition(definition.getExtends(),
233             locale);
234         if (parent == null) { // error
235             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 exception
242             throw new NoSuchDefinitionException(msg);
243         }
244 
245         resolveInheritance(parent, locale, alreadyResolvedDefinitions);
246 
247         overload(parent, definition);
248     }
249 
250     /***
251      * Overloads a child definition with a given parent.
252      * All attributes present in child are kept. All missing attributes are
253      * copied from the parent.
254      * Special attribute 'template','role' and 'extends' are overloaded in child
255      * if not defined
256      *
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.
261     protected void overload(Definition parent, Definition child) {
262         // Iterate on each parent's attribute and add it if not defined in child.
263         for (Map.Entry<String, Attribute> entry : parent.getAttributes().entrySet()) {
264             if (!child.hasAttributeValue(entry.getKey())) {
265                 child.putAttribute(entry.getKey(), new Attribute(entry.getValue()));
266             }
267         }
268         // Set template and role if not setted
269         if (child.getTemplate() == null) {
270             child.setTemplate(parent.getTemplate());
271         }
272         if (child.getRoles() == null) {
273             child.setRoles(parent.getRoles());
274         }
275         if (child.getPreparer() == null) {
276             child.setPreparer(parent.getPreparer());
277         }
278     }
279 }