001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.ui;
039:
040: import java.text.ParseException;
041: import java.text.SimpleDateFormat;
042: import java.util.Calendar;
043: import java.util.Date;
044: import java.util.Locale;
045: import java.util.Map;
046:
047: import org.millstone.base.data.Property;
048: import org.millstone.base.terminal.PaintException;
049: import org.millstone.base.terminal.PaintTarget;
050:
051: /**
052: * <p>
053: * A date editor component that can be bound to any bindable Property. that is
054: * compatible with java.util.Date.
055: *
056: * <p>
057: * Since <code>DateField</code> extends <code>AbstractField</code> it
058: * implements the {@link org.millstone.base.data.Buffered}interface. A
059: * <code>DateField</code> is in write-through mode by default, so
060: * {@link org.millstone.base.ui.AbstractField#setWriteThrough(boolean)}must be
061: * called to enable buffering.
062: * </p>
063: *
064: * @author IT Mill Ltd.
065: * @version
066: * 3.1.1
067: * @since 3.0
068: */
069: public class DateField extends AbstractField {
070:
071: /* Private members ************************************************* */
072:
073: /** Resolution identifier: milliseconds */
074: public static final int RESOLUTION_MSEC = 0;
075:
076: /** Resolution identifier: seconds. */
077: public static final int RESOLUTION_SEC = 1;
078:
079: /** Resolution identifier: minutes. */
080: public static final int RESOLUTION_MIN = 2;
081:
082: /** Resolution identifier: hours. */
083: public static final int RESOLUTION_HOUR = 3;
084:
085: /** Resolution identifier: days. */
086: public static final int RESOLUTION_DAY = 4;
087:
088: /** Resolution identifier: months. */
089: public static final int RESOLUTION_MONTH = 5;
090:
091: /** Resolution identifier: years. */
092: public static final int RESOLUTION_YEAR = 6;
093:
094: /** Specified smallest modifiable unit */
095: private int resolution = RESOLUTION_MSEC;
096:
097: /** Specified largest modifiable unit */
098: private static final int largestModifiable = RESOLUTION_YEAR;
099:
100: /** The internal calendar to be used in java.utl.Date conversions */
101: private Calendar calendar;
102:
103: /* Constructors **************************************************** */
104:
105: /** Constructs an empty <code>DateField</code> with no caption. */
106: public DateField() {
107: }
108:
109: /**
110: * Constructs an empty <code>DateField</code> with caption.
111: *
112: * @param caption
113: * The caption of the datefield.
114: */
115: public DateField(String caption) {
116: setCaption(caption);
117: }
118:
119: /**
120: * Constructs a new <code>DateField</code> that's bound to the specified
121: * <code>Property</code> and has the given caption <code>String</code>.
122: *
123: * @param caption
124: * caption <code>String</code> for the editor
125: * @param dataSource
126: * the Property to be edited with this editor
127: */
128: public DateField(String caption, Property dataSource) {
129: this (dataSource);
130: setCaption(caption);
131: }
132:
133: /**
134: * Constructs a new <code>DateField</code> that's bound to the specified
135: * <code>Property</code> and has no caption.
136: *
137: * @param dataSource
138: * the Property to be edited with this editor
139: */
140: public DateField(Property dataSource)
141: throws IllegalArgumentException {
142: if (!Date.class.isAssignableFrom(dataSource.getType()))
143: throw new IllegalArgumentException("Can't use "
144: + dataSource.getType().getName()
145: + " typed property as datasource");
146:
147: setPropertyDataSource(dataSource);
148: }
149:
150: /**
151: * Constructs a new <code>DateField</code> with the given caption and
152: * initial text contents. The editor constructed this way will not be bound
153: * to a Property unless
154: * {@link org.millstone.base.data.Property.Viewer#setPropertyDataSource(Property)}
155: * is called to bind it.
156: *
157: * @param caption
158: * caption <code>String</code> for the editor
159: * @param text
160: * initial text content of the editor
161: */
162: public DateField(String caption, Date value) {
163: setValue(value);
164: setCaption(caption);
165: }
166:
167: /* Component basic features ********************************************* */
168:
169: /*
170: * Paint this component. Don't add a JavaDoc comment here, we use the
171: * default documentation from implemented interface.
172: */
173: public void paintContent(PaintTarget target) throws PaintException {
174: super .paintContent(target);
175:
176: // Add locale as attribute
177: Locale l = getLocale();
178: if (l != null) {
179: target.addAttribute("locale", l.toString());
180: }
181:
182: // Get the calendar
183: Calendar calendar = getCalendar();
184: Date currentDate = (Date) getValue();
185:
186: for (int r = resolution; r <= largestModifiable; r++)
187: switch (r) {
188: case RESOLUTION_MSEC:
189: target.addVariable(this , "msec",
190: currentDate != null ? calendar
191: .get(Calendar.MILLISECOND) : -1);
192: break;
193: case RESOLUTION_SEC:
194: target.addVariable(this , "sec",
195: currentDate != null ? calendar
196: .get(Calendar.SECOND) : -1);
197: break;
198: case RESOLUTION_MIN:
199: target.addVariable(this , "min",
200: currentDate != null ? calendar
201: .get(Calendar.MINUTE) : -1);
202: break;
203: case RESOLUTION_HOUR:
204: target.addVariable(this , "hour",
205: currentDate != null ? calendar
206: .get(Calendar.HOUR_OF_DAY) : -1);
207: break;
208: case RESOLUTION_DAY:
209: target.addVariable(this , "day",
210: currentDate != null ? calendar
211: .get(Calendar.DAY_OF_MONTH) : -1);
212: break;
213: case RESOLUTION_MONTH:
214: target.addVariable(this , "month",
215: currentDate != null ? calendar
216: .get(Calendar.MONTH) + 1 : -1);
217: break;
218: case RESOLUTION_YEAR:
219: target.addVariable(this , "year",
220: currentDate != null ? calendar
221: .get(Calendar.YEAR) : -1);
222: break;
223: }
224: }
225:
226: /*
227: * Gets the components UIDL tag string. Don't add a JavaDoc comment here, we
228: * use the default documentation from implemented interface.
229: */
230: public String getTag() {
231: return "datefield";
232: }
233:
234: /*
235: * Invoked when a variable of the component changes. Don't add a JavaDoc
236: * comment here, we use the default documentation from implemented
237: * interface.
238: */
239: public void changeVariables(Object source, Map variables) {
240:
241: if (!isReadOnly()
242: && (variables.containsKey("year")
243: || variables.containsKey("month")
244: || variables.containsKey("day")
245: || variables.containsKey("hour")
246: || variables.containsKey("min")
247: || variables.containsKey("sec") || variables
248: .containsKey("msec"))) {
249:
250: // Old and new dates
251: Date oldDate = (Date) getValue();
252: Date newDate = null;
253:
254: // Get the new date in parts
255: // Null values are converted to negative values.
256: int year = variables.containsKey("year") ? (variables
257: .get("year") == null ? -1 : ((Integer) variables
258: .get("year")).intValue()) : -1;
259: int month = variables.containsKey("month") ? (variables
260: .get("month") == null ? -1 : ((Integer) variables
261: .get("month")).intValue() - 1) : -1;
262: int day = variables.containsKey("day") ? (variables
263: .get("day") == null ? -1 : ((Integer) variables
264: .get("day")).intValue()) : -1;
265: int hour = variables.containsKey("hour") ? (variables
266: .get("hour") == null ? -1 : ((Integer) variables
267: .get("hour")).intValue()) : -1;
268: int min = variables.containsKey("min") ? (variables
269: .get("min") == null ? -1 : ((Integer) variables
270: .get("min")).intValue()) : -1;
271: int sec = variables.containsKey("sec") ? (variables
272: .get("sec") == null ? -1 : ((Integer) variables
273: .get("sec")).intValue()) : -1;
274: int msec = variables.containsKey("msec") ? (variables
275: .get("msec") == null ? -1 : ((Integer) variables
276: .get("msec")).intValue()) : -1;
277:
278: // If all of the components is < 0 use the previous value
279: if (year < 0 && month < 0 && day < 0 && hour < 0 && min < 0
280: && sec < 0 && msec < 0)
281: newDate = null;
282: else {
283:
284: // Clone the calendar for date operation
285: Calendar cal = (Calendar) getCalendar();
286:
287: // Make sure that meaningful values exists
288: // Use the previous value if some of the variables
289: // have not been changed.
290: year = year < 0 ? cal.get(Calendar.YEAR) : year;
291: month = month < 0 ? cal.get(Calendar.MONTH) : month;
292: day = day < 0 ? cal.get(Calendar.DAY_OF_MONTH) : day;
293: hour = hour < 0 ? cal.get(Calendar.HOUR_OF_DAY) : hour;
294: min = min < 0 ? cal.get(Calendar.MINUTE) : min;
295: sec = sec < 0 ? cal.get(Calendar.SECOND) : sec;
296: msec = msec < 0 ? cal.get(Calendar.MILLISECOND) : msec;
297:
298: // Set the calendar fields
299: cal.set(Calendar.YEAR, year);
300: cal.set(Calendar.MONTH, month);
301: cal.set(Calendar.DAY_OF_MONTH, day);
302: cal.set(Calendar.HOUR_OF_DAY, hour);
303: cal.set(Calendar.MINUTE, min);
304: cal.set(Calendar.SECOND, sec);
305: cal.set(Calendar.MILLISECOND, msec);
306:
307: // Assign the date
308: newDate = cal.getTime();
309: }
310:
311: if (newDate != oldDate
312: && (newDate == null || !newDate.equals(oldDate)))
313: setValue(newDate);
314: }
315: }
316:
317: /* Property features **************************************************** */
318:
319: /*
320: * Gets the edited property's type. Don't add a JavaDoc comment here, we use
321: * the default documentation from implemented interface.
322: */
323: public Class getType() {
324: return Date.class;
325: }
326:
327: /*
328: * Return the value of the property in human readable textual format. Don't
329: * add a JavaDoc comment here, we use the default documentation from
330: * implemented interface.
331: */
332: public String toString() {
333: Date value = (Date) getValue();
334: if (value != null)
335: return value.toString();
336: return null;
337: }
338:
339: /*
340: * Set the value of the property. Don't add a JavaDoc comment here, we use
341: * the default documentation from implemented interface.
342: */
343: public void setValue(Object newValue)
344: throws Property.ReadOnlyException,
345: Property.ConversionException {
346:
347: // Allow setting dates directly
348: if (newValue == null || newValue instanceof Date)
349: super .setValue(newValue);
350: else {
351:
352: // Try to parse as string
353: try {
354: SimpleDateFormat parser = new SimpleDateFormat();
355: Date val = parser.parse(newValue.toString());
356: super .setValue(val);
357: } catch (ParseException e) {
358: throw new Property.ConversionException(e.getMessage());
359: }
360: }
361: }
362:
363: /**
364: * Set DateField datasource. Datasource type must assignable to Date.
365: *
366: * @see org.millstone.base.data.Property.Viewer#setPropertyDataSource(Property)
367: */
368: public void setPropertyDataSource(Property newDataSource) {
369: if (newDataSource == null
370: || Date.class.isAssignableFrom(newDataSource.getType()))
371: super .setPropertyDataSource(newDataSource);
372: else
373: throw new IllegalArgumentException(
374: "DateField only supports Date properties");
375: }
376:
377: /**
378: * Returns the resolution.
379: *
380: * @return int
381: */
382: public int getResolution() {
383: return resolution;
384: }
385:
386: /**
387: * Sets the resolution of the DateField
388: *
389: * @param resolution
390: * The resolution to set
391: */
392: public void setResolution(int resolution) {
393: this .resolution = resolution;
394: }
395:
396: /**
397: * Returns new instance calendar used in Date conversions.
398: *
399: * Returns new clone of the calendar object initialized using the the
400: * current date (if available)
401: *
402: * If this is no calendar is assigned the Calendar.getInstance() is used.
403: *
404: * @see #setCalendar(Calendar)
405: * @return Calendar
406: */
407: private Calendar getCalendar() {
408:
409: // Make sure we have an calendar instance
410: if (this .calendar == null) {
411: this .calendar = Calendar.getInstance();
412: }
413:
414: // Clone the instance
415: Calendar newCal = (Calendar) this .calendar.clone();
416:
417: // Assign the current time tom calendar.
418: Date currentDate = (Date) getValue();
419: if (currentDate != null)
420: newCal.setTime(currentDate);
421:
422: return newCal;
423: }
424: }
|