1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 }
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 }
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
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
229 alreadyResolvedDefinitions.add(definition.getName());
230
231
232 Definition parent = getDefinition(definition.getExtends(),
233 locale);
234 if (parent == null) {
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
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
261 protected void overload(Definition parent, Definition child) {
262
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
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 }