to get and set it, added "date" dialog field and updated registration, profile, and admin modify to use it to change a user's date of birth; added BDAY support to vCards and rigged user import/export to take it into account; cleaned up a few other matters while I was in that particular code
593 lines
18 KiB
Java
593 lines
18 KiB
Java
/*
|
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
|
* (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.mozilla.org/MPL/>.
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
|
|
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Venice Web Communities System.
|
|
*
|
|
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@ricochet.com>,
|
|
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
|
|
* Copyright (C) 2004 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
package com.silverwrist.venice.ui.dlg;
|
|
|
|
import java.io.IOException;
|
|
import java.text.*;
|
|
import java.util.*;
|
|
import org.apache.log4j.*;
|
|
import org.apache.regexp.*;
|
|
import org.w3c.dom.*;
|
|
import com.silverwrist.util.*;
|
|
import com.silverwrist.venice.except.*;
|
|
import com.silverwrist.venice.ui.*;
|
|
import com.silverwrist.venice.ui.helpers.HTMLRendering;
|
|
import com.silverwrist.venice.util.XMLLoader;
|
|
|
|
public class DateField implements DialogField
|
|
{
|
|
/*--------------------------------------------------------------------------------
|
|
* Static data members
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public static final String TAGNAME = "date";
|
|
|
|
private static Logger logger = Logger.getLogger(DateField.class);
|
|
|
|
private static final int FLD_MONTH = 1;
|
|
private static final int FLD_DAY = 2;
|
|
private static final int FLD_YEAR = 3;
|
|
|
|
private static REProgram YRANGE = null;
|
|
private static REProgram YPLUS = null;
|
|
private static REProgram YMINUS = null;
|
|
private static REProgram YPLUSMINUS = null;
|
|
private static REProgram YMINUSPLUS = null;
|
|
private static REProgram YSOLO = null;
|
|
|
|
private static Hashtable s_locale_ordering = new Hashtable();
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Attributes
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private String m_name; // field name (parameter name)
|
|
private String m_caption; // primary caption
|
|
private String m_caption2; // secondary caption
|
|
private boolean m_required; // is this field required?
|
|
private boolean m_enabled; // are we enabled?
|
|
private int m_year_start; // first year for dropdown
|
|
private int m_year_stop; // last year for dropdown
|
|
private java.sql.Date m_value = null; // selected date
|
|
private int m_ndx_month = -1; // index of selected month
|
|
private int m_ndx_day = -1; // index of selected day
|
|
private int m_ndx_year = -1; // index of selected year
|
|
private boolean m_input_error = false; // input error?
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Constructors
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public DateField(String name, String caption, String caption2, boolean required, boolean enabled, int year_start,
|
|
int year_stop)
|
|
{
|
|
m_name = name;
|
|
m_caption = caption;
|
|
m_caption2 = caption2;
|
|
m_required = required;
|
|
m_enabled = enabled;
|
|
m_year_start = year_start;
|
|
m_year_stop = year_stop;
|
|
|
|
} // end constructor
|
|
|
|
public DateField(Element elt) throws ConfigException
|
|
{
|
|
XMLLoader loader = XMLLoader.get();
|
|
m_name = loader.configGetAttribute(elt,"name");
|
|
m_caption = loader.configGetAttribute(elt,"capt");
|
|
m_caption2 = elt.getAttribute("capt2");
|
|
DOMElementHelper h = new DOMElementHelper(elt);
|
|
m_required = h.hasAttribute("required");
|
|
m_enabled = !(h.hasAttribute("disabled"));
|
|
String countdir = elt.getAttribute("direction");
|
|
if (StringUtil.isStringEmpty(countdir))
|
|
countdir = "down";
|
|
else if (!(countdir.equalsIgnoreCase("up") || countdir.equalsIgnoreCase("down")))
|
|
throw new ConfigException("<date/> direction= attribute must be 'up' or 'down'",elt);
|
|
int y1, y2;
|
|
String yearspec = elt.getAttribute("years");
|
|
if (StringUtil.isStringEmpty(yearspec))
|
|
{ // use a 70 year span back from the current year
|
|
Calendar cal = Calendar.getInstance();
|
|
y1 = cal.get(Calendar.YEAR);
|
|
y2 = y1 - 70;
|
|
|
|
} // end if
|
|
else
|
|
{ // now recognize what the field value is
|
|
do
|
|
{ // attempt to recognize a range
|
|
RE re = new RE(YRANGE);
|
|
if (re.match(yearspec))
|
|
{ // got a year range
|
|
y1 = Integer.parseInt(re.getParen(1));
|
|
y2 = Integer.parseInt(re.getParen(2));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
Calendar cal = Calendar.getInstance();
|
|
re = new RE(YPLUS);
|
|
if (re.match(yearspec))
|
|
{ // +number - range from current year forward some years
|
|
y1 = cal.get(Calendar.YEAR);
|
|
y2 = y1 + Integer.parseInt(re.getParen(1));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
re = new RE(YMINUS);
|
|
if (re.match(yearspec))
|
|
{ // -number - range from current year backward some years
|
|
y1 = cal.get(Calendar.YEAR);
|
|
y2 = y1 - Integer.parseInt(re.getParen(1));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
re = new RE(YPLUSMINUS);
|
|
if (re.match(yearspec))
|
|
{ // +number -number - brackets around the current year
|
|
int foo = cal.get(Calendar.YEAR);
|
|
y1 = foo + Integer.parseInt(re.getParen(1));
|
|
y2 = foo - Integer.parseInt(re.getParen(2));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
re = new RE(YMINUSPLUS);
|
|
if (re.match(yearspec))
|
|
{ // -number +number - brackets around the current year
|
|
int foo = cal.get(Calendar.YEAR);
|
|
y1 = foo - Integer.parseInt(re.getParen(1));
|
|
y2 = foo + Integer.parseInt(re.getParen(2));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
re = new RE(YSOLO);
|
|
if (re.match(yearspec))
|
|
{ // solo number - one end of year spec, current year being the other
|
|
y1 = cal.get(Calendar.YEAR);
|
|
y2 = Integer.parseInt(re.getParen(1));
|
|
break;
|
|
|
|
} // end if
|
|
|
|
throw new ConfigException("<date/> years= attribute: invalid syntax",elt);
|
|
|
|
} while (false); // end do
|
|
|
|
} // end else
|
|
|
|
if (countdir.equalsIgnoreCase("down"))
|
|
{ // organize for counting downward
|
|
m_year_start = Math.max(y1,y2);
|
|
m_year_stop = Math.min(y1,y2);
|
|
|
|
} // end if
|
|
else
|
|
{ // organize for counting upward
|
|
m_year_start = Math.min(y1,y2);
|
|
m_year_stop = Math.max(y1,y2);
|
|
|
|
} // end else
|
|
|
|
} // end constructor
|
|
|
|
protected DateField(DateField other)
|
|
{
|
|
m_name = other.m_name;
|
|
m_caption = other.m_caption;
|
|
m_caption2 = other.m_caption2;
|
|
m_required = other.m_required;
|
|
m_enabled = other.m_enabled;
|
|
m_year_start = other.m_year_start;
|
|
m_year_stop = other.m_year_stop;
|
|
|
|
} // end constructor
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static final int[] getDateFieldOrdering(Locale locale)
|
|
{
|
|
int[] rc = (int[])(s_locale_ordering.get(locale));
|
|
if (rc!=null)
|
|
return rc;
|
|
|
|
// We determine the date format by encoding the date September 18, 2003, into "short"
|
|
// date format, then measuring the relative positions of the digits 9, 8, and 3.
|
|
// It's severely hackish, but there seems to be no Java API whereby we can directly
|
|
// get this information.
|
|
DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT,locale);
|
|
Calendar cal = fmt.getCalendar();
|
|
cal.set(Calendar.YEAR,2003);
|
|
cal.set(Calendar.MONTH,Calendar.SEPTEMBER);
|
|
cal.set(Calendar.DAY_OF_MONTH,18);
|
|
String s = fmt.format(cal.getTime());
|
|
int pday = s.indexOf('8');
|
|
int pmonth = s.indexOf('9');
|
|
int pyear = s.indexOf('3');
|
|
rc = new int[3];
|
|
if (pday<pmonth)
|
|
{ // day before month
|
|
if (pday<pyear)
|
|
{ // we're definitely DAY first
|
|
rc[0] = FLD_DAY;
|
|
if (pmonth<pyear)
|
|
{ // D-M-Y (Euro standard)
|
|
rc[1] = FLD_MONTH;
|
|
rc[2] = FLD_YEAR;
|
|
|
|
} // end if
|
|
else
|
|
{ // D-Y-M? Well, OK...
|
|
rc[1] = FLD_YEAR;
|
|
rc[2] = FLD_MONTH;
|
|
|
|
} // end else
|
|
|
|
} // end if
|
|
else
|
|
{ // day before month, but after year - Y-D-M? OK...
|
|
rc[0] = FLD_YEAR;
|
|
rc[1] = FLD_DAY;
|
|
rc[2] = FLD_MONTH;
|
|
|
|
} // end else
|
|
|
|
} // end if
|
|
else
|
|
{ // month before day
|
|
if (pmonth<pyear)
|
|
{ // definitely MONTH first...
|
|
rc[0] = FLD_MONTH;
|
|
if (pday<pyear)
|
|
{ // M-D-Y (American standard)
|
|
rc[1] = FLD_DAY;
|
|
rc[2] = FLD_YEAR;
|
|
|
|
} // end if
|
|
else
|
|
{ // M-Y-D? OK...
|
|
rc[1] = FLD_YEAR;
|
|
rc[2] = FLD_DAY;
|
|
|
|
} // end else
|
|
|
|
} // end if
|
|
else
|
|
{ // month before day, but after year - Y-M-D (Japanese standard)
|
|
rc[0] = FLD_YEAR;
|
|
rc[1] = FLD_MONTH;
|
|
rc[2] = FLD_DAY;
|
|
|
|
} // end else
|
|
|
|
} // end else
|
|
|
|
s_locale_ordering.put(locale,rc);
|
|
return rc;
|
|
|
|
} // end getDateFieldOrdering
|
|
|
|
private final void renderDropDown(int which, RequestOutput out) throws IOException
|
|
{
|
|
HTMLRendering html = (HTMLRendering)(out.queryService(HTMLRendering.class));
|
|
Calendar cal = Calendar.getInstance(TimeZone.getDefault(),html.getLocale());
|
|
if (which==FLD_MONTH)
|
|
{ // render the "month" field
|
|
out.write("<select class=\"content\" name=\"" + m_name + "_month\" size=\"1\"");
|
|
if (!m_enabled)
|
|
out.write(" disabled=\"disabled\"");
|
|
out.write("><option value=\"-1\"");
|
|
if (m_ndx_month==-1)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">---</option>");
|
|
DateFormatSymbols syms = new DateFormatSymbols(html.getLocale());
|
|
String[] months = syms.getMonths();
|
|
for (int i=cal.getMinimum(Calendar.MONTH); i<=cal.getMaximum(Calendar.MONTH); i++)
|
|
{ // write the choices
|
|
out.write("<option value=\"" + i + "\"");
|
|
if (m_ndx_month==i)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">" + StringUtil.encodeHTML(months[i]) + "</option>");
|
|
|
|
} // end for
|
|
|
|
out.write("</select>\n");
|
|
|
|
} // end if
|
|
else if (which==FLD_DAY)
|
|
{ // render the "day" field
|
|
out.write("<select class=\"content\" name=\"" + m_name + "_day\" size=\"1\"");
|
|
if (!m_enabled)
|
|
out.write(" disabled=\"disabled\"");
|
|
out.write("><option value=\"-1\"");
|
|
if (m_ndx_day==-1)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">---</option>");
|
|
for (int i=cal.getMinimum(Calendar.DAY_OF_MONTH); i<=cal.getMaximum(Calendar.DAY_OF_MONTH); i++)
|
|
{ // write the day values
|
|
out.write("<option value=\"" + i + "\"");
|
|
if (m_ndx_day==i)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">" + i + "</option>");
|
|
|
|
} // end for
|
|
|
|
out.write("</select>\n");
|
|
|
|
} // end if
|
|
else if (which==FLD_YEAR)
|
|
{ // render the "year" drop down
|
|
out.write("<select class=\"content\" name=\"" + m_name + "_year\" size=\"1\"");
|
|
if (!m_enabled)
|
|
out.write(" disabled=\"disabled\"");
|
|
out.write("><option value=\"-1\"");
|
|
if (m_ndx_year==-1)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">---</option>");
|
|
int step = ((m_year_start>m_year_stop) ? -1 : 1);
|
|
for (int i=m_year_start; i!=m_year_stop; i+=step)
|
|
{ // write out years
|
|
out.write("<option value=\"" + i + "\"");
|
|
if (m_ndx_year==i)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">" + i + "</option>");
|
|
|
|
} // end for
|
|
|
|
// write the last year value
|
|
out.write("<option value=\"" + m_year_stop + "\"");
|
|
if (m_ndx_year==m_year_stop)
|
|
out.write(" selected=\"selected\"");
|
|
out.write(">" + m_year_stop + "</option></select>\n");
|
|
|
|
} // end else if
|
|
|
|
} // end renderDropDown
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface RenderDirect
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public void render(RequestOutput out) throws IOException
|
|
{
|
|
HTMLRendering html = (HTMLRendering)(out.queryService(HTMLRendering.class));
|
|
Calendar cal = Calendar.getInstance(TimeZone.getDefault(),html.getLocale());
|
|
if ((m_value!=null) && !m_input_error)
|
|
{ // split date into month, day, and year
|
|
cal.setTime(m_value);
|
|
if (m_ndx_month==-1)
|
|
m_ndx_month = cal.get(Calendar.MONTH);
|
|
if (m_ndx_day==-1)
|
|
m_ndx_day = cal.get(Calendar.DAY_OF_MONTH);
|
|
if (m_ndx_year==-1)
|
|
m_ndx_year = cal.get(Calendar.YEAR);
|
|
|
|
} // end if
|
|
else // everything is not set
|
|
m_ndx_month = m_ndx_day = m_ndx_year = -1;
|
|
|
|
// write the caption and the basic table framework
|
|
out.write("<tr valign=\"middle\">\n<td align=\"right\" class=\"content\">");
|
|
out.write(html.getFontTag(m_enabled ? html.CONTENT_FOREGROUND : html.CONTENT_DISABLED,"content"));
|
|
out.write(StringUtil.encodeHTML(m_caption));
|
|
if (!(StringUtil.isStringEmpty(m_caption2)))
|
|
out.write(" " + StringUtil.encodeHTML(m_caption2));
|
|
out.write(":</font>");
|
|
if (m_required)
|
|
out.write(html.getFontTag("red","content") + "*</font>");
|
|
out.write("</td>\n<td align=\"left\" class=\"content\">");
|
|
|
|
// write the drop-down list boxes
|
|
int[] order = getDateFieldOrdering(html.getLocale());
|
|
renderDropDown(order[0],out);
|
|
out.write(" \n");
|
|
renderDropDown(order[1],out);
|
|
out.write(" \n");
|
|
renderDropDown(order[2],out);
|
|
|
|
// finish up
|
|
out.write("</td>\n</tr>\n");
|
|
|
|
} // end render
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface DialogField
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public String getName()
|
|
{
|
|
return m_name;
|
|
|
|
} // end getName
|
|
|
|
public Object getValue()
|
|
{
|
|
return m_value;
|
|
|
|
} // end getValue
|
|
|
|
public void setValue(Object o)
|
|
{
|
|
if (o==null)
|
|
m_value = null;
|
|
else if (o instanceof java.sql.Date)
|
|
m_value = (java.sql.Date)(((java.sql.Date)o).clone());
|
|
else if (o instanceof java.util.Date)
|
|
m_value = new java.sql.Date(((java.util.Date)o).getTime());
|
|
else if (o instanceof Calendar)
|
|
m_value = new java.sql.Date(((Calendar)o).getTimeInMillis());
|
|
else if (o instanceof Number)
|
|
m_value = new java.sql.Date(((Number)o).longValue());
|
|
else
|
|
throw new IllegalArgumentException("cannot convert argument to java.sql.Date");
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Set date: " + m_value);
|
|
m_ndx_month = m_ndx_day = m_ndx_year = -1; // kill indexes
|
|
m_input_error = false;
|
|
|
|
} // end setValue
|
|
|
|
public void setValueFrom(RequestInput ri)
|
|
{
|
|
m_ndx_month = ri.getParameterInt(m_name + "_month",-1);
|
|
m_ndx_day = ri.getParameterInt(m_name + "_day",-1);
|
|
m_ndx_year = ri.getParameterInt(m_name + "_year",-1);
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Raw input: M=" + m_ndx_month + ", D=" + m_ndx_day + ", Y=" + m_ndx_year);
|
|
|
|
if ((m_ndx_month==-1) || (m_ndx_day==-1) || (m_ndx_year==-1))
|
|
{ // the date is to be treated as "unspecified"
|
|
logger.debug("Killing the kittens!");
|
|
m_value = null;
|
|
m_ndx_month = m_ndx_day = m_ndx_year = -1; // kill indexes
|
|
m_input_error = false;
|
|
return;
|
|
|
|
} // end if
|
|
|
|
HTMLRendering html = (HTMLRendering)(ri.queryService(HTMLRendering.class));
|
|
Calendar cal = Calendar.getInstance(TimeZone.getDefault(),html.getLocale());
|
|
cal.clear();
|
|
cal.setLenient(false);
|
|
m_input_error = false;
|
|
try
|
|
{ // set the calendar and create the actual value
|
|
cal.set(Calendar.YEAR,m_ndx_year);
|
|
cal.set(Calendar.MONTH,m_ndx_month);
|
|
cal.set(Calendar.DAY_OF_MONTH,m_ndx_day);
|
|
cal.set(Calendar.HOUR,0);
|
|
cal.set(Calendar.MINUTE,0);
|
|
cal.set(Calendar.SECOND,0);
|
|
cal.set(Calendar.MILLISECOND,0);
|
|
m_value = new java.sql.Date(cal.getTimeInMillis());
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Verified date: " + m_value);
|
|
|
|
} // end try
|
|
catch (IllegalArgumentException e)
|
|
{ // set error flag and recompute value with relaxed rules
|
|
m_input_error = true;
|
|
cal.clear();
|
|
cal.setLenient(true);
|
|
cal.set(Calendar.YEAR,m_ndx_year);
|
|
cal.set(Calendar.MONTH,m_ndx_month);
|
|
cal.set(Calendar.DAY_OF_MONTH,m_ndx_day);
|
|
cal.set(Calendar.HOUR,0);
|
|
cal.set(Calendar.MINUTE,0);
|
|
cal.set(Calendar.SECOND,0);
|
|
cal.set(Calendar.MILLISECOND,0);
|
|
m_value = new java.sql.Date(cal.getTimeInMillis());
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("UN-Verified date: " + m_value);
|
|
|
|
} // end catch
|
|
|
|
} // end setValueFrom
|
|
|
|
public boolean isRequired()
|
|
{
|
|
return m_required;
|
|
|
|
} // end isRequired
|
|
|
|
public boolean isFile()
|
|
{
|
|
return false;
|
|
|
|
} // end isFile
|
|
|
|
public boolean isHidden()
|
|
{
|
|
return false;
|
|
|
|
} // end isHidden
|
|
|
|
public void validate() throws ValidationException
|
|
{
|
|
if (m_required && (m_value==null))
|
|
throw new ValidationException("The '" + m_caption + "' field is required.");
|
|
if (m_input_error)
|
|
throw new ValidationException("Invalid date entered in the '" + m_caption + "' field.");
|
|
|
|
} // end validate
|
|
|
|
public boolean isEnabled()
|
|
{
|
|
return m_enabled;
|
|
|
|
} // end isEnabled
|
|
|
|
public void setEnabled(boolean flag)
|
|
{
|
|
m_enabled = flag;
|
|
|
|
} // end setEnabled
|
|
|
|
public Object sendMessage(String msg, Object data)
|
|
{
|
|
return null;
|
|
|
|
} // end sendMessage
|
|
|
|
public DialogField duplicate()
|
|
{
|
|
return new DateField(this);
|
|
|
|
} // end duplicate
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Static initializer
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static
|
|
{
|
|
RECompiler compiler = new RECompiler();
|
|
try
|
|
{ // compile all local regular expressions
|
|
YRANGE = compiler.compile("^\\s*(\\d+)\\s*-\\s*(\\d+)\\s*$");
|
|
YPLUS = compiler.compile("^\\s*\\+\\s*(\\d+)\\s*$");
|
|
YMINUS = compiler.compile("^\\s*-\\s*(\\d+)\\s*$");
|
|
YPLUSMINUS = compiler.compile("^\\s*\\+\\s*(\\d+)\\s*-\\s*(\\d+)\\s*$");
|
|
YMINUSPLUS = compiler.compile("^\\s*-\\s*(\\d+)\\s*\\+\\s*(\\d+)\\s*$");
|
|
YSOLO = compiler.compile("^\\s*(\\d+)\\s*$");
|
|
|
|
} // end try
|
|
catch (RESyntaxException e)
|
|
{ // whoops!
|
|
logger.fatal("Regexp Syntax Error",e);
|
|
|
|
} // end catch
|
|
|
|
} // end static initializer
|
|
|
|
} // end class DateField
|