001 /*
002 * This file is part of the Echo Point Project. This project is a
003 * collection of Components that have extended the Echo Web Application
004 * Framework Version 3.
005 *
006 * Version: MPL 1.1
007 *
008 * The contents of this file are subject to the Mozilla Public License Version
009 * 1.1 (the "License"); you may not use this file except in compliance with
010 * the License. You may obtain a copy of the License at
011 * http://www.mozilla.org/MPL/
012 *
013 * Software distributed under the License is distributed on an "AS IS" basis,
014 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
015 * for the specific language governing rights and limitations under the
016 * License.
017 */
018 package echopoint.jquery;
019
020 import nextapp.echo.app.*;
021
022 import java.util.Date;
023 import java.util.Calendar;
024 import java.text.SimpleDateFormat;
025 import java.text.ParseException;
026 import java.io.IOException;
027 import java.io.InputStreamReader;
028 import echopoint.able.Sizeable;
029 import echopoint.able.Alignable;
030 import java.util.logging.Level;
031 import java.util.logging.Logger;
032
033
034 /**
035 * <code>DateField</code> is a drop down component that contains a text field and a drop down calendar.
036 * The text field is updated with the contents of the DateField calendar.
037 * DateField is both a date- and time picker.
038 * If the component should be used as a time-picker the DateFormat must include an hour field, e.g. setDateFormat("dd.MM.yyyy HH:mm")
039 * It is based on the jQuery plugin DynDateTime
040 *
041 * @author Hans Holmlund - 2009-04-03
042 * @version $Id: DateField.java 242 2009-09-29 20:16:11Z aschild $
043 */
044 public class DateField extends Component implements Sizeable, Alignable
045 {
046
047 /** The logger to use to log the download progress. */
048 protected static final Logger logger = Logger.getAnonymousLogger();
049
050 private static final String USETIME_PROPERTY = "useTime";
051 public static final String DATE_CHANGED_PROPERTY = "date";
052 public static final String PROPERTY_EDITABLE = "editable";
053 public static final String PROPERTY_DATEFORMAT = "dateFormat";
054 public static final String PROPERTY_BORDER = "border";
055 public static final String PROPERTY_CALENDAR_ICON = "icon";
056 public static final String PROPERTY_CSS = "css";
057 public static final String PROPERTY_INSETS = "insets";
058 public static final String PROPERTY_LANGUAGE = "language";
059 public static final ImageReference imageReference = new ResourceImageReference("/resource/images/calendar.gif");
060 public static final String cssReference = getFileAsString("resource/js/jquery/calendar-win2k-cold-2.css");
061 public static final String PROPERTY_INPUT_WIDTH = "inputWidth";
062 public static final String PROPERTY_INPUT_HEIGHT = "inputHeight";
063 public static final String PROPERTY_SHOW_WEEKS = "showWeeks";
064 public static final String PROPERTY_FIRST_DAY_OF_WEEK = "firstDayOfWeek";
065
066
067 /**
068 * The selected date.
069 */
070 private Date date;
071 private String dateFormatPattern = "yyyy-MM-dd HH:mm";
072 private SimpleDateFormat dateFormatter;
073
074 /**
075 * Creates a new <code>CalendarSelect</code>.
076 */
077 public DateField() {
078 this((Date)null);
079 }
080
081 public DateField(Calendar calendar) {
082 this(calendar.getTime());
083 }
084
085 /**
086 * Creates a new <code>CalendarSelect</code>.
087 *
088 * @param date the initially selected date
089 */
090 public DateField(Date date) {
091 super();
092 this.date = date;
093 setDateFormat(dateFormatPattern);
094 setIcon(imageReference);
095 setCSS(cssReference);
096 // set(PROPERTY_LANGUAGE, defaultLanguage);
097 }
098
099 private void appendPattern(StringBuffer datePattern, char currentChar, int numberOfThisChar) {
100 switch (currentChar) {
101 case 'y': {
102 if (numberOfThisChar >= 3) {
103 datePattern.append("%").append("Y");
104 }
105 else {
106 datePattern.append("%").append("y");
107 }
108 break;
109 }
110 case 'M': {
111 if (numberOfThisChar >= 4) {
112 datePattern.append("%").append("B");
113 }
114 if (numberOfThisChar == 3) {
115 datePattern.append("%").append("b");
116 }
117 else {
118 datePattern.append("%").append("m");
119 }
120 break;
121 }
122 case 'w': {
123 datePattern.append("%").append("W");
124 break;
125 }
126 case 'G': {
127 throw new IllegalArgumentException("Era designator is not allowed");
128 }
129 case 'W': {
130 throw new IllegalArgumentException("Week in month is not allowed");
131 }
132 case 'D': {
133 datePattern.append("%").append("j");
134 break;
135 }
136 case 'd': {
137 datePattern.append("%").append("d");
138 break;
139 }
140 case 'F': {
141 throw new IllegalArgumentException("Day of week in month is not allowed");
142 }
143 case 'E': {
144 if (numberOfThisChar > 3) {
145 datePattern.append("%").append("A");
146 }
147 else {
148 datePattern.append("%").append("a");
149 }
150 break;
151 }
152 case 'a': {
153 datePattern.append("%").append("p");
154 break;
155 }
156 case 'H': {
157 datePattern.append("%").append("H");
158 break;
159 }
160 case 'k': {
161 throw new IllegalArgumentException("Hour in day (1-24) is not allowed. Use 'H' instead.");
162 }
163 case 'K': {
164 throw new IllegalArgumentException("Hour in am/pm (0-11) is not allowed. Use 'h' instead.");
165 }
166 case 'h': {
167 datePattern.append("%").append("l");
168 break;
169 }
170 case 'm': {
171 datePattern.append("%").append("M");
172 break;
173 }
174 case 's': {
175 datePattern.append("%").append("S");
176 break;
177 }
178 case 'S': {
179 throw new IllegalArgumentException("Milliseconds is not allowed..");
180 }
181 case 'z':
182 case 'Z':{
183 throw new IllegalArgumentException("Time zone is not allowed..");
184 }
185 default:
186 datePattern.append(currentChar);
187 }
188 }
189
190 private String convertDateFormat(String simpleDateFormat) {
191 StringBuffer datePattern = new StringBuffer();
192 char currentChar = 0;
193 int numberOfThisChar = 0;
194 for (char ch : simpleDateFormat.toCharArray()) {
195 if (currentChar == 0) {
196 currentChar = ch;
197 numberOfThisChar = 1;
198 }
199 else if (ch == currentChar) {
200 numberOfThisChar++;
201 }
202 else {
203 appendPattern(datePattern, currentChar, numberOfThisChar);
204 currentChar = ch;
205 numberOfThisChar = 1;
206 }
207 }
208 appendPattern(datePattern, currentChar, numberOfThisChar);
209 return datePattern.toString();
210 }
211
212 /**
213 * Sets the date format for this calendar.
214 * Depending on the date format the USETIME property will be automatically set.
215 * @param dateFormat
216 * @throws IllegalArgumentException
217 */
218 public void setDateFormat(String dateFormat) throws IllegalArgumentException {
219 String jsDateFormatPattern = convertDateFormat(dateFormat);
220 set(PROPERTY_DATEFORMAT, jsDateFormatPattern);
221 dateFormatPattern = dateFormat;
222 dateFormatter = new SimpleDateFormat(dateFormatPattern);
223 if (jsDateFormatPattern.contains("%H") || jsDateFormatPattern.contains("%l")) {
224 set(USETIME_PROPERTY, true);
225 }
226 else {
227 set(USETIME_PROPERTY, false);
228 }
229 }
230
231 public String getDateFormat() {
232 return dateFormatPattern;
233 }
234
235 /**
236 * Sets the inset margin of the content.
237 * Values may only be specified in pixel-based units.
238 *
239 * @param newValue the new inset margin
240 */
241 public void setInsets(Insets newValue) {
242 set(PROPERTY_INSETS, newValue);
243 }
244
245 /**
246 * Returns the inset margin of the content.
247 *
248 * @return newValue the inset margin
249 */
250 public Insets getInsets() {
251 return (Insets) get(PROPERTY_INSETS);
252 }
253
254 /**
255 * Sets the icon displayed in the calendar button.
256 *
257 * @param newValue the new icon
258 */
259 public void setIcon(ImageReference newValue) {
260 set(PROPERTY_CALENDAR_ICON, newValue);
261 }
262
263 /**
264 * Returns the cascading style sheet for this calendar.
265 *
266 * @return the cascading style sheet
267 */
268 public String getCSS() {
269 return (String) get(PROPERTY_CSS);
270 }
271
272 /**
273 * Sets the cascading style sheet for this calendar.
274 *
275 * @param newValue the new icon
276 */
277 public void setCSS(String newValue) {
278 set(PROPERTY_CSS, newValue);
279 }
280
281 /**
282 * Returns the alignment of the DateField component.
283 *
284 * @return the alignment
285 */
286 public Alignment getAlignment() {
287 return (Alignment) get(PROPERTY_ALIGNMENT);
288 }
289
290 /**
291 * Sets the alignment of the DateField component.
292 *
293 * @param newValue the new alignment
294 */
295 public void setAlignment(Alignment newValue) {
296 set(PROPERTY_ALIGNMENT, newValue);
297 }
298
299 /**
300 * Returns the icon displayed in the calendar button.
301 *
302 * @return the icon
303 */
304 public ImageReference getIcon() {
305 return (ImageReference) get(PROPERTY_CALENDAR_ICON);
306 }
307
308 /**
309 * Returns the border surrounding the entire component.
310 *
311 * @return the border
312 */
313 public Border getBorder() {
314 return (Border) get(PROPERTY_BORDER);
315 }
316
317 /**
318 * Sets the width extent of the DateField component.
319 *
320 * @param newValue - the new width extent of the DateField component
321 */
322 public void setWidth(Extent newValue) {
323 set(PROPERTY_WIDTH,newValue);
324 }
325
326 /**
327 * Returns the width extent of the DateField component.
328 * @return the width extent of the DateField component.
329 */
330 public Extent getWidth() {
331 return (Extent) get(PROPERTY_WIDTH);
332 }
333
334 /**
335 * Sets the height extent of the DateField component.
336 *
337 * @param newValue - the new height extent of the DateField component
338 */
339 public void setHeight(Extent newValue) {
340 set(PROPERTY_HEIGHT,newValue);
341 }
342
343 /**
344 * Returns the height extent of DateField component.
345 *
346 * @return the height extent of DateField component.
347 */
348 public Extent getHeight() {
349 return (Extent) get(PROPERTY_HEIGHT);
350 }
351
352 /**
353 * Returns the selected date.
354 *
355 * @return the selected date
356 */
357 public Date getDate() {
358 return date;
359 }
360
361 public String getDateStr() {
362 if (date == null) return null;
363 return dateFormatter.format(date);
364 }
365
366 /**
367 * @see nextapp.echo.app.Component#processInput(java.lang.String, java.lang.Object)
368 */
369 @Override
370 public void processInput(String inputName, Object inputValue) {
371 if (DATE_CHANGED_PROPERTY.equals(inputName)) {
372 if (inputValue != null) {
373 try {
374 setDate(dateFormatter.parse((String)inputValue));
375 } catch (ParseException e) {
376 // todo
377 }
378 }
379 else
380 {
381 setDate(null); // Set date to null when empty string returned from client side
382 }
383 }
384 }
385
386 /**
387 * Sets the border surrounding the entire component.
388 *
389 * @param newValue the new border
390 */
391 public void setBorder(Border newValue) {
392 set(PROPERTY_BORDER, newValue);
393 }
394
395 /**
396 * Sets the selected date.
397 *
398 * @param newValue the new date
399 */
400 public void setDate(Date newValue) {
401 Date oldValue = date;
402 date = newValue;
403 firePropertyChange(DATE_CHANGED_PROPERTY, (oldValue != null ? dateFormatter.format(oldValue) : null), (newValue != null ? dateFormatter.format(newValue) : null));
404 }
405
406 /**
407 * Sets the editable state of this component.
408 *
409 * @param newValue the new editable state
410 */
411 public void setEditable(boolean newValue) {
412 set(PROPERTY_EDITABLE, Boolean.valueOf(newValue));
413 }
414
415 /**
416 * Determines the editable state of this component.
417 *
418 * @return <code>true</code> if this component is editable
419 */
420 public boolean isEditable() {
421 Object property = get(PROPERTY_EDITABLE);
422 return null == property ? true : ((Boolean) property).booleanValue();
423 }
424
425
426 public static String getFileAsString(String resource) {
427 InputStreamReader in = null;
428 StringBuffer sb = new StringBuffer();
429
430 try {
431 in = new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream(resource));
432 if (in == null) {
433 throw new IllegalArgumentException("Specified resource does not exist: " + resource + ".");
434 }
435 int character;
436 while ((character = in.read()) != -1) {
437 sb.append((char) character);
438 }
439 }
440 catch (Exception e) {
441 logger.log(Level.SEVERE, "Could not load resource <"+resource+">", e);
442 }
443 finally {
444 if (in != null) { try { in.close(); } catch (IOException ex) { } }
445 }
446 return sb.toString();
447 }
448
449 /**
450 * Returns the input field width.
451 *
452 * @return The input field width
453 */
454 public Extent getInputWidth() {
455 return (Extent) get(PROPERTY_INPUT_WIDTH);
456 }
457
458 /**
459 * Sets the width of the input field for the date.
460 *
461 * @param newValue the new width of the input field
462 */
463 public void setInputWidth(Extent newValue) {
464 set(PROPERTY_INPUT_WIDTH, newValue);
465 }
466
467 /**
468 * Returns the input field height.
469 *
470 * @return The input field height
471 */
472 public Extent getInputHeight() {
473 return (Extent) get(PROPERTY_INPUT_HEIGHT);
474 }
475
476 /**
477 * Sets the editable state of this component.
478 *
479 * @param newValue the new height of the input field
480 */
481 public void setInputHeight(Extent newValue) {
482 set(PROPERTY_INPUT_HEIGHT, newValue);
483 }
484
485 /**
486 * Returns the first day of the week
487 *
488 * @return The first day of the week. (0=Sunday, 1= Monday etc.)
489 */
490 public int getFirstDayOfWeek() {
491 return (Integer) get(PROPERTY_FIRST_DAY_OF_WEEK);
492 }
493
494 /**
495 * Sets the the first day of week. Used to calculate week numbers.
496 * Usually 0 for US (Sunday) and 1 for europe (Monday)
497 *
498 * @param newValue (0=Sunday, 1= Monday etc.)
499 */
500 public void setFirstDayOfWeek(int newValue) {
501 set(PROPERTY_FIRST_DAY_OF_WEEK, newValue);
502 }
503
504 /**
505 * Returns the status of week number display
506 *
507 * @return The week number display mode
508 */
509 public boolean getShowWeeks() {
510 Object property = get(PROPERTY_SHOW_WEEKS);
511 return null == property ? true : ((Boolean) property).booleanValue();
512 }
513
514 /**
515 * Sets the display of week numbers
516 *
517 * @param newValue Should week numbers be displayed?
518 */
519 public void setShowWeeks(boolean newValue) {
520 set(PROPERTY_SHOW_WEEKS, newValue);
521 }
522 }