001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.web.forms;
007    
008    import java.lang.reflect.*;
009    import java.io.*;
010    import java.text.*;
011    import java.util.*;
012    
013    import fc.jdbc.*;
014    import fc.io.*;
015    import fc.util.*;
016    import fc.web.forms.*;
017    //for testing
018    import fc.jdbc.dbo.generated.*;
019    
020    /**
021    Misc. form related utilities.
022    
023    @author hursh jain
024    */
025    public class FormUtil
026    {
027    private static final Class[]  classarr = new Class[] { };
028    private static final Object[] objarr = new Object[] { };
029    
030    /**
031    <b>Note: This method is almost never needed. Use the {@link Select#useQuery(Connection, String)}
032    method instead.
033    </b>
034    <p>
035    Fills in the specified select with values from the supplied list. This is
036    intended to show a select widget corresponding to a lookup table in the
037    database. The list will typically be returned by <tt>getAll/getWhere</tt>
038    methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
039    <p>
040    For example, given a lookup table object <tt>Foo</tt> and a corresponding
041    manager object <tt>FooMgr</tt> and an empty previously-instantiated
042    select:
043    <tt>
044    <blockquote> 
045    <pre>
046    Select select = new Select("myselect")
047    fillSelect(select, FooMgr.getAll(), 
048      "--choose--", 
049         Foo.class, <font color=red>"getID"</font>, 
050         <font color=red>"getValue"</font>);
051    </pre>
052    </blockquote>
053    </tt>
054    and where table Foo has the following data
055    <blockquote>
056    <tt><pre>
057     Table Foo
058     <font color=red>
059      ID      Value</font>
060     -----------------------------------
061      1     "lookup_one"
062      2     "lookup_two"
063      3     "lookup_three"
064    </pre></tt>
065    </blockquote>
066    will add the following values to the select:
067    <blockquote>
068    <tt>
069    <pre>
070    &lt;option&gt;--choose--&lt;/option&gt;
071    &lt;option value=1&gt;lookup_one&lt;/option&gt;
072    &lt;option value=2&gt;lookup_two&lt;/option&gt;
073    &lt;option value=3&gt;lookup_three&lt;/option&gt;
074    </pre>
075    </tt>
076    </blockquote>
077    <p>
078    
079    @param  select
080      a select object to be filled in
081    @param  list        
082      list of objects of type beanClass. Typically
083      this would be obtained via invoking the
084      <tt>beanClassMgr.getAll()</tt> method
085    @param  message
086      an optional message to show as the first value of
087      the select option (typically <tt>---select--</tt>
088      or <tt>--choose an option--</tt> etc.). Specify
089      <tt>null</tt> to skip creating this optional message.
090    @param  beanClass     
091      the {@link DBO} class corresponding to some lookuptable in the
092      database
093    @param  valueMethodName   
094      the name of the method in the {@link DBO} class which will be used to
095      create the value for a radio button [ without "()"]
096    @param  htmlTextMethodName  
097      the name of the method in the {@link DBO} class which will be used to
098      create the html text displayed to the user for a radio button [name
099      should be without "()"]
100    
101    @throws IllegalArgumentException 
102      if an error occurred in getting the specified methods
103      from the specified class and invoking them on the
104      specified list
105    */
106    public static void fillSelect(
107        Select select, List list, String message, 
108        Class beanClass, 
109        String valueMethodName, String htmlTextMethodName)  
110      {
111      if (message != null)
112        select.add(new Select.Option(message));
113      
114      try {
115        Method value_method = beanClass.getDeclaredMethod(
116                     valueMethodName, classarr);
117        Method html_method =  beanClass.getDeclaredMethod(
118                     htmlTextMethodName, classarr);
119      
120        for (int n = 0; n < list.size(); n++)
121          {
122          Object obj = list.get(n);
123          String value = String.valueOf(value_method.invoke(obj, objarr));
124          String html  = String.valueOf(html_method.invoke(obj, objarr));
125          select.add(new Select.Option(html, value)); 
126          }
127        }
128      catch (Exception e) {
129        throw new IllegalArgumentException(IOUtil.throwableToString(e));
130        }   
131      }
132    
133    /**
134    <b>Note: This method is almost never needed. Use the {@link Select#useQuery(Connection, String)}
135    method instead.
136    </b>
137    <p>
138    Fills in the specified select with values from the supplied map. This is
139    intended to show a select widget corresponding to a lookup table in the
140    database. The list will typically be returned by <tt>getAll/getWhere</tt>
141    methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
142    <p>
143    For example, given a lookup table object <tt>Foo</tt> and a corresponding
144    manager object <tt>FooMgr</tt> and an empty previously-instantiated
145    select:
146    <tt>
147    <blockquote>
148    <pre>
149    Select select = new Select("myselect")
150    fillSelect(select, FooMgr.getAll(), 
151      "--choose--", 
152         Foo.class, <font color=red>"getID"</font>, 
153         <font color=red>"getValue"</font>);
154    </pre>
155    </blockquote>
156    </tt>
157    and where table Foo has the following data
158    <blockquote>
159    <tt><pre>
160     Table Foo
161     <font color=red>
162      ID      Value</font>
163     -----------------------------------
164      1     "lookup_one"
165      2     "lookup_two"
166      3     "lookup_three"
167    </pre></tt>
168    </blockquote>
169    will add the following values to the select:
170    <blockquote>
171    <tt>
172    <pre>
173    &lt;option&gt;--choose--&lt;/option&gt;
174    &lt;option value=1&gt;lookup_one&lt;/option&gt;
175    &lt;option value=2&gt;lookup_two&lt;/option&gt;
176    &lt;option value=3&gt;lookup_three&lt;/option&gt;
177    </pre>
178    </tt>
179    </blockquote>
180    <p>
181    
182    @param  select
183      a select object to be filled in
184    @param  values
185      A map containing the values for the select. For each key, value pair,
186      the following will be generated for the select:
187      <pre><option value="key-part">value-part</option>
188    @param  message
189      an optional message to show as the first value of the select option
190      (typically <tt>---select--</tt> or <tt>--choose an option--</tt> etc.).
191      Specify <tt>null</tt> to skip creating this optional message.
192    
193    */
194    public static void fillSelect(Select select, Map values, String message)
195      {
196      if (message != null)
197        select.add(new Select.Option(message));
198      
199      try {
200        Iterator it = values.entrySet().iterator(); 
201        while (it.hasNext()) {
202          Map.Entry e = (Map.Entry)it.next();
203          select.add(new Select.Option((String)e.getKey(), (String)e.getValue())); 
204          }
205        }
206      catch (Exception e) {
207        throw new IllegalArgumentException(IOUtil.throwableToString(e));
208        }   
209      }
210    
211    
212    /** 
213    Fills in the specified radiogroup with values from the supplied list. This
214    is intended to show radio groups corresponding to a lookup table in the
215    database. The list will typically be returned by <tt>getAll/getWhere</tt>
216    methods of some generated {@link fc.jdbc.dbo.DBOMgr} class. <p> For
217    example, given a lookup table object <tt>Foo</tt> and a corresponding
218    manager object <tt>FooMgr</tt> and a empty previously-instantiated radio
219    group:
220    <tt>
221    <blockquote>
222    <pre>
223    RadioGroup rg = new RadioGroup("myradio")
224    fillRadioGroup(select, FooMgr.getAll(), 
225         Foo.class, <font color=red>"getID"</font>, 
226         <font color=red>"getValue"</font>);
227    </pre>
228    </blockquote>
229    </tt>
230    and where table Foo has the following data:
231    <blockquote>
232    <tt><pre>
233     Table Foo
234     <font color=red>
235      ID      Value</font>
236     -----------------------------------
237      1     "lookup_one"
238      2     "lookup_two"
239      3     "lookup_three"
240    </pre></tt>
241    </blockquote>
242    will add those values to the radio group.
243    
244    @param  rg
245      a radio group object to be filled in
246    @param  list        
247      list of objects of type beanClass. Typically
248      this would be obtained via invoking the
249      <tt>beanClassMgr.getAll()</tt> method
250    @param  beanClass     
251      the {@link DBO} class corresponding to some lookuptable in the
252      database
253    @param  valueMethodName   
254      the name of the method in the {@link DBO} class which will be used to
255      create the value for a radio button [ without "()"]
256    @param  htmlTextMethodName  
257      the name of the method in the {@link DBO} class which will be used to
258      create the html text displayed to the user for a radio button [name
259      should be without "()"]
260    
261    @throws IllegalArgumentException 
262      if an error occurred in getting the specified methods
263      from the specified class and invoking them on the
264      specified list
265    */
266    public static void fillRadioGroup(
267      RadioGroup rg, List list, Class beanClass,
268      String valueMethodName, String htmlTextMethodName)  
269      {
270      try {
271        Method value_method = beanClass.getDeclaredMethod(
272                  valueMethodName, classarr);
273        Method html_method = beanClass.getDeclaredMethod(
274                  htmlTextMethodName, classarr);
275        for (int n = 0; n < list.size(); n++)
276          {
277          Object obj = list.get(n);
278          String value = String.valueOf(value_method.invoke(obj, objarr));
279          String html  = String.valueOf(html_method.invoke(obj, objarr));
280          ChoiceGroup.Choice c = new ChoiceGroup.Choice(html, value); //auto adds it to rg
281          rg.add(c);
282          }
283        }
284      catch (Exception e) {
285        throw new IllegalArgumentException(IOUtil.throwableToString(e));
286        }   
287      }
288    
289    /** 
290    Fills in the specified checkboxgroup with values from the supplied list.
291    This is intended to show checkbox groups corresponding to a lookup table
292    in the database. The list will typically be returned by <tt>getAll or
293    getWhere</tt> methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
294    <p>
295    For example, given a lookup table object <tt>Foo</tt> and a corresponding
296    manager object <tt>FooMgr</tt> and a empty previously-instantiated
297    checkbox group:
298    <tt>
299    <blockquote>
300    <pre>
301    CheckboxGroup rg = new CheckboxGroup("mycbgroup")
302    fillCheckboxGroup(select, FooMgr.getAll(), 
303         Foo.class, <font color=red>"getID"</font>, 
304         <font color=red>"getValue"</font>);
305    </pre>
306    </blockquote>
307    </tt>
308    and where table Foo has the following data:
309    <blockquote>
310    <tt><pre>
311     Table Foo
312     <font color=red>
313      ID      Value</font>
314     -----------------------------------
315      1     "lookup_one"
316      2     "lookup_two"
317      3     "lookup_three"
318    </pre></tt>
319    </blockquote>
320    will add those values to the checkbox group.
321    
322    @param  cbg
323      a checkbox group object to be filled in
324    @param  list        
325      list of objects of type beanClass. Typically
326      this would be obtained via invoking the
327      <tt>beanClassMgr.getAll()</tt> method
328    @param  beanClass     
329      the {@link DBO} class corresponding to some lookuptable in the
330      database
331    @param  valueMethodName   
332      the name of the method in the {@link DBO} class which will be used to
333      create the value for a radio button [ without "()"]
334    @param  htmlTextMethodName  
335      the name of the method in the {@link DBO} class which will be used to
336      create the html text displayed to the user for a radio button [name
337      should be without "()"]
338    
339    @throws IllegalArgumentException 
340      if an error occurred in getting the specified methods
341      from the specified class and invoking them on the
342      specified list
343    */
344    public static void fillCheckboxGroup(
345      CheckboxGroup cbg, List list, Class beanClass,
346      String valueMethodName, String htmlTextMethodName)  
347      {
348      try {
349        Method value_method = beanClass.getDeclaredMethod(
350                  valueMethodName, classarr);
351        Method html_method = beanClass.getDeclaredMethod(
352                  htmlTextMethodName, classarr);
353        for (int n = 0; n < list.size(); n++)
354          {
355          Object obj = list.get(n);
356          String value = String.valueOf(value_method.invoke(obj, objarr));
357          String html  = String.valueOf(html_method.invoke(obj, objarr));
358          ChoiceGroup.Choice c = new ChoiceGroup.Choice(html, value); //auto adds it to rg
359          cbg.add(c);
360        }
361        }
362      catch (Exception e) {
363        throw new IllegalArgumentException(IOUtil.throwableToString(e));
364        }   
365      }
366    
367    
368    /**
369    Fills the specified select from years, starting with the specified year
370    till the current year. No year option is pre-selected.
371    */
372    public static Select fillSelectWithYears(Select s, int startYear)
373      { 
374      int now = Calendar.getInstance().get(Calendar.YEAR);
375      for (int n = startYear; n <= now; n++)
376        {
377        Select.Option so = new Select.Option(n + "");
378        s.add(so);
379        }
380      return s;
381      }
382    
383    /**
384    Fills the specified select from years, starting with the specified year.
385    The current year (on the server) is pre-selected.
386    */
387    public static Select fillSelectWithYearsToday(Select s, int startYear)
388      {
389      return fillSelectWithYears(s, startYear, Calendar.getInstance());
390      }
391    
392    /**
393    Fills the specified select from years, starting with the specified year,
394    upto the current year. The date from the specified calendar is
395    pre-selected.
396    */
397    public static Select fillSelectWithYears(
398      Select s, int startYear, Calendar yearToSelect)
399      {
400      int now = Calendar.getInstance().get(Calendar.YEAR);
401      int select = yearToSelect.get(Calendar.YEAR);
402      
403      for (int n = startYear; n <= now; n++)
404        {
405        Select.Option so = (n == select) ? new Select.Option(n + "", true) :
406                        new Select.Option(n + "");
407        s.add(so);
408        }
409      return s;
410      }
411    
412    /**
413    Fills the specified select from years, from the specified start and end
414    years (both inclusive). The date from the specified calendar is
415    pre-selected.
416    */
417    public static Select fillSelectWithYears(
418     Select s, int startYear, int endYear, Calendar cal)
419      {
420      int select = cal.get(Calendar.YEAR);
421      
422      for (int n = startYear; n <= endYear; n++)
423        {
424        Select.Option so = (n == select) ? new Select.Option(n + "", true) :
425                        new Select.Option(n + "");
426        s.add(so);
427        }
428      return s;
429      }
430    
431    private static String[] months = new String[]
432      {
433      /*0-th is empty -->*/ "",
434      "Jan", "Feb", "March", "Apr", "May", "June", "July",
435      "Aug", "Sept", "Oct", "Nov", "Dec"  
436      };
437    
438    //TODO: i18n
439    /**
440    Fills the specified select from years, starting with the specified year.
441    <tt>monthsAsText</tt> specifies whether months are displayed as names
442    or numbers. (true for names). No month is preselected.
443    */
444    public static Select fillSelectWithMonths(Select s, boolean monthsAsText)
445      {
446      String month = null;
447      for (int n = 1; n <= 12; n++)
448        {
449        if (monthsAsText)
450          month = months[n];
451        else
452          month = n + "";
453          
454        Select.Option so = new Select.Option(n+"", month);
455        s.add(so);
456        }
457      return s;
458      }
459    
460    /**
461    Fills the specified select from years, starting with the specified year.
462    <tt>monthsAsText</tt> specifies whether months are displayed as names or
463    numbers. (true for names). The current month (on the server) is preselected.
464    */
465    public static Select fillSelectWithMonthsToday(Select s, boolean monthsAsText)
466      {
467      return fillSelectWithMonths(s, monthsAsText, Calendar.getInstance());
468      }
469    
470    /**
471    Fills the specified select from years, starting with the specified year.
472    <tt>monthsAsText</tt> specifies whether months are displayed as names or
473    numbers. (true for names). The current month from the specified calendar is
474    is preselected.
475    */
476    public static Select fillSelectWithMonths(
477        Select s, boolean monthsAsText, Calendar cal)
478      {
479      String month = null;
480      int current_month = cal.get(Calendar.MONTH)/*0-based*/ + 1;
481    
482      for (int n = 1; n <= 12; n++)
483        {
484        if (monthsAsText)
485          month = months[n];
486        else
487          month = n + "";
488          
489        Select.Option so = (n == current_month) ? 
490                    new Select.Option(n+"", month, true) 
491                    : new Select.Option(n+"", month);
492        s.add(so);
493        }
494      return s;
495      }
496    
497    
498    /**
499    Fills the specified select from years, starting with the specified year.
500    No day is pre-selected.
501    */
502    public static Select fillSelectWithDays(Select s)
503      {
504      for (int n = 1; n <= 31; n++)
505        {
506        Select.Option so = new Select.Option(n + "");
507        s.add(so);
508        }
509      return s;
510      }
511    
512    /**
513    Fills the specified select from years, starting with the specified year.
514    Today (on the server side) is preselected.
515    */
516    public static Select fillSelectWithDaysToday(Select s)
517      {
518      return fillSelectWithDays(s, Calendar.getInstance());
519      }
520    
521    /**
522    Fills the specified select from years, starting with the specified year.
523    The day in the specified date is preselected.
524    */
525    public static Select fillSelectWithDays(Select s, Calendar cal)
526      {
527      int today = cal.get(Calendar.DATE); 
528      for (int n = 1; n <= 31; n++)
529        {
530        Select.Option so = (n == today) ? new Select.Option(n + "", true)
531                        : new Select.Option(n + "");
532        s.add(so);  
533        }
534      return s;
535      }
536    
537    /**
538    Converts a year, month and day into a java.sql.Date
539    */
540    public static java.sql.Date toDate(String year, String month, String day)
541      {
542      
543      java.sql.Date date = new java.sql.Date(
544                  new java.util.Date(
545                    Integer.parseInt(year)-1900, 
546                    Integer.parseInt(month)-1, 
547                    Integer.parseInt(day)).getTime());
548      return date;
549      }
550    
551    /**
552    Converts a time string into a java.sql.Time. Time is of the format: HH:MM
553    (for example: <tt>1:23</tt> or <tt>01:23</tt>). Returns null if the
554    string could not be parsed. (Hours upto 12 are allowed but not 13 or
555    more). This method is suitable for a text box that takes 1-12 hours 
556    and a seperate am/pm select option next to it.
557    */
558    public static java.sql.Time toTime(final String time)
559      { 
560      java.util.Date date = null;
561      final DateFormat df = new SimpleDateFormat("h:mm");
562      try {
563        date = df.parse(time);
564        }
565      catch (Exception e) {
566        return null;
567        }
568      
569      return new java.sql.Time(date.getTime());
570      }
571    
572    
573    public static void main(String args[])  throws Exception
574      {
575      PrintWriter pw = new PrintWriter(System.out);
576      Select select = new Select("foo");
577    
578      Args myargs = new Args(args);
579      myargs.setUsage("java fc.web.form.FormUtil -conf conf-file");
580      
581      String propfile = myargs.getRequired("conf");
582      
583      ConnectionMgr mgr = new SimpleConnectionMgr(  
584                    new FilePropertyMgr(
585                      new File(propfile)));
586      java.sql.Connection con = mgr.getConnection();
587    
588      List list = alltypesMgr.getAll(con);
589    
590      long start = System.currentTimeMillis();
591      fillSelect(select, list, "--choose--", alltypes.class, 
592          "get_id", "get_char_val"); 
593      System.out.println("time for first fillSelect() calls = " + (System.currentTimeMillis() - start) + " ms");
594      select.render(pw);
595      fillSelect(select, list, "--choose--", alltypes.class, 
596          "get_id", "get_date_val"); 
597      select.render(pw);
598      fillSelect(select, list, "--choose--", alltypes.class, 
599          "get_id", "get_int_val"); 
600      select.render(pw);
601      fillSelect(select, list, "--choose--", alltypes.class, 
602          "get_id", "get_boolean_val"); 
603      select.render(pw);
604    
605    
606      RadioGroup rg = new RadioGroup("rg");
607      start = System.currentTimeMillis();
608      fillRadioGroup(rg, list, alltypes.class, 
609          "get_id", "get_char_val"); 
610      System.out.println("time for first fillRadioGroup() calls = " + (System.currentTimeMillis() - start) + " ms");
611      rg.render(pw);
612      fillRadioGroup(rg, list, alltypes.class, 
613          "get_id", "get_date_val"); 
614      rg.render(pw);
615      fillRadioGroup(rg, list, alltypes.class, 
616          "get_id", "get_int_val"); 
617      rg.render(pw);
618      fillRadioGroup(rg, list, alltypes.class, 
619          "get_id", "get_boolean_val"); 
620      rg.render(pw);
621    
622      Date d = new Date();
623      System.out.println(d.getYear() + " " + d.getMonth() + " " + d.getDay());
624    
625      select = new Select("years");
626      fillSelectWithYears(select, 1995);
627      select.render(pw);
628    
629      fillSelectWithYears(select, 1995);
630      select.render(pw);
631    
632      select = new Select("months");
633      fillSelectWithMonths(select, true);
634      select.render(pw);
635    
636      select = new Select("days");
637      fillSelectWithDays(select);
638      select.render(pw);
639    
640      pw.close();
641    //  con.close();
642      }
643    
644    } //~class