View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/taglib/DbFilterValueTag.java,v 1.39 2006/03/30 02:08:07 nicparise Exp $
3    * $Revision: 1.39 $
4    * $Date: 2006/03/30 02:08:07 $
5    *
6    * DbForms - a Rapid Application Development Framework
7    * Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public
11   * License as published by the Free Software Foundation; either
12   * version 2.1 of the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22   */
23  
24  package org.dbforms.taglib;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  import org.dbforms.config.Constants;
30  import org.dbforms.config.DbFormsConfigRegistry;
31  import org.dbforms.config.Field;
32  import org.dbforms.config.FieldValue;
33  import org.dbforms.config.FieldValues;
34  import org.dbforms.config.Table;
35  import org.dbforms.interfaces.IDataContainer;
36  import org.dbforms.interfaces.StaticData;
37  
38  import org.dbforms.util.MessageResources;
39  import org.dbforms.util.ParseUtil;
40  import org.dbforms.util.StringUtil;
41  import org.dbforms.util.Util;
42  
43  import java.text.SimpleDateFormat;
44  
45  import java.util.List;
46  
47  import javax.servlet.http.HttpServletRequest;
48  import javax.servlet.jsp.JspException;
49  import javax.servlet.jsp.PageContext;
50  
51  
52  
53  /***
54   * Map a placeholder (?) in sql code to an input tag. Used as nested tag inside
55   * filterCondition. Implements DataContainer interface to use the nested tags
56   * queryData, staticData ...
57   *
58   * @author Sergio Moretti
59   * @version $Revision: 1.39 $
60   */
61  public class DbFilterValueTag extends AbstractDbBaseHandlerTag implements IDataContainer,
62                                                                    javax.servlet.jsp.tagext.TryCatchFinally {
63     /*** DOCUMENT ME! */
64     private static String FLT_VALUETYPE_DATE = "date";
65  
66     /*** DOCUMENT ME! */
67     private static String FLT_VALUETYPE_NUMERIC = "numeric";
68  
69     /*** DOCUMENT ME! */
70     private static String FLT_VALUETYPE_SELECT = "select";
71  
72     /*** DOCUMENT ME! */
73     private static String FLT_VALUETYPE_TEXT = "text";
74  
75     /*** DOCUMENT ME! */
76     private static String FLT_VALUETYPE_TIMESTAMP = "timestamp";
77     private static Log    logCat = LogFactory.getLog(DbFilterValueTag.class
78                                                      .getName());
79  
80     /*** contain the state of this tag object */
81     private transient State state;
82  
83     /***
84                                                                                    *
85                                                                                    */
86     public DbFilterValueTag() {
87        super();
88        state = new State();
89     }
90  
91     /***
92      * Allows an additional (independant) entry into the select list
93      *
94      * @param string
95      */
96     public void setCustomEntry(String string) {
97        state.customEntry = string;
98     }
99  
100 
101    /***
102     * This method is a "hookup" for EmbeddedData - Tags which can assign the
103     * lines of data they loaded (by querying a database, or by rendering
104     * data-subelements, etc. etc.) and make the data available to this tag.
105     * [this method is defined in Interface DataContainer]
106     *
107     * @param embeddedData DOCUMENT ME!
108     */
109    public void setEmbeddedData(List embeddedData) {
110       state.embeddedData = embeddedData;
111    }
112 
113 
114    /***
115     * property jsCalendarDateFormat.
116     *
117     * @param string
118     */
119    public void setJsCalendarDateFormat(String string) {
120       state.jsCalendarDateFormat = string;
121    }
122 
123 
124    /***
125     * DOCUMENT ME!
126     *
127     * @return
128     */
129    public String getJsCalendarDateFormat() {
130       return state.jsCalendarDateFormat;
131    }
132 
133 
134    /***
135     * property label showed before input tag
136     *
137     * @param string
138     */
139    public void setLabel(String string) {
140       state.label = string;
141    }
142 
143 
144    /***
145     * DOCUMENT ME!
146     *
147     * @return
148     */
149    public String getLabel() {
150       return state.label;
151    }
152 
153 
154    /***
155     * DOCUMENT ME!
156     *
157     * @param value DOCUMENT ME!
158     */
159    public void setSearchAlgo(String value) {
160       state.searchAlgo = value;
161    }
162 
163 
164    /***
165     * DOCUMENT ME!
166     *
167     * @return DOCUMENT ME!
168     */
169    public String getSearchAlgo() {
170       return state.searchAlgo;
171    }
172 
173 
174    /***
175     * property currently selected index, valid only when type = select
176     *
177     * @param string
178     */
179    public void setSelectedIndex(String string) {
180       state.selectedIndex = string;
181    }
182 
183 
184    /***
185     * property html input's attribute size
186     *
187     * @param string
188     */
189    public void setSize(String string) {
190       state.size = string;
191    }
192 
193 
194    /***
195     * DOCUMENT ME!
196     *
197     * @param pg DOCUMENT ME!
198     * @param parent DOCUMENT ME!
199     * @param state
200     */
201    public void setState(PageContext          pg,
202                         DbFilterConditionTag parent,
203                         State                state) {
204       setPageContext(pg);
205       setParent(parent);
206       this.state = state;
207    }
208 
209 
210    /***
211     * css class to be applied to input element
212     *
213     * @param string
214     */
215    public void setStyleClass(String string) {
216       state.styleClass = string;
217    }
218 
219 
220    /***
221     * type of the input element that will be rendered, possible values are:
222     *
223     * <dl>
224     * <dt>
225     * text
226     * </dt>
227     * <dd>
228     * text input
229     * </dd>
230     * <dt>
231     * date
232     * </dt>
233     * <dd>
234     * input text for date type, a validation of the value will be done, and it
235     * supports the jscal object
236     * </dd>
237     * <dt>
238     * timestamp
239     * </dt>
240     * <dd>
241     * input text for timestamp type, a validation of the value will be done,
242     * and it supports the jscal object (it doesn't fit very well, anyway ...)
243     * </dd>
244     * <dt>
245     * numeric
246     * </dt>
247     * <dd>
248     * input text for number, a validation of the value will be done
249     * </dd>
250     * <dt>
251     * select
252     * </dt>
253     * <dd>
254     * render an html select element, filled with nested tags like queryData,
255     * staticData and so on.
256     * </dd>
257     * </dl>
258     *
259     *
260     * @param string
261     */
262    public void setType(String string) {
263       state.type = string;
264    }
265 
266 
267    /***
268     * DOCUMENT ME!
269     *
270     * @param string
271     */
272    public void setUseJsCalendar(String string) {
273       state.useJsCalendar = string;
274    }
275 
276 
277    /***
278     * DOCUMENT ME!
279     *
280     * @return
281     */
282    public String getUseJsCalendar() {
283       return state.useJsCalendar;
284    }
285 
286 
287    /***
288     * reset tag state
289     *
290     * @see javax.servlet.jsp.tagext.TryCatchFinally#doFinally()
291     */
292    public void doFinally() {
293       state = new State();
294       super.doFinally();
295    }
296 
297 
298    /***
299     * initialize environment
300     *
301     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
302     */
303    public int doStartTag() throws JspException {
304       init();
305 
306       return EVAL_BODY_BUFFERED;
307    }
308 
309 
310    /***
311     * read from request all values associated to the condition identified with
312     * &lt;tableId&gt;, &lt;conditionId&gt;. It try to read the value with
313     * identifier 0, if succeded go on with identifier 1, and so on.
314     *
315     * @param request
316     * @param tableId identify filter in request's parameters
317     * @param conditionId identify condition in request's parameter
318     *
319     * @return list of all values readed from request
320     */
321    protected static FieldValue[] readValuesFromRequest(HttpServletRequest request,
322                                                        int                tableId,
323                                                        int                conditionId) {
324       FieldValues values = new FieldValues();
325 
326       for (int valueId = 0; true; ++valueId) {
327          // read from parameter's request the value and the type having this
328          // id
329          String paramValue = DbFilterConditionTag.getConditionName(tableId,
330                                                                    conditionId)
331                              + DbFilterTag.FLT_VALUE + valueId;
332          String paramType = DbFilterConditionTag.getConditionName(tableId,
333                                                                   conditionId)
334                             + DbFilterTag.FLT_VALUETYPE + valueId;
335          String searchAlgoType = DbFilterConditionTag.getConditionName(tableId,
336                                                                        conditionId)
337                                  + DbFilterTag.FLT_SEARCHALGO + valueId;
338 
339          String value     = ParseUtil.getParameter(request, paramValue);
340          String valueType = ParseUtil.getParameter(request, paramType);
341 
342          String aSearchAlgorithm = ParseUtil.getParameter(request,
343                                                           searchAlgoType);
344          int    algorithm = Constants.SEARCH_ALGO_SHARP;
345 
346          if (!Util.isNull(aSearchAlgorithm)) {
347             if (aSearchAlgorithm.startsWith("weakStartEnd")) {
348                algorithm = Constants.SEARCH_ALGO_WEAK_START_END;
349             } else if (aSearchAlgorithm.startsWith("weakStart")) {
350                algorithm = Constants.SEARCH_ALGO_WEAK_START;
351             } else if (aSearchAlgorithm.startsWith("weakEnd")) {
352                algorithm = Constants.SEARCH_ALGO_WEAK_END;
353             } else if (aSearchAlgorithm.startsWith("weak")) {
354                algorithm = Constants.SEARCH_ALGO_WEAK;
355             }
356          }
357 
358          valueType = Util.isNull(valueType) ? FLT_VALUETYPE_TEXT
359                                             : valueType;
360 
361          if (value != null) {
362             // add value, possibly converted, to list
363             Field f = new Field();
364             f.setName(paramValue);
365             f.setId(valueId);
366             f.setFieldType(valueType);
367 
368             Table table = null;
369 
370             try {
371                table = DbFormsConfigRegistry.instance()
372                                             .lookup()
373                                             .getTable(tableId);
374             } catch (Exception e) {
375                logCat.error("readValuesFromRequest", e);
376             }
377 
378             f.setTable(table);
379 
380             FieldValue fv = new FieldValue(f, value);
381             fv.setLocale(MessageResources.getLocale(request));
382             fv.setSearchAlgorithm(algorithm);
383             values.put(fv);
384          } else {
385             // didn't find any parameter with this id, so we have finished
386             break;
387          }
388       }
389 
390       return values.toArray();
391    }
392 
393 
394    /***
395     * DOCUMENT ME!
396     *
397     * @return DOCUMENT ME!
398     */
399    protected Object getFieldObject() {
400       FieldValue fv = new FieldValue(getField(), state.value);
401       fv.setLocale(MessageResources.getLocale((HttpServletRequest) pageContext
402                                               .getRequest()));
403 
404       return fv.getFieldValueAsObject();
405    }
406 
407 
408    /***
409     * DOCUMENT ME!
410     *
411     * @return
412     */
413    protected State getState() {
414       return state;
415    }
416 
417 
418    /***
419     * render output of this value object. This is called only if its parent's
420     * condition is selected
421     *
422     * @return
423     *
424     * @throws JspException
425     */
426    protected StringBuffer render() throws JspException {
427       StringBuffer buf = new StringBuffer();
428 
429       Field        f = new Field();
430       f.setName(state.label);
431       f.setId(state.valueId);
432       f.setFieldType(state.type);
433       f.setTable(getParentForm().getTable());
434       setField(f);
435 
436       if (state.label != null) {
437     	  if (getParentForm().hasCaptionResourceSet()) {
438     		  try {
439     			  String message = MessageResources.getMessage((HttpServletRequest)pageContext.getRequest(), state.label);
440     			  if (message != null) {
441     				  state.label = message;
442     			  }
443     		  } catch (Exception e) {
444     			  logCat.debug("setCaption(" + state.label + ") Exception : "
445     					  + e.getMessage());
446     		  }
447     	  }
448          buf.append("<b>" + state.label + "</b>\n");
449       }
450 
451       if (state.type.equalsIgnoreCase(FLT_VALUETYPE_TEXT)
452                 || state.type.equalsIgnoreCase(FLT_VALUETYPE_NUMERIC)) {
453          renderTextElement(buf);
454       } else if (state.type.equalsIgnoreCase(FLT_VALUETYPE_DATE)
455                        || state.type.equalsIgnoreCase(FLT_VALUETYPE_TIMESTAMP)) {
456          renderDateElement(buf);
457       } else if (FLT_VALUETYPE_SELECT.equalsIgnoreCase(state.type)
458                        && (state.embeddedData != null)) {
459          renderSelectElement(buf);
460       } else {
461          throw new JspException("type not correct");
462       }
463 
464       return buf;
465    }
466 
467 
468    private String getSearchAlgoType() {
469       return ((DbFilterConditionTag) getParent()).getConditionName()
470              + DbFilterTag.FLT_SEARCHALGO + state.valueId;
471    }
472 
473 
474    private String getValueName() {
475       return ((DbFilterConditionTag) getParent()).getConditionName()
476              + DbFilterTag.FLT_VALUE + state.valueId;
477    }
478 
479 
480    private String getValueType() {
481       return ((DbFilterConditionTag) getParent()).getConditionName()
482              + DbFilterTag.FLT_VALUETYPE + state.valueId;
483    }
484 
485 
486    /***
487     * generate option element for select. borrowed from
488     *
489     * @param value
490     * @param description
491     * @param selected
492     *
493     * @return string containing an html option element
494     *
495     * @see DbSearchComboTag#generateTagString
496     */
497    private String generateTagString(String  value,
498                                     String  description,
499                                     boolean selected) {
500       StringBuffer tagBuf = new StringBuffer();
501       tagBuf.append("<option value=\"");
502       tagBuf.append(value);
503       tagBuf.append("\"");
504 
505       if (selected) {
506          tagBuf.append(" selected=\"selected\"");
507       }
508 
509       tagBuf.append(">");
510       tagBuf.append(description.trim());
511       tagBuf.append("</option>");
512 
513       return tagBuf.toString();
514    }
515 
516 
517    /***
518     * initialize tag's state before start using it
519     */
520    private void init() {
521       // state object is createad in constructor (for the first use) and in
522       // doEndTag next
523       if (state.type == null) {
524          state.type = "text";
525       }
526 
527       if (state.styleClass == null) {
528          state.styleClass = "";
529       }
530 
531       state.valueId = ((DbFilterConditionTag) getParent()).addValue(this);
532       state.value   = ParseUtil.getParameter((HttpServletRequest) pageContext
533                                              .getRequest(), getValueName());
534 
535       if (state.value == null) {
536          state.value = "";
537       }
538 
539       // the type attribute can be read either from request, with
540       // FLT_VALUETYPE, or directly from page
541       state.embeddedData = null;
542    }
543 
544 
545    /***
546     * render input's type "date"
547     *
548     * @param buf
549     */
550    private void renderDateElement(StringBuffer buf) {
551       renderTextElement(buf);
552 
553       // if property useJSCalendar is set to 'true' we will now add a little
554       // image that can be clicked to popup a small JavaScript Calendar
555       // written by Robert W. Husted to edit the field:
556       if ("true".equals(state.useJsCalendar)) {
557          buf.append(" <a href=\"javascript:doNothing()\" ")
558             .append(" onclick=\"");
559 
560          setPattern(state.jsCalendarDateFormat);
561          state.jsCalendarDateFormat = ((SimpleDateFormat) getFormatter())
562                                       .toPattern();
563 
564          if (state.jsCalendarDateFormat != null) // JS Date Format set ?
565           {
566             buf.append("calDateFormat='" + state.jsCalendarDateFormat + "';");
567          }
568 
569          buf.append("setDateField(document.dbform['")
570             .append(getValueName())
571             .append("']);")
572             .append(" top.newWin = window.open('")
573             .append(((HttpServletRequest) pageContext.getRequest())
574                     .getContextPath())
575             .append("/dbformslib/jscal/calendar.html','cal','width=270,height=280')\">")
576             .append("<img src=\"")
577             .append(((HttpServletRequest) pageContext.getRequest())
578                     .getContextPath())
579             .append("/dbformslib/jscal/calendar.gif\" width=\"32\" height=\"32\" ")
580             .append(" border=0  alt=\"Click on the Calendar to activate the Pop-Up Calendar Window.\">")
581             .append("</img>")
582             .append("</a>");
583       }
584    }
585 
586 
587    /***
588     * render input's type "select"
589     *
590     * @param buf
591     */
592    private void renderSelectElement(StringBuffer buf) {
593       String sizestr = "";
594 
595       if (state.size != null) {
596          sizestr = "size=\"" + state.size + "\" ";
597       }
598 
599       buf.append("<select name=\"" + getValueName() + "\" " + sizestr
600                  + " class=\"" + state.styleClass + "\">\n");
601 
602       if ((state.customEntry != null)
603                 && (state.customEntry.trim()
604                                            .length() > 0)) {
605          String aKey = org.dbforms.util.StringUtil.getEmbeddedStringWithoutDots(state.customEntry,
606                                                                                0,
607                                                                                ',');
608          String aValue = org.dbforms.util.StringUtil
609                          .getEmbeddedStringWithoutDots(state.customEntry, 1, ',');
610                    
611     	 // is captionResource is activated, retrieve the value from the MessageResources bundle
612          if (getParentForm().hasCaptionResourceSet()) {
613         	 aValue = MessageResources.getMessage((HttpServletRequest)pageContext.getRequest(),aValue);
614          }
615         	 
616          boolean isSelected = false;
617 
618          if ((state.selectedIndex == null)
619                    || (state.selectedIndex.trim()
620                                                 .length() == 0)) {
621             isSelected = "true".equals(StringUtil.getEmbeddedStringWithoutDots(state.customEntry,
622                                                                               2,
623                                                                               ','));
624          }
625 
626          buf.append(generateTagString(aKey, aValue, isSelected));
627       }
628 
629       int embeddedDataSize = state.embeddedData.size();
630 
631       for (int i = 0; i < embeddedDataSize; i++) {
632          StaticData aKeyValuePair = (StaticData) state.embeddedData.get(i);
633          String       aKey   = aKeyValuePair.getKey();
634          String       aValue = aKeyValuePair.getValue();
635 
636          // select, if datadriven and data matches with current value OR if
637          // explicitly set by user
638          boolean isSelected = aKey.equals(state.value);
639          buf.append(generateTagString(aKey, aValue, isSelected));
640       }
641 
642       buf.append("</select>\n");
643    }
644 
645 
646    /***
647     * render input's type "text"
648     *
649     * @param buf
650     */
651    private void renderTextElement(StringBuffer buf) {
652       String sizestr = "";
653 
654       if (state.size != null) {
655          sizestr = "size=\"" + state.size + "\" ";
656       }
657 
658       buf.append("<input type=\"text\" name=\"" + getValueName()
659                  + "\" value=\"" + this.getFormattedFieldValue() + "\""
660                  + sizestr + " class=\"" + state.styleClass + "\"/>\n");
661       buf.append("<input type=\"hidden\" name=\"" + getValueType()
662                  + "\" value=\"" + state.type.toLowerCase() + "\"/>\n");
663 
664       if (!Util.isNull(state.searchAlgo)) {
665          buf.append("<input type=\"hidden\" name=\"" + getSearchAlgoType()
666                     + "\" value=\"" + state.searchAlgo + "\"/>\n");
667       }
668    }
669 
670    /***
671     * tag's state holder. Used a separate class to hold tag's state to
672     * workaround to Tag pooling, in which an tag object is reused, but we have
673     * the need to store informations about all child tags in the parent, so we
674     * store the state, and apply it to a dummy tag when needed.
675     *
676     * @author Sergio Moretti
677     */
678    protected static class State {
679       /***
680        * contains list of elements to show as options when type is select,
681        * (DataContainer interface)
682        */
683       protected List embeddedData = null;
684 
685       /*** Allows an additional (independant) entry into the select list */
686       protected String customEntry = null;
687 
688       /*** Holds value of property jsCalendarDateFormat. */
689       protected String jsCalendarDateFormat = null;
690 
691       /*** label showed before input tag */
692       protected String label = null;
693 
694       /*
695        * holds the searchAlgo
696        */
697       protected String searchAlgo = null;
698 
699       /*** currently selected index, valid only when type = select */
700       protected String selectedIndex = null;
701 
702       /*** html input's attribute size */
703       protected String size = null;
704 
705       /*** css class to be applied to input element */
706       protected String styleClass = null;
707 
708       /*** type of input */
709       protected String type = null;
710 
711       /*** Holds value of property useJsCalendar. */
712       protected String useJsCalendar = null;
713 
714       /*** current value, readed from request */
715       protected String value = null;
716 
717       /*** identifier of this value object */
718       protected int valueId = -1;
719    }
720 }