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
006package fc.web.forms;
007
008import java.lang.reflect.*;
009import java.io.*;
010import java.text.*;
011import java.util.*;
012
013import fc.jdbc.*;
014import fc.io.*;
015import fc.util.*;
016import fc.web.forms.*;
017//for testing
018import fc.jdbc.dbo.generated.*;
019
020/**
021Misc. form related utilities.
022
023@author hursh jain
024*/
025public class FormUtil
026{
027private static final Class[]  classarr = new Class[] { };
028private static final Object[] objarr = new Object[] { };
029
030/**
031<b>Note: This method is almost never needed. Use the {@link Select#useQuery(Connection, String)}
032method instead.
033</b>
034<p>
035Fills in the specified select with values from the supplied list. This is
036intended to show a select widget corresponding to a lookup table in the
037database. The list will typically be returned by <tt>getAll/getWhere</tt>
038methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
039<p>
040For example, given a lookup table object <tt>Foo</tt> and a corresponding
041manager object <tt>FooMgr</tt> and an empty previously-instantiated
042select:
043<tt>
044<blockquote> 
045<pre>
046Select select = new Select("myselect")
047fillSelect(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>
054and 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>
066will 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*/
106public 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)}
135method instead.
136</b>
137<p>
138Fills in the specified select with values from the supplied map. This is
139intended to show a select widget corresponding to a lookup table in the
140database. The list will typically be returned by <tt>getAll/getWhere</tt>
141methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
142<p>
143For example, given a lookup table object <tt>Foo</tt> and a corresponding
144manager object <tt>FooMgr</tt> and an empty previously-instantiated
145select:
146<tt>
147<blockquote>
148<pre>
149Select select = new Select("myselect")
150fillSelect(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>
157and 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>
169will 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*/
194public 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/** 
213Fills in the specified radiogroup with values from the supplied list. This
214is intended to show radio groups corresponding to a lookup table in the
215database. The list will typically be returned by <tt>getAll/getWhere</tt>
216methods of some generated {@link fc.jdbc.dbo.DBOMgr} class. <p> For
217example, given a lookup table object <tt>Foo</tt> and a corresponding
218manager object <tt>FooMgr</tt> and a empty previously-instantiated radio
219group:
220<tt>
221<blockquote>
222<pre>
223RadioGroup rg = new RadioGroup("myradio")
224fillRadioGroup(select, FooMgr.getAll(), 
225     Foo.class, <font color=red>"getID"</font>, 
226     <font color=red>"getValue"</font>);
227</pre>
228</blockquote>
229</tt>
230and 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>
242will 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*/
266public 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/** 
290Fills in the specified checkboxgroup with values from the supplied list.
291This is intended to show checkbox groups corresponding to a lookup table
292in the database. The list will typically be returned by <tt>getAll or
293getWhere</tt> methods of some generated {@link fc.jdbc.dbo.DBOMgr} class.
294<p>
295For example, given a lookup table object <tt>Foo</tt> and a corresponding
296manager object <tt>FooMgr</tt> and a empty previously-instantiated
297checkbox group:
298<tt>
299<blockquote>
300<pre>
301CheckboxGroup rg = new CheckboxGroup("mycbgroup")
302fillCheckboxGroup(select, FooMgr.getAll(), 
303     Foo.class, <font color=red>"getID"</font>, 
304     <font color=red>"getValue"</font>);
305</pre>
306</blockquote>
307</tt>
308and 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>
320will 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*/
344public 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/**
369Fills the specified select from years, starting with the specified year
370till the current year. No year option is pre-selected.
371*/
372public 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/**
384Fills the specified select from years, starting with the specified year.
385The current year (on the server) is pre-selected.
386*/
387public static Select fillSelectWithYearsToday(Select s, int startYear)
388  {
389  return fillSelectWithYears(s, startYear, Calendar.getInstance());
390  }
391
392/**
393Fills the specified select from years, starting with the specified year,
394upto the current year. The date from the specified calendar is
395pre-selected.
396*/
397public 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/**
413Fills the specified select from years, from the specified start and end
414years (both inclusive). The date from the specified calendar is
415pre-selected.
416*/
417public 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
431private 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/**
440Fills the specified select from years, starting with the specified year.
441<tt>monthsAsText</tt> specifies whether months are displayed as names
442or numbers. (true for names). No month is preselected.
443*/
444public 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/**
461Fills the specified select from years, starting with the specified year.
462<tt>monthsAsText</tt> specifies whether months are displayed as names or
463numbers. (true for names). The current month (on the server) is preselected.
464*/
465public static Select fillSelectWithMonthsToday(Select s, boolean monthsAsText)
466  {
467  return fillSelectWithMonths(s, monthsAsText, Calendar.getInstance());
468  }
469
470/**
471Fills the specified select from years, starting with the specified year.
472<tt>monthsAsText</tt> specifies whether months are displayed as names or
473numbers. (true for names). The current month from the specified calendar is
474is preselected.
475*/
476public 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/**
499Fills the specified select from years, starting with the specified year.
500No day is pre-selected.
501*/
502public 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/**
513Fills the specified select from years, starting with the specified year.
514Today (on the server side) is preselected.
515*/
516public static Select fillSelectWithDaysToday(Select s)
517  {
518  return fillSelectWithDays(s, Calendar.getInstance());
519  }
520
521/**
522Fills the specified select from years, starting with the specified year.
523The day in the specified date is preselected.
524*/
525public 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/**
538Converts a year, month and day into a java.sql.Date
539*/
540public 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/**
552Converts 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
554string could not be parsed. (Hours upto 12 are allowed but not 13 or
555more). This method is suitable for a text box that takes 1-12 hours 
556and a seperate am/pm select option next to it.
557*/
558public 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
573public 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