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

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