2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.usecases.rolodex;
19  
20  import java.io.UnsupportedEncodingException;
21  import java.net.URLDecoder;
22  import java.util.List;
23  
24  import javax.faces.context.FacesContext;
25  import javax.faces.model.SelectItem;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.shale.clay.config.beans.AttributeBean;
30  import org.apache.shale.clay.config.beans.ComponentBean;
31  import org.apache.shale.clay.config.beans.ElementBean;
32  import org.apache.shale.view.AbstractViewController;
33  
34  /***
35   * <p>
36   * The ViewController for the rolodex use case. This is a contacts list that is
37   * to demonstrate the reuse ability for the Clay component.
38   * </p>
39   * 
40   */
41  public class Rolodex extends AbstractViewController {
42  
43      /***
44       * <p>
45       * Commons logging utility object static instance.
46       * </p>
47       */
48      private static Log log;
49      static {
50          log = LogFactory.getLog(Rolodex.class);
51      }
52  
53      /***
54       * <p>
55       * The the selected tab index.
56       * </p>
57       */
58      private int selectedTab = 0;
59  
60      /***
61       * <p>
62       * Returns the current tab index.
63       * </p>
64       */
65      public int getSelectedTab() {
66          return selectedTab;
67      }
68  
69      /***
70       * <p>
71       * The contact that is selected for edit.
72       * </p>
73       */
74      private Contact selectedContact = null;
75  
76      /***
77       * <p>
78       * Returns the selected contact for edit.
79       * </p>
80       */
81      public Contact getSelectedContact() {
82          return selectedContact;
83      }
84  
85      /***
86       * <p>
87       * Sets the selected contact for edit.
88       * </p>
89       */
90      public void setSelectedContact(Contact contact) {
91          selectedContact = contact;
92      }
93  
94      /***
95       * <p>
96       * Records a change in tab index.
97       * </p>
98       */
99      public void setSelectedTab(int index) {
100         if (log.isInfoEnabled())
101             log.info("Switching from tab " + selectedTab + " to tab " + index);
102 
103         selectedTab = index;
104         contacts = null;
105     }
106 
107     /***
108      * <p>
109      * Creates an object graph uses to build the rolodex folder tabs dynamically.
110      * </p>
111      * 
112      * @param item -
113      *            SelectItem holding the information to create the tab link
114      * @param context -
115      *            FacesContext
116      * @return returns a top level clay meta component bean
117      */
118     protected ElementBean createCommandLinkMetadata(SelectItem item,
119             FacesContext context) {
120 
121         // create a command link attribute
122         ElementBean link = new ElementBean();
123         link.setRenderId(generateId());
124         link.setJsfid("commandLink");
125         link.setComponentType("javax.faces.HtmlCommandLink");
126 
127         // add a value attribute
128         AttributeBean attr = new AttributeBean();
129         attr.setName("value");
130         attr.setValue(item.getLabel());
131         link.addAttribute(attr);
132 
133         // turn on the the immediate attribute so the validation
134         // logic is not invoked when switching tabs
135         attr = new AttributeBean();
136         attr.setName("immediate");
137         attr.setValue("true");
138         link.addAttribute(attr);
139 
140         // add a action method binding event when the link is clicked
141         attr = new AttributeBean();
142         attr.setName("action");
143         attr.setValue("#{@managed-bean-name.changeTab}");
144         link.addAttribute(attr);
145 
146         // create a parameter
147         ElementBean param = new ElementBean();
148         param.setJsfid("param");
149         param.setComponentType("javax.faces.Parameter");
150         // RenderId is the key to the Map. Increment for each new parameter
151         param.setRenderId(generateId());
152 
153         
154         // add a query param for the selected tab index
155         attr = new AttributeBean();
156         attr.setName("name");
157         attr.setValue("tabIndex");
158         param.addAttribute(attr);
159 
160         // add a query parameter for the tab index
161         attr = new AttributeBean();
162         attr.setName("value");
163         attr.setValue(((Integer) item.getValue()).toString());
164         param.addAttribute(attr);
165 
166         // add a parameter to the commandLink
167         link.addChild(param);
168 
169         return link;
170     }
171 
172     /***
173      * <p>
174      * A sequential counter to generate a unique renderId.
175      * </p>
176      */
177     int renderId = 0;
178 
179     /***
180      * <p>
181      * Returns the next sequential renderId. Because the logic builds the list
182      * in a linear fashion, a sequential unintelligent counter works.
183      * </p>
184      */
185     private int generateId() {
186         return renderId++;
187     }
188 
189     /***
190      * <p>
191      * This method builds a verbatim meta tag. It is passed a "rendered"
192      * expression that will be evaluated to determine if the component is
193      * visible.
194      * </p>
195      * 
196      * @param html
197      *            The HTML tag to write to the document
198      * @param renderExp
199      *            A value binding EL for the "rendered" attribute.
200      * @param context
201      *            faces context
202      * @return A clay element bean used to construct a faces outputText
203      *         component.
204      */
205     protected ElementBean createVerbatimMetadata(String html, String renderExp,
206             FacesContext context) {
207         ElementBean text = createVerbatimMetadata(html, context);
208 
209         // add a rendered attribute
210         AttributeBean attr = new AttributeBean();
211         attr.setName("rendered");
212         attr.setValue(renderExp);
213         attr.setBindingType(AttributeBean.BINDING_TYPE_VALUE);
214         text.addAttribute(attr);
215 
216         return text;
217 
218     }
219 
220     /***
221      * <p>
222      * This mehtod builds a simple verbatim meta tag. The value will not be
223      * escaped.
224      * <p>
225      * 
226      * @param html
227      *            HTML tag to write to the document
228      * @param context -
229      *            faces context
230      * @return A clay element bean used to construct a faces outputText
231      *         component.
232      */
233     protected ElementBean createVerbatimMetadata(String html,
234             FacesContext context) {
235 
236         // create an output Text
237         ElementBean text = new ElementBean();
238         text.setRenderId(generateId());
239         text.setJsfid("f:verbatim");
240         text.setComponentType("javax.faces.HtmlOutputText");
241              
242         // add a value attribute
243         AttributeBean attr = new AttributeBean();
244         attr.setName("value");
245         attr.setValue(html);
246         text.addAttribute(attr);
247 
248         // add a escape attribute
249         attr = new AttributeBean();
250         attr.setName("escape");
251         attr.setValue(Boolean.FALSE.toString());
252         text.addAttribute(attr);
253         
254         // add a isTransient attribute
255         attr = new AttributeBean();
256         attr.setName("isTransient");
257         attr.setValue(Boolean.TRUE.toString());
258         text.addAttribute(attr);
259 
260         return text;
261     }
262 
263     /***
264      * <p>
265      * This action event is fired when clicking on a tab link. The
266      * <code>selectedTab</code> is set with the value of the
267      * <code>tabIndex</code> request parameter.
268      * </p>
269      */
270     public String changeTab() {
271         if (log.isInfoEnabled())
272             log.info("changeTab()");
273         
274         QueryParam paramObj = (QueryParam) getBean("queryParam");
275         String tabIndex = paramObj.getTabIndex();
276         
277         if (tabIndex != null) {
278             setSelectedTab(Integer.parseInt(tabIndex));
279 
280             // clear out the selected contact
281             setSelectedContact(null);
282         }
283 
284         return "rolodex$test";
285     }
286 
287     /***
288      * <p>
289      * This is a method binding event fired from the <strong>Clay</strong>
290      * component before building the component tree. The method signature is a
291      * "Validator" event signature and the binding attribute is
292      * <code>shapeValidator</code>.
293      * </p>
294      * 
295      * @param context
296      *            facesContext
297      * @param component
298      * @param displayElementRoot
299      */
300     public void createTabs(javax.faces.context.FacesContext context,
301             javax.faces.component.UIComponent component,
302             java.lang.Object displayElementRoot) {
303 
304         if (log.isInfoEnabled())
305             log.info("createTabs()");
306 
307         // find the dao cached in application scope
308         RolodexDao dao = (RolodexDao) getBean("rolodexDao");
309 
310         // return a list of tabs
311         List tabs = dao.getTabs();
312 
313         ComponentBean root = (ComponentBean) displayElementRoot;
314         root.setComponentType("javax.faces.HtmlPanelGroup");
315         root.addChild(createVerbatimMetadata("<ul id=\"menu\">", context));
316 
317         for (int i = 0; i < tabs.size(); i++) {
318             SelectItem item = (SelectItem) tabs.get(i);
319 
320             root.addChild(createVerbatimMetadata("<li id=\"", context));
321 
322             root.addChild(createVerbatimMetadata("nav-sel",
323                     "#{@managed-bean-name.selectedTab == " + i + "}", context));
324             root.addChild(createVerbatimMetadata("nav",
325                     "#{@managed-bean-name.selectedTab != " + i + "}", context));
326 
327             root.addChild(createVerbatimMetadata("\">", context));
328             root.addChild(createCommandLinkMetadata(item, context));
329             root.addChild(createVerbatimMetadata("</li>", context));
330         }
331 
332         root.addChild(createVerbatimMetadata("</ul>", context));
333 
334     }
335 
336     /***
337      * <p>Caches the current contacts.</p>
338      */
339     private List contacts = null;
340     
341     /***
342      * <p>
343      * This is called by the data table component to return a list of
344      * {@link Contact}s that belong within the selected index.
345      * </p>
346      */
347     public List getContactsForTab() {
348         if (log.isInfoEnabled())
349             log.info("getContactsForTab()");
350 
351         if (contacts == null) {
352             // find the dao cached in application scope
353             RolodexDao dao = (RolodexDao) getBean("rolodexDao");
354             
355             // gets a list of contacts matching the selected tab index
356             contacts = dao.findContactsForTab(getSelectedTab());
357         }
358         
359         return contacts;
360     }
361 
362         
363     /***
364      * <p>
365      * This is an action event fired from clicking on a contact in the data
366      * grid. The latest contact is located using the data access object and is
367      * set as the "select" contact.
368      * </p>
369      */
370     public String selectContact() {
371         if (log.isInfoEnabled())
372             log.info("selectContact()");
373 
374         // look for the commandLink query parameter
375         QueryParam paramObj = (QueryParam) getBean("queryParam");
376         String name = paramObj.getSelectedName();
377         
378         if (name != null) {
379             // find the dao cached in application scope
380             RolodexDao dao = (RolodexDao) getBean("rolodexDao");
381 
382             //decode value 
383             try {
384                 name = URLDecoder.decode(name, "UTF-8");
385             } catch (UnsupportedEncodingException e) {}
386             
387             // finds the selected contact by name
388             setSelectedContact(dao.findContact(name));
389         }
390 
391         return "rolodex$test";
392     }
393 
394     /***
395      * <p>
396      * Saves a {@link Contact} to the mock data store.
397      * </p>
398      */
399     public String saveContact() {
400         if (log.isInfoEnabled())
401             log.info("saveContact()");
402 
403         if (getSelectedContact() != null) {
404             RolodexDao dao = (RolodexDao) getBean("rolodexDao");
405             // the saveContact method will return the page the 
406             // new contact will appear
407             setSelectedTab(dao.saveContact(getSelectedContact()));
408         }
409 
410         return "rolodex$test";
411     }
412 
413     /***
414      * <p>
415      * Removes a {@link Contact} from the mock data store.
416      * </p>
417      */
418     public String deleteContact() {
419         if (log.isInfoEnabled())
420             log.info("deleteContact()");
421 
422         if (getSelectedContact() != null) {
423             RolodexDao dao = (RolodexDao) getBean("rolodexDao");
424             dao.deleteContact(getSelectedContact());
425             setSelectedContact(null);
426             contacts = null;
427         }
428 
429         return "rolodex$test";
430     }
431 
432     /***
433      * <p>
434      * Creates a new {@link Contact} instance to be entered and saved.
435      * </p>
436      */
437     public String newContact() {
438        if (log.isInfoEnabled())
439            log.info("newContact()");
440                
441         setSelectedContact(new Contact());
442         
443         return "rolodex$test";
444     }
445 
446 }