You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by jd...@apache.org on 2010/11/27 21:37:33 UTC

svn commit: r1039756 - /wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java

Author: jdonnerstag
Date: Sat Nov 27 20:37:33 2010
New Revision: 1039756

URL: http://svn.apache.org/viewvc?rev=1039756&view=rev
Log:
fixed WICKET-3158 DateField, DateTimeField classes available, but no TimeField

Added:
    wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java

Added: wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java
URL: http://svn.apache.org/viewvc/wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java?rev=1039756&view=auto
==============================================================================
--- wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java (added)
+++ wicket/trunk/wicket-datetime/src/main/java/org/apache/wicket/extensions/yui/calendar/TimeField.java Sat Nov 27 20:37:33 2010
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * This is a modified version of the wicket-datetime project's DateTimeField.
+ */
+package org.apache.wicket.extensions.yui.calendar;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.wicket.Session;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.FormComponentPanel;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.protocol.http.request.WebClientInfo;
+import org.apache.wicket.request.ClientInfo;
+import org.apache.wicket.util.convert.IConverter;
+import org.apache.wicket.util.convert.converter.ZeroPaddingIntegerConverter;
+import org.apache.wicket.util.lang.EnumeratedType;
+import org.apache.wicket.validation.validator.RangeValidator;
+import org.joda.time.DateTimeFieldType;
+import org.joda.time.DateTimeZone;
+import org.joda.time.MutableDateTime;
+import org.joda.time.format.DateTimeFormat;
+
+/**
+ * Works on a {@link java.util.Date} object. Displays a field for hours and a field for minutes, and
+ * an AM/PM field. The format (12h/24h) of the hours field depends on the time format of this
+ * {@link TimeField}'s {@link Locale}, as does the visibility of the AM/PM field (see
+ * {@link TimeField#use12HourFormat}).
+ * 
+ * @author eelcohillenius
+ * @see DateField for a variant with just the date field and date picker
+ */
+public class TimeField extends FormComponentPanel<Date>
+{
+	/**
+	 * Enumerated type for different ways of handling the render part of requests.
+	 */
+	private static class AM_PM extends EnumeratedType
+	{
+		private static final long serialVersionUID = 1L;
+
+		static final AM_PM AM = new AM_PM("AM");
+
+		static final AM_PM PM = new AM_PM("PM");
+
+		public static AM_PM[] values()
+		{
+			return new AM_PM[] { AM, PM };
+		}
+
+		private AM_PM(final String name)
+		{
+			super(name);
+		}
+	}
+
+	private static final IConverter MINUTES_CONVERTER = new ZeroPaddingIntegerConverter(2);
+
+	private static final long serialVersionUID = 1L;
+
+	private AM_PM amOrPm = AM_PM.AM;
+
+	private DropDownChoice<AM_PM> amOrPmChoice;
+
+	private MutableDateTime date;
+
+	private Integer hours;
+
+	private TextField<Integer> hoursField;
+
+	private Integer minutes;
+
+	private TextField<Integer> minutesField;
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 */
+	public TimeField(String id)
+	{
+		this(id, null);
+	}
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 * @param model
+	 */
+	public TimeField(String id, IModel<Date> model)
+	{
+		super(id, model);
+		setType(Date.class);
+		add(hoursField = new TextField<Integer>("hours", new PropertyModel<Integer>(this, "hours"),
+			Integer.class));
+		hoursField.add(new HoursValidator());
+		hoursField.setLabel(new Model<String>("hours"));
+		add(minutesField = new TextField<Integer>("minutes", new PropertyModel<Integer>(this,
+			"minutes"), Integer.class)
+		{
+			private static final long serialVersionUID = 1L;
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public IConverter getConverter(Class type)
+			{
+				return MINUTES_CONVERTER;
+			}
+		});
+		minutesField.add(new RangeValidator<Integer>(0, 59));
+		minutesField.setLabel(new Model<String>("minutes"));
+		add(amOrPmChoice = new DropDownChoice<AM_PM>("amOrPmChoice", new PropertyModel<AM_PM>(this,
+			"amOrPm"), Arrays.asList(AM_PM.values())));
+	}
+
+	/**
+	 * Gets amOrPm.
+	 * 
+	 * @return amOrPm
+	 */
+	public AM_PM getAmOrPm()
+	{
+		return amOrPm;
+	}
+
+	/**
+	 * Gets date.
+	 * 
+	 * @return date
+	 */
+	public Date getDate()
+	{
+		return (date != null) ? date.toDate() : null;
+	}
+
+	/**
+	 * Gets hours.
+	 * 
+	 * @return hours
+	 */
+	public Integer getHours()
+	{
+		return hours;
+	}
+
+	protected void configure(Map widgetProperties)
+	{
+
+	}
+
+	/**
+	 * @see org.apache.wicket.markup.html.form.FormComponent#getInput()
+	 */
+	@Override
+	public String getInput()
+	{
+		// since we override convertInput, we can let this method return a value
+		// that is just suitable for error reporting
+		return hoursField.getInput() + ":" + minutesField.getInput();
+	}
+
+	/**
+	 * Gets minutes.
+	 * 
+	 * @return minutes
+	 */
+	public Integer getMinutes()
+	{
+		return minutes;
+	}
+
+	/**
+	 * Sets amOrPm.
+	 * 
+	 * @param amOrPm
+	 *            amOrPm
+	 */
+	public void setAmOrPm(AM_PM amOrPm)
+	{
+		this.amOrPm = amOrPm;
+	}
+
+	/**
+	 * Sets date.
+	 * 
+	 * @param date
+	 *            date
+	 */
+	public void setDate(Date date)
+	{
+		if (date == null)
+		{
+			this.date = null;
+			setDefaultModelObject(null);
+			setHours(null);
+			setMinutes(null);
+			return;
+		}
+		this.date = new MutableDateTime(date);
+		setDefaultModelObject(date);
+
+		Integer hours = getHours();
+		Integer minutes = getMinutes();
+		boolean use12HourFormat = use12HourFormat();
+		if (hours != null)
+		{
+			this.date.set(DateTimeFieldType.hourOfDay(), hours.intValue() %
+				(use12HourFormat ? 12 : 24));
+			this.date.setMinuteOfHour((minutes != null) ? minutes.intValue() : 0);
+		}
+		setDefaultModelObject(this.date.toDate());
+	}
+
+	/**
+	 * Sets hours.
+	 * 
+	 * @param hours
+	 *            hours
+	 */
+	public void setHours(Integer hours)
+	{
+		this.hours = hours;
+	}
+
+	/**
+	 * Sets minutes.
+	 * 
+	 * @param minutes
+	 *            minutes
+	 */
+	public void setMinutes(Integer minutes)
+	{
+		this.minutes = minutes;
+	}
+
+	/**
+	 * Gets the client's time zone.
+	 * 
+	 * @return The client's time zone or null
+	 */
+	protected TimeZone getClientTimeZone()
+	{
+		ClientInfo info = Session.get().getClientInfo();
+		if (info instanceof WebClientInfo)
+		{
+			return ((WebClientInfo)info).getProperties().getTimeZone();
+		}
+		return null;
+	}
+
+	/**
+	 * Sets the converted input, which is an instance of {@link Date}, possibly null. It combines
+	 * the inputs of the nested date, hours, minutes and am/pm fields and constructs a date from it.
+	 * <p>
+	 * Note that overriding this method is a better option than overriding {@link #updateModel()}
+	 * like the first versions of this class did. The reason for that is that this method can be
+	 * used by form validators without having to depend on the actual model being updated, and this
+	 * method is called by the default implementation of {@link #updateModel()} anyway (so we don't
+	 * have to override that anymore).
+	 * </p>
+	 * 
+	 * @see org.apache.wicket.markup.html.form.FormComponent#convertInput()
+	 */
+	@Override
+	protected void convertInput()
+	{
+		MutableDateTime date = new MutableDateTime(createDate());
+		Integer hours = hoursField.getConvertedInput();
+		Integer minutes = minutesField.getConvertedInput();
+		AM_PM amOrPm = amOrPmChoice.getConvertedInput();
+		try
+		{
+			boolean use12HourFormat = use12HourFormat();
+			if (hours != null)
+			{
+				date.set(DateTimeFieldType.hourOfDay(), hours.intValue() %
+					getMaximumHours(use12HourFormat));
+				date.setMinuteOfHour((minutes != null) ? minutes.intValue() : 0);
+			}
+			if (use12HourFormat)
+			{
+				date.set(DateTimeFieldType.halfdayOfDay(), amOrPm == AM_PM.PM ? 1 : 0);
+			}
+			TimeZone zone = getClientTimeZone();
+			if (zone != null)
+			{
+				date.setMillis(getMillis(zone, TimeZone.getDefault(), date.getMillis()));
+			}
+			// the date will be in the server's timezone
+			setConvertedInput(date.toDate());
+		}
+		catch (RuntimeException e)
+		{
+			TimeField.this.error(e.getMessage());
+			invalid();
+		}
+	}
+
+	private long getMillis(TimeZone to, TimeZone from, long instant)
+	{
+		return DateTimeZone.forTimeZone(from).getMillisKeepLocal(DateTimeZone.forTimeZone(to),
+			instant);
+	}
+
+	/**
+	 * @see org.apache.wicket.Component#onBeforeRender()
+	 */
+	@Override
+	protected void onBeforeRender()
+	{
+		hoursField.setRequired(isRequired());
+		minutesField.setRequired(isRequired());
+
+		boolean use12HourFormat = use12HourFormat();
+		amOrPmChoice.setVisible(use12HourFormat);
+
+		Date d = (Date)getDefaultModelObject();
+		if (d != null)
+		{
+			date = new MutableDateTime(d);
+		}
+		else
+		{
+			date = null;
+			hours = null;
+			minutes = null;
+		}
+
+		if (date != null)
+		{
+			// convert date to the client's time zone if we have that info
+			TimeZone zone = getClientTimeZone();
+			// instantiate with the previously set date
+			if (zone != null)
+			{
+				date.setMillis(getMillis(TimeZone.getDefault(), zone, date.getMillis()));
+			}
+
+			if (use12HourFormat)
+			{
+				int hourOfHalfDay = date.get(DateTimeFieldType.hourOfHalfday());
+				hours = new Integer(hourOfHalfDay == 0 ? 12 : hourOfHalfDay);
+			}
+			else
+			{
+				hours = new Integer(date.get(DateTimeFieldType.hourOfDay()));
+			}
+			amOrPm = (date.get(DateTimeFieldType.halfdayOfDay()) == 0) ? AM_PM.AM : AM_PM.PM;
+			minutes = new Integer(date.getMinuteOfHour());
+
+			// we don't really have to reset the date field to the server's
+			// timezone, as it's the same milliseconds from EPOCH anyway, and
+			// toDate will always get the Date object initialized for the time zone
+			// of the server
+		}
+
+		super.onBeforeRender();
+	}
+
+	/**
+	 * Checks whether the current {@link Locale} uses the 12h or 24h time format. This method can be
+	 * overridden to e.g. always use 24h format.
+	 * 
+	 * @return true, if the current {@link Locale} uses the 12h format.<br/>
+	 *         false, otherwise
+	 */
+	protected boolean use12HourFormat()
+	{
+		String pattern = DateTimeFormat.patternForStyle("-S", getLocale());
+		return pattern.indexOf('a') != -1 || pattern.indexOf('h') != -1 ||
+			pattern.indexOf('K') != -1;
+	}
+
+	/**
+	 * @return either 12 or 24, depending on the hour format of the current {@link Locale}
+	 */
+	private int getMaximumHours()
+	{
+		return getMaximumHours(use12HourFormat());
+	}
+
+	/**
+	 * Convenience method (mainly for optimization purposes), in case {@link #use12HourFormat()} has
+	 * already been stored in a local variable and thus doesn't need to be computed again.
+	 * 
+	 * @param use12HourFormat
+	 *            the hour format to use
+	 * @return either 12 or 24, depending on the parameter <code>use12HourFormat</code>
+	 */
+	private int getMaximumHours(boolean use12HourFormat)
+	{
+		return use12HourFormat ? 12 : 24;
+	}
+
+	/**
+	 * Validator for the {@link DateTimeField}'s hours field. Behaves like
+	 * <code>RangeValidator</code>, setting appropriate range according to
+	 * {@link DateTimeField#getMaximumHours()}
+	 * 
+	 * @see DateTimeField#getMaximumHours()
+	 * @author Gerolf Seitz
+	 */
+	private class HoursValidator extends RangeValidator<Integer>
+	{
+		private static final long serialVersionUID = 1L;
+
+		/**
+		 * Constructor
+		 */
+		public HoursValidator()
+		{
+			if (getMaximumHours() == 24)
+			{
+				setRange(0, 23);
+			}
+			else
+			{
+				setRange(1, 12);
+			}
+		}
+	}
+
+	private Date createDate()
+	{
+		Calendar cal = Calendar.getInstance();
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.AM_PM, Calendar.PM);
+		cal.set(Calendar.HOUR, 12);
+		return cal.getTime();
+	}
+}
\ No newline at end of file