001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.form; 016 017 import java.text.DateFormatSymbols; 018 import java.text.SimpleDateFormat; 019 import java.util.Calendar; 020 import java.util.Date; 021 import java.util.HashMap; 022 import java.util.Locale; 023 import java.util.Map; 024 025 import org.apache.tapestry.IAsset; 026 import org.apache.tapestry.IMarkupWriter; 027 import org.apache.tapestry.IRequestCycle; 028 import org.apache.tapestry.IScript; 029 import org.apache.tapestry.PageRenderSupport; 030 import org.apache.tapestry.TapestryUtils; 031 import org.apache.tapestry.form.translator.DateTranslator; 032 import org.apache.tapestry.valid.ValidatorException; 033 034 /** 035 * Provides a Form <tt>java.util.Date</tt> field component for selecting dates. [ <a 036 * href="../../../../../ComponentReference/DatePicker.html">Component Reference </a>] As of 4.0, 037 * DatePicker can indicate that it is required, use a custom translator (e.g. for java.sql.Date), 038 * and perform validation on the submitted date. 039 * <p> 040 * As of 4.0, this component can be configurably translated and validated. 041 * 042 * @author Paul Geerts 043 * @author Malcolm Edgar 044 * @author Paul Ferraro 045 * @since 2.2 046 */ 047 048 public abstract class DatePicker extends AbstractFormComponent implements TranslatedField 049 { 050 public abstract Date getValue(); 051 052 public abstract void setValue(Date value); 053 054 public abstract boolean isDisabled(); 055 056 public abstract boolean getIncludeWeek(); 057 058 public abstract IAsset getIcon(); 059 060 private static final String SYM_NAME = "name"; 061 062 private static final String SYM_FORMNAME = "formName"; 063 064 private static final String SYM_MONTHNAMES = "monthNames"; 065 066 private static final String SYM_SHORT_MONTHNAMES = "shortMonthNames"; 067 068 private static final String SYM_WEEKDAYNAMES = "weekDayNames"; 069 070 private static final String SYM_SHORT_WEEKDAYNAMES = "shortWeekDayNames"; 071 072 private static final String SYM_FIRSTDAYINWEEK = "firstDayInWeek"; 073 074 private static final String SYM_MINDAYSINFIRSTWEEK = "minimalDaysInFirstWeek"; 075 076 private static final String SYM_FORMAT = "format"; 077 078 private static final String SYM_INCL_WEEK = "includeWeek"; 079 080 private static final String SYM_CLEAR_BUTTON_LABEL = "clearButtonLabel"; 081 082 private static final String SYM_VALUE = "value"; 083 084 private static final String SYM_BUTTONONCLICKHANDLER = "buttonOnclickHandler"; 085 086 /** 087 * Injected 088 * 089 * @since 4.0 090 */ 091 public abstract IScript getScript(); 092 093 /** 094 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, 095 * org.apache.tapestry.IRequestCycle) 096 */ 097 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) 098 { 099 PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this); 100 101 boolean disabled = isDisabled(); 102 DateTranslator translator = (DateTranslator) getTranslator(); 103 Locale locale = getPage().getLocale(); 104 SimpleDateFormat format = translator.getDateFormat(locale); 105 106 DateFormatSymbols dfs = format.getDateFormatSymbols(); 107 Calendar cal = Calendar.getInstance(locale); 108 109 String name = getName(); 110 111 String value = getTranslatedFieldSupport().format(this, getValue()); 112 113 Map symbols = new HashMap(); 114 115 symbols.put(SYM_NAME, name); 116 symbols.put(SYM_FORMAT, format.toPattern()); 117 symbols.put(SYM_INCL_WEEK, getIncludeWeek() ? Boolean.TRUE : Boolean.FALSE); 118 119 symbols.put(SYM_MONTHNAMES, makeStringList(dfs.getMonths(), 0, 12)); 120 symbols.put(SYM_SHORT_MONTHNAMES, makeStringList(dfs.getShortMonths(), 0, 12)); 121 symbols.put(SYM_WEEKDAYNAMES, makeStringList(dfs.getWeekdays(), 1, 8)); 122 symbols.put(SYM_SHORT_WEEKDAYNAMES, makeStringList(dfs.getShortWeekdays(), 1, 8)); 123 symbols.put(SYM_FIRSTDAYINWEEK, new Integer(cal.getFirstDayOfWeek() - 1)); 124 symbols.put(SYM_MINDAYSINFIRSTWEEK, new Integer(cal.getMinimalDaysInFirstWeek())); 125 symbols.put(SYM_CLEAR_BUTTON_LABEL, getMessages().getMessage("clear")); 126 symbols.put(SYM_FORMNAME, getForm().getName()); 127 symbols.put(SYM_VALUE, getValue()); 128 129 getScript().execute(cycle, pageRenderSupport, symbols); 130 131 renderDelegatePrefix(writer, cycle); 132 133 writer.beginEmpty("input"); 134 writer.attribute("type", "text"); 135 writer.attribute("name", name); 136 writer.attribute("value", value); 137 writer.attribute("title", format.toLocalizedPattern()); 138 139 if (disabled) 140 writer.attribute("disabled", "disabled"); 141 142 renderIdAttribute(writer, cycle); 143 144 renderDelegateAttributes(writer, cycle); 145 146 getTranslatedFieldSupport().renderContributions(this, writer, cycle); 147 getValidatableFieldSupport().renderContributions(this, writer, cycle); 148 149 renderInformalParameters(writer, cycle); 150 151 writer.printRaw(" "); 152 153 if (!disabled) 154 { 155 writer.begin("a"); 156 writer.attribute("href", (String) symbols.get(SYM_BUTTONONCLICKHANDLER)); 157 } 158 159 IAsset icon = getIcon(); 160 161 writer.beginEmpty("img"); 162 writer.attribute("src", icon.buildURL()); 163 writer.attribute("border", 0); 164 165 if (!disabled) 166 writer.end(); 167 168 renderDelegateSuffix(writer, cycle); 169 } 170 171 /** 172 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, 173 * org.apache.tapestry.IRequestCycle) 174 */ 175 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) 176 { 177 String value = cycle.getParameter(getName()); 178 179 try 180 { 181 Date date = (Date) getTranslatedFieldSupport().parse(this, value); 182 183 getValidatableFieldSupport().validate(this, writer, cycle, date); 184 185 setValue(date); 186 } 187 catch (ValidatorException e) 188 { 189 getForm().getDelegate().record(e); 190 } 191 } 192 193 /** 194 * Create a list of quoted strings. The list is suitable for initializing a JavaScript array. 195 */ 196 private String makeStringList(String[] a, int offset, int length) 197 { 198 StringBuffer b = new StringBuffer(); 199 for (int i = offset; i < length; i++) 200 { 201 // JavaScript is sensitive to some UNICODE characters. So for 202 // the sake of simplicity, we just escape everything 203 b.append('"'); 204 char[] ch = a[i].toCharArray(); 205 for (int j = 0; j < ch.length; j++) 206 { 207 if (ch[j] < 128) 208 { 209 b.append(ch[j]); 210 } 211 else 212 { 213 b.append(escape(ch[j])); 214 } 215 } 216 217 b.append('"'); 218 if (i < length - 1) 219 { 220 b.append(", "); 221 } 222 } 223 return b.toString(); 224 225 } 226 227 /** 228 * Create an escaped Unicode character 229 * 230 * @param c 231 * @return The unicode character in escaped string form 232 */ 233 private static String escape(char c) 234 { 235 StringBuffer b = new StringBuffer(); 236 for (int i = 0; i < 4; i++) 237 { 238 b.append(Integer.toHexString(c & 0x000F).toUpperCase()); 239 c >>>= 4; 240 } 241 b.append("u\\"); 242 return b.reverse().toString(); 243 } 244 245 /** 246 * Injected. 247 */ 248 public abstract ValidatableFieldSupport getValidatableFieldSupport(); 249 250 /** 251 * Injected. 252 */ 253 public abstract TranslatedFieldSupport getTranslatedFieldSupport(); 254 255 /** 256 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() 257 */ 258 public boolean isRequired() 259 { 260 return getValidatableFieldSupport().isRequired(this); 261 } 262 }