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 }