View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.custom.schedule;
21  
22  import java.io.IOException;
23  import java.io.Serializable;
24  import java.text.DateFormat;
25  import java.text.SimpleDateFormat;
26  import java.util.Calendar;
27  import java.util.Date;
28  import java.util.Locale;
29  import java.util.Map;
30  
31  import javax.faces.component.UIComponent;
32  import javax.faces.context.FacesContext;
33  import javax.faces.context.ResponseWriter;
34  import javax.faces.el.ValueBinding;
35  import javax.faces.event.ActionEvent;
36  import javax.faces.event.ComponentSystemEvent;
37  import javax.faces.event.ComponentSystemEventListener;
38  import javax.faces.render.Renderer;
39  
40  import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
41  import org.apache.myfaces.custom.schedule.util.ScheduleEntryComparator;
42  import org.apache.myfaces.custom.schedule.util.ScheduleUtil;
43  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
44  import org.apache.myfaces.tomahawk.application.PreRenderViewAddResourceEvent;
45  import org.apache.myfaces.tomahawk.util.TomahawkResourceUtils;
46  
47  /**
48   * <p>
49   * Abstract superclass for all renderer of the UISchedule component
50   * </p>
51   * @since 1.1.7
52   * @author Jurgen Lust (latest modification by $Author: mkienenb $)
53   * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
54   * @version $Revision: 389938 $
55   */
56  public abstract class AbstractScheduleRenderer extends Renderer implements
57          Serializable, ComponentSystemEventListener
58  {
59      //~ Static fields/initializers ---------------------------------------------
60      protected static final ScheduleEntryComparator comparator = new ScheduleEntryComparator();
61      protected static final String LAST_CLICKED_DATE = "_last_clicked_date";
62      protected static final String LAST_CLICKED_Y = "_last_clicked_y";
63      private static final String CSS_RESOURCE = "css/schedule.css";
64      public static final String DEFAULT_THEME = "default";
65      public static final String OUTLOOK_THEME = "outlookxp";
66      public static final String EVOLUTION_THEME = "evolution";
67  
68      //~ Methods ----------------------------------------------------------------
69      public void processEvent(ComponentSystemEvent event)
70      {
71          if (event instanceof PreRenderViewAddResourceEvent)
72          {
73              HtmlSchedule schedule = (HtmlSchedule) event.getComponent();
74              
75              String theme = schedule.getTheme();
76              //The default css file is only loaded if the theme is one of the provided
77              //themes.
78              if (DEFAULT_THEME.equals(theme) || OUTLOOK_THEME.equals(theme)
79                      || EVOLUTION_THEME.equals(theme))
80              {
81                  FacesContext facesContext = FacesContext.getCurrentInstance();
82                  
83                  //addResource.addStyleSheet(context, AddResource.HEADER_BEGIN,
84                  //       HtmlSchedule.class, CSS_RESOURCE);
85                  TomahawkResourceUtils.addOutputStylesheetResource(facesContext,
86                          "oam.custom.schedule.css",
87                          "schedule.css");
88              }
89      
90              if (schedule.isTooltip())
91              {
92                  FacesContext facesContext = FacesContext.getCurrentInstance();
93                  
94                  TomahawkResourceUtils.addOutputScriptResource(facesContext,
95                          "oam.custom.schedule.javascript",
96                          "alphaAPI.js");
97                  
98                  TomahawkResourceUtils.addOutputScriptResource(facesContext,
99                          "oam.custom.schedule.javascript",
100                         "domLib.js");
101     
102                 TomahawkResourceUtils.addOutputScriptResource(facesContext,
103                         "oam.custom.schedule.javascript",
104                         "domTT.js");
105     
106                 TomahawkResourceUtils.addOutputScriptResource(facesContext,
107                         "oam.custom.schedule.javascript",
108                         "fadomatic.js");
109             }
110         }
111     }
112         
113     /**
114      * @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext,
115      *      javax.faces.component.UIComponent)
116      */
117     public void decode(FacesContext context, UIComponent component)
118     {
119         HtmlSchedule schedule = (HtmlSchedule) component;
120         boolean queueAction = false;
121         if (ScheduleUtil.canModifyValue(component))
122         {
123             Map parameters = context.getExternalContext()
124                     .getRequestParameterMap();
125             String selectedEntryId = (String) parameters.get((String) schedule
126                     .getClientId(context));
127             String lastClickedDateId = (String) parameters
128                     .get((String) schedule.getClientId(context)
129                             + LAST_CLICKED_DATE);
130             String lastClickedY = (String) parameters.get((String) schedule
131                     .getClientId(context)
132                     + LAST_CLICKED_Y);
133 
134             ScheduleMouseEvent mouseEvent = null;
135 
136             if ((selectedEntryId != null) && (selectedEntryId.length() > 0))
137             {
138                 ScheduleEntry entry = schedule.findEntry(selectedEntryId);
139                 schedule.setSubmittedEntry(entry);
140                 mouseEvent = new ScheduleMouseEvent(schedule,
141                         ScheduleMouseEvent.SCHEDULE_ENTRY_CLICKED);
142                 queueAction = true;
143             }
144 
145             if (schedule.isSubmitOnClick())
146             {
147                 schedule.resetMouseEvents();
148                 if ((lastClickedY != null) && (lastClickedY.length() > 0))
149                 {
150                     //the body of the schedule was clicked
151                     schedule
152                             .setLastClickedDateAndTime(determineLastClickedDate(
153                                     schedule, lastClickedDateId, lastClickedY));
154                     mouseEvent = new ScheduleMouseEvent(schedule,
155                             ScheduleMouseEvent.SCHEDULE_BODY_CLICKED);
156                     queueAction = true;
157                 }
158                 else if ((lastClickedDateId != null)
159                         && (lastClickedDateId.length() > 0))
160                 {
161                     //the header of the schedule was clicked
162                     schedule
163                             .setLastClickedDateAndTime(determineLastClickedDate(
164                                     schedule, lastClickedDateId, "0"));
165                     mouseEvent = new ScheduleMouseEvent(schedule,
166                             ScheduleMouseEvent.SCHEDULE_HEADER_CLICKED);
167                     queueAction = true;
168                 }
169                 else if (mouseEvent == null)
170                 {
171                     //the form was posted without mouse events on the schedule
172                     mouseEvent = new ScheduleMouseEvent(schedule,
173                             ScheduleMouseEvent.SCHEDULE_NOTHING_CLICKED);
174                 }
175             }
176 
177             if (mouseEvent != null)
178                 schedule.queueEvent(mouseEvent);
179         }
180         if (queueAction)
181         {
182             schedule.queueEvent(new ActionEvent(schedule));
183         }
184     }
185 
186     /**
187      * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext,
188      *      javax.faces.component.UIComponent)
189      */
190     public void encodeBegin(FacesContext context, UIComponent component)
191             throws IOException
192     {
193         if (!component.isRendered())
194         {
195             return;
196         }
197 
198         HtmlSchedule schedule = (HtmlSchedule) component;
199         ResponseWriter writer = context.getResponseWriter();
200 
201         //add needed CSS and Javascript files to the header 
202         /*
203         AddResource addResource = AddResourceFactory.getInstance(context);
204         String theme = schedule.getTheme();
205         //The default css file is only loaded if the theme is one of the provided
206         //themes.
207         if (DEFAULT_THEME.equals(theme) || OUTLOOK_THEME.equals(theme)
208                 || EVOLUTION_THEME.equals(theme))
209         {
210             addResource.addStyleSheet(context, AddResource.HEADER_BEGIN,
211                     HtmlSchedule.class, CSS_RESOURCE);
212         }
213         addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
214                 HtmlSchedule.class, "javascript/schedule.js");
215         
216         if (schedule.isTooltip())
217         {
218             addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
219                     HtmlSchedule.class, "javascript/alphaAPI.js");
220             addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
221                     HtmlSchedule.class, "javascript/domLib.js");
222             addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
223                     HtmlSchedule.class, "javascript/domTT.js");
224             addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN,
225                     HtmlSchedule.class, "javascript/fadomatic.js");
226         }*/
227         
228         //hidden input field containing the id of the selected entry
229         writer.startElement(HTML.INPUT_ELEM, schedule);
230         writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
231         writer.writeAttribute(HTML.NAME_ATTR, schedule.getClientId(context),
232                 "clientId");
233         writer.endElement(HTML.INPUT_ELEM);
234         //hidden input field containing the id of the last clicked date
235         writer.startElement(HTML.INPUT_ELEM, schedule);
236         writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
237         writer.writeAttribute(HTML.NAME_ATTR, schedule.getClientId(context)
238                 + "_last_clicked_date", "clicked_date");
239         writer.endElement(HTML.INPUT_ELEM);
240         //hidden input field containing the y coordinate of the mouse when
241         //the schedule was clicked. This will be used to determine the hour
242         //of day.
243         writer.startElement(HTML.INPUT_ELEM, schedule);
244         writer.writeAttribute(HTML.TYPE_ATTR, "hidden", null);
245         writer.writeAttribute(HTML.NAME_ATTR, schedule.getClientId(context)
246                 + "_last_clicked_y", "clicked_y");
247         writer.endElement(HTML.INPUT_ELEM);
248     }
249 
250     /**
251      * <p>
252      * Get the String representation of a date, taking into account the
253      * specified date format or the current Locale.
254      * </p>
255      *
256      * @param context the FacesContext
257      * @param schedule the component
258      * @param date the date
259      *
260      * @return the date string
261      */
262     protected String getDateString(FacesContext context, UIScheduleBase schedule,
263             Date date)
264     {
265 
266         return getDateFormat(context, schedule, schedule.getHeaderDateFormat(), date).format(date);
267     }
268 
269     protected static DateFormat getDateFormat(FacesContext context, UIScheduleBase schedule, String pattern)
270     {
271         Locale viewLocale = context.getViewRoot().getLocale();               
272         DateFormat format = (pattern != null && pattern.length() > 0) ? 
273                 new SimpleDateFormat(pattern, viewLocale) :
274                 DateFormat.getDateInstance(DateFormat.MEDIUM, viewLocale);
275         
276         format.setTimeZone(schedule.getModel().getTimeZone());
277         
278         return format;
279     }
280     
281     protected static DateFormat getDateFormat(FacesContext context, UIScheduleBase schedule, String pattern, Date date)
282     {
283         Locale viewLocale = context.getViewRoot().getLocale();
284         
285         if (pattern != null && pattern.indexOf("d'th'") >= 0)
286         {
287             pattern = pattern.replaceAll("d'th'", "d'" + daySuffix(schedule, date, viewLocale) + "'");
288         }
289         
290         return getDateFormat(context, schedule, pattern);
291     }
292     
293     private static String daySuffix(UIScheduleBase schedule, Date date, Locale locale) {
294         String language = locale.getLanguage();
295         Calendar calendar = ScheduleUtil.getCalendarInstance(date, schedule.getModel().getTimeZone());
296 
297         int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
298 
299         if (Locale.ENGLISH.getLanguage().equals(language))
300         {
301             switch(dayOfMonth) {
302             case 1:
303             case 21:
304             case 31:
305                 return "st";
306             case 2:
307             case 22:
308                 return "nd";
309             case 3:
310             case 23:
311                 return "rd";
312             default:
313                 return "th";
314             }
315         }
316         else if (Locale.GERMAN.getLanguage().equals(language))
317         {
318             return ".";
319         }
320         else
321         {
322             return "";
323         }
324     }
325     
326     /**
327      * <p>
328      * Allow the developer to specify custom CSS classnames for the schedule
329      * component.
330      * </p>
331      * @param component the component
332      * @param className the default CSS classname
333      * @return the custom classname
334      */
335     protected String getStyleClass(UIComponent component, String className)
336     {
337         //first check if the styleClass property is a value binding expression
338         ValueBinding binding = component.getValueBinding(className);
339         if (binding != null)
340         {
341             String value = (String) binding.getValue(FacesContext
342                     .getCurrentInstance());
343 
344             if (value != null)
345             {
346                 return value;
347             }
348         }
349         //it's not a value binding expression, so check for the string value
350         //in the attributes
351         Map attributes = component.getAttributes();
352         String returnValue = (String) attributes.get(className + "Class");
353         return returnValue == null ? className : returnValue;
354     }
355 
356     /**
357      * The user of the Schedule component can customize the look and feel
358      * by specifying a custom implementation of the ScheduleEntryRenderer.
359      * This method gets an instance of the specified class, or if none
360      * was specified, takes the default.
361      * 
362      * @param component the Schedule component
363      * @return a ScheduleEntryRenderer instance
364      */
365     protected ScheduleEntryRenderer getEntryRenderer(HtmlSchedule schedule)
366     {
367             Object entryRenderer = schedule.getEntryRenderer();
368             if (entryRenderer instanceof ScheduleEntryRenderer)
369             {
370                 return (ScheduleEntryRenderer) entryRenderer;
371             } else {
372                 return new DefaultScheduleEntryRenderer();
373             }
374     }
375 
376     /**
377      * @return The default height, in pixels, of one row in the schedule grid
378      */
379     protected abstract int getDefaultRowHeight();
380 
381     /**
382      * @param schedule
383      *            The schedule
384      * 
385      * @return The row height, in pixels
386      */
387     protected abstract int getRowHeight(UIScheduleBase schedule);
388 
389     /**
390      * Determine the last clicked date
391      * @param schedule the schedule component
392      * @param dateId the string identifying the date
393      * @param yPos the y coordinate of the mouse
394      * @return the clicked date
395      */
396     protected abstract Date determineLastClickedDate(HtmlSchedule schedule,
397             String dateId, String yPos);
398 
399     /**
400      * @see javax.faces.render.Renderer#getRendersChildren()
401      */
402     public boolean getRendersChildren()
403     {
404         return true;
405     }
406     
407     protected Calendar getCalendarInstance(UIScheduleBase schedule, Date date)
408     {       
409         return ScheduleUtil.getCalendarInstance(date, schedule.getModel().getTimeZone());
410     }
411 }
412 //The End