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}