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

1   /*
2    * $Id: CachingTilesContainer.java 1045365 2010-12-13 20:46:46Z 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  
26  import org.apache.tiles.Definition;
27  import org.apache.tiles.TilesContainer;
28  import org.apache.tiles.TilesContainerWrapper;
29  import org.apache.tiles.definition.NoSuchDefinitionException;
30  import org.apache.tiles.mgmt.MutableTilesContainer;
31  import org.apache.tiles.request.Request;
32  
33  /**
34   * Manages custom and configured definitions, so they can be used by the
35   * container, instead of using a simple {@link org.apache.tiles.definition.DefinitionsFactory}.
36   *
37   * @version $Rev: 1045365 $ $Date: 2010-12-14 07:46:46 +1100 (Tue, 14 Dec 2010) $
38   */
39  public class CachingTilesContainer extends TilesContainerWrapper implements MutableTilesContainer {
40  
41      /**
42       * The default name of the attribute in which storing custom definitions.
43       */
44      private static final String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
45          "org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
46  
47      /**
48       * The name of the attribute in which storing custom definitions.
49       */
50      private String definitionsAttributeName;
51  
52      /**
53       * Constructor.
54       * @param originalContainer The original container to wrap.
55       */
56      public CachingTilesContainer(TilesContainer originalContainer) {
57          super(originalContainer);
58          definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
59      }
60  
61      /**
62       * Constructor.
63       * @param originalContainer The original container to wrap.
64       * @param definitionsAttributeName The name of the attribute in which
65       * storing custom definitions.
66       */
67      public CachingTilesContainer(TilesContainer originalContainer, String definitionsAttributeName) {
68          super(originalContainer);
69          this.definitionsAttributeName = definitionsAttributeName;
70          if (this.definitionsAttributeName == null) {
71              this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
72          }
73      }
74  
75      /**
76       * Returns a definition by name.
77       *
78       * @param definition The name of the definition.
79       * @param request The current request.
80       * @return The requested definition, either main or custom.
81       * @throws org.apache.tiles.definition.DefinitionsFactoryException If
82       * something goes wrong when obtaining a main definition.
83       */
84      public Definition getDefinition(String definition,
85              Request request) {
86          Definition retValue = null;
87          retValue = getCustomDefinition(definition, request);
88          if (retValue == null) {
89              retValue = super.getDefinition(definition, request);
90          }
91          return retValue;
92      }
93  
94      /** {@inheritDoc} */
95      @Override
96      public boolean isValidDefinition(String definition, Request request) {
97          if (getCustomDefinition(definition, request) != null) {
98              return true;
99          }
100         return super.isValidDefinition(definition, request);
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     public void register(Definition definition, Request request) {
106         Map<String, Definition> definitions = getOrCreateDefinitions(request);
107         if (definition.getName() == null) {
108             definition.setName(getNextUniqueDefinitionName(definitions));
109         }
110 
111         if (definition.isExtending()) {
112             this.resolveInheritance(definition, request);
113         }
114 
115         definitions.put(definition.getName(), definition);
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public void render(String definition, Request request) {
121         Definition toRender = getDefinition(definition, request);
122         if (toRender == null) {
123             throw new NoSuchDefinitionException(
124                     "Cannot find definition named '" + definition + "'");
125         }
126         super.render(toRender, request);
127     }
128 
129     /**
130      * Resolve inheritance.
131      * First, resolve parent's inheritance, then set template to the parent's
132      * template.
133      * Also copy attributes set in parent, and not set in child
134      * If instance doesn't extend anything, do nothing.
135      *
136      * @param definition The definition that needs to have its inheritances
137      * resolved.
138      * @param request The current request.
139      * @throws org.apache.tiles.definition.DefinitionsFactoryException If an
140      * inheritance can not be solved.
141      */
142     private void resolveInheritance(Definition definition,
143             Request request) {
144         // Already done, or not needed ?
145         if (!definition.isExtending()) {
146             return;
147         }
148 
149         String parentDefinitionName = definition.getExtends();
150 
151         boolean recurse = true;
152         Definition parent = getCustomDefinition(parentDefinitionName, request);
153         if (parent == null) {
154             parent = container.getDefinition(parentDefinitionName, request);
155             recurse = false;
156         }
157 
158         if (parent == null) {
159             throw new NoSuchDefinitionException(
160                     "Error while resolving definition inheritance: child '"
161                             + definition.getName()
162                             + "' can't find its ancestor '"
163                             + parentDefinitionName
164                             + "'. Please check your description file.");
165         }
166 
167         // Resolve parent before itself.
168         if (recurse) {
169             resolveInheritance(parent, request);
170         }
171         definition.inherit(parent);
172     }
173 
174     /**
175      * Returns the map with custom definitions for the current request.
176      *
177      * @param request The current request.
178      * @return A map that connects a definition name to a definition.
179      */
180     @SuppressWarnings("unchecked")
181     private Map<String, Definition> getDefinitions(
182             Request request) {
183         return (Map<String, Definition>) request.getContext("request")
184                 .get(definitionsAttributeName);
185     }
186 
187     /**
188      * Returns a map of type "definition name -> definition" and, if it has not
189      * been defined before, creates one.
190      *
191      * @param request The current request.
192      * @return A map that connects a definition name to a definition.
193      */
194     @SuppressWarnings("unchecked")
195     private Map<String, Definition> getOrCreateDefinitions(
196             Request request) {
197         Map<String, Definition> definitions =
198             (Map<String, Definition>) request.getContext("request").get(definitionsAttributeName);
199         if (definitions == null) {
200             definitions = new HashMap<String, Definition>();
201             request.getContext("request")
202                     .put(definitionsAttributeName, definitions);
203         }
204 
205         return definitions;
206     }
207 
208     /**
209      * Create a unique definition name usable to store anonymous definitions.
210      *
211      * @param definitions The already created definitions.
212      * @return The unique definition name to be used to store the definition.
213      * @since 2.1.0
214      */
215     private String getNextUniqueDefinitionName(
216             Map<String, Definition> definitions) {
217         String candidate;
218         int anonymousDefinitionIndex = 1;
219 
220         do {
221             candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
222             anonymousDefinitionIndex++;
223         } while (definitions.containsKey(candidate));
224 
225         return candidate;
226     }
227 
228     /**
229      * Returns a custom definition from the cache.
230      *
231      * @param definition The definition to search.
232      * @param request The request.
233      * @return The requested definition.
234      */
235     private Definition getCustomDefinition(String definition, Request request) {
236         Map<String, Definition> definitions = getDefinitions(request);
237         if (definitions != null) {
238             return definitions.get(definition);
239         }
240         return null;
241     }
242 }