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 private static final String SYM_NAME = "name"; 051 052 private static final String SYM_FORMNAME = "formName"; 053 054 private static final String SYM_MONTHNAMES = "monthNames"; 055 056 private static final String SYM_SHORT_MONTHNAMES = "shortMonthNames"; 057 058 private static final String SYM_WEEKDAYNAMES = "weekDayNames"; 059 060 private static final String SYM_SHORT_WEEKDAYNAMES = "shortWeekDayNames"; 061 062 private static final String SYM_FIRSTDAYINWEEK = "firstDayInWeek"; 063 064 private static final String SYM_MINDAYSINFIRSTWEEK = "minimalDaysInFirstWeek"; 065 066 private static final String SYM_FORMAT = "format"; 067 068 private static final String SYM_INCL_WEEK = "includeWeek"; 069 070 private static final String SYM_CLEAR_BUTTON_LABEL = "clearButtonLabel"; 071 072 private static final String SYM_VALUE = "value"; 073 074 private static final String SYM_BUTTONONCLICKHANDLER = "buttonOnclickHandler"; 075 076 public abstract Date getValue(); 077 078 public abstract void setValue(Date value); 079 080 public abstract boolean isDisabled(); 081 082 public abstract boolean getIncludeWeek(); 083 084 public abstract IAsset getIcon(); 085 086 public abstract String getImageClass(); 087 088 /** 089 * @since 4.1.1 090 */ 091 public abstract String getTitle(); 092 093 /** 094 * Injected. 095 * 096 * @since 4.0 097 */ 098 public abstract IScript getScript(); 099 100 /** 101 * @see org.apache.tapestry.form.AbstractFormComponent#renderFormComponent(org.apache.tapestry.IMarkupWriter, 102 * org.apache.tapestry.IRequestCycle) 103 */ 104 protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) 105 { 106 PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this); 107 108 boolean disabled = isDisabled(); 109 DateTranslator translator = (DateTranslator) getTranslator(); 110 Locale locale = getPage().getLocale(); 111 SimpleDateFormat format = translator.getDateFormat(locale); 112 113 DateFormatSymbols dfs = format.getDateFormatSymbols(); 114 Calendar cal = Calendar.getInstance(locale); 115 116 String name = getName(); 117 118 String title = getTitle(); 119 if (title == null) 120 title = format.toLocalizedPattern(); 121 122 String value = getTranslatedFieldSupport().format(this, getValue()); 123 124 Map symbols = new HashMap(); 125 126 symbols.put(SYM_NAME, name); 127 symbols.put(SYM_FORMAT, format.toPattern()); 128 symbols.put(SYM_INCL_WEEK, getIncludeWeek() ? Boolean.TRUE : Boolean.FALSE); 129 130 symbols.put(SYM_MONTHNAMES, makeStringList(dfs.getMonths(), 0, 12)); 131 symbols.put(SYM_SHORT_MONTHNAMES, makeStringList(dfs.getShortMonths(), 0, 12)); 132 symbols.put(SYM_WEEKDAYNAMES, makeStringList(dfs.getWeekdays(), 1, 8)); 133 symbols.put(SYM_SHORT_WEEKDAYNAMES, makeStringList(dfs.getShortWeekdays(), 1, 8)); 134 symbols.put(SYM_FIRSTDAYINWEEK, new Integer(cal.getFirstDayOfWeek() - 1)); 135 symbols.put(SYM_MINDAYSINFIRSTWEEK, new Integer(cal.getMinimalDaysInFirstWeek())); 136 symbols.put(SYM_CLEAR_BUTTON_LABEL, getMessages().getMessage("clear")); 137 symbols.put(SYM_FORMNAME, getForm().getName()); 138 symbols.put(SYM_VALUE, getValue()); 139 140 getScript().execute(this, cycle, pageRenderSupport, symbols); 141 142 renderDelegatePrefix(writer, cycle); 143 144 writer.beginEmpty("input"); 145 writer.attribute("type", "text"); 146 writer.attribute("name", name); 147 writer.attribute("value", value); 148 writer.attribute("title", title); 149 150 if (disabled) 151 writer.attribute("disabled", "disabled"); 152 153 renderIdAttribute(writer, cycle); 154 155 renderDelegateAttributes(writer, cycle); 156 157 getTranslatedFieldSupport().renderContributions(this, writer, cycle); 158 getValidatableFieldSupport().renderContributions(this, writer, cycle); 159 160 renderInformalParameters(writer, cycle); 161 162 writer.printRaw(" "); 163 164 if (!disabled) 165 { 166 writer.begin("a"); 167 writer.attribute("href", (String) symbols.get(SYM_BUTTONONCLICKHANDLER)); 168 } 169 170 IAsset icon = getIcon(); 171 172 writer.beginEmpty("img"); 173 writer.attribute("src", icon.buildURL()); 174 writer.attribute("alt", getMessages().getMessage("alt")); 175 writer.attribute("border", 0); 176 writer.attribute("class", getImageClass()); 177 178 if (!disabled) 179 writer.end(); 180 181 renderDelegateSuffix(writer, cycle); 182 } 183 184 /** 185 * @see org.apache.tapestry.form.AbstractFormComponent#rewindFormComponent(org.apache.tapestry.IMarkupWriter, 186 * org.apache.tapestry.IRequestCycle) 187 */ 188 protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) 189 { 190 String value = cycle.getParameter(getName()); 191 192 try 193 { 194 Date date = (Date) getTranslatedFieldSupport().parse(this, value); 195 196 getValidatableFieldSupport().validate(this, writer, cycle, date); 197 198 setValue(date); 199 } 200 catch (ValidatorException e) 201 { 202 getForm().getDelegate().record(e); 203 } 204 } 205 206 /** 207 * Create a list of quoted strings. The list is suitable for initializing a JavaScript array. 208 */ 209 private String makeStringList(String[] a, int offset, int length) 210 { 211 StringBuffer b = new StringBuffer(); 212 for (int i = offset; i < length; i++) 213 { 214 // JavaScript is sensitive to some UNICODE characters. So for 215 // the sake of simplicity, we just escape everything 216 b.append('"'); 217 char[] ch = a[i].toCharArray(); 218 for (int j = 0; j < ch.length; j++) 219 { 220 if (ch[j] < 128) 221 { 222 b.append(ch[j]); 223 } 224 else 225 { 226 b.append(escape(ch[j])); 227 } 228 } 229 230 b.append('"'); 231 if (i < length - 1) 232 { 233 b.append(", "); 234 } 235 } 236 return b.toString(); 237 238 } 239 240 /** 241 * Create an escaped Unicode character. 242 * 243 * @param c 244 * @return The unicode character in escaped string form 245 */ 246 private static String escape(char c) 247 { 248 char unescapedChar = c; 249 StringBuffer b = new StringBuffer(); 250 for (int i = 0; i < 4; i++) 251 { 252 b.append(Integer.toHexString(unescapedChar & 0x000F).toUpperCase()); 253 unescapedChar >>>= 4; 254 } 255 b.append("u\\"); 256 return b.reverse().toString(); 257 } 258 259 /** 260 * Injected. 261 */ 262 public abstract ValidatableFieldSupport getValidatableFieldSupport(); 263 264 /** 265 * Injected. 266 */ 267 public abstract TranslatedFieldSupport getTranslatedFieldSupport(); 268 269 /** 270 * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() 271 */ 272 public boolean isRequired() 273 { 274 return getValidatableFieldSupport().isRequired(this); 275 } 276 }