KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > riotfamily > website > template > TemplateController


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1
3  * The contents of this file are subject to the Mozilla Public License Version
4  * 1.1 (the "License"); you may not use this file except in compliance with
5  * the License. You may obtain a copy of the License at
6  * http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the
11  * License.
12  *
13  * The Original Code is Riot.
14  *
15  * The Initial Developer of the Original Code is
16  * Neteye GmbH.
17  * Portions created by the Initial Developer are Copyright (C) 2006
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  * Felix Gnass [fgnass at neteye dot de]
22  * Alf Werder <alf.werder@glonz.com>
23  *
24  * ***** END LICENSE BLOCK ***** */

25 package org.riotfamily.website.template;
26
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Map JavaDoc;
32
33 import javax.servlet.http.HttpServletRequest JavaDoc;
34 import javax.servlet.http.HttpServletResponse JavaDoc;
35
36 import org.springframework.beans.factory.InitializingBean;
37 import org.springframework.web.servlet.DispatcherServlet;
38 import org.springframework.web.servlet.ModelAndView;
39 import org.springframework.web.servlet.mvc.AbstractController;
40
41 /**
42  * Controller that passes a map of URLs to it's view (the template). The view
43  * is responsible for including the URLs (using a RequestDispatcher) at the
44  * right place.
45  * <p>
46  * The most simple way to achieve this is to use a JSTL view that contains
47  * a <code>&lt;c:import value="${<i>slotname</i>}" /&gt;</code> tag for each
48  * slot, where <code><i>slotname</i></code> has to be one of the keys present
49  * in the controllers configuration map.
50  * </p>
51  * <p>
52  * You may extend existing template configurations by setting the
53  * {@link #setParent(TemplateController) parent} property to another
54  * TemplateController. The local configuration will then be merged with the
55  * one of the parent, overriding previously defined URLs.
56  * </p>
57  * <p>
58  * Additionally the controller supports nested templates, i.e. the URL of a
59  * slot may in turn map to another TemplateController. These nested structures
60  * are taken into account when configurations are merged. When extending a
61  * parent you may also override URLs defined in nested templates.
62  * </p>
63  * <p>
64  * Let's say Template A has two slots, <i>left</i> and <i>right</i>. The right
65  * slot includes another Template B which also has two slots <i>top</i> and
66  * <i>bottom</i>, where <i>top</i> contains the URL <code>/foo.html</code>.
67  * </p>
68  * We can now define a third TemplateController A2 which extends A:
69  * <pre>
70  * &lt;template:definition name="A2" parent="A"&gt;
71  * &lt;template:insert slot="right.top" url="/bar.html" /&gt;
72  * &lt;/template:definition&gt;
73  * </pre>
74  * <p>
75  * The syntax above makes use of the Spring 2.0 namespace support. See
76  * {@link org.riotfamily.website.template.config.TemplateNamespaceHandler}
77  * for more information.
78  * </p>
79  *
80  * @author Alf Werder
81  * @author Felix Gnass
82  */

83 public class TemplateController extends AbstractController
84         implements InitializingBean {
85
86     /** NOTE: The DispatcherServlet class name prefix forces an attribute
87      * cleanup to be performed after an include, regardless of the servlet's
88      * cleanupAfterIncludes setting.
89      */

90     protected static final String JavaDoc SLOTS_CONFIGURATION_ATTRIBUTE =
91             DispatcherServlet.class.getName() + "#" +
92             TemplateController.class.getName() + ".SLOTS_CONFIG";
93
94     protected static final String JavaDoc SLOT_PATH_ATTRIBUTE =
95         DispatcherServlet.class.getName() + "#" +
96         TemplateController.class.getName() + ".SLOT_PATH";
97
98     protected static final String JavaDoc SLOT_PARAMETER =
99             TemplateController.class.getName() + ".SLOT";
100
101     private TemplateController parent;
102
103     private String JavaDoc viewName;
104
105     private Map JavaDoc configuration;
106
107     private Map JavaDoc mergedConfiguration;
108
109     private boolean session;
110
111     public TemplateController getParent() {
112         return parent;
113     }
114
115     public void setParent(TemplateController parent) {
116         this.parent = parent;
117     }
118     
119     public String JavaDoc getViewName() {
120         return viewName;
121     }
122
123     public void setViewName(String JavaDoc view) {
124         this.viewName = view;
125     }
126
127     public Map JavaDoc getConfiguration() {
128         return configuration;
129     }
130
131     public void setConfiguration(Map JavaDoc configuration) {
132         this.configuration = configuration;
133     }
134
135     public void setSession(boolean session) {
136         this.session = session;
137     }
138
139     /**
140      * Initializes the controller after all properties have been set. If a
141      * parent controller is set the view will be inherited (if not set locally).
142      */

143     public final void afterPropertiesSet() throws Exception JavaDoc {
144         inheritView();
145         inheritConfiguration();
146         initController();
147     }
148     
149     /**
150      * Subclasses may overwrite this method to perform initialization tasks
151      * after all properties have been set. The default implementation
152      * does nothing.
153      */

154     protected void initController() {
155     }
156         
157     /**
158      * Sets the view to the parent view if it has not been set locally.
159      */

160     private void inheritView() {
161         if (viewName == null && getParent() != null) {
162             viewName = getParent().getViewName();
163         }
164     }
165     
166     /**
167      * Merges the configuration map with the ones defined by ancestors.
168      */

169     protected void inheritConfiguration() {
170         mergedConfiguration = new HashMap JavaDoc();
171         if (parent != null) {
172             mergedConfiguration.putAll(parent.getMergedConfiguration());
173         }
174         if (configuration != null) {
175             mergedConfiguration.putAll(configuration);
176         }
177     }
178     
179     protected Map JavaDoc getMergedConfiguration() {
180         return this.mergedConfiguration;
181     }
182
183     /**
184      * Gets the effective configuration in case the template is nested within
185      * another template. The surrounding template(s) may override the local
186      * slot configuration.
187      */

188     private Map JavaDoc getEffectiveConfiguration(HttpServletRequest JavaDoc request) {
189         Map JavaDoc effectiveConfiguration = new HashMap JavaDoc(getMergedConfiguration());
190         applyOverrides(effectiveConfiguration, request);
191         return effectiveConfiguration;
192     }
193
194     /**
195      * Applies the overrides defined by surrounding templates.
196      */

197     protected void applyOverrides(Map JavaDoc config, HttpServletRequest JavaDoc request) {
198         Map JavaDoc slotsConfiguration = (Map JavaDoc) request.getAttribute(
199                 SLOTS_CONFIGURATION_ATTRIBUTE);
200
201         if (slotsConfiguration != null) {
202             String JavaDoc slot = request.getParameter(SLOT_PARAMETER);
203             if (slot != null) {
204                 String JavaDoc prefix = slot + '.';
205                 config.putAll(selectEntries(slotsConfiguration, prefix));
206             }
207             else {
208                 config.putAll(slotsConfiguration);
209             }
210         }
211     }
212     
213     /**
214      * Creates a new map containing all entries starting with the given prefix.
215      * The prefix is stripped from the keys of the new map.
216      */

217     private static Map JavaDoc selectEntries(Map JavaDoc map, String JavaDoc prefix) {
218         Map JavaDoc result = new HashMap JavaDoc();
219         int prefixLength = prefix.length();
220         Iterator JavaDoc i = map.entrySet().iterator();
221         while (i.hasNext()) {
222             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
223             String JavaDoc key = (String JavaDoc) entry.getKey();
224             if (key.startsWith(prefix)) {
225                 result.put(key.substring(prefixLength), entry.getValue());
226             }
227         }
228         return result;
229     }
230
231     /**
232      * Builds a map of URLs that is used as model for the template view.
233      */

234     protected Map JavaDoc buildUrlMap(Map JavaDoc config) {
235         Map JavaDoc model = new HashMap JavaDoc();
236         Iterator JavaDoc i = config.entrySet().iterator();
237         while (i.hasNext()) {
238             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
239             String JavaDoc slot = (String JavaDoc) entry.getKey();
240             if (slot.indexOf('.') == -1) {
241                 if (entry.getValue() != null) {
242                     model.put(slot, getUrlMapValue(entry.getValue(), slot));
243                 }
244                 else {
245                     model.remove(slot);
246                 }
247             }
248         }
249         return model;
250     }
251
252     /**
253      * Returns either a single String or a list of URLs.
254      */

255     protected Object JavaDoc getUrlMapValue(Object JavaDoc value, String JavaDoc slot) {
256         if (value instanceof Collection JavaDoc) {
257             ArrayList JavaDoc urls = new ArrayList JavaDoc();
258             Iterator JavaDoc it = ((Collection JavaDoc) value).iterator();
259             while (it.hasNext()) {
260                 String JavaDoc location = (String JavaDoc) it.next();
261                 urls.add(getSlotUrl(location, slot));
262             }
263             return urls;
264         }
265         else {
266             return getSlotUrl((String JavaDoc) value, slot);
267         }
268     }
269     
270     /**
271      * Returns the include URL for the given location and slot. By default
272      * <code>SLOT_REQUEST_PARAMETER_NAME</code> is appended, containing the
273      * given slot name.
274      */

275     protected String JavaDoc getSlotUrl(String JavaDoc location, String JavaDoc slot) {
276         StringBuffer JavaDoc url = new StringBuffer JavaDoc();
277         url.append(location);
278         url.append((url.indexOf("?") != -1) ? '&' : '?');
279         url.append(SLOT_PARAMETER);
280         url.append('=');
281         url.append(slot);
282         return url.toString();
283     }
284
285     protected ModelAndView handleRequestInternal(HttpServletRequest JavaDoc request,
286             HttpServletResponse JavaDoc response) throws Exception JavaDoc {
287
288         if (session) {
289             request.getSession();
290         }
291         Map JavaDoc config = getEffectiveConfiguration(request);
292         request.setAttribute(SLOTS_CONFIGURATION_ATTRIBUTE, config);
293         request.setAttribute(SLOT_PATH_ATTRIBUTE, getSlotPath(request));
294         return new ModelAndView(getViewName(), buildUrlMap(config));
295     }
296
297     /**
298      * Returns the fully qualified slot-path for the given request.
299      */

300     public static String JavaDoc getSlotPath(HttpServletRequest JavaDoc request) {
301         String JavaDoc slotPath = (String JavaDoc) request.getAttribute(SLOT_PATH_ATTRIBUTE);
302         String JavaDoc slot = request.getParameter(SLOT_PARAMETER);
303         if (slot != null) {
304             if (slotPath != null) {
305                 slotPath = slotPath + '.' + slot;
306             }
307             else {
308                 slotPath = slot;
309             }
310         }
311         return slotPath;
312     }
313
314 }
Popular Tags