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