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