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  package org.apache.myfaces.custom.calendar;
20  
21  import org.apache.commons.lang.StringEscapeUtils;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.myfaces.component.UserRoleUtils;
25  import org.apache.myfaces.custom.inputTextHelp.HtmlInputTextHelp;
26  import org.apache.myfaces.custom.prototype.PrototypeResourceLoader;
27  import org.apache.myfaces.dateformat.SimpleDateFormatter;
28  import org.apache.myfaces.renderkit.html.util.AddResource;
29  import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
30  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
31  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
32  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
33  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
34  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
35  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
36  import org.apache.myfaces.shared_tomahawk.util.MessageUtils;
37  import org.apache.myfaces.tomahawk.util.Constants;
38  
39  import javax.faces.application.Application;
40  import javax.faces.application.FacesMessage;
41  import javax.faces.component.EditableValueHolder;
42  import javax.faces.component.UIComponent;
43  import javax.faces.component.UIInput;
44  import javax.faces.component.UIParameter;
45  import javax.faces.component.html.HtmlCommandLink;
46  import javax.faces.component.html.HtmlOutputText;
47  import javax.faces.context.FacesContext;
48  import javax.faces.context.ResponseWriter;
49  import javax.faces.convert.Converter;
50  import javax.faces.convert.ConverterException;
51  import javax.faces.convert.DateTimeConverter;
52  import java.io.IOException;
53  import java.text.DateFormat;
54  import java.text.DateFormatSymbols;
55  import java.text.ParseException;
56  import java.text.SimpleDateFormat;
57  import java.util.*;
58  
59  /**
60   * Render a "calendar" which the user can use to choose a specific day.
61   * <p>
62   * This renderer behaves quite differently for "inline" and "popup" calendars.
63   * <p>
64   * When inline, this component automatically creates child components (text and
65   * link components) to represent all the dates, scrollers, etc. within itself and
66   * renders those children. Clicking on any link in the component causes the
67   * surrounding form to immediately submit.
68   * <p>
69   * When popup, this component just renders an empty span with the component id,
70   * and a dozen or so lines of javascript which create an instance of a
71   * "tomahawk calendar object", set its properties from the properties on the
72   * associated HtmlInputCalendar component, and then invoke it. That javascript
73   * object then dynamically builds DOM objects and attaches them to the empty span.
74   * This component also renders a button or image that toggles the visibility of
75   * that empty span, thereby making the popup appear and disappear.
76   * <p>
77   * Note that the two ways of generating the calendar use totally different code;
78   * one implementation is here and the other is in a javascript resource file. For
79   * obvious reasons the appearance of the two calendars should be similar, so if a
80   * feature is added in one place it is recommended that the other be updated also. 
81   * <p>
82   * The behaviour of both of the Calendar objects varies depending upon the 
83   * "current locale". This is derived from getViewRoot().getLocale(), which is
84   * normally set according to the browser preferences; users whose browsers
85   * have "english" as the preferred language will get the "en" locale, while
86   * users with "deutsch" as the preferred language will get the "de" locale.
87   * One specific example is the "first day of week" (ie the day displayed at
88   * the left of each week row); this is "sunday" for the english locale, and
89   * "monday" for the german locale. There is currently no way for the
90   * calendar component to be configured to force a specific firstDayOfWeek
91   * to be used for all users.
92   * <p>
93   * 
94   * @JSFRenderer
95   *   renderKitId = "HTML_BASIC" 
96   *   family = "javax.faces.Input"
97   *   type = "org.apache.myfaces.Calendar"
98   * 
99   * @author Martin Marinschek (latest modification by $Author: lu4242 $)
100  * @version $Revision: 1301249 $ $Date: 2012-03-15 17:24:05 -0500 (Thu, 15 Mar 2012) $
101  */
102 public class HtmlCalendarRenderer
103         extends HtmlRenderer
104 {
105     private final Log log = LogFactory.getLog(HtmlCalendarRenderer.class);
106 
107     private static final String JAVASCRIPT_ENCODED = "org.apache.myfaces.calendar.JAVASCRIPT_ENCODED";
108 
109     // TODO: move this to HtmlRendererUtils in shared
110     private static final String RESOURCE_NONE = "none";
111 
112     public void encodeEnd(FacesContext facesContext, UIComponent component)
113             throws IOException
114     {
115         RendererUtils.checkParamValidity(facesContext, component, HtmlInputCalendar.class);
116 
117         HtmlInputCalendar inputCalendar = (HtmlInputCalendar) component;
118 
119         Locale currentLocale = facesContext.getViewRoot().getLocale();
120         log.debug("current locale:" + currentLocale.toString());
121 
122         String textValue;
123         
124         Converter converter = inputCalendar.getConverter();
125         Object submittedValue = inputCalendar.getSubmittedValue();
126         
127         Date value;
128 
129         if (submittedValue != null)
130         {
131             //Don't need to convert anything, the textValue is the same as the submittedValue
132             textValue = (String) submittedValue;
133             
134             if(textValue ==null || textValue.trim().length()==0 || textValue.equals(getHelperString(inputCalendar)))
135             {
136                 value = null;
137             }
138             else
139             {
140                 try
141                 {
142                     String formatStr = CalendarDateTimeConverter.createJSPopupFormat(facesContext, inputCalendar.getPopupDateFormat());
143                     Calendar timeKeeper = Calendar.getInstance(currentLocale);
144                     int firstDayOfWeek = timeKeeper.getFirstDayOfWeek() - 1;
145                     org.apache.myfaces.dateformat.DateFormatSymbols symbols = new org.apache.myfaces.dateformat.DateFormatSymbols(currentLocale);
146     
147                     SimpleDateFormatter dateFormat = new SimpleDateFormatter(formatStr, symbols, firstDayOfWeek);
148                     value = dateFormat.parse(textValue);
149                 }
150                 catch (IllegalArgumentException illegalArgumentException)
151                 {
152                     value = null;
153                 }
154             }
155         }
156         else
157         {
158             if (converter == null)
159             {
160                 CalendarDateTimeConverter defaultConverter = new CalendarDateTimeConverter();
161                 
162                 value = (Date) getDateBusinessConverter(inputCalendar).getDateValue(facesContext, component, inputCalendar.getValue());
163 
164                 textValue = defaultConverter.getAsString(facesContext, inputCalendar, value);
165             }
166             else
167             {
168                 Object componentValue = null;
169                 boolean usedComponentValue = false;
170                 //Use converter to retrieve the value.
171                 if(converter instanceof DateConverter)
172                 {
173                     value = ((DateConverter) converter).getAsDate(facesContext, inputCalendar);
174                 }
175                 else
176                 {
177                     componentValue = inputCalendar.getValue();
178                     if (componentValue instanceof Date)
179                     {
180                         value = (Date) componentValue;
181                     }
182                     else
183                     {
184                         usedComponentValue = true;
185                         value = null;
186                     }
187                 }
188                 textValue = converter.getAsString(facesContext, inputCalendar, usedComponentValue ? componentValue : value);
189             }
190         }
191         /*        
192         try
193         {
194             // value = RendererUtils.getDateValue(inputCalendar);
195 
196             Converter converter = getConverter(inputCalendar);
197             if (converter instanceof DateConverter)
198             {
199                 value = ((DateConverter) converter).getAsDate(facesContext, component);
200             }
201             else
202             {
203                 //value = RendererUtils.getDateValue(inputCalendar);
204                 Object objectValue = RendererUtils.getObjectValue(component);
205                 if (objectValue == null || objectValue instanceof Date)
206                 {
207                     value = (Date) objectValue;
208                 }
209                 else
210                 {
211                     //Use Converter.getAsString and convert to date using 
212                     String stringValue = converter.getAsString(facesContext, component, objectValue);
213 
214                     if(stringValue ==null || stringValue.trim().length()==0 ||stringValue.equals(getHelperString(inputCalendar)))
215                     {
216                         value = null;
217                     }
218                     else
219                     {
220                         String formatStr = CalendarDateTimeConverter.createJSPopupFormat(facesContext, inputCalendar.getPopupDateFormat());
221                         Calendar timeKeeper = Calendar.getInstance(currentLocale);
222                         int firstDayOfWeek = timeKeeper.getFirstDayOfWeek() - 1;
223                         org.apache.myfaces.dateformat.DateFormatSymbols symbols = new org.apache.myfaces.dateformat.DateFormatSymbols(currentLocale);
224     
225                         SimpleDateFormatter dateFormat = new SimpleDateFormatter(formatStr, symbols, firstDayOfWeek);
226                         value = dateFormat.parse(stringValue);
227                     }
228                 }
229             }
230         }
231         catch (IllegalArgumentException illegalArgumentException)
232         {
233             value = null;
234         }
235         */
236 
237         Calendar timeKeeper = Calendar.getInstance(currentLocale);
238         timeKeeper.setTime(value!=null?value:new Date());
239 
240         DateFormatSymbols symbols = new DateFormatSymbols(currentLocale);
241 
242         if(inputCalendar.isRenderAsPopup())
243         {
244             renderPopup(facesContext, inputCalendar, textValue, timeKeeper, symbols);
245         }
246         else
247         {
248             renderInline(facesContext, inputCalendar, timeKeeper, symbols);
249         }
250 
251         component.getChildren().removeAll(component.getChildren());
252     }
253 
254     private void renderPopup(
255             FacesContext facesContext, 
256             HtmlInputCalendar inputCalendar,
257             String value,
258             Calendar timeKeeper,
259             DateFormatSymbols symbols) throws IOException
260     {
261         if(inputCalendar.isAddResources())
262             addScriptAndCSSResources(facesContext, inputCalendar);
263 
264          // Check for an enclosed converter:
265          UIInput uiInput = (UIInput) inputCalendar;
266          Converter converter = uiInput.getConverter();
267          String dateFormat = null;
268          if (converter != null && converter instanceof DateTimeConverter) {
269              dateFormat = ((DateTimeConverter) converter).getPattern();
270          }
271          if (dateFormat == null) {
272              dateFormat = CalendarDateTimeConverter.createJSPopupFormat(facesContext,
273                                                                         inputCalendar.getPopupDateFormat());
274          }
275 
276         Application application = facesContext.getApplication();
277 
278         HtmlInputTextHelp inputText = getOrCreateInputTextChild(inputCalendar, application);
279 
280         RendererUtils.copyHtmlInputTextAttributes(inputCalendar, inputText);
281 
282         inputText.setConverter(null); // value for this transient component will already be converted
283         inputText.setTransient(true);
284         inputText.setHelpText(inputCalendar.getHelpText());
285         inputText.setSelectText(true);
286 
287         inputText.setValue(value);
288         /*
289         if (value == null && inputCalendar.getSubmittedValue() != null)
290         {
291             inputText.setValue(inputCalendar.getSubmittedValue());
292         }
293         else
294         {
295             inputText.setValue(getConverter(inputCalendar).getAsString(
296                     facesContext,inputCalendar,value));
297         }*/
298         inputText.setDisabled(inputCalendar.isDisabled());
299         inputText.setReadonly(inputCalendar.isReadonly());
300         inputText.setEnabledOnUserRole(inputCalendar.getEnabledOnUserRole());
301         inputText.setVisibleOnUserRole(inputCalendar.getVisibleOnUserRole());
302 
303         //This is where two components with the same id are in the tree,
304         //so make sure that during the rendering the id is unique.
305 
306         inputCalendar.setId(inputCalendar.getId()+"tempId");
307 
308         inputCalendar.getChildren().add(inputText);
309 
310         RendererUtils.renderChild(facesContext, inputText);
311 
312         inputCalendar.getChildren().remove(inputText);
313 
314         //Set back the correct id to the input calendar
315         inputCalendar.setId(inputText.getId());
316 
317         ResponseWriter writer = facesContext.getResponseWriter();
318 
319         writer.startElement(HTML.SPAN_ELEM,inputCalendar);
320         writer.writeAttribute(HTML.ID_ATTR,inputCalendar.getClientId(facesContext)+"Span",
321                               JSFAttr.ID_ATTR);
322         writer.endElement(HTML.SPAN_ELEM);
323 
324         if (!isDisabled(facesContext, inputCalendar) && !inputCalendar.isReadonly())
325         {
326             writer.startElement(HTML.SCRIPT_ELEM, inputCalendar);
327             writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null);
328 
329             String calendarVar = JavascriptUtils.getValidJavascriptName(
330                     inputCalendar.getClientId(facesContext)+"CalendarVar",false);
331 
332             writer.writeText(calendarVar+"=new org_apache_myfaces_PopupCalendar();\n",null);
333             writer.writeText(getLocalizedLanguageScript(facesContext,symbols,
334                                                         timeKeeper.getFirstDayOfWeek(),inputCalendar,calendarVar)+"\n",null);
335             // pass the selectMode attribute
336             StringBuffer script = new StringBuffer();
337             setStringVariable(script, calendarVar +".initData.selectMode",inputCalendar.getPopupSelectMode());
338             writer.writeText(script.toString(), null);
339 
340             writer.writeText(calendarVar+".init(document.getElementById('"+
341                              inputCalendar.getClientId(facesContext)+"Span"+"'));\n",null);
342             writer.endElement(HTML.SCRIPT_ELEM);
343             if(!inputCalendar.isDisplayValueOnly())
344             {
345                 getScriptBtn(writer, facesContext, inputCalendar,
346                                               dateFormat,inputCalendar.getPopupButtonString(), new FunctionCallProvider(){
347                     public String getFunctionCall(FacesContext facesContext, UIComponent uiComponent, String dateFormat)
348                     {
349                         String clientId = uiComponent.getClientId(facesContext);
350 
351                         String clientVar = JavascriptUtils.getValidJavascriptName(clientId+"CalendarVar",true);
352 
353                         return clientVar+"._popUpCalendar(this,document.getElementById('"+clientId+"'),'"+dateFormat+"')";
354                     }
355                 });
356             }
357         }
358     }
359 
360     private void renderInline(
361             FacesContext facesContext, 
362             HtmlInputCalendar inputCalendar,
363             Calendar timeKeeper,
364             DateFormatSymbols symbols) throws IOException
365     {
366         String[] weekdays = mapShortWeekdays(symbols);
367         String[] months = mapMonths(symbols);
368 
369         int lastDayInMonth = timeKeeper.getActualMaximum(Calendar.DAY_OF_MONTH);
370 
371         int currentDay = timeKeeper.get(Calendar.DAY_OF_MONTH);
372 
373         if (currentDay > lastDayInMonth)
374             currentDay = lastDayInMonth;
375 
376         timeKeeper.set(Calendar.DAY_OF_MONTH, 1);
377 
378         int weekDayOfFirstDayOfMonth = mapCalendarDayToCommonDay(timeKeeper.get(Calendar.DAY_OF_WEEK));
379 
380         int weekStartsAtDayIndex = mapCalendarDayToCommonDay(timeKeeper.getFirstDayOfWeek());
381 
382         ResponseWriter writer = facesContext.getResponseWriter();
383 
384         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
385         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
386 
387         writer.startElement(HTML.TABLE_ELEM, inputCalendar);
388         HtmlRendererUtils.renderHTMLAttributes(writer, inputCalendar, HTML.UNIVERSAL_ATTRIBUTES);
389         HtmlRendererUtils.renderHTMLAttributes(writer, inputCalendar, HTML.EVENT_HANDLER_ATTRIBUTES);
390         HtmlRendererUtils.renderHTMLAttributes(writer, inputCalendar, HTML.COMMON_FIELD_EVENT_ATTRIBUTES_WITHOUT_ONSELECT_AND_ONCHANGE);
391         writer.flush();
392 
393         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
394 
395         writer.startElement(HTML.TR_ELEM, inputCalendar);
396 
397         if(inputCalendar.getMonthYearRowClass() != null)
398             writer.writeAttribute(HTML.CLASS_ATTR, inputCalendar.getMonthYearRowClass(), null);
399 
400         writeMonthYearHeader(facesContext, writer, inputCalendar, timeKeeper,
401                              currentDay, weekdays, months);
402 
403         writer.endElement(HTML.TR_ELEM);
404 
405         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
406 
407         writer.startElement(HTML.TR_ELEM, inputCalendar);
408 
409         if(inputCalendar.getWeekRowClass() != null)
410             writer.writeAttribute(HTML.CLASS_ATTR, inputCalendar.getWeekRowClass(), null);
411 
412         writeWeekDayNameHeader(weekStartsAtDayIndex, weekdays,
413                                facesContext, writer, inputCalendar);
414 
415         writer.endElement(HTML.TR_ELEM);
416 
417         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
418 
419         writeDays(facesContext, writer, inputCalendar, timeKeeper,
420                   currentDay, weekStartsAtDayIndex, weekDayOfFirstDayOfMonth,
421                   lastDayInMonth, weekdays);
422 
423         writer.endElement(HTML.TABLE_ELEM);
424     }
425 
426     private HtmlInputTextHelp getOrCreateInputTextChild(HtmlInputCalendar inputCalendar, Application application)
427     {
428         HtmlInputTextHelp inputText = null;
429 
430         List li = inputCalendar.getChildren();
431 
432         for (int i = 0; i < li.size(); i++)
433         {
434             UIComponent uiComponent = (UIComponent) li.get(i);
435 
436             if(uiComponent instanceof HtmlInputTextHelp)
437             {
438                 inputText = (HtmlInputTextHelp) uiComponent;
439                 break;
440             }
441         }
442 
443         if(inputText == null)
444         {
445             inputText = (HtmlInputTextHelp) application.createComponent(HtmlInputTextHelp.COMPONENT_TYPE);
446         }
447         return inputText;
448     }
449 
450     /**
451      * Used by the x:inputDate renderer : HTMLDateRenderer
452      */
453     static public void addScriptAndCSSResources(FacesContext facesContext, UIComponent component){
454         // Check to see if javascript has already been written (which could happen if more than one calendar
455         // on the same page). Note that this means that if two calendar controls in the same page have
456         // different styleLocation or scriptLocation settings then all but the first one get ignored.
457         // Having different settings for calendars on the same page would be unusual, so ignore this
458         // for now..
459         if (facesContext.getExternalContext().getRequestMap().containsKey(JAVASCRIPT_ENCODED))
460         {
461             return;
462         }
463 
464         AddResource addresource = AddResourceFactory.getInstance(facesContext);
465         // Add the javascript and CSS pages
466 
467         String styleLocation = HtmlRendererUtils.getStyleLocation(component);
468 
469         if(styleLocation==null)
470         {
471             addresource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, HtmlCalendarRenderer.class, "WH/theme.css");
472             addresource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, HtmlCalendarRenderer.class, "DB/theme.css");
473         }
474         else if (!RESOURCE_NONE.equals(styleLocation))
475         {
476             addresource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, styleLocation+"/theme.css");
477         }
478         else
479         {
480             // output nothing; presumably the page directly references the necessary stylesheet
481         }
482 
483         String javascriptLocation = HtmlRendererUtils.getJavascriptLocation(component);
484 
485         if(javascriptLocation==null)
486         {
487             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, PrototypeResourceLoader.class, "prototype.js");
488             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, HtmlCalendarRenderer.class, "date.js");
489             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, HtmlCalendarRenderer.class, "popcalendar.js");
490         }
491         else if (!RESOURCE_NONE.equals(javascriptLocation))
492         {
493             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, javascriptLocation+ "/prototype.js");
494             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, javascriptLocation+ "/date.js");
495             addresource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, javascriptLocation+ "/popcalendar.js");
496         }
497         else
498         {
499             // output nothing; presumably the page directly references the necessary javascript
500         }
501 
502         facesContext.getExternalContext().getRequestMap().put(JAVASCRIPT_ENCODED, Boolean.TRUE);
503     }
504     
505     
506     /**
507      * Creates and returns a String which contains the initialisation data for
508      * the popup calendar control as a sequence of javascript commands that
509      * assign values to properties of a javascript object whose name is in
510      * parameter popupCalendarVariable.
511      * <p>
512      * 
513      * @param firstDayOfWeek
514      *            is in java.util.Calendar form, ie Sun=1, Mon=2, Sat=7
515      */
516     public static String getLocalizedLanguageScript(FacesContext facesContext,
517             DateFormatSymbols symbols, int firstDayOfWeek, UIComponent uiComponent,
518             String popupCalendarVariable)
519     {
520 
521         // Convert day value to java.util.Date convention (Sun=0, Mon=1, Sat=6).
522         // This is the convention that javascript Date objects use.
523         int realFirstDayOfWeek = firstDayOfWeek - 1;
524 
525         String[] weekDays;
526 
527         if (realFirstDayOfWeek == 0)
528         {
529             // Sunday
530             weekDays = mapShortWeekdaysStartingWithSunday(symbols);
531         }
532         else if (realFirstDayOfWeek == 1)
533         {
534             // Monday
535             weekDays = mapShortWeekdays(symbols);
536         }
537         else if (realFirstDayOfWeek == 6)
538         {
539             // Saturday. Often used in Arabic countries
540             weekDays = mapShortWeekdaysStartingWithSaturday(symbols);
541         }
542         else
543         {
544             throw new IllegalStateException("Week may only start with saturday, sunday or monday.");
545         }
546 
547         StringBuffer script = new StringBuffer();
548         AddResource ar = AddResourceFactory.getInstance(facesContext);
549 
550         if (uiComponent instanceof HtmlInputCalendar)
551         {
552             HtmlInputCalendar calendar = (HtmlInputCalendar) uiComponent;
553             // Set the themePrefix variable
554             String popupTheme = calendar.getPopupTheme();
555             if (popupTheme == null)
556             {
557                 popupTheme = "DB";
558             }
559             setStringVariable(script, popupCalendarVariable + ".initData.themePrefix",
560                     "jscalendar-" + popupTheme);
561 
562             // specify the URL for the directory in which all the .gif images
563             // can be found
564             String imageLocation = HtmlRendererUtils.getImageLocation(uiComponent);
565             if (imageLocation == null)
566             {
567                 String uri = ar.getResourceUri(facesContext, HtmlCalendarRenderer.class, popupTheme
568                         + "/");
569                 setStringVariable(script, popupCalendarVariable + ".initData.imgDir",
570                         JavascriptUtils.encodeString(uri));
571             }
572             else
573             {
574                 setStringVariable(script, popupCalendarVariable + ".initData.imgDir",
575                         (JavascriptUtils.encodeString(AddResourceFactory.getInstance(facesContext)
576                                 .getResourceUri(facesContext, imageLocation + "/"))));
577             }
578         }
579         else
580         {
581             String imageLocation = HtmlRendererUtils.getImageLocation(uiComponent);
582             if (imageLocation == null)
583             {
584                 String uri = ar.getResourceUri(facesContext, HtmlCalendarRenderer.class, "images/");
585                 setStringVariable(script, popupCalendarVariable + ".initData.imgDir",
586                         JavascriptUtils.encodeString(uri));
587             }
588             else
589             {
590                 setStringVariable(script, popupCalendarVariable + ".initData.imgDir",
591                         (JavascriptUtils.encodeString(AddResourceFactory.getInstance(facesContext)
592                                 .getResourceUri(facesContext, imageLocation + "/"))));
593             }
594         }
595         defineStringArray(script, popupCalendarVariable + ".initData.monthName", mapMonths(symbols));
596         defineStringArray(script, popupCalendarVariable + ".initData.dayName", weekDays);
597         setIntegerVariable(script, popupCalendarVariable + ".initData.startAt", realFirstDayOfWeek);
598 
599         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.weekdays",
600                 mapWeekdaysStartingWithSunday(symbols));
601         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.shortWeekdays",
602                 mapShortWeekdaysStartingWithSunday(symbols));
603         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.shortMonths",
604                 mapShortMonths(symbols));
605         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.months",
606                 mapMonths(symbols));
607         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.eras", symbols
608                 .getEras());
609         defineStringArray(script, popupCalendarVariable + ".dateFormatSymbols.ampms", symbols
610                 .getAmPmStrings());
611 
612         if (uiComponent instanceof HtmlInputCalendar)
613         {
614 
615             HtmlInputCalendar inputCalendar = (HtmlInputCalendar) uiComponent;
616 
617             if (inputCalendar.getPopupGotoString() != null)
618                 setStringVariable(script, popupCalendarVariable + ".initData.gotoString",
619                         inputCalendar.getPopupGotoString());
620             if (inputCalendar.getPopupTodayString() != null)
621                 setStringVariable(script, popupCalendarVariable + ".initData.todayString",
622                         inputCalendar.getPopupTodayString());
623             if (inputCalendar.getPopupTodayDateFormat() != null)
624                 setStringVariable(script, popupCalendarVariable + ".initData.todayDateFormat",
625                         inputCalendar.getPopupTodayDateFormat());
626             else if (inputCalendar.getPopupDateFormat() != null)
627                 setStringVariable(script, popupCalendarVariable + ".initData.todayDateFormat",
628                         inputCalendar.getPopupDateFormat());
629             if (inputCalendar.getPopupWeekString() != null)
630                 setStringVariable(script, popupCalendarVariable + ".initData.weekString",
631                         inputCalendar.getPopupWeekString());
632             if (inputCalendar.getPopupScrollLeftMessage() != null)
633                 setStringVariable(script, popupCalendarVariable + ".initData.scrollLeftMessage",
634                         inputCalendar.getPopupScrollLeftMessage());
635             if (inputCalendar.getPopupScrollRightMessage() != null)
636                 setStringVariable(script, popupCalendarVariable + ".initData.scrollRightMessage",
637                         inputCalendar.getPopupScrollRightMessage());
638             if (inputCalendar.getPopupSelectMonthMessage() != null)
639                 setStringVariable(script, popupCalendarVariable + ".initData.selectMonthMessage",
640                         inputCalendar.getPopupSelectMonthMessage());
641             if (inputCalendar.getPopupSelectYearMessage() != null)
642                 setStringVariable(script, popupCalendarVariable + ".initData.selectYearMessage",
643                         inputCalendar.getPopupSelectYearMessage());
644             if (inputCalendar.getPopupSelectDateMessage() != null)
645                 setStringVariable(script, popupCalendarVariable + ".initData.selectDateMessage",
646                         inputCalendar.getPopupSelectDateMessage());
647             setBooleanVariable(script, popupCalendarVariable + ".initData.popupLeft", inputCalendar
648                     .isPopupLeft());
649 
650         }
651 
652         return script.toString();
653     }
654 
655     private static void setBooleanVariable(StringBuffer script, String name, boolean value)
656     {
657         script.append(name);
658         script.append(" = ");
659         script.append(value);
660         script.append(";\n");
661     }
662 
663     private static void setIntegerVariable(StringBuffer script, String name, int value)
664     {
665         script.append(name);
666         script.append(" = ");
667         script.append(value);
668         script.append(";\n");
669     }
670 
671     private static void setStringVariable(StringBuffer script, String name, String value)
672     {
673         script.append(name);
674         script.append(" = \"");
675         script.append(StringEscapeUtils.escapeJavaScript(value));
676         script.append("\";\n");
677     }
678 
679     private static void defineStringArray(StringBuffer script, String arrayName, String[] array)
680     {
681         script.append(arrayName);
682         script.append(" = new Array(");
683 
684         for(int i=0;i<array.length;i++)
685         {
686             if(i!=0)
687                 script.append(",");
688 
689             script.append("\"");
690             script.append(StringEscapeUtils.escapeJavaScript(array[i]));
691             script.append("\"");
692         }
693 
694         script.append(");\n");
695     }
696 
697     public static void getScriptBtn(ResponseWriter writer, FacesContext facesContext, UIComponent uiComponent,
698                                       String dateFormat, String popupButtonString, FunctionCallProvider prov)
699         throws IOException
700     {
701         boolean renderButtonAsImage = false;
702         String popupButtonStyle = null;
703         String popupButtonStyleClass = null;
704 
705         if(uiComponent instanceof HtmlInputCalendar)
706         {
707             HtmlInputCalendar calendar = (HtmlInputCalendar)uiComponent;
708             renderButtonAsImage = calendar.isRenderPopupButtonAsImage();
709             popupButtonStyle = calendar.getPopupButtonStyle();
710             popupButtonStyleClass = calendar.getPopupButtonStyleClass();
711         }
712 
713         if (!renderButtonAsImage) {
714             // render the button
715             writer.startElement(HTML.INPUT_ELEM, uiComponent);
716             writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_BUTTON, null);
717 
718             writer.writeAttribute(HTML.ONCLICK_ATTR,
719                                   prov.getFunctionCall(facesContext,uiComponent,dateFormat),
720                                   null);
721 
722             if(popupButtonString==null)
723                 popupButtonString="...";
724             writer.writeAttribute(HTML.VALUE_ATTR, StringEscapeUtils.escapeJavaScript(popupButtonString), null);
725 
726             if(popupButtonStyle != null)
727             {
728                 writer.writeAttribute(HTML.STYLE_ATTR, popupButtonStyle, null);
729             }
730 
731             if(popupButtonStyleClass != null)
732             {
733                 writer.writeAttribute(HTML.CLASS_ATTR, popupButtonStyleClass, null);
734             }
735             
736             writer.endElement(HTML.INPUT_ELEM);
737         } else {
738             // render the image
739             writer.startElement(HTML.IMG_ELEM, uiComponent);
740             AddResource addResource = AddResourceFactory.getInstance(facesContext);
741 
742             String imgUrl = (String) uiComponent.getAttributes().get("popupButtonImageUrl");
743 
744             if(imgUrl!=null)
745             {
746                 writer.writeAttribute(HTML.SRC_ATTR, addResource.getResourceUri(facesContext, imgUrl), null);
747             }
748             else
749             {
750                 writer.writeAttribute(HTML.SRC_ATTR, addResource.getResourceUri(facesContext, HtmlCalendarRenderer.class, "images/calendar.gif"), null);
751             }
752 
753             if(popupButtonStyle != null)
754             {
755                 writer.writeAttribute(HTML.STYLE_ATTR, popupButtonStyle, null);
756             }
757             else
758             {
759                 writer.writeAttribute(HTML.STYLE_ATTR, "vertical-align:bottom;", null);
760             }
761 
762             if(popupButtonStyleClass != null)
763             {
764                 writer.writeAttribute(HTML.CLASS_ATTR, popupButtonStyleClass, null);
765             }
766 
767             writer.writeAttribute(HTML.ONCLICK_ATTR, prov.getFunctionCall(facesContext, uiComponent, dateFormat),
768                                   null);
769 
770             writer.endElement(HTML.IMG_ELEM);
771         }
772     }
773 
774 
775     private void writeMonthYearHeader(FacesContext facesContext, ResponseWriter writer, UIInput inputComponent, Calendar timeKeeper,
776                                       int currentDay, String[] weekdays,
777                                       String[] months)
778             throws IOException
779     {
780         Calendar cal = shiftMonth(facesContext, timeKeeper, currentDay, -1);
781 
782         writeCell(facesContext, writer, inputComponent, "<", cal.getTime(), null);
783 
784         writer.startElement(HTML.TD_ELEM, inputComponent);
785         writer.writeAttribute(HTML.COLSPAN_ATTR, new Integer(weekdays.length - 2), null);
786         writer.writeText(months[timeKeeper.get(Calendar.MONTH)] + " " + timeKeeper.get(Calendar.YEAR), null);
787         writer.endElement(HTML.TD_ELEM);
788 
789         cal = shiftMonth(facesContext, timeKeeper, currentDay, 1);
790 
791         writeCell(facesContext, writer, inputComponent, ">", cal.getTime(), null);
792     }
793 
794     private Calendar shiftMonth(FacesContext facesContext,
795                                 Calendar timeKeeper, int currentDay, int shift)
796     {
797         Calendar cal = copyCalendar(facesContext, timeKeeper);
798 
799         cal.set(Calendar.DAY_OF_MONTH, 1);
800         cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + shift);
801 
802         if(currentDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH))
803             currentDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
804 
805         cal.set(Calendar.DAY_OF_MONTH, currentDay);
806         return cal;
807     }
808 
809     private Calendar copyCalendar(FacesContext facesContext, Calendar timeKeeper)
810     {
811         Calendar cal = Calendar.getInstance(facesContext.getViewRoot().getLocale());
812         cal.setTime(timeKeeper.getTime());
813         return cal;
814     }
815 
816     private void writeWeekDayNameHeader(int weekStartsAtDayIndex, String[] weekdays, FacesContext facesContext, ResponseWriter writer, UIInput inputComponent)
817             throws IOException
818     {
819         for (int i = weekStartsAtDayIndex; i < weekdays.length; i++)
820             writeCell(facesContext,
821                       writer, inputComponent, weekdays[i], null, null);
822 
823         for (int i = 0; i < weekStartsAtDayIndex; i++)
824             writeCell(facesContext, writer,
825                       inputComponent, weekdays[i], null, null);
826     }
827 
828     private void writeDays(FacesContext facesContext, ResponseWriter writer,
829                            HtmlInputCalendar inputComponent, Calendar timeKeeper, int currentDay, int weekStartsAtDayIndex,
830                            int weekDayOfFirstDayOfMonth, int lastDayInMonth, String[] weekdays)
831             throws IOException
832     {
833         Calendar cal;
834 
835         int space = (weekStartsAtDayIndex < weekDayOfFirstDayOfMonth) ? (weekDayOfFirstDayOfMonth - weekStartsAtDayIndex)
836                     : (weekdays.length - weekStartsAtDayIndex + weekDayOfFirstDayOfMonth);
837 
838         if (space == weekdays.length)
839             space = 0;
840 
841         int columnIndexCounter = 0;
842 
843         for (int i = 0; i < space; i++)
844         {
845             if (columnIndexCounter == 0)
846             {
847                 writer.startElement(HTML.TR_ELEM, inputComponent);
848             }
849 
850             writeCell(facesContext, writer, inputComponent, "",
851                       null, inputComponent.getDayCellClass());
852             columnIndexCounter++;
853         }
854 
855         for (int i = 0; i < lastDayInMonth; i++)
856         {
857             if (columnIndexCounter == 0)
858             {
859                 writer.startElement(HTML.TR_ELEM, inputComponent);
860             }
861 
862             cal = copyCalendar(facesContext, timeKeeper);
863             cal.set(Calendar.DAY_OF_MONTH, i + 1);
864 
865             String cellStyle = inputComponent.getDayCellClass();
866 
867             if((currentDay - 1) == i)
868                 cellStyle = inputComponent.getCurrentDayCellClass();
869 
870             writeCell(facesContext, writer,
871                       inputComponent, String.valueOf(i + 1), cal.getTime(),
872                       cellStyle);
873 
874             columnIndexCounter++;
875 
876             if (columnIndexCounter == weekdays.length)
877             {
878                 writer.endElement(HTML.TR_ELEM);
879                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
880                 columnIndexCounter = 0;
881             }
882         }
883 
884         if (columnIndexCounter != 0)
885         {
886             for (int i = columnIndexCounter; i < weekdays.length; i++)
887             {
888                 writeCell(facesContext, writer,
889                           inputComponent, "", null, inputComponent.getDayCellClass());
890             }
891 
892             writer.endElement(HTML.TR_ELEM);
893             HtmlRendererUtils.writePrettyLineSeparator(facesContext);
894         }
895     }
896 
897     /**
898      * Generate components and output for a single "day" cell within the calendar display.
899      */
900     private void writeCell(FacesContext facesContext,
901                            ResponseWriter writer, UIInput component, String content,
902                            Date valueForLink, String styleClass)
903             throws IOException
904     {
905         writer.startElement(HTML.TD_ELEM, component);
906 
907         if (styleClass != null)
908             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, null);
909 
910         if (valueForLink == null)
911             writer.writeText(content, JSFAttr.VALUE_ATTR);
912         else
913         {
914             writeLink(content, component, facesContext, valueForLink);
915         }
916 
917         writer.endElement(HTML.TD_ELEM);
918     }
919 
920     /**
921      * Create child components to represent a link to a specific date value, and render them.
922      * <p>
923      * For a disabled calendar, this just creates a Text component, attaches it as a child
924      * of the calendar and renders it. The value of the component is the string returned by
925      * valueForLink.getTime().
926      * <p>
927      * For a non-disabled calendar, create an HtmlCommandLink child that wraps the text
928      * returned by valueForLink.getTime(), and add it to the component.
929      */
930     private void writeLink(String content,
931                            UIInput component,
932                            FacesContext facesContext,
933                            Date valueForLink)
934             throws IOException
935     {
936         Converter converter = getConverter(component);
937         Application application = facesContext.getApplication();
938 
939         HtmlOutputText text
940                 = (HtmlOutputText)application.createComponent(HtmlOutputText.COMPONENT_TYPE);
941         text.setValue(content);
942         text.setId(component.getId() + "_" + valueForLink.getTime() + "_text");
943         text.setTransient(true);
944 
945         HtmlInputCalendar calendar = (HtmlInputCalendar)component;
946         if (isDisabled(facesContext, component) || calendar.isReadonly())
947         {
948             component.getChildren().add(text);
949 
950             RendererUtils.renderChild(facesContext, text);
951             return;
952         }
953 
954         HtmlCommandLink link
955                 = (HtmlCommandLink)application.createComponent(HtmlCommandLink.COMPONENT_TYPE);
956         link.setId(component.getId() + "_" + valueForLink.getTime() + "_link");
957         link.setTransient(true);
958         link.setImmediate(component.isImmediate());
959 
960         UIParameter parameter
961                 = (UIParameter)application.createComponent(UIParameter.COMPONENT_TYPE);
962         parameter.setId(component.getId() + "_" + valueForLink.getTime() + "_param");
963         parameter.setTransient(true);
964         parameter.setName(component.getClientId(facesContext));
965         parameter.setValue(converter.getAsString(facesContext, component, valueForLink));
966 
967         RendererUtils.addOrReplaceChild(component,link);
968         link.getChildren().add(parameter);
969         link.getChildren().add(text);
970 
971         RendererUtils.renderChild(facesContext, link);
972     }
973 
974     private Converter getConverter(UIInput component)
975     {
976         Converter converter = component.getConverter();
977 
978         if (converter == null)
979         {
980             converter = new CalendarDateTimeConverter();
981         }
982         return converter;
983     }
984     
985     private DateBusinessConverter getDateBusinessConverter(AbstractHtmlInputCalendar component)
986     {
987         DateBusinessConverter dateBusinessConverter = component.getDateBusinessConverter(); 
988         if (dateBusinessConverter == null)
989         {
990             dateBusinessConverter = new DefaultDateBusinessConverter();
991         }
992         return dateBusinessConverter;
993     }
994 
995     private int mapCalendarDayToCommonDay(int day)
996     {
997         switch (day)
998         {
999             case Calendar.TUESDAY:
1000                 return 1;
1001             case Calendar.WEDNESDAY:
1002                 return 2;
1003             case Calendar.THURSDAY:
1004                 return 3;
1005             case Calendar.FRIDAY:
1006                 return 4;
1007             case Calendar.SATURDAY:
1008                 return 5;
1009             case Calendar.SUNDAY:
1010                 return 6;
1011             default:
1012                 return 0;
1013         }
1014     }
1015 
1016     private static String[] mapShortWeekdays(DateFormatSymbols symbols)
1017     {
1018         String[] weekdays = new String[7];
1019 
1020         String[] localeWeekdays = symbols.getShortWeekdays();
1021 
1022         weekdays[0] = localeWeekdays[Calendar.MONDAY];
1023         weekdays[1] = localeWeekdays[Calendar.TUESDAY];
1024         weekdays[2] = localeWeekdays[Calendar.WEDNESDAY];
1025         weekdays[3] = localeWeekdays[Calendar.THURSDAY];
1026         weekdays[4] = localeWeekdays[Calendar.FRIDAY];
1027         weekdays[5] = localeWeekdays[Calendar.SATURDAY];
1028         weekdays[6] = localeWeekdays[Calendar.SUNDAY];
1029 
1030         return weekdays;
1031     }
1032 
1033     private static String[] mapShortWeekdaysStartingWithSunday(DateFormatSymbols symbols)
1034     {
1035         String[] weekdays = new String[7];
1036 
1037         String[] localeWeekdays = symbols.getShortWeekdays();
1038 
1039         weekdays[0] = localeWeekdays[Calendar.SUNDAY];
1040         weekdays[1] = localeWeekdays[Calendar.MONDAY];
1041         weekdays[2] = localeWeekdays[Calendar.TUESDAY];
1042         weekdays[3] = localeWeekdays[Calendar.WEDNESDAY];
1043         weekdays[4] = localeWeekdays[Calendar.THURSDAY];
1044         weekdays[5] = localeWeekdays[Calendar.FRIDAY];
1045         weekdays[6] = localeWeekdays[Calendar.SATURDAY];
1046 
1047         return weekdays;
1048     }
1049 
1050     
1051     private static String[] mapShortWeekdaysStartingWithSaturday(DateFormatSymbols symbols) 
1052     {
1053         String[] weekdays = new String[7];
1054 
1055         String[] localeWeekdays = symbols.getShortWeekdays();
1056 
1057         weekdays[0] = localeWeekdays[Calendar.SATURDAY];
1058         weekdays[1] = localeWeekdays[Calendar.SUNDAY];
1059         weekdays[2] = localeWeekdays[Calendar.MONDAY];
1060         weekdays[3] = localeWeekdays[Calendar.TUESDAY];
1061         weekdays[4] = localeWeekdays[Calendar.WEDNESDAY];
1062         weekdays[5] = localeWeekdays[Calendar.THURSDAY];
1063         weekdays[6] = localeWeekdays[Calendar.FRIDAY];
1064 
1065         return weekdays;
1066     }    
1067     
1068     private static String[] mapWeekdaysStartingWithSunday(DateFormatSymbols symbols)
1069     {
1070         String[] weekdays = new String[7];
1071 
1072         String[] localeWeekdays = symbols.getWeekdays();
1073 
1074         weekdays[0] = localeWeekdays[Calendar.SUNDAY];
1075         weekdays[1] = localeWeekdays[Calendar.MONDAY];
1076         weekdays[2] = localeWeekdays[Calendar.TUESDAY];
1077         weekdays[3] = localeWeekdays[Calendar.WEDNESDAY];
1078         weekdays[4] = localeWeekdays[Calendar.THURSDAY];
1079         weekdays[5] = localeWeekdays[Calendar.FRIDAY];
1080         weekdays[6] = localeWeekdays[Calendar.SATURDAY];
1081 
1082         return weekdays;
1083     }
1084 
1085     public static String[] mapMonths(DateFormatSymbols symbols)
1086     {
1087         String[] months = new String[12];
1088 
1089         String[] localeMonths = symbols.getMonths();
1090 
1091         months[0] = localeMonths[Calendar.JANUARY];
1092         months[1] = localeMonths[Calendar.FEBRUARY];
1093         months[2] = localeMonths[Calendar.MARCH];
1094         months[3] = localeMonths[Calendar.APRIL];
1095         months[4] = localeMonths[Calendar.MAY];
1096         months[5] = localeMonths[Calendar.JUNE];
1097         months[6] = localeMonths[Calendar.JULY];
1098         months[7] = localeMonths[Calendar.AUGUST];
1099         months[8] = localeMonths[Calendar.SEPTEMBER];
1100         months[9] = localeMonths[Calendar.OCTOBER];
1101         months[10] = localeMonths[Calendar.NOVEMBER];
1102         months[11] = localeMonths[Calendar.DECEMBER];
1103 
1104         return months;
1105     }
1106 
1107     public static String[] mapShortMonths(DateFormatSymbols symbols)
1108     {
1109         String[] months = new String[12];
1110 
1111         String[] localeMonths = symbols.getShortMonths();
1112 
1113         months[0] = localeMonths[Calendar.JANUARY];
1114         months[1] = localeMonths[Calendar.FEBRUARY];
1115         months[2] = localeMonths[Calendar.MARCH];
1116         months[3] = localeMonths[Calendar.APRIL];
1117         months[4] = localeMonths[Calendar.MAY];
1118         months[5] = localeMonths[Calendar.JUNE];
1119         months[6] = localeMonths[Calendar.JULY];
1120         months[7] = localeMonths[Calendar.AUGUST];
1121         months[8] = localeMonths[Calendar.SEPTEMBER];
1122         months[9] = localeMonths[Calendar.OCTOBER];
1123         months[10] = localeMonths[Calendar.NOVEMBER];
1124         months[11] = localeMonths[Calendar.DECEMBER];
1125 
1126         return months;
1127     }
1128 
1129 
1130     public void decode(FacesContext facesContext, UIComponent component)
1131     {
1132         if(HtmlRendererUtils.isDisabledOrReadOnly(component))
1133         {
1134             // nothing to do here
1135             return;
1136         }
1137 
1138         RendererUtils.checkParamValidity(facesContext, component, HtmlInputCalendar.class);
1139 
1140         //String helperString = getHelperString(component);
1141 
1142         if (!(component instanceof EditableValueHolder)) {
1143             throw new IllegalArgumentException("Component "
1144                                                + component.getClientId(facesContext)
1145                                                + " is not an EditableValueHolder");
1146         }
1147         Map paramMap = facesContext.getExternalContext()
1148                 .getRequestParameterMap();
1149         String clientId = component.getClientId(facesContext);
1150 
1151         if(paramMap.containsKey(clientId))
1152         {
1153             String value = (String) paramMap.get(clientId);
1154 
1155             //if(!value.equalsIgnoreCase(helperString))
1156             //{
1157                 ((EditableValueHolder) component).setSubmittedValue(value);
1158             //}
1159             //else
1160             //{
1161                 // The field was initially filled with the "helper string", and has
1162                 // not been altered by the user so treat this as if null had been
1163                 // passed by the user.
1164                 //
1165                 // TODO: does this mean the target date is set to todays date?
1166                 // And how does this affect the "required" property?
1167                 //((EditableValueHolder) component).setSubmittedValue("");
1168             //}
1169         }
1170         else
1171         {
1172             log.warn(HtmlRendererUtils.NON_SUBMITTED_VALUE_WARNING +
1173                 " Component : "+
1174                 RendererUtils.getPathToComponent(component));
1175         }
1176 
1177     }
1178     
1179     protected static boolean isDisabled(FacesContext facesContext, UIComponent uiComponent)
1180     {
1181         if (!UserRoleUtils.isEnabledOnUserRole(uiComponent))
1182         {
1183             return true;
1184         }
1185         else
1186         {
1187             if (uiComponent instanceof HtmlInputCalendar)
1188             {
1189                 return ((HtmlInputCalendar)uiComponent).isDisabled();
1190             }
1191             else
1192             {
1193                 return org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils.getBooleanAttribute(uiComponent, HTML.DISABLED_ATTR, false);
1194             }
1195         }
1196     }
1197 
1198     public Object getConvertedValue(FacesContext facesContext, UIComponent uiComponent, Object submittedValue) throws ConverterException
1199     {
1200         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlInputCalendar.class);
1201 
1202         AbstractHtmlInputCalendar uiInput = (AbstractHtmlInputCalendar) uiComponent;
1203 
1204         Converter converter = uiInput.getConverter();
1205 
1206         if (submittedValue != null && !(submittedValue instanceof String))
1207         {
1208             throw new IllegalArgumentException("Submitted value of type String expected");
1209         }
1210         
1211         //Do not convert if submittedValue is helper string  
1212         if(submittedValue != null && submittedValue.equals(getHelperString(uiComponent)))
1213             return null;
1214         
1215         if(converter==null)
1216         {
1217             converter = new CalendarDateTimeConverter();
1218             
1219             Date date = (Date) converter.getAsObject(facesContext, uiComponent, (String) submittedValue);
1220             
1221             return getDateBusinessConverter(uiInput).getBusinessValue(facesContext, uiComponent, date);
1222         }
1223         else
1224         {
1225             return converter.getAsObject(facesContext, uiComponent, (String) submittedValue);
1226         }
1227     }
1228 
1229     public interface DateConverter extends Converter
1230     {
1231         public Date getAsDate(FacesContext facesContext, UIComponent uiComponent);
1232     }
1233 
1234     private static String getHelperString(UIComponent uiComponent)
1235     {
1236         return uiComponent instanceof HtmlInputCalendar?((HtmlInputCalendar) uiComponent).getHelpText():null;
1237     }
1238 
1239     public static class CalendarDateTimeConverter implements DateConverter
1240     {
1241         private static final String CONVERSION_MESSAGE_ID = "org.apache.myfaces.calendar.CONVERSION";
1242 
1243         public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s)
1244         {
1245             if(s==null || s.trim().length()==0 || s.equals(getHelperString(uiComponent)))
1246                 return null;
1247 
1248             if(uiComponent instanceof HtmlInputCalendar && ((HtmlInputCalendar) uiComponent).isRenderAsPopup())
1249             {
1250                 HtmlInputCalendar calendar = (HtmlInputCalendar) uiComponent;
1251                 String popupDateFormat = calendar.getPopupDateFormat();
1252                 String formatStr = createJSPopupFormat(facesContext, popupDateFormat);
1253                 Locale locale = facesContext.getViewRoot().getLocale();
1254                 Calendar timeKeeper = Calendar.getInstance(locale);
1255                 int firstDayOfWeek = timeKeeper.getFirstDayOfWeek() - 1;
1256                 org.apache.myfaces.dateformat.DateFormatSymbols symbols = new org.apache.myfaces.dateformat.DateFormatSymbols(locale);
1257                 SimpleDateFormatter dateFormat = new SimpleDateFormatter(formatStr, symbols, firstDayOfWeek);
1258                 
1259                 Date date = dateFormat.parse(s); 
1260                 if (date != null) {
1261                     return date;
1262                 }
1263                 FacesMessage msg = MessageUtils.getMessage(Constants.TOMAHAWK_DEFAULT_BUNDLE,FacesMessage.SEVERITY_ERROR,CONVERSION_MESSAGE_ID,new Object[]{
1264                         uiComponent.getId(),s},facesContext);
1265                 throw new ConverterException(msg);
1266             }
1267             else
1268             {
1269                 DateFormat dateFormat = createStandardDateFormat(facesContext);
1270                 dateFormat.setLenient(false);
1271                 try
1272                 {
1273                     Date date = dateFormat.parse(s); 
1274                     return date;
1275                 }
1276                 catch (ParseException e)
1277                 {
1278                     FacesMessage msg = MessageUtils.getMessage(Constants.TOMAHAWK_DEFAULT_BUNDLE,FacesMessage.SEVERITY_ERROR,CONVERSION_MESSAGE_ID,new Object[]{
1279                             uiComponent.getId(),s},facesContext);
1280                     throw new ConverterException(msg,e);
1281                 }
1282             }
1283         }
1284 
1285         public Date getAsDate(FacesContext facesContext, UIComponent uiComponent)
1286         {
1287             return RendererUtils.getDateValue(uiComponent);
1288         }
1289 
1290         public static String createJSPopupFormat(FacesContext facesContext, String popupDateFormat)
1291         {
1292 
1293             if(popupDateFormat == null)
1294             {
1295                 SimpleDateFormat defaultDateFormat = createStandardDateFormat(facesContext);
1296                 popupDateFormat = defaultDateFormat.toPattern();
1297             }
1298 
1299             return popupDateFormat;
1300         }
1301 
1302         public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o)
1303         {
1304             Date date = (Date) o;
1305 
1306             if(date==null)
1307                 return getHelperString(uiComponent);
1308 
1309             if(uiComponent instanceof HtmlInputCalendar && ((HtmlInputCalendar) uiComponent).isRenderAsPopup())
1310             {
1311                 HtmlInputCalendar calendar = (HtmlInputCalendar) uiComponent;
1312                 String popupDateFormat = calendar.getPopupDateFormat();
1313                 String formatStr = createJSPopupFormat(facesContext, popupDateFormat);
1314                 Locale locale = facesContext.getViewRoot().getLocale();
1315                 Calendar timeKeeper = Calendar.getInstance(locale);
1316                 int firstDayOfWeek = timeKeeper.getFirstDayOfWeek() - 1;
1317                 org.apache.myfaces.dateformat.DateFormatSymbols symbols = new org.apache.myfaces.dateformat.DateFormatSymbols(locale);
1318 
1319                 SimpleDateFormatter dateFormat = new SimpleDateFormatter(formatStr, symbols, firstDayOfWeek);
1320                 return dateFormat.format(date);
1321             }
1322             else
1323             {
1324                 DateFormat dateFormat = createStandardDateFormat(facesContext);
1325                 dateFormat.setLenient(false);
1326                 return dateFormat.format(date);
1327             }
1328         }
1329 
1330         private static SimpleDateFormat createStandardDateFormat(FacesContext facesContext)
1331         {
1332             DateFormat dateFormat;
1333             dateFormat = DateFormat.getDateInstance(DateFormat.SHORT,
1334                                                     facesContext.getViewRoot().getLocale());
1335 
1336             if(dateFormat instanceof SimpleDateFormat)
1337                 return (SimpleDateFormat) dateFormat;
1338             else
1339                 return new SimpleDateFormat("dd.MM.yyyy", facesContext.getViewRoot().getLocale());
1340         }
1341     }
1342 }