| /* |
| * $Id$ |
| * |
| * 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. |
| */ |
| |
| package org.apache.struts2.dojo.components; |
| |
| import java.text.DateFormat; |
| import java.text.Format; |
| import java.text.MessageFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Random; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.struts2.components.UIBean; |
| import org.apache.struts2.views.annotations.StrutsTag; |
| import org.apache.struts2.views.annotations.StrutsTagAttribute; |
| import org.apache.struts2.views.annotations.StrutsTagSkipInheritance; |
| |
| import com.opensymphony.xwork2.util.ValueStack; |
| import com.opensymphony.xwork2.util.logging.Logger; |
| import com.opensymphony.xwork2.util.logging.LoggerFactory; |
| |
| /** |
| * <!-- START SNIPPET: javadoc --> |
| * <p> |
| * Renders a date/time picker in a dropdown container. |
| * </p> |
| * <p> |
| * A stand-alone DateTimePicker widget that makes it easy to select a date/time, or increment by week, month, |
| * and/or year. |
| * </p> |
| * |
| * <p> |
| * It is possible to customize the user-visible formatting with either the |
| * 'formatLength' (long, short, medium or full) or 'displayFormat' attributes. By defaulty current |
| * locale will be used.</p> |
| * </p> |
| * |
| * Syntax supported by 'displayFormat' is (http://www.unicode.org/reports/tr35/tr35-4.html#Date_Format_Patterns):- |
| * <table border="1"> |
| * <tr> |
| * <td>Format</td> |
| * <td>Description</td> |
| * </tr> |
| * <tr> |
| * <td>d</td> |
| * <td>Day of the month</td> |
| * </tr> |
| * <tr> |
| * <td>D</td> |
| * <td>Day of year</td> |
| * </tr> |
| * <tr> |
| * <td>M</td> |
| * <td>Month - Use one or two for the numerical month, three for the abbreviation, or four for the full name, or 5 for the narrow name.</td> |
| * </tr> |
| * <tr> |
| * <td>y</td> |
| * <td>Year</td> |
| * </tr> |
| * <tr> |
| * <td>h</td> |
| * <td>Hour [1-12].</td> |
| * </tr> |
| * <tr> |
| * <td>H</td> |
| * <td>Hour [0-23].</td> |
| * </tr> |
| * <tr> |
| * <td>m</td> |
| * <td>Minute. Use one or two for zero padding.</td> |
| * </tr> |
| * <tr> |
| * <td>s</td> |
| * <td>Second. Use one or two for zero padding.</td> |
| * </tr> |
| * </table> |
| * |
| * <p> |
| * The value sent to the server is a locale-independent value, in a hidden field as defined |
| * by the name attribute. The value will be formatted conforming to RFC3 339 |
| * (yyyy-MM-dd'T'HH:mm:ss) |
| * </p> |
| * <p> |
| * The following formats(in order) will be used to parse the values of the attributes 'value', |
| * 'startDate' and 'endDate': |
| * </p> |
| * <ul> |
| * <li>SimpleDateFormat built using RFC 3339 (yyyy-MM-dd'T'HH:mm:ss) |
| * <li>SimpleDateFormat.getTimeInstance(DateFormat.SHORT) |
| * <li>SimpleDateFormat.getDateInstance(DateFormat.SHORT) |
| * <li>SimpleDateFormat.getDateInstance(DateFormat.MEDIUM) |
| * <li>SimpleDateFormat.getDateInstance(DateFormat.FULL) |
| * <li>SimpleDateFormat.getDateInstance(DateFormat.LONG) |
| * <li>SimpleDateFormat built using the value of the 'displayFormat' attribute(if any) |
| * </ul> |
| * <!-- END SNIPPET: javadoc --> |
| * |
| * <b>Examples</b> |
| * |
| * <pre> |
| * <!-- START SNIPPET: example1 --> |
| * <sx:datetimepicker name="order.date" label="Order Date" /> |
| * <sx:datetimepicker name="delivery.date" label="Delivery Date" displayFormat="yyyy-MM-dd" /> |
| * <sx:datetimepicker name="delivery.date" label="Delivery Date" value="%{date}" /> |
| * <sx:datetimepicker name="delivery.date" label="Delivery Date" value="%{'2007-01-01'}" /> |
| * <sx:datetimepicker name="order.date" label="Order Date" value="%{'today'}"/> |
| * <!-- END SNIPPET: example1 --> |
| * </pre> |
| * |
| * <!-- START SNIPPET: example2 --> |
| * <sx:datetimepicker id="picker" label="Order Date" /> |
| * <script type="text/javascript"> |
| * function setValue() { |
| * var picker = dojo.widget.byId("picker"); |
| * |
| * //string value |
| * picker.setValue('2007-01-01'); |
| * |
| * //Date value |
| * picker.setValue(new Date()); |
| * } |
| * |
| * function showValue() { |
| * var picker = dojo.widget.byId("picker"); |
| * |
| * //string value |
| * var stringValue = picker.getValue(); |
| * alert(stringValue); |
| * |
| * //date value |
| * var dateValue = picker.getDate(); |
| * alert(dateValue); |
| * } |
| * </script> |
| * <!-- END SNIPPET: example2 --> |
| * |
| * <!-- START SNIPPET: example3 --> |
| * <sx:datetimepicker id="picker" label="Order Date" valueNotifyTopics="/value"/> |
| * |
| * <script type="text/javascript"> |
| * dojo.event.topic.subscribe("/value", function(textEntered, date, widget){ |
| * alert('value changed'); |
| * //textEntered: String enetered in the textbox |
| * //date: JavaScript Date object with the value selected |
| * //widet: widget that published the topic |
| * }); |
| * </script> |
| * <!-- END SNIPPET: example3 --> |
| */ |
| @StrutsTag(name="datetimepicker", tldTagClass="org.apache.struts2.dojo.views.jsp.ui.DateTimePickerTag", description="Render datetimepicker") |
| public class DateTimePicker extends UIBean { |
| |
| final public static String TEMPLATE = "datetimepicker"; |
| // SimpleDateFormat is not thread-safe see: |
| // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6231579 |
| // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997 |
| // solution is to use stateless MessageFormat instead: |
| final private static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; |
| final private static String RFC3339_PATTERN = "{0,date," + RFC3339_FORMAT + "}"; |
| final protected static Logger LOG = LoggerFactory.getLogger(DateTimePicker.class); |
| final private static transient Random RANDOM = new Random(); |
| |
| protected String iconPath; |
| protected String formatLength; |
| protected String displayFormat; |
| protected String toggleType; |
| protected String toggleDuration; |
| protected String type; |
| |
| protected String displayWeeks; |
| protected String adjustWeeks; |
| protected String startDate; |
| protected String endDate; |
| protected String weekStartsOn; |
| protected String staticDisplay; |
| protected String dayWidth; |
| protected String language; |
| protected String templateCssPath; |
| protected String valueNotifyTopics; |
| |
| public DateTimePicker(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { |
| super(stack, request, response); |
| } |
| |
| protected String getDefaultTemplate() { |
| return TEMPLATE; |
| } |
| |
| public void evaluateParams() { |
| super.evaluateParams(); |
| |
| if(displayFormat != null) |
| addParameter("displayFormat", findString(displayFormat)); |
| if(displayWeeks != null) |
| addParameter("displayWeeks", findString(displayWeeks)); |
| if(adjustWeeks != null) |
| addParameter("adjustWeeks", findValue(adjustWeeks, Boolean.class)); |
| |
| if(disabled != null) |
| addParameter("disabled", findValue(disabled, Boolean.class)); |
| |
| if(startDate != null) |
| addParameter("startDate", format(findValue(startDate))); |
| if(endDate != null) |
| addParameter("endDate", format(findValue(endDate))); |
| if(weekStartsOn != null) |
| addParameter("weekStartsOn", findString(weekStartsOn)); |
| if(staticDisplay != null) |
| addParameter("staticDisplay", findValue(staticDisplay, Boolean.class)); |
| if(dayWidth != null) |
| addParameter("dayWidth", findValue(dayWidth, Integer.class)); |
| if(language != null) |
| addParameter("language", findString(language)); |
| if(value != null) |
| addParameter("value", format(findValue(value))); |
| |
| if(iconPath != null) |
| addParameter("iconPath", findString(iconPath)); |
| if(formatLength != null) |
| addParameter("formatLength", findString(formatLength)); |
| if(toggleType != null) |
| addParameter("toggleType", findString(toggleType)); |
| if(toggleDuration != null) |
| addParameter("toggleDuration", findValue(toggleDuration, |
| Integer.class)); |
| if(type != null) |
| addParameter("type", findString(type)); |
| else |
| addParameter("type", "date"); |
| if(templateCssPath != null) |
| addParameter("templateCssPath", findString(templateCssPath)); |
| if(valueNotifyTopics != null) |
| addParameter("valueNotifyTopics", findString(valueNotifyTopics)); |
| |
| // format the value to RFC 3399 |
| if(parameters.containsKey("value")) { |
| addParameter("nameValue", parameters.get("value")); |
| } else { |
| if(parameters.containsKey("name")) { |
| addParameter("nameValue", format(findValue((String)parameters.get("name")))); |
| } |
| } |
| |
| // generate a random ID if not explicitly set and not parsing the content |
| Boolean parseContent = (Boolean)stack.getContext().get(Head.PARSE_CONTENT); |
| boolean generateId = (parseContent != null ? !parseContent : true); |
| |
| addParameter("pushId", generateId); |
| if ((this.id == null || this.id.length() == 0) && generateId) { |
| // resolves Math.abs(Integer.MIN_VALUE) issue reported by FindBugs |
| // http://findbugs.sourceforge.net/bugDescriptions.html#RV_ABSOLUTE_VALUE_OF_RANDOM_INT |
| int nextInt = RANDOM.nextInt(); |
| nextInt = nextInt == Integer.MIN_VALUE ? Integer.MAX_VALUE : Math.abs(nextInt); |
| this.id = "widget_" + String.valueOf(nextInt); |
| addParameter("id", this.id); |
| } |
| } |
| |
| @Override |
| @StrutsTagSkipInheritance |
| public void setTheme(String theme) { |
| super.setTheme(theme); |
| } |
| |
| @Override |
| public String getTheme() { |
| return "ajax"; |
| } |
| |
| @StrutsTagAttribute(description="If true, weekly size of calendar changes to acomodate the month if false," + |
| " 42 day format is used", type="Boolean", defaultValue="false") |
| public void setAdjustWeeks(String adjustWeeks) { |
| this.adjustWeeks = adjustWeeks; |
| } |
| |
| @StrutsTagAttribute(description="How to render the names of the days in the header(narrow, abbr or wide)", defaultValue="narrow") |
| public void setDayWidth(String dayWidth) { |
| this.dayWidth = dayWidth; |
| } |
| |
| @StrutsTagAttribute(description="Total weeks to display", type="Integer", defaultValue="6") |
| public void setDisplayWeeks(String displayWeeks) { |
| this.displayWeeks = displayWeeks; |
| } |
| |
| @StrutsTagAttribute(description="Last available date in the calendar set", type="Date", defaultValue="2941-10-12") |
| public void setEndDate(String endDate) { |
| this.endDate = endDate; |
| } |
| |
| @StrutsTagAttribute(description="First available date in the calendar set", type="Date", defaultValue="1492-10-12") |
| public void setStartDate(String startDate) { |
| this.startDate = startDate; |
| } |
| |
| @StrutsTagAttribute(description="Disable all incremental controls, must pick a date in the current display", type="Boolean", defaultValue="false") |
| public void setStaticDisplay(String staticDisplay) { |
| this.staticDisplay = staticDisplay; |
| } |
| |
| @StrutsTagAttribute(description="Adjusts the first day of the week 0==Sunday..6==Saturday", type="Integer", defaultValue="0") |
| public void setWeekStartsOn(String weekStartsOn) { |
| this.weekStartsOn = weekStartsOn; |
| } |
| |
| @StrutsTagAttribute(description="Language to display this widget in", defaultValue="brower's specified preferred language") |
| public void setLanguage(String language) { |
| this.language = language; |
| } |
| |
| @StrutsTagAttribute(description="A pattern used for the visual display of the formatted date, e.g. dd/MM/yyyy") |
| public void setDisplayFormat(String displayFormat) { |
| this.displayFormat = displayFormat; |
| } |
| |
| @StrutsTagAttribute(description="Type of formatting used for visual display. Possible values are " + |
| "long, short, medium or full", defaultValue="short") |
| public void setFormatLength(String formatLength) { |
| this.formatLength = formatLength; |
| } |
| |
| @StrutsTagAttribute(description="Path to icon used for the dropdown") |
| public void setIconPath(String iconPath) { |
| this.iconPath = iconPath; |
| } |
| |
| @StrutsTagAttribute(description="Duration of toggle in milliseconds", type="Integer", defaultValue="100") |
| public void setToggleDuration(String toggleDuration) { |
| this.toggleDuration = toggleDuration; |
| } |
| |
| @StrutsTagAttribute(description="Defines the type of the picker on the dropdown. Possible values are 'date'" + |
| " for a DateTimePicker, and 'time' for a timePicker", defaultValue="date") |
| public void setType(String type) { |
| this.type = type; |
| } |
| |
| @StrutsTagAttribute(description="oggle type of the dropdown. Possible values are plain,wipe,explode,fade", defaultValue="plain") |
| public void setToggleType(String toggleType) { |
| this.toggleType = toggleType; |
| } |
| |
| @StrutsTagAttribute(description="Template css path") |
| public void setTemplateCssPath(String templateCssPath) { |
| this.templateCssPath = templateCssPath; |
| } |
| |
| @StrutsTagAttribute(description="Preset the value of input element") |
| public void setValue(String arg0) { |
| super.setValue(arg0); |
| } |
| |
| @StrutsTagAttribute(description="Comma delimmited list of topics that will published when a value is selected") |
| public void setValueNotifyTopics(String valueNotifyTopics) { |
| this.valueNotifyTopics = valueNotifyTopics; |
| } |
| |
| private String format(Object obj) { |
| if(obj == null) |
| return null; |
| |
| if(obj instanceof Date) { |
| return MessageFormat.format(RFC3339_PATTERN, (Date) obj); |
| } else if(obj instanceof Calendar) { |
| return MessageFormat.format(RFC3339_PATTERN, ((Calendar) obj).getTime()); |
| } |
| else { |
| // try to parse a date |
| String dateStr = obj.toString(); |
| if(dateStr.equalsIgnoreCase("today")) |
| return MessageFormat.format(RFC3339_PATTERN, new Date()); |
| |
| |
| Date date = null; |
| //formats used to parse the date |
| List<DateFormat> formats = new ArrayList<DateFormat>(); |
| formats.add(new SimpleDateFormat(RFC3339_FORMAT)); |
| formats.add(SimpleDateFormat.getTimeInstance(DateFormat.SHORT)); |
| formats.add(SimpleDateFormat.getDateInstance(DateFormat.SHORT)); |
| formats.add(SimpleDateFormat.getDateInstance(DateFormat.MEDIUM)); |
| formats.add(SimpleDateFormat.getDateInstance(DateFormat.FULL)); |
| formats.add(SimpleDateFormat.getDateInstance(DateFormat.LONG)); |
| if (this.displayFormat != null) { |
| try { |
| SimpleDateFormat displayFormat = new SimpleDateFormat( |
| (String) getParameters().get("displayFormat")); |
| formats.add(displayFormat); |
| } catch (Exception e) { |
| // don't use it then (this attribute is used by Dojo, not java code) |
| LOG.error("Cannot use attribute", e); |
| } |
| } |
| |
| for (DateFormat format : formats) { |
| try { |
| date = format.parse(dateStr); |
| if (date != null) |
| return MessageFormat.format(RFC3339_PATTERN, date); |
| } catch (Exception e) { |
| //keep going |
| } |
| } |
| |
| // last resource, assume already in correct/default format |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Unable to parse date " + dateStr); |
| return dateStr; |
| } |
| } |
| |
| } |