001    package 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    
022    import java.text.NumberFormat;
023    import java.util.Calendar;
024    import java.util.Date;
025    
026    import org.apache.ecs.ConcreteElement;
027    import org.apache.ecs.ElementContainer;
028    import org.apache.ecs.html.Comment;
029    import org.apache.ecs.html.Input;
030    import org.apache.ecs.html.Option;
031    import 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 1078552 2011-03-06 19:58:46Z tv $
072     */
073    public 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         * @return A select object with minute options.
346         */
347        public static Select getMinuteSelector(String name, Calendar now)
348        {
349            return (getMinuteSelector(name, now, DEFAULT_MINUTE_INTERVAL));
350        }
351    
352        /**
353         * Return a minute selector.
354         *
355         * @param name The name to use for the selected minute.
356         * @param now Calendar to start with.
357         * @param interval Interval between options.
358         * @return A select object with minute options.
359         */
360        public static Select getMinuteSelector(String name, Calendar now,
361                                               int interval)
362        {
363            Select minuteSelect = new Select().setName(name);
364    
365            for (int curMinute = 0; curMinute <= 59; curMinute += interval)
366            {
367                Option o = new Option();
368                o.addElement(nbrFmt.format(curMinute));
369                o.setValue(curMinute);
370                int nearestMinute =
371                        ((now.get(Calendar.MINUTE)) / interval) * interval;
372    
373                if (nearestMinute == curMinute)
374                {
375                    o.setSelected(true);
376                }
377                minuteSelect.addElement(o);
378            }
379            return (minuteSelect);
380        }
381    
382        /**
383         * Return an 12 hour selector.
384         *
385         * @param name The name to use for the selected hour.
386         * @return A select object with all the hours.
387         */
388        public static Select getHourSelector(String name)
389        {
390            return (getHourSelector(name, Calendar.getInstance()));
391        }
392    
393        /**
394         * Return an 12 hour selector.
395         *
396         * @param name The name to use for the selected hour.
397         * @param now Calendar to start with.
398         * @return A select object with all the hours.
399         */
400        public static Select getHourSelector(String name, Calendar now)
401        {
402            return (getHourSelector(name, Calendar.getInstance(), TWELVE_HOUR));
403        }
404    
405        /**
406         * Return an hour selector (either 12hr or 24hr depending on
407         * <code>format</code>.
408         *
409         * @param name The name to use for the selected hour.
410         * @param now Calendar to start with.
411         * @param format Time format.
412         * @return A select object with all the hours.
413         */
414        @SuppressWarnings("cast")
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((long) 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                    tmp.setOnChange(onChange);
611                secondSelect = tmp;
612            }
613            else
614            {
615                secondSelect = new Input(Input.hidden,
616                        selName + SECOND_SUFFIX,
617                        setSeconds);
618            }
619    
620            if (onChangeSet)
621            {
622                hourSelect.setOnChange(onChange);
623                minuteSelect.setOnChange(onChange);
624                ampmSelect.setOnChange(onChange);
625            }
626    
627            ElementContainer ec = new ElementContainer();
628            ec.addElement(new Comment(
629                    "== BEGIN org.apache.turbine.util.TimeSelector.ecsOutput() =="));
630            ec.addElement(hourSelect);
631            ec.addElement(":");
632            ec.addElement(minuteSelect);
633            if (this.showSeconds == true)
634                ec.addElement(":");
635            ec.addElement(secondSelect);
636            if (this.timeFormat == TimeSelector.TWELVE_HOUR)
637            {
638                ec.addElement(ampmSelect);
639            }
640            ec.addElement(new Comment(
641                    "== END org.apache.turbine.util.TimeSelector.ecsOutput() =="));
642            return (ec);
643        }
644    }