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

1   /*
2    * $Id: DefinitionManager.java 791161 2009-07-04 18:53:36Z 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  package org.apache.tiles.impl.mgmt;
22  
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.tiles.Attribute;
28  import org.apache.tiles.Definition;
29  import org.apache.tiles.context.TilesRequestContext;
30  import org.apache.tiles.definition.DefinitionsFactory;
31  import org.apache.tiles.definition.NoSuchDefinitionException;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /***
36   * Manages custom and configured definitions, so they can be used by the
37   * container, instead of using a simple {@link DefinitionsFactory}.
38   *
39   * @version $Rev: 791161 $ $Date: 2009-07-04 20:53:36 +0200 (sab, 04 lug 2009) $
40   */
41  public class DefinitionManager {
42  
43      /***
44       * The logging object.
45       */
46      private final Logger log = LoggerFactory.getLogger(DefinitionManager.class);
47  
48      /***
49       * The default name of the attribute in which storing custom definitions.
50       */
51      private static final String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
52          "org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
53  
54      /***
55       * The definitions factory to use to get main definitions.
56       */
57      private DefinitionsFactory factory;
58  
59      /***
60       * The name of the attribute in which storing custom definitions.
61       */
62      private String definitionsAttributeName;
63  
64      /***
65       * Constructor.
66       */
67      public DefinitionManager() {
68          definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
69      }
70  
71      /***
72       * Constructor.
73       *
74       * @param definitionsAttributeName The name of the attribute in which
75       * storing custom definitions.
76       */
77      public DefinitionManager(String definitionsAttributeName) {
78          this.definitionsAttributeName = definitionsAttributeName;
79          if (this.definitionsAttributeName == null) {
80              this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
81          }
82      }
83  
84      /***
85       * Returns the used definitions factory.
86       *
87       * @return The used definitions factory.
88       */
89      public DefinitionsFactory getFactory() {
90          return factory;
91      }
92  
93      /***
94       * Sets the definitions factory to use.
95       *
96       * @param factory The definitions factory.
97       */
98      public void setFactory(DefinitionsFactory factory) {
99          this.factory = factory;
100     }
101 
102     /***
103      * Returns a definition by name.
104      *
105      * @param definition The name of the definition.
106      * @param request The current request.
107      * @return The requested definition, either main or custom.
108      * @throws org.apache.tiles.definition.DefinitionsFactoryException If
109      * something goes wrong when obtaining a main definition.
110      */
111     public Definition getDefinition(String definition,
112             TilesRequestContext request) {
113         Map<String, Definition> definitions =
114             getDefinitions(request);
115         if (definitions != null && definitions.containsKey(definition)) {
116             return definitions.get(definition);
117         }
118         return getFactory().getDefinition(definition, request);
119     }
120 
121     /***
122      * Adds a definition to the set of custom ones.
123      *
124      * @param definition The definition to add.
125      * @param request The current request.
126      * @throws org.apache.tiles.definition.DefinitionsFactoryException If
127      * something goes wrong during the addition.
128      */
129     public void addDefinition(Definition definition,
130             TilesRequestContext request) {
131         Map<String, Definition> definitions = getOrCreateDefinitions(request);
132         if (definition.getName() == null) {
133             definition.setName(getNextUniqueDefinitionName(definitions));
134         }
135         validate(definition);
136 
137         if (definition.isExtending()) {
138             this.resolveInheritance(definition, request);
139         }
140 
141         definitions.put(definition.getName(), definition);
142     }
143 
144     /***
145      * Validates a custom definition.
146      *
147      * @param definition The definition to validate.
148      */
149     private void validate(Definition definition) {
150         Set<String> names = definition.getLocalAttributeNames();
151         if (names != null) {
152             for (String name : names) {
153                 Attribute attribute = definition.getLocalAttribute(name);
154                 if (attribute.getValue() == null) {
155                     throw new IllegalArgumentException(
156                             "Attribute '" + name + "' value not defined");
157                 }
158             }
159         }
160         names = definition.getCascadedAttributeNames();
161         if (names != null) {
162             for (String name : names) {
163                 Attribute attribute = definition.getCascadedAttribute(name);
164                 if (attribute.getValue() == null) {
165                     throw new IllegalArgumentException(
166                             "Attribute '" + name + "' value not defined");
167                 }
168             }
169         }
170     }
171 
172     /***
173      * Resolve inheritance.
174      * First, resolve parent's inheritance, then set template to the parent's
175      * template.
176      * Also copy attributes setted in parent, and not set in child
177      * If instance doesn't extend anything, do nothing.
178      *
179      * @param definition The definition that needs to have its inheritances
180      * resolved.
181      * @param request The current request.
182      * @throws org.apache.tiles.definition.DefinitionsFactoryException If an
183      * inheritance can not be solved.
184      */
185     protected void resolveInheritance(Definition definition,
186             TilesRequestContext request) {
187         // Already done, or not needed ?
188         if (!definition.isExtending()) {
189             return;
190         }
191 
192         if (log.isDebugEnabled()) {
193             log.debug("Resolve definition for child name='"
194                 + definition.getName()
195                 + "' extends='" + definition.getExtends() + "'.");
196         }
197 
198         // TODO Factories our factory implementations will be context agnostic,
199         //  however, this may cause errors for other implementations.
200         //  we should probably make all factories agnostic and allow the manager to
201         //  utilize the correct factory based on the context.
202         Definition parent = getDefinition(definition.getExtends(), request);
203 
204         if (parent == null) { // error
205             String msg = "Error while resolving definition inheritance: child '"
206                 + definition.getName()
207                 + "' can't find its ancestor '"
208                 + definition.getExtends()
209                 + "'. Please check your description file.";
210             log.error(msg);
211             // to do : find better exception
212             throw new NoSuchDefinitionException(msg);
213         }
214 
215         // Resolve parent before itself.
216         resolveInheritance(parent, request);
217         definition.inherit(parent);
218     }
219 
220     /***
221      * Overloads a child definition with a given parent.
222      * All attributes present in child are kept. All missing attributes are
223      * copied from the parent.
224      * Special attribute 'template','role' and 'extends' are overloaded in child
225      * if not defined
226      *
227      * @param parent The parent definition.
228      * @param child  The child that will be overloaded.
229      * @deprecated Use {@link Definition#inherit(org.apache.tiles.BasicAttributeContext)}.
230      */
231     protected void overload(Definition parent, Definition child) {
232         child.inherit(parent);
233     }
234 
235     /***
236      * Returns the map with custom definitions for the current request.
237      *
238      * @param request The current request.
239      * @return A map that connects a definition name to a definition.
240      */
241     @SuppressWarnings("unchecked")
242     protected Map<String, Definition> getDefinitions(
243             TilesRequestContext request) {
244         return (Map<String, Definition>) request.getRequestScope()
245                 .get(definitionsAttributeName);
246     }
247 
248     /***
249      * Returns a map of type "definition name -> definition" and, if it has not
250      * been defined before, creates one.
251      *
252      * @param request The current request.
253      * @return A map that connects a definition name to a definition.
254      */
255     @SuppressWarnings("unchecked")
256     protected Map<String, Definition> getOrCreateDefinitions(
257             TilesRequestContext request) {
258         Map<String, Definition> definitions =
259             (Map<String, Definition>) request
260                 .getRequestScope().get(definitionsAttributeName);
261         if (definitions == null) {
262             definitions = new HashMap<String, Definition>();
263             request.getRequestScope()
264                     .put(definitionsAttributeName, definitions);
265         }
266 
267         return definitions;
268     }
269 
270     /***
271      * Create a unique definition name usable to store anonymous definitions.
272      *
273      * @param definitions The already created definitions.
274      * @return The unique definition name to be used to store the definition.
275      * @since 2.1.0
276      */
277     protected String getNextUniqueDefinitionName(
278             Map<String, Definition> definitions) {
279         String candidate;
280         int anonymousDefinitionIndex = 1;
281 
282         do {
283             candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
284             anonymousDefinitionIndex++;
285         } while (definitions.containsKey(candidate));
286 
287         return candidate;
288     }
289 }