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