KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > tools > struts > TilesTool


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.velocity.tools.struts;
18
19 import java.util.Stack JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Iterator JavaDoc;
22
23 import javax.servlet.http.HttpSession JavaDoc;
24
25 import org.apache.struts.tiles.ComponentContext;
26 import org.apache.struts.tiles.ComponentDefinition;
27 import org.apache.struts.tiles.AttributeDefinition;
28 import org.apache.struts.tiles.DirectStringAttribute;
29 import org.apache.struts.tiles.DefinitionAttribute;
30 import org.apache.struts.tiles.DefinitionNameAttribute;
31 import org.apache.struts.tiles.PathAttribute;
32 import org.apache.struts.tiles.TilesUtil;
33 import org.apache.struts.tiles.DefinitionsFactoryException;
34 import org.apache.struts.tiles.Controller;
35
36 import org.apache.velocity.app.Velocity;
37 import org.apache.velocity.context.Context;
38 import org.apache.velocity.tools.view.ImportSupport;
39 import org.apache.velocity.tools.view.context.ViewContext;
40 import org.apache.velocity.tools.view.tools.ViewTool;
41
42 /**
43  * View tool to use struts-tiles with Velocity.
44  * <p><pre>
45  * Template example(s):
46  * &lt;!-- insert a tile --&gt;
47  * $tiles.myTileDefinition
48  *
49  * &lt;!-- get named attribute value from the current tiles-context --&gt;
50  * $tiles.getAttribute("myTileAttribute")
51  *
52  * &lt;!-- import all attributes of the current tiles-context into the velocity-context. --&gt;
53  * $tiles.importAttributes()
54  *
55  * Toolbox configuration:
56  * &lt;tool&gt;
57  * &lt;key&gt;tiles&lt;/key&gt;
58  * &lt;scope&gt;request&lt;/scope&gt;
59  * &lt;class&gt;org.apache.velocity.tools.struts.TilesTool&lt;/class&gt;
60  * &lt;/tool&gt;
61  * </pre></p>
62  *
63  * <p>This tool should only be used in the request scope.</p>
64  *
65  * @author <a HREF="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
66  * @since VelocityTools 1.1
67  * @version $Revision: 1.11.2.1 $ $Date: 2004/03/12 20:16:28 $
68  */

69 public class TilesTool extends ImportSupport implements ViewTool
70 {
71     static final String JavaDoc PAGE_SCOPE = "page";
72     static final String JavaDoc REQUEST_SCOPE = "request";
73     static final String JavaDoc SESSION_SCOPE = "session";
74     static final String JavaDoc APPLICATION_SCOPE = "application";
75
76     protected Context velocityContext;
77
78     /**
79      * A stack to hold ComponentContexts while nested tile-definitions
80      * are rendered.
81      */

82     protected Stack JavaDoc contextStack;
83
84     /******************************* Constructors ****************************/
85
86     /**
87      * Default constructor. Tool must be initialized before use.
88      */

89     public TilesTool() {}
90
91     /**
92      * Initializes this tool.
93      *
94      * @param obj the current ViewContext
95      * @throws IllegalArgumentException if the param is not a ViewContext
96      */

97     public void init(Object JavaDoc obj)
98     {
99         if (!(obj instanceof ViewContext))
100         {
101             throw new IllegalArgumentException JavaDoc("Tool can only be initialized with a ViewContext");
102         }
103
104         ViewContext viewContext = (ViewContext)obj;
105         this.velocityContext = viewContext.getVelocityContext();
106         this.request = viewContext.getRequest();
107         this.response = viewContext.getResponse();
108         this.application = viewContext.getServletContext();
109     }
110
111     /***************************** View Helpers ******************************/
112
113     /**
114      * A generic tiles insert function.
115      *
116      * <p>This is functionally equivalent to
117      * <code>&lt;tiles:insert attribute="foo" /&gt;</code>.</p>
118      *
119      * @param obj Can be any of the following:
120      * AttributeDefinition,
121      * tile-definition name,
122      * tile-attribute name,
123      * regular uri.
124      * (checked in that order)
125      * @return the rendered template or value as a String
126      * @throws Exception on failure
127      */

128     public String JavaDoc get(Object JavaDoc obj)
129     {
130         try
131         {
132             Object JavaDoc value = getCurrentContext().getAttribute(obj.toString());
133             if (value != null)
134             {
135                 return processObjectValue(value);
136             }
137             return processAsDefinitionOrURL(obj.toString());
138         }
139         catch (Exception JavaDoc e)
140         {
141             Velocity.error("TilesTool: Exeption while rendering Tile "
142                            + obj + ": " + e.getMessage());
143             return null;
144         }
145     }
146
147     /**
148      * Fetches a named attribute-value from the current tiles-context.
149      *
150      * <p>This is functionally equivalent to
151      * <code>&lt;tiles:getAsString name="foo" /&gt;</code>.</p>
152      *
153      * @param name the name of the tiles-attribute to fetch
154      * @return attribute value for the named attribute
155      */

156     public Object JavaDoc getAttribute(String JavaDoc name)
157     {
158         Object JavaDoc value = getCurrentContext().getAttribute(name);
159         if (value == null)
160         {
161             Velocity.warn("TilesTool: Tile attribute '"
162                            + name + "' was not found in context.");
163         }
164         return value;
165     }
166
167     /**
168      * Imports the named attribute-value from the current tiles-context into the
169      * current Velocity context.
170      *
171      * <p>This is functionally equivalent to
172      * <code>&lt;tiles:importAttribute name="foo" /&gt;</code>
173      *
174      * @param name the name of the tiles-attribute to import
175      */

176     public void importAttribute(String JavaDoc name)
177     {
178         this.importAttribute(name, PAGE_SCOPE);
179     }
180
181     /**
182      * Imports the named attribute-value from the current tiles-context into the
183      * named context ("page", "request", "session", or "application").
184      *
185      * <p>This is functionally equivalent to
186      * <code>&lt;tiles:importAttribute name="foo" scope="scopeValue" /&gt;</code>
187      *
188      * @param name the name of the tiles-attribute to import
189      * @param scope the named context scope to put the attribute into.
190      */

191     public void importAttribute(String JavaDoc name, String JavaDoc scope)
192     {
193         Object JavaDoc value = getCurrentContext().getAttribute(name);
194         if (value == null)
195         {
196             Velocity.warn("TilesTool: Tile attribute '"
197                            + name + "' was not found in context.");
198         }
199
200         if (scope.equals(PAGE_SCOPE))
201         {
202             velocityContext.put(name, value);
203         }
204         else if (scope.equals(REQUEST_SCOPE))
205         {
206             request.setAttribute(name, value);
207         }
208         else if (scope.equals(SESSION_SCOPE))
209         {
210             request.getSession().setAttribute(name, value);
211         }
212         else if (scope.equals(APPLICATION_SCOPE))
213         {
214             application.setAttribute(name, value);
215         }
216     }
217
218     /**
219      * Imports all attributes in the current tiles-context into the
220      * current velocity-context.
221      *
222      * <p>This is functionally equivalent to
223      * <code>&lt;tiles:importAttribute /&gt;</code>.</p>
224      */

225     public void importAttributes()
226     {
227         this.importAttributes(PAGE_SCOPE);
228     }
229
230     /**
231      * Imports all attributes in the current tiles-context into the named
232      * context ("page", "request", "session", or "application").
233      *
234      * <p>This is functionally equivalent to
235      * <code>&lt;tiles:importAttribute scope="scopeValue" /&gt;</code>.</p>
236      *
237      * @param scope the named context scope to put the attributes into.
238      */

239     public void importAttributes(String JavaDoc scope)
240     {
241         ComponentContext context = getCurrentContext();
242         Iterator JavaDoc names = context.getAttributeNames();
243
244         if (scope.equals(PAGE_SCOPE))
245         {
246             while (names.hasNext())
247             {
248                 String JavaDoc name = (String JavaDoc)names.next();
249                 velocityContext.put(name, context.getAttribute(name));
250             }
251         }
252         else if (scope.equals(REQUEST_SCOPE))
253         {
254             while (names.hasNext())
255             {
256                 String JavaDoc name = (String JavaDoc)names.next();
257                 request.setAttribute(name, context.getAttribute(name));
258             }
259         }
260         else if (scope.equals(SESSION_SCOPE))
261         {
262             HttpSession JavaDoc session = request.getSession();
263             while (names.hasNext())
264             {
265                 String JavaDoc name = (String JavaDoc)names.next();
266                 session.setAttribute(name, context.getAttribute(name));
267             }
268         }
269         else if (scope.equals(APPLICATION_SCOPE))
270         {
271             while (names.hasNext())
272             {
273                 String JavaDoc name = (String JavaDoc)names.next();
274                 application.setAttribute(name, context.getAttribute(name));
275             }
276         }
277     }
278
279
280     /************************** Protected Methods ****************************/
281
282     /**
283      * Process an object retrieved as a bean or attribute.
284      *
285      * @param value - Object can be a typed attribute, a String, or anything
286      * else. If typed attribute, use associated type. Otherwise, apply
287      * toString() on object, and use returned string as a name.
288      * @throws Exception - Throws by underlying nested call to
289      * processDefinitionName()
290      * @return the fully processed value as String
291      */

292     protected String JavaDoc processObjectValue(Object JavaDoc value) throws Exception JavaDoc
293     {
294         /* First, check if value is one of the Typed Attribute */
295         if (value instanceof AttributeDefinition)
296         {
297             /* We have a type => return appropriate IncludeType */
298             return processTypedAttribute((AttributeDefinition)value);
299         }
300         else if (value instanceof ComponentDefinition)
301         {
302             return processDefinition((ComponentDefinition)value);
303         }
304         /* Value must denote a valid String */
305         return processAsDefinitionOrURL(value.toString());
306     }
307
308     /**
309      * Process typed attribute according to its type.
310      *
311      * @param value Typed attribute to process.
312      * @return the fully processed attribute value as String.
313      * @throws Exception - Throws by underlying nested call to processDefinitionName()
314      */

315     protected String JavaDoc processTypedAttribute(AttributeDefinition value)
316         throws Exception JavaDoc
317     {
318         if (value instanceof DirectStringAttribute)
319         {
320             return (String JavaDoc)value.getValue();
321         }
322         else if (value instanceof DefinitionAttribute)
323         {
324             return processDefinition((ComponentDefinition)value.getValue());
325         }
326         else if (value instanceof DefinitionNameAttribute)
327         {
328             return processAsDefinitionOrURL((String JavaDoc)value.getValue());
329         }
330         /* else if( value instanceof PathAttribute ) */
331         return doInsert((String JavaDoc)value.getValue(), null, null);
332     }
333
334     /**
335      * Try to process name as a definition, or as an URL if not found.
336      *
337      * @param name Name to process.
338      * @return the fully processed definition or URL
339      * @throws Exception
340      */

341     protected String JavaDoc processAsDefinitionOrURL(String JavaDoc name) throws Exception JavaDoc
342     {
343         try
344         {
345             ComponentDefinition definition =
346                     TilesUtil.getDefinition(name, this.request, this.application);
347             if (definition != null)
348             {
349                 return processDefinition(definition);
350             }
351         }
352         catch (DefinitionsFactoryException ex)
353         {
354         /* silently failed, because we can choose to not define a factory. */
355         }
356         /* no definition found, try as url */
357         return processUrl(name);
358     }
359
360     /**
361      * End of Process for definition.
362      *
363      * @param definition Definition to process.
364      * @return the fully processed definition.
365      * @throws Exception from InstantiationException Can't create requested controller
366      */

367     protected String JavaDoc processDefinition(ComponentDefinition definition) throws
368             Exception JavaDoc
369     {
370         Controller controller = null;
371         try
372         {
373             controller = definition.getOrCreateController();
374
375             String JavaDoc role = definition.getRole();
376             String JavaDoc page = definition.getTemplate();
377
378             return doInsert(definition.getAttributes(),
379                             page,
380                             role,
381                             controller);
382         }
383         catch (InstantiationException JavaDoc ex)
384         {
385             throw new Exception JavaDoc(ex.getMessage());
386         }
387     }
388
389     /**
390      * Processes an url
391      *
392      * @param url the URI to process.
393      * @return the rendered template as String.
394      * @throws Exception
395      */

396     protected String JavaDoc processUrl(String JavaDoc url) throws Exception JavaDoc
397     {
398         return doInsert(url, null, null);
399     }
400
401     /**
402      * Use this if there is no nested tile.
403      *
404      * @param page the page to process.
405      * @param role possible user-role
406      * @param controller possible tiles-controller
407      * @return the rendered template as String.
408      * @throws Exception
409      */

410     protected String JavaDoc doInsert(String JavaDoc page, String JavaDoc role, Controller controller) throws
411             Exception JavaDoc
412     {
413         if (role != null && !this.request.isUserInRole(role))
414         {
415             return null;
416         }
417
418         ComponentContext subCompContext = new ComponentContext();
419         return doInsert(subCompContext, page, role, controller);
420     }
421
422     /**
423      * Use this if there is a nested tile.
424      *
425      * @param attributes attributes for the sub-context
426      * @param page the page to process.
427      * @param role possible user-role
428      * @param controller possible tiles-controller
429      * @return the rendered template as String.
430      * @throws Exception
431      */

432     protected String JavaDoc doInsert(Map JavaDoc attributes,
433                               String JavaDoc page,
434                               String JavaDoc role,
435                               Controller controller) throws Exception JavaDoc
436     {
437         if (role != null && !this.request.isUserInRole(role))
438         {
439             return null;
440         }
441
442         ComponentContext subCompContext = new ComponentContext(attributes);
443         return doInsert(subCompContext, page, role, controller);
444     }
445
446     /**
447      * An extension of the other two doInsert functions
448      *
449      * @param subCompContext the sub-context to set in scope when the
450      * template is rendered.
451      * @param page the page to process.
452      * @param role possible user-role
453      * @param controller possible tiles-controller
454      * @return the rendered template as String.
455      * @throws Exception
456      */

457     protected String JavaDoc doInsert(ComponentContext subCompContext,
458                               String JavaDoc page,
459                               String JavaDoc role,
460                               Controller controller) throws Exception JavaDoc
461     {
462         pushTilesContext();
463         try
464         {
465             ComponentContext.setContext(subCompContext, this.request);
466
467             /* Call controller if any */
468             if (controller != null)
469             {
470                 controller.perform(subCompContext,
471                                    this.request,
472                                    this.response,
473                                    this.application);
474             }
475             /* pass things off to ImportSupport */
476             return this.acquireString(page);
477         }
478         finally
479         {
480             popTilesContext();
481         }
482     }
483
484     /**
485      * Retrieve the current tiles component context.
486      * This is pretty much just a convenience method.
487      */

488     protected ComponentContext getCurrentContext()
489     {
490         return ComponentContext.getContext(this.request);
491     }
492
493     /**
494      * <p>pushes the current tiles context onto the context-stack.
495      * preserving the context is necessary so that a sub-context can be
496      * put into request scope and lower level tiles can be rendered</p>
497      */

498     protected void pushTilesContext()
499     {
500         if (this.contextStack == null)
501         {
502             this.contextStack = new Stack JavaDoc();
503         }
504         contextStack.push(getCurrentContext());
505     }
506
507     /**
508      * Pops the tiles sub-context off the context-stack after the lower level
509      * tiles have been rendered.
510      */

511     protected void popTilesContext()
512     {
513         ComponentContext context = (ComponentContext)this.contextStack.pop();
514         ComponentContext.setContext(context, this.request);
515     }
516
517 }
518
Popular Tags