/**
* License Agreement.
*
* JBoss RichFaces - Ajax4jsf Component Library
*
* Copyright (C) 2007 Exadel, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.richfaces.renderkit;
import java.io.IOException;
import java.text.DateFormatSymbols;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.DateTimeConverter;
import javax.faces.event.PhaseId;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.event.AjaxEvent;
import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSFunctionDefinition;
import org.ajax4jsf.javascript.JSReference;
import org.ajax4jsf.javascript.ScriptUtils;
import org.ajax4jsf.renderkit.AjaxRendererUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.UICalendar;
import org.richfaces.component.util.ComponentUtil;
import org.richfaces.event.CurrentDateChangeEvent;
/**
* @author Nick Belaevski - mailto:nbelaevski@exadel.com created 08.06.2007
*
*/
public class CalendarRendererBase extends TemplateEncoderRendererBase {
protected static final String MONTH_LABELS_SHORT = "monthLabelsShort";
protected static final String MONTH_LABELS = "monthLabels";
protected static final String WEEK_DAY_LABELS_SHORT = "weekDayLabelsShort";
protected static final String WEEK_DAY_LABELS = "weekDayLabels";
/**
* The constant used to resolve id of hidden input placed on the page
* for storing current date in "MM/yyyy" format.
* Actual id of hidden input used on the page is #{clientId}InputCurrentDate
*/
public static final String CURRENT_DATE_INPUT = "InputCurrentDate";
protected static final String MARKUP_SUFFIX = "Markup";
public static final String CALENDAR_BUNDLE = "org.richfaces.renderkit.calendar";
private final static Log log = LogFactory
.getLog(CalendarRendererBase.class);
/*
* (non-Javadoc)
*
* @see org.ajax4jsf.framework.renderer.RendererBase#getComponentClass()
*/
protected Class getComponentClass() {
return UICalendar.class;
}
public void addPopupToAjaxRendered(FacesContext context,
UICalendar component) {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(context);
Set ajaxRenderedAreas = ajaxContext.getAjaxRenderedAreas();
String clientId = component.getClientId(context);
if (ajaxContext.isAjaxRequest() && ajaxRenderedAreas.contains(clientId)) {
ajaxRenderedAreas.add(
clientId + "Popup");
ajaxRenderedAreas.add(
clientId + "IFrame");
ajaxRenderedAreas.add(
clientId + "Script");
}
}
public Object getConvertedValue(FacesContext context,
UIComponent component, Object submittedValue)
throws ConverterException {
if (submittedValue instanceof Date) {
return (Date) submittedValue;
}
UICalendar calendar = (UICalendar) component;
if (calendar.getConverter() != null) {
return calendar.getConverter().getAsObject(context, component,
(String) submittedValue);
} else {
DateTimeConverter converter = new DateTimeConverter();
converter.setPattern(calendar.getDatePattern());
converter.setLocale(calendar.getAsLocale(calendar.getLocale()));
converter.setTimeZone(calendar.getTimeZone());
return converter.getAsObject(context, component,
(String) submittedValue);
}
}
protected void doDecode(FacesContext context, UIComponent component) {
// TODO Auto-generated method stub
super.doDecode(context, component);
String clientId = component.getClientId(context);
Map requestParameterMap = context.getExternalContext()
.getRequestParameterMap();
String currentDateString = (String) requestParameterMap.get(clientId + CURRENT_DATE_INPUT);
if (currentDateString != null) {
CurrentDateChangeEvent ev = new CurrentDateChangeEvent(component,
currentDateString);
ev.setPhaseId(PhaseId.PROCESS_VALIDATIONS);
ev.queue();
// TODO nick - nick - queue this event when ValueChangeEvent is
// queued?
new AjaxEvent(component).queue();
}
String selectedDateString = (String) requestParameterMap.get(clientId
+ "InputDate");
if (selectedDateString != null) {
((UICalendar) component).setSubmittedValue(selectedDateString);
}
}
public void encodeChildren(FacesContext context, UIComponent calendar)
throws IOException {
}
public void writeClass(FacesContext context, UIComponent component)
throws IOException {
UICalendar calendar = (UICalendar) component;
String styleClass = (String) calendar.getAttributes().get("styleClass");
if (styleClass != null && styleClass.length() != 0) {
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n className: '" + styleClass+"'", null);
}
}
public void writeDayStyleClass(FacesContext context, UIComponent component)
throws IOException {
UICalendar calendar = (UICalendar) component;
String dayStyleClass = (String) calendar.getAttributes().get(
"dayStyleClass");
if (dayStyleClass != null && dayStyleClass.length() != 0) {
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n dayStyleClass: " + dayStyleClass, null);
}
}
public void writeIsDayEnabled(FacesContext context, UIComponent component)
throws IOException {
UICalendar calendar = (UICalendar) component;
String isDayEnabled = (String) calendar.getAttributes().get(
"isDayEnabled");
if (isDayEnabled != null && isDayEnabled.length() != 0) {
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n isDayEnabled: " + isDayEnabled, null);
}
}
public void writeMarkupScriptBody(FacesContext context,
UIComponent component, boolean children) throws IOException {
writeScriptBody(context, component, children);
}
public void writeOptionalFacetMarkupScriptBody(FacesContext context,
UIComponent component, String facetName) throws IOException {
UIComponent facet = component.getFacet(facetName);
if (facet != null && facet.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n " + facetName + MARKUP_SUFFIX + ": ", null);
writeMarkupScriptBody(context, facet, false);
}
}
public void dayCellClass(FacesContext context, UIComponent component)
throws IOException {
// if cellWidth/Height is set send dayCellClass to script
String cellwidth = (String) component.getAttributes().get("cellWidth");
String cellheight = (String) component.getAttributes()
.get("cellHeight");
ResponseWriter writer = context.getResponseWriter();
String clientId = component.getClientId(context);
String divStyle = "";
//Fix for jdk 1.4. Contains method in java.lang.String exists since jdk 1.5
if (cellwidth != null && cellwidth.length() != 0) {
if ((cellwidth.indexOf("px") > -1) || (cellwidth.indexOf("%")>-1)) {
divStyle = divStyle + "width:" + cellwidth + ";";
} else {
divStyle = divStyle + "width:" + cellwidth + "px;";
}
}
if (cellheight != null && cellheight.length() != 0) {
if ((cellheight.indexOf("px") > -1) || (cellheight.indexOf("%")>-1)) {
divStyle = divStyle + "height:" + cellheight.toString() + ";";
} else {
divStyle = divStyle + "height:" + cellheight.toString() + "px;";
}
}
if (divStyle.length() != 0) {
writer.startElement("style", component);
getUtils().writeAttribute(writer, "type", "text/css");
writer.writeText("." + clientId.replace(':', '_') + "DayCell{"
+ divStyle + "}", null);
writer.endElement("style");
}
}
public void writeDayCellClass(FacesContext context, UIComponent component)
throws IOException {
String cellwidth = (String) component.getAttributes().get("cellWidth");
String cellheight = (String) component.getAttributes()
.get("cellHeight");
ResponseWriter writer = context.getResponseWriter();
if (cellwidth != null && cellwidth.length() != 0 || cellheight != null
&& cellheight.length() != 0) {
String clientId = component.getClientId(context);
writer.writeText(",\n dayCellClass: '" + clientId.replace(':', '_')
+ "DayCell'", null);
}
}
public void writeFacetMarkupScriptBody(FacesContext context,
UIComponent component, String facetName) throws IOException {
UIComponent facet = component.getFacet(facetName);
if (facet != null && facet.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n " + facetName + MARKUP_SUFFIX + ": ", null);
writeMarkupScriptBody(context, facet, false);
}
}
public void writePreloadBody(FacesContext context, UICalendar calendar)
throws IOException {
Object preload = calendar.getPreload();
if (preload != null) {
ResponseWriter writer = context.getResponseWriter();
writer.write(ScriptUtils.toScript(preload));
}
}
public void writeSubmitFunction(FacesContext context, UICalendar calendar)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
String clientId = calendar.getClientId(context);
JSFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(calendar,
context, AjaxRendererUtils.AJAX_FUNCTION_NAME);
ajaxFunction.addParameter(JSReference.NULL);
Map options = AjaxRendererUtils.buildEventOptions(context, calendar);
options.put("calendar", JSReference.THIS);
boolean isSingle = ((Boolean) calendar.getAttributes()
.get("ajaxSingle")).booleanValue();
if (isSingle) {
options.put("single", JSReference.TRUE);
}
String oncomplete = AjaxRendererUtils.getAjaxOncomplete(calendar);
JSFunctionDefinition oncompleteDefinition = new JSFunctionDefinition();
oncompleteDefinition.addParameter("request");
oncompleteDefinition.addParameter("event");
oncompleteDefinition.addParameter("data");
oncompleteDefinition.addToBody("this.calendar.load(data, true);");
if (oncomplete != null) {
oncompleteDefinition.addToBody(oncomplete);
}
options.put("oncomplete", oncompleteDefinition);
JSReference requestValue = new JSReference("requestValue");
ajaxFunction.addParameter(options);
JSFunctionDefinition definition = new JSFunctionDefinition();
definition.addParameter(requestValue);
definition.addToBody(ajaxFunction);
writer.write(definition.toScript());
}
public void writeEventHandlerFunction(FacesContext context,
UIComponent component, String eventName) throws IOException {
ResponseWriter writer = context.getResponseWriter();
Object script = component.getAttributes().get(eventName);
if (script != null && !script.equals("")) {
JSFunctionDefinition onEventDefinition = new JSFunctionDefinition();
onEventDefinition.addParameter("event");
onEventDefinition.addToBody(script);
writer.writeText(",\n" + eventName + ": "
+ onEventDefinition.toScript(), null);
}
}
public String getInputValue(FacesContext context, UIComponent component) {
UICalendar input = (UICalendar) component;
// XXX nick - nick - can contain either Date or String instance
//Fix for myFaces 1.1.x RF-997
Date value = null;
try {
value = input.getAsDate(input.getSubmittedValue());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(" InputValue: "
+ e.toString(), e);
}
value = null;
}
Date curVal = input.getAsDate(input.getValue());
DateTimeConverter converter = new DateTimeConverter();
converter.setPattern(input.getDatePattern());
Object locale = input.getLocale();
converter.setLocale(input.getAsLocale(locale));
converter.setTimeZone(input.getTimeZone());
if (value == null) {
return converter.getAsString(context, input, curVal);
} else {
return converter.getAsString(context, input, value);
}
}
public void writeSymbols(FacesContext facesContext, UICalendar calendar)
throws IOException {
ResponseWriter writer = facesContext.getResponseWriter();
Map symbolsMap = getSymbolsMap(facesContext, calendar);
Iterator entryIterator = symbolsMap.entrySet().iterator();
writer.writeText(", \n", null);
while (entryIterator.hasNext()) {
Map.Entry entry = (Map.Entry) entryIterator.next();
writer.writeText(ScriptUtils.toScript(entry.getKey()), null);
writer.writeText(": ", null);
writer.writeText(ScriptUtils.toScript(entry.getValue()), null);
if (entryIterator.hasNext()) {
writer.writeText(", \n", null);
}
}
}
private static String[] shiftDates(int minimum, int maximum, String[] labels) {
if (minimum == 0 && (maximum - minimum == labels.length - 1)) {
return labels;
}
String[] shiftedLabels = new String[maximum - minimum + 1];
System.arraycopy(labels, minimum, shiftedLabels, 0, maximum - minimum
+ 1);
return shiftedLabels;
}
protected Map getSymbolsMap(FacesContext facesContext, UICalendar calendar) {
Map map = new HashMap();
Locale locale = calendar.getAsLocale(calendar.getLocale());
Calendar cal = calendar.getCalendar();
int maximum = cal.getActualMaximum(Calendar.DAY_OF_WEEK);
int minimum = cal.getActualMinimum(Calendar.DAY_OF_WEEK);
int monthMax = cal.getActualMaximum(Calendar.MONTH);
int monthMin = cal.getActualMinimum(Calendar.MONTH);
DateFormatSymbols symbols = new DateFormatSymbols(locale);
String[] weekDayLabels = ComponentUtil.asArray(calendar
.getWeekDayLabels());
if (weekDayLabels == null) {
weekDayLabels = symbols.getWeekdays();
weekDayLabels = shiftDates(minimum, maximum, weekDayLabels);
}
String[] weekDayLabelsShort = ComponentUtil.asArray(calendar
.getWeekDayLabelsShort());
if (weekDayLabelsShort == null) {
weekDayLabelsShort = symbols.getShortWeekdays();
weekDayLabelsShort = shiftDates(minimum, maximum,
weekDayLabelsShort);
}
String[] monthLabels = ComponentUtil.asArray(calendar.getMonthLabels());
if (monthLabels == null) {
monthLabels = symbols.getMonths();
monthLabels = shiftDates(monthMin, monthMax, monthLabels);
}
String[] monthLabelsShort = ComponentUtil.asArray(calendar
.getMonthLabelsShort());
if (monthLabelsShort == null) {
monthLabelsShort = symbols.getShortMonths();
monthLabelsShort = shiftDates(monthMin, monthMax, monthLabelsShort);
}
map.put(WEEK_DAY_LABELS, weekDayLabels);
map.put(WEEK_DAY_LABELS_SHORT, weekDayLabelsShort);
map.put(MONTH_LABELS, monthLabels);
map.put(MONTH_LABELS_SHORT, monthLabelsShort);
return map;
}
public String getFirstWeekDay(FacesContext context, UICalendar calendar)
throws IOException {
return String.valueOf(calendar.getFirstWeekDay());
}
public String getMinDaysInFirstWeek(FacesContext context,
UICalendar calendar) throws IOException {
return String.valueOf(calendar.getMinDaysInFirstWeek());
}
public String getCurrentDateAsString(FacesContext context, UICalendar calendar, Date date)
throws IOException {
Format formatter = new SimpleDateFormat("MM/yyyy");
return formatter.format(date);
}
public String getCurrentDate(FacesContext context, UICalendar calendar, Date date)
throws IOException {
return ScriptUtils.toScript(formatDate(date));
}
public String getSelectedDate(FacesContext context, UICalendar calendar)
throws IOException {
//Fix for myFaces 1.1.x RF-997
Date date;
if(calendar.isValid()){
// Hack. Use converter in "Render response" phase
// in order to have java.util.Date object to highlight
// selected date in Calendar popup.
Date submittedValue = null;
try {
submittedValue = calendar.getAsDate(calendar.getSubmittedValue());
} catch(ConverterException ce) {
// ignore ConverterException for the case when the user
// has entered invalid date and there was no validation
// (and conversion) before, i.e. the form submit has attribute
// "immediate" = true; and Calendar has "immediate" = false
submittedValue = null;
}
if (submittedValue != null) {
date = submittedValue;
} else {
date = calendar.getAsDate(calendar.getValue());
}
}else{
date = calendar.getAsDate(calendar.getValue());
//Date submittedValue = null;
}
if (date != null) {
return ScriptUtils.toScript(formatSelectedDate(date));
//TODO KAA Add ServerTime Processing;
}
return ScriptUtils.toScript(null);
}
public static Object formatDate(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
JSFunction result = new JSFunction("new Date");
//Fix for jdk 1.4 compatibility.
result.addParameter(new Integer(calendar.get(Calendar.YEAR)));
result.addParameter(new Integer(calendar.get(Calendar.MONTH)));
result.addParameter(new Integer(calendar.get(Calendar.DATE)));
return result;
}
public static Object formatSelectedDate(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
JSFunction result = new JSFunction("new Date");
//Fix for jdk 1.4 compatibility.
result.addParameter(new Integer(calendar.get(Calendar.YEAR)));
result.addParameter(new Integer(calendar.get(Calendar.MONTH)));
result.addParameter(new Integer(calendar.get(Calendar.DATE)));
result.addParameter(new Integer(calendar.get(Calendar.HOUR_OF_DAY)));
result.addParameter(new Integer(calendar.get(Calendar.MINUTE)));
result.addParameter(new Integer(0));
return result;
}
/**
* Write labels used in the Calendar component, taken from message bundles.
* Try to use bundle1 at first. If the 1st bundle is null or it doesn't
* contain requested message key, use the bundle2.
* @param bundle1 - 1st bundle to be used as a source for messages
* @param bundle2 - 2nd bundle to be used as a source for messages
* @param name - name of the requested label
* @param writer - response writer
* @throws IOException
*/
public void writeStringsFromBundle(ResourceBundle bundle1, ResourceBundle bundle2, String name,
ResponseWriter writer) throws IOException {
String label = null;
String bundleKey = "RICH_CALENDAR_" + name.toUpperCase() + "_LABEL";
if (bundle1 != null) {
try {
label = bundle1.getString(bundleKey);
} catch (MissingResourceException mre) {
// Current key was not found, ignore this exception;
}
}
// Current key wasn't found in application bundle, use CALENDAR_BUNDLE,
// if it is not null
if ((label == null) && (bundle2 != null)) {
try {
label = bundle2.getString(bundleKey);
} catch (MissingResourceException mre) {
// Current key was not found, ignore this exception;
}
}
writeStringFoundInBundle(name, label, writer);
}
public void writeStringFoundInBundle(String name, String value, ResponseWriter writer) throws IOException {
if(null!=value){
if (!("close").equals(name.toLowerCase())) {
writer.writeText(name.toLowerCase() + ":'" + value + "', ",null);
} else {
writer.writeText("close:'" + value + "'", null);
}
}else{
if (!("close").equals(name.toLowerCase())) {
writer.writeText(name.toLowerCase() + ":'" + name + "', ",null);
} else {
writer.writeText("close:'x'", null);
}
}
}
public void writeLabels(FacesContext context, UICalendar calendar)
throws IOException {
ResourceBundle bundle1 = null;
ResourceBundle bundle2 = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String messageBundle = context.getApplication().getMessageBundle();
Object locale = calendar.getLocale();
if (null != messageBundle) {
bundle1 = ResourceBundle.getBundle(messageBundle, calendar.getAsLocale(locale), loader);
}
try {
bundle2 = ResourceBundle.getBundle(CALENDAR_BUNDLE, calendar.getAsLocale(locale), loader);
} catch (MissingResourceException e) {
//No external bundle was found, ignore this exception.
}
ResponseWriter writer = context.getResponseWriter();
writer.writeText(",\n labels:{", null);
if (null != bundle1 || null != bundle2) {
writeStringsFromBundle(bundle1, bundle2, "Apply", writer);
writeStringsFromBundle(bundle1, bundle2, "Today", writer);
writeStringsFromBundle(bundle1, bundle2, "Clean", writer);
writeStringsFromBundle(bundle1, bundle2, "Cancel", writer);
writeStringsFromBundle(bundle1, bundle2, "OK", writer);
writeStringsFromBundle(bundle1, bundle2, "Close", writer);
}else{
// No bundles were found, use default labels.
writer.writeText("apply:'Apply', today:'Today', clean:'Clean', ok:'OK', cancel:'Cancel', close:'x'", null);
}
writer.writeText("}", null);
}
}