    001package org.apache.turbine.util;
    002
    003/*
    004 * Licensed to the Apache Software Foundation (ASF) under one
    005 * or more contributor license agreements.  See the NOTICE file
    006 * distributed with this work for additional information
    007 * regarding copyright ownership.  The ASF licenses this file
    008 * to you under the Apache License, Version 2.0 (the
    009 * "License"); you may not use this file except in compliance
    010 * with the License.  You may obtain a copy of the License at
    011 *
    012 *   http://www.apache.org/licenses/LICENSE-2.0
    013 *
    014 * Unless required by applicable law or agreed to in writing,
    015 * software distributed under the License is distributed on an
    016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    017 * KIND, either express or implied.  See the License for the
    018 * specific language governing permissions and limitations
    019 * under the License.
    020 */
    021
    022import java.text.NumberFormat;
    023import java.util.Calendar;
    024import java.util.Date;
    025
    026import org.apache.ecs.ConcreteElement;
    027import org.apache.ecs.ElementContainer;
    028import org.apache.ecs.html.Comment;
    029import org.apache.ecs.html.Input;
    030import org.apache.ecs.html.Option;
    031import org.apache.ecs.html.Select;
    032
    033/**
    034 * TimeSelector is a utility class to handle the creation of a set of
    035 * time drop-down menus.  The code is broken into a set of static methods
    036 * for quick and easy access to the individual select objects:
    037 *
    038 *  <pre>
    039 *  ElementContainer ec timeSelect = new ElementContainer();
    040 *  String myName = "mytime";
    041 *  ec.addElement(TimeSelector.getHourSelector(myName));
    042 *  ec.addElement(TimeSelector.getMinuteSelector(myName));
    043 *  ec.addElement(TimeSelector.getAMPMSelector(myName));
    044 *  </pre>
    045 *
    046 * There are also methods which will use attributes to build a
    047 * complete time selector in the default 12hr format (eg HH:MM am/pm):
    048 *
    049 *  <pre>
    050 *  TimeSelector ts = new TimeSelector(myName);
    051 *  timeSelect = ts.ecsOutput();
    052 *  </pre>
    053 *
    054 * Minutes/Seconds are by default rounded to the nearest 5 units
    055 * although this can be easily changed.
    056 *
    057 * 24hr TimeSelectors can also be produced. The following example
    058 * creates a full precision TimeSelector (eg HH:MM:SS):
    059 *
    060 *  <pre>
    061 *  TimeSelector ts = new TimeSelector(myName);
    062 *  ts.setTimeFormat(TimeSelector.TWENTY_FOUR_HOUR);
    063 *  ts.setMinuteInterval(1);
    064 *  ts.setSecondInterval(1);
    065 *  ts.setShowSeconds(true);
    066 *  timeSelect = ts.toString();
    067 *  </pre>
    068 *
    069 * @author <a href="mailto:ekkerbj@netscape.net">Jeffrey D. Brekke</a>
    070 * @author <a href="mailto:rich@thenetrevolution.com">Rich Aston</a>
    071 * @version $Id: TimeSelector.java 1706239 2015-10-01 13:18:35Z tv $
    072 */
    073public class TimeSelector
    074{
    075    /** Prefix for time names. */
    076    public static final String DEFAULT_PREFIX = "TimeSelector";
    077
    078    /** Suffix for hour parameter. */
    079    public static final String HOUR_SUFFIX = "_hour";
    080
    081    /** Suffix for minute parameter. */
    082    public static final String MINUTE_SUFFIX = "_minute";
    083
    084    /** Suffix for second parameter. */
    085    public static final String SECOND_SUFFIX = "_second";
    086
    087    /** Suffix for am/pm parameter. */
    088    public static final String AMPM_SUFFIX = "_ampm";
    089
    090    /** Constant for 12hr format */
    091    public static final int TWELVE_HOUR = 0;
    092
    093    /** Constant for 24hr format */
    094    public static final int TWENTY_FOUR_HOUR = 1;
    095
    096    /** TODO: Add ability to specify Locale. */
    097    private static final NumberFormat nbrFmt;
    098
    099    private static final int DEFAULT_MINUTE_INTERVAL = 5;
    100    private static final int DEFAULT_SECOND_INTERVAL = 5;
    101    private static final int DEFAULT_TIME_FORMAT = TWELVE_HOUR;
    102
    103    private int timeFormat = DEFAULT_TIME_FORMAT;
    104    private int minuteInterval = DEFAULT_MINUTE_INTERVAL;
    105    private int secondInterval = DEFAULT_SECOND_INTERVAL;
    106
    107    private Calendar useDate = null;
    108    private String selName = null;
    109    private String onChange = null;
    110    private boolean onChangeSet = false;
    111    private boolean showSeconds = false;
    112    private int setSeconds = 0;
    113
    114    static
    115    {
    116        nbrFmt = NumberFormat.getInstance();
    117        nbrFmt.setMinimumIntegerDigits(2);
    118        nbrFmt.setMaximumIntegerDigits(2);
    119    }
    120
    121    /**
    122     * Constructor defaults to current date/time and uses the default
    123     * prefix: <pre>TimeSelector.DEFAULT</pre>
    124     */
    125    public TimeSelector()
    126    {
    127        this.selName = DEFAULT_PREFIX;
    128        this.useDate = Calendar.getInstance();
    129        this.useDate.setTime(new Date());
    130    }
    131
    132    /**
    133     * Constructor, uses the date/time set in the calendar
    134     * passed in (with the date/time set correctly).
    135     *
    136     * @param selName A String with the selector name.
    137     * @param useDate A Calendar with a date/time.
    138     */
    139    public TimeSelector(String selName, Calendar useDate)
    140    {
    141        this.useDate = useDate;
    142        this.selName = selName;
    143    }
    144
    145    /**
    146     * Constructor defaults to current date/time.
    147     *
    148     * @param selName A String with the selector name.
    149     */
    150    public TimeSelector(String selName)
    151    {
    152        this.selName = selName;
    153        this.useDate = Calendar.getInstance();
    154        this.useDate.setTime(new Date());
    155    }
    156
    157    /**
    158     * Adds the onChange to all of <code>&lt;SELECT&gt;</code> tags.
    159     * This is limited to one function for all three popups and is only
    160     * used when the output() methods are used.  Individual getHour,
    161     * getMinute, getSecond, getAMPM static methods will not use this
    162     * setting.
    163     *
    164     * @param onChange A String to use for onChange attribute.  If null,
    165     * then nothing will be set.
    166     * @return A TimeSelector (self).
    167     */
    168    public TimeSelector setOnChange(String onChange)
    169    {
    170        if (onChange != null)
    171        {
    172            this.onChange = onChange;
    173            this.onChangeSet = true;
    174        }
    175        else
    176        {
    177            this.onChange = null;
    178            this.onChangeSet = false;
    179        }
    180        return this;
    181    }
    182
    183    /**
    184     * Select the second to be selected if the showSeconds(false) behavior
    185     * is used.  Individual getHour, getMinute, getSecond, getAMPM
    186     * static methods will not use this setting.
    187     *
    188     * @param seconds The second.
    189     * @return A TimeSelector (self).
    190     */
    191    public TimeSelector setSeconds(int seconds)
    192    {
    193        this.setSeconds = seconds;
    194        this.showSeconds = false;
    195        return this;
    196    }
    197
    198    /**
    199     * Set the interval between options in the minute select box.
    200     * Individual getHour, getMinute, getSecond, getAMPM static methods
    201     * will not use this setting.
    202     *
    203     * @param minutes Interval in minutes.
    204     * @return A TimeSelector (self).
    205     */
    206    public TimeSelector setMinuteInterval(int minutes)
    207    {
    208        this.minuteInterval = minutes;
    209        return this;
    210    }
    211
    212    /**
    213     * Set the interval between options in the second select box.
    214     * Individual getHour, getMinute, getSecond, getAMPM static methods
    215     * will not use this setting.
    216     *
    217     * @param seconds Interval in seconds.
    218     * @return A TimeSelector (self).
    219     */
    220    public TimeSelector setSecondInterval(int seconds)
    221    {
    222        this.secondInterval = seconds;
    223        return this;
    224    }
    225
    226    /**
    227     * Set the time format to 12 or 24 hour. Individual getHour,
    228     * getMinute, getSecond, getAMPM static methods
    229     * will not use this setting.
    230     *
    231     * @param format Time format.
    232     * @return A TimeSelector (self).
    233     */
    234    public TimeSelector setTimeFormat(int format)
    235    {
    236        this.timeFormat = format;
    237        return this;
    238    }
    239
    240    /**
    241     * Whether or not to show the seconds as a popup menu.  The seconds will
    242     * be a hidden parameter and the value set with setSeconds is used.
    243     * Individual getHour, getMinute, getSecond, getAMPM static methods
    244     * will not use this setting.
    245     *
    246     * @param show True if the second should be shown.
    247     * @return A TimeSelector (self).
    248     */
    249    public TimeSelector setShowSeconds(boolean show)
    250    {
    251        this.showSeconds = show;
    252        return this;
    253    }
    254
    255    /**
    256     * Set the selector name prefix.  Individual getHour, getMinute,
    257     * getSeconds, getAMPM static methods will not use this setting.
    258     *
    259     * @param selName A String with the select name prefix.
    260     */
    261    public void setSelName(String selName)
    262    {
    263        this.selName = selName;
    264    }
    265
    266    /**
    267     * Get the selector name prefix.
    268     *
    269     * @return A String with the select name prefix.
    270     */
    271    public String getSelName()
    272    {
    273        return selName;
    274    }
    275
    276    /**
    277     * Return a second selector.
    278     *
    279     * @param name The name to use for the selected second.
    280     * @return A select object with second options.
    281     */
    282    public static Select getSecondSelector(String name)
    283    {
    284        return (getSecondSelector(name, Calendar.getInstance()));
    285    }
    286
    287    /**
    288     * Return a second selector.
    289     *
    290     * @param name The name to use for the selected second.
    291     * @param now Calendar to start with.
    292     * @return A select object with second options.
    293     */
    294    public static Select getSecondSelector(String name, Calendar now)
    295    {
    296        return (getSecondSelector(name, Calendar.getInstance(),
    297                DEFAULT_SECOND_INTERVAL));
    298    }
    299
    300    /**
    301     * Return a second selector.
    302     *
    303     * @param name The name to use for the selected second.
    304     * @param now Calendar to start with.
    305     * @param interval Interval between options.
    306     * @return A select object with second options.
    307     */
    308    public static Select getSecondSelector(String name, Calendar now,
    309                                           int interval)
    310    {
    311        Select secondSelect = new Select().setName(name);
    312
    313        for (int currentSecond = 0; currentSecond <= 59; currentSecond += interval)
    314        {
    315            Option o = new Option();
    316            o.addElement(nbrFmt.format(currentSecond));
    317            o.setValue(currentSecond);
    318            int nearestSecond =
    319                    ((now.get(Calendar.SECOND) / interval) * interval);
    320
    321            if (nearestSecond == currentSecond)
    322            {
    323                o.setSelected(true);
    324            }
    325            secondSelect.addElement(o);
    326        }
    327        return (secondSelect);
    328    }
    329
    330    /**
    331     * Return a minute selector.
    332     *
    333     * @param name The name to use for the selected minute.
    334     * @return A select object with minute options.
    335     */
    336    public static Select getMinuteSelector(String name)
    337    {
    338        return (getMinuteSelector(name, Calendar.getInstance()));
    339    }
    340
    341    /**
    342     * Return a minute selector.
    343     *
    344     * @param name The name to use for the selected minute.
    345     * @param now Calendar to start with.
    346     * @return A select object with minute options.
    347     */
    348    public static Select getMinuteSelector(String name, Calendar now)
    349    {
    350        return (getMinuteSelector(name, now, DEFAULT_MINUTE_INTERVAL));
    351    }
    352
    353    /**
    354     * Return a minute selector.
    355     *
    356     * @param name The name to use for the selected minute.
    357     * @param now Calendar to start with.
    358     * @param interval Interval between options.
    359     * @return A select object with minute options.
    360     */
    361    public static Select getMinuteSelector(String name, Calendar now,
    362                                           int interval)
    363    {
    364        Select minuteSelect = new Select().setName(name);
    365
    366        for (int curMinute = 0; curMinute <= 59; curMinute += interval)
    367        {
    368            Option o = new Option();
    369            o.addElement(nbrFmt.format(curMinute));
    370            o.setValue(curMinute);
    371            int nearestMinute =
    372                    ((now.get(Calendar.MINUTE)) / interval) * interval;
    373
    374            if (nearestMinute == curMinute)
    375            {
    376                o.setSelected(true);
    377            }
    378            minuteSelect.addElement(o);
    379        }
    380        return (minuteSelect);
    381    }
    382
    383    /**
    384     * Return an 12 hour selector.
    385     *
    386     * @param name The name to use for the selected hour.
    387     * @return A select object with all the hours.
    388     */
    389    public static Select getHourSelector(String name)
    390    {
    391        return (getHourSelector(name, Calendar.getInstance()));
    392    }
    393
    394    /**
    395     * Return an 12 hour selector.
    396     *
    397     * @param name The name to use for the selected hour.
    398     * @param now Calendar to start with.
    399     * @return A select object with all the hours.
    400     */
    401    public static Select getHourSelector(String name, Calendar now)
    402    {
    403        return (getHourSelector(name, Calendar.getInstance(), TWELVE_HOUR));
    404    }
    405
    406    /**
    407     * Return an hour selector (either 12hr or 24hr depending on
    408     * <code>format</code>.
    409     *
    410     * @param name The name to use for the selected hour.
    411     * @param now Calendar to start with.
    412     * @param format Time format.
    413     * @return A select object with all the hours.
    414     */
    415    public static Select getHourSelector(String name, Calendar now, int format)
    416    {
    417        Select hourSelect = new Select().setName(name);
    418
    419        if (format == TWENTY_FOUR_HOUR)
    420        {
    421            for (int currentHour = 0; currentHour <= 23; currentHour++)
    422            {
    423                Option o = new Option();
    424                o.addElement(nbrFmt.format(currentHour));
    425                o.setValue(currentHour);
    426                if (now.get(Calendar.HOUR_OF_DAY) == currentHour)
    427                {
    428                    o.setSelected(true);
    429                }
    430                hourSelect.addElement(o);
    431            }
    432        }
    433        else
    434        {
    435            for (int curHour = 1; curHour <= 12; curHour++)
    436            {
    437                Option o = new Option();
    438
    439                o.addElement(nbrFmt.format(curHour));
    440                o.setValue(curHour);
    441                if (now.get(Calendar.AM_PM) == Calendar.AM)
    442                {
    443                    if (((now.get(Calendar.HOUR_OF_DAY)) == 0) &&
    444                            (curHour == 12))
    445                    {
    446                        o.setSelected(true);
    447                    }
    448                    else
    449                    {
    450                        if (now.get(Calendar.HOUR_OF_DAY) == curHour)
    451                        {
    452                            o.setSelected(true);
    453                        }
    454                    }
    455                }
    456                else
    457                {
    458                    if (((now.get(Calendar.HOUR_OF_DAY)) == 12) &&
    459                            (curHour == 12))
    460                    {
    461                        o.setSelected(true);
    462                    }
    463                    else
    464                    {
    465                        if (now.get(Calendar.HOUR_OF_DAY) == curHour + 12)
    466                        {
    467                            o.setSelected(true);
    468                        }
    469                    }
    470                }
    471                hourSelect.addElement(o);
    472            }
    473        }
    474        return (hourSelect);
    475    }
    476
    477    /**
    478     * Return an am/pm selector.
    479     *
    480     * @param name The name to use for the selected am/pm.
    481     * @return A select object with am/pm
    482     */
    483    public static Select getAMPMSelector(String name)
    484    {
    485        Calendar c = Calendar.getInstance();
    486        c.setTime(new Date());
    487        return (getAMPMSelector(name, c));
    488    }
    489
    490    /**
    491     * Return an am/pm selector.
    492     *
    493     * @param name The name to use for the selected am/pm.
    494     * @param now Calendar to start with.
    495     * @return A select object with am/pm.
    496     */
    497    public static Select getAMPMSelector(String name,
    498                                         Calendar now)
    499    {
    500        Select ampmSelect = new Select().setName(name);
    501
    502        Option o = new Option();
    503        o.addElement("am");
    504        o.setValue(Calendar.AM);
    505        if (now.get(Calendar.AM_PM) == Calendar.AM)
    506        {
    507            o.setSelected(true);
    508        }
    509        ampmSelect.addElement(o);
    510
    511        o = new Option();
    512        o.addElement("pm");
    513        o.setValue(Calendar.PM);
    514        if (now.get(Calendar.AM_PM) == Calendar.PM)
    515        {
    516            o.setSelected(true);
    517        }
    518        ampmSelect.addElement(o);
    519
    520        return (ampmSelect);
    521    }
    522
    523    /**
    524     * Used to build the popupmenu in HTML.  The properties set in the
    525     * object are used to generate the correct HTML.  The selName
    526     * attribute is used to seed the names of the select lists.  The
    527     * names will be generated as follows:
    528     *
    529     * <ul>
    530     *  <li>selName + "_hour"</li>
    531     *  <li>selName + "_minute"</li>
    532     *  <li>selName + "_ampm"</li>
    533     * </ul>
    534     *
    535     * If onChange was set it is also used in the generation of the
    536     * output.  The output HTML will list the select lists in the
    537     * following order: hour minute ampm.
    538     *
    539     * If setShowSeconds(true) is used then an addition second select box
    540     * is produced after the minute select box.
    541     *
    542     * If setTimeFormat(TimeSelector.TWENTY_FOUR_HOUR) is used then
    543     * the ampm select box is omitted.
    544     *
    545     * @return A String with the correct HTML for the date selector.
    546     */
    547    public String output()
    548    {
    549        return (ecsOutput().toString());
    550    }
    551
    552    /**
    553     * Used to build the popupmenu in HTML.  The properties set in the
    554     * object are used to generate the correct HTML.  The selName
    555     * attribute is used to seed the names of the select lists.  The
    556     * names will be generated as follows:
    557     *
    558     * <ul>
    559     *  <li>selName + "_hour"</li>
    560     *  <li>selName + "_minute"</li>
    561     *  <li>selName + "_ampm"</li>
    562     * </ul>
    563     *
    564     * If onChange was set it is also used in the generation of the
    565     * output.  The output HTML will list the select lists in the
    566     * following order: hour minute ampm.
    567     *
    568     * If setShowSeconds(true) is used then an addition second select box
    569     * is produced after the minute select box.
    570     *
    571     * If setTimeFormat(TimeSelector.TWENTY_FOUR_HOUR) is used then
    572     * the ampm select box is omitted.
    573     *
    574     * @return A String with the correct HTML for the date selector.
    575     */
    576    @Override
    577    public String toString()
    578    {
    579        return (ecsOutput().toString());
    580    }
    581
    582    /**
    583     * Return an ECS container with the select objects inside.
    584     *
    585     * @return An ECS container.
    586     */
    587    public ElementContainer ecsOutput()
    588    {
    589        if (this.useDate == null)
    590        {
    591            this.useDate = Calendar.getInstance();
    592            this.useDate.setTime(new Date());
    593        }
    594
    595        ConcreteElement secondSelect = null;
    596
    597        Select ampmSelect = getAMPMSelector(selName + AMPM_SUFFIX, useDate);
    598
    599        Select hourSelect = getHourSelector(selName + HOUR_SUFFIX,
    600                useDate, this.timeFormat);
    601
    602        Select minuteSelect = getMinuteSelector(selName + MINUTE_SUFFIX,
    603                useDate, this.minuteInterval);
    604
    605        if (this.showSeconds)
    606        {
    607            Select tmp = getSecondSelector(selName + SECOND_SUFFIX, useDate,
    608                    this.secondInterval);
    609            if (onChangeSet)
    610            {
    611                tmp.setOnChange(onChange);
    612            }
    613            secondSelect = tmp;
    614        }
    615        else
    616        {
    617            secondSelect = new Input(Input.hidden,
    618                    selName + SECOND_SUFFIX,
    619                    setSeconds);
    620        }
    621
    622        if (onChangeSet)
    623        {
    624            hourSelect.setOnChange(onChange);
    625            minuteSelect.setOnChange(onChange);
    626            ampmSelect.setOnChange(onChange);
    627        }
    628
    629        ElementContainer ec = new ElementContainer();
    630        ec.addElement(new Comment(
    631                "== BEGIN org.apache.turbine.util.TimeSelector.ecsOutput() =="));
    632        ec.addElement(hourSelect);
    633        ec.addElement(":");
    634        ec.addElement(minuteSelect);
    635        if (this.showSeconds == true)
    636        {
    637            ec.addElement(":");
    638        }
    639        ec.addElement(secondSelect);
    640        if (this.timeFormat == TimeSelector.TWELVE_HOUR)
    641        {
    642            ec.addElement(ampmSelect);
    643        }
    644        ec.addElement(new Comment(
    645                "== END org.apache.turbine.util.TimeSelector.ecsOutput() =="));
    646        return (ec);
    647    }
    648}