View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/taglib/DbFilterTag.java,v 1.30 2006/01/13 13:38:50 hkollmann Exp $
3    * $Revision: 1.30 $
4    * $Date: 2006/01/13 13:38:50 $
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.FieldValue;
30  
31  import org.dbforms.util.MessageResources;
32  import org.dbforms.util.ParseUtil;
33  import org.dbforms.util.Util;
34  
35  import java.io.IOException;
36  
37  import java.util.ArrayList;
38  import java.util.Iterator;
39  import java.util.Locale;
40  
41  import javax.servlet.http.HttpServletRequest;
42  import javax.servlet.jsp.JspException;
43  import javax.servlet.jsp.JspWriter;
44  
45  /***
46   * custom tag that build up a set of sql filters. Create a set of sql filter
47   * conditions, letting user select which one will be applied. A filter tag
48   * contains one or more filterCondition tag. Each filterCondition represent a
49   * sql condition and is identified by its label. In the body of the
50   * filterCondition tag there is the piece of SQL code that we want to insert in
51   * the where clause, the character ? act like a placeholder, so a ? in the sql
52   * code will be substituted with the some user input. To tell the system what
53   * type of user input we want, the last tag is used, the filterValue tag. Each ?
54   * found in body will be subsituted by its corresponding filterValue tag. With
55   * the "type" attribute of this tag you can select the input more. Selecting
56   * "text", a filterValue will render an html input tag, with "select" you'll
57   * have an html select, and so on. An example is like this:
58   * 
59   * <pre>
60   *  &lt;db:filter&gt;
61   *      &lt;db:filterCondition label=&quot;author name like&quot;&gt;
62   *          NAME LIKE '%?%'
63   *          &lt;db:filterValue type=&quot;timestamp&quot; useJsCalendar=&quot;true&quot; /&gt;
64   *      &lt;/db:filterCondition&gt;
65   *      &lt;db:filterCondition label=&quot;ID &gt; V1 AND ID &lt; V2&quot;&gt;
66   *          AUTHOR_ID &gt;= ? AND AUTHOR_ID &lt;= ?
67   *          &lt;db:filterValue label=&quot;V1&quot; type=&quot;numeric&quot;/&gt;
68   *          &lt;db:filterValue label=&quot;V2&quot; type=&quot;numeric&quot;/&gt;
69   *      &lt;/db:filterCondition&gt;
70   *      &lt;db:filterCondition label=&quot;author = &quot;&gt;
71   *          NAME = '?'
72   *          &lt;db:filterValue type=&quot;select&quot;&gt;
73   *              &lt;db:queryData name=&quot;q1&quot; query=&quot;select distinct name as n1, name as n2 from author where AUTHOR_ID &lt; 100 order by name&quot;/&gt;
74   *          &lt;/db:filterValue&gt;
75   *      &lt;/db:filterCondition&gt;
76   *      &lt;db:filterCondition label=&quot;now is after date&quot;&gt;
77   *          CURRENT_DATE &gt; ?
78   *          &lt;db:filterValue type=&quot;date&quot; useJsCalendar=&quot;true&quot; /&gt;
79   *      &lt;/db:filterCondition&gt;
80   *      &lt;db:filterCondition label=&quot;filter without user input&quot;&gt;
81   *          AUTHOR_ID &gt; 10
82   *      &lt;/db:filterCondition&gt;
83   *  &lt;/db:filter&gt;
84   * </pre>
85   * 
86   * This structure will be rendered as a html select element to select the
87   * condition the you want to apply. On the onchange event there is a submit, so
88   * the page reload with the input elements of the condition that you have
89   * selected. After all input elements, there are two buttons, one to apply the
90   * condition, one to unset the current applied condition.
91   * 
92   * <p>
93   * Internals:
94   * 
95   * <dl>
96   * <dt> filter_[tableId] </dt>
97   * <dd> prefix that all the parameters created by this tag have. </dd>
98   * <dt> filter_[tableId]_sel </dt>
99   * <dd> index of the currently selected condition </dd>
100  * <dt> filter_[tableId]_cond_[condId] </dt>
101  * <dd> text of the currently selected condition </dd>
102  * <dt> filter_[tableId]_cond_[condId]_value_[valueId] </dt>
103  * <dd> current value of the input tag corresponding to the filterValue tag
104  * identified by [valueId] </dd>
105  * <dt> filter_[tableId]_cond_[condId]_valuetype_[valueId] </dt>
106  * <dd> type of the value identified by [valueId] </dd>
107  * </dl>
108  * </p>
109  * 
110  * <p>
111  * Reading data from request, and update corrispondently the sqlFilter attribute
112  * of DbFormTag is done in the static method generateSqlFilter, which produce in
113  * output a valid filter string. This method is called in DbFormTag's method
114  * doStartTag, setting with it the sqlFilter attribute value. The only other
115  * changes needed in DbFormTag's doStartTag is the nullifing of the
116  * firstPosition and lastPosition variables that normally contain the current
117  * position in the case of applying of a filter (.i.e. when user press the set
118  * button, and so the filter_&lt;tableId&gt;_set parameter is found in request).
119  * This is needed because here we must force the goto event to move to the first
120  * avalilable row.
121  * </p>
122  * 
123  * <p>
124  * </p>
125  * 
126  * @author Sergio Moretti
127  * @version $Revision: 1.30 $
128  */
129 public class DbFilterTag extends AbstractDbBaseHandlerTag implements
130 		javax.servlet.jsp.tagext.TryCatchFinally {
131 	/*** DOCUMENT ME! */
132 	protected static final String FLT_COND = "_cond_";
133 
134 	/*** DOCUMENT ME! */
135 	protected static final String FLT_PREFIX = "filter_";
136 
137 	/*** DOCUMENT ME! */
138 	protected static final String FLT_SEL = "_sel";
139 
140 	/*** DOCUMENT ME! */
141 	protected static final String FLT_SET = "_set";
142 
143 	/*** DOCUMENT ME! */
144 	protected static final String FLT_VALUE = "_value_";
145 
146 	/*** DOCUMENT ME! */
147 	protected static final String FLT_VALUETYPE = "_valuetype_";
148 
149 	/*** DOCUMENT ME! */
150 	protected static final String FLT_SEARCHALGO = "_searchalgo_";
151 
152 	private static Log logCat = LogFactory.getLog(DbFilterTag.class.getName());
153 
154 	/*** list of conditions defined for this filter element */
155 	private ArrayList conds;
156 
157 	/*** used to override the label of the main select's first option element */
158 	private String disabledCaption;
159 
160 	/*** prefix for this filter of the request's parameters */
161 	private String filterName;
162 
163 	/*** caption of the SET button */
164 	private String setCaption;
165 
166 	/*** size attribute for select element */
167 	private String size;
168 
169 	/*** caption of the UNSET button */
170 	private String unsetCaption;
171 
172 	/*** CSS stylesheet class to be applied to the SET button */
173 	private String setStyleClass;
174 
175 	/*** CSS stylesheet class to be applied to the UNSET button */
176 	private String unsetStyleClass;
177 
178 	/***
179 	 * return the currently setted filter condition, reading it from request.
180 	 * 
181 	 * @param request
182 	 * @param tableId
183 	 * 
184 	 * @return filter string
185 	 */
186 	public static String getSqlFilter(HttpServletRequest request, int tableId) {
187 		int conditionId = getCurrentCondition(request, tableId);
188 
189 		if (conditionId > -1) {
190 			// if there's an active condition, build up it from request
191 			return DbFilterConditionTag.getSqlFilter(request, tableId,
192 					conditionId);
193 		}
194 
195 		return null;
196 	}
197 
198 	/***
199 	 * return the parametes of the currently setted filter condition, reading it
200 	 * from request.
201 	 * 
202 	 * @param request
203 	 * @param tableId
204 	 * 
205 	 * @return filter string
206 	 */
207 	public static FieldValue[] getSqlFilterParams(HttpServletRequest request,
208 			int tableId) {
209 		int conditionId = getCurrentCondition(request, tableId);
210 
211 		if (conditionId > -1) {
212 			// if there's an active condition, build up it from request
213 			return DbFilterConditionTag.getSqlFilterParams(request, tableId,
214 					conditionId);
215 		}
216 
217 		return null;
218 	}
219 
220 	/***
221 	 * DOCUMENT ME!
222 	 * 
223 	 * @param string
224 	 */
225 	public void setDisabledCaption(String string) {
226 		disabledCaption = string;
227 	}
228 
229 	/***
230 	 * DOCUMENT ME!
231 	 * 
232 	 * @param string
233 	 */
234 	public void setSetCaption(String string) {
235 		setCaption = string;
236 	}
237 
238 	/***
239 	 * DOCUMENT ME!
240 	 * 
241 	 * @param string
242 	 */
243 	public void setSize(String string) {
244 		size = string;
245 	}
246 
247 	/***
248 	 * DOCUMENT ME!
249 	 * 
250 	 * @param string
251 	 */
252 	public void setUnsetCaption(String string) {
253 		unsetCaption = string;
254 	}
255 
256 	/***
257 	 * here we read information from nested tags and we render output to the
258 	 * page.
259 	 * 
260 	 * @see javax.servlet.jsp.tagext.IterationTag#doEndTag()
261 	 */
262 	public int doEndTag() throws JspException {
263 		// retrieve the currently active condition from request
264 		int currentCondId = getCurrentCondition(
265 				(HttpServletRequest) pageContext.getRequest(), getTableId());
266 		DbFilterConditionTag currentCond = null;
267 
268 		if (currentCondId > -1) {
269 			currentCond = new DbFilterConditionTag();
270 
271 			// read the object's state stored in array and apply it in newly
272 			// created object
273 			currentCond.setState(pageContext, this,
274 					(DbFilterConditionTag.State) conds.get(currentCondId));
275 		}
276 
277 		StringBuffer buf = render(currentCond);
278 
279 		try {
280 			// clear body content.
281 			// It's meaningless for filter tag and should not be rendered!
282 			if (bodyContent != null) {
283 				bodyContent.clearBody();
284 			}
285 
286 			JspWriter out = pageContext.getOut();
287 			out.write(buf.toString());
288 		} catch (IOException e) {
289 			throw new JspException(e.getMessage());
290 		}
291 
292 		return SKIP_BODY;
293 	}
294 
295 	/***
296 	 * reset tag state
297 	 * 
298 	 * @see javax.servlet.jsp.tagext.TryCatchFinally#doFinally()
299 	 */
300 	public void doFinally() {
301 		conds = null;
302 		disabledCaption = null;
303 		filterName = null;
304 		setCaption = null;
305 		size = null;
306 		unsetCaption = null;
307 		super.doFinally();
308 	}
309 
310 	/***
311 	 * initialize environment
312 	 * 
313 	 * @see javax.servlet.jsp.tagext.Tag#doStartTag()
314 	 */
315 	public int doStartTag() throws JspException {
316 		init();
317 
318 		return EVAL_BODY_INCLUDE;
319 	}
320 
321 	/***
322 	 * filter prefix
323 	 * 
324 	 * @param tableId
325 	 * 
326 	 * @return
327 	 */
328 	protected static String getFilterName(int tableId) {
329 		return FLT_PREFIX + tableId;
330 	}
331 
332 	/***
333 	 * filter's parameters prefix in request
334 	 * 
335 	 * @return
336 	 */
337 	protected String getFilterName() {
338 		return filterName;
339 	}
340 
341 	/***
342 	 * return tableId of the parent dbform tag
343 	 * 
344 	 * @return
345 	 */
346 	protected int getTableId() {
347 		return getParentForm().getTable().getId();
348 	}
349 
350 	/***
351 	 * add a condition object to the list. Called by nested DbFilterConditionTag
352 	 * 
353 	 * @param condition
354 	 *            to add
355 	 * 
356 	 * @return index of the newly added condition
357 	 */
358 	protected int addCondition(DbFilterConditionTag condition) {
359 		conds.add(condition.getState());
360 
361 		return conds.size() - 1;
362 	}
363 
364 	/***
365 	 * retrieve the currently active condition from request
366 	 * 
367 	 * @param request
368 	 * @param tableId
369 	 * 
370 	 * @return the condition id
371 	 */
372 	private static int getCurrentCondition(HttpServletRequest request,
373 			int tableId) {
374 		int curCondId = -1;
375 
376 		// retrieve the current condition from parameters
377 		String param = ParseUtil.getParameter(request, getFilterName(tableId)
378 				+ FLT_SEL);
379 
380 		if (!Util.isNull(param)) {
381 			// try to transform parameter string in integer
382 			try {
383 				curCondId = Integer.parseInt(param);
384 			} catch (NumberFormatException e) {
385 				logCat.error("getCurrentCondition", e);
386 			}
387 		}
388 
389 		logCat.debug("setting current filter: " + curCondId);
390 
391 		return curCondId;
392 	}
393 
394 	/***
395 	 * initialize class fields.
396 	 */
397 	private void init() {
398 		conds = new ArrayList();
399 		filterName = getFilterName(getTableId());
400 
401 		if (size == null) {
402 			size = "1";
403 		}
404 
405 		if (disabledCaption == null) {
406 			disabledCaption = "";
407 		}
408 
409 		if (setCaption == null) {
410 			setCaption = "set";
411 		}
412 
413 		if (unsetCaption == null) {
414 			unsetCaption = "unset";
415 		}
416 		if (unsetStyleClass == null) {
417 			unsetStyleClass = getStyleClass();
418 		}
419 
420 		if (setStyleClass == null) {
421 			setStyleClass = getStyleClass();
422 		}
423 	}
424 
425 	/***
426 	 * render output
427 	 * 
428 	 * @param currentCond
429 	 * 
430 	 * @return
431 	 * 
432 	 * @throws JspException
433 	 */
434 	private StringBuffer render(DbFilterConditionTag currentCond)
435 			throws JspException {
436 		StringBuffer buf = new StringBuffer();
437 
438 		// render main select
439 		buf.append("\n<select name=\"" + filterName + FLT_SEL + "\" class=\""
440 				+ getStyleClass() + "\" size=\"" + size
441 				+ "\" onchange=\"document.dbform.submit()\" >\n");
442 
443 		int cnt = 0;
444 		buf
445 				.append("\t<option value=\"-1\" >" + disabledCaption
446 						+ "</option>\n");
447 
448 		// render an option for each nested condition
449 		DbFilterConditionTag cond = new DbFilterConditionTag();
450 		Locale locale = MessageResources
451 				.getLocale((HttpServletRequest) pageContext.getRequest());
452 
453 		for (Iterator i = conds.iterator(); i.hasNext();) {
454 			// read DbFilterConditionTag object's state stored in array and
455 			// apply to cond object
456 			cond.setState(this.pageContext, this,
457 					(DbFilterConditionTag.State) i.next());
458 
459 			// select the currently active condition
460 			String selected = ((currentCond != null) && currentCond
461 					.equals(cond)) ? "selected" : "";
462 
463 			// NAK Added support for localization of option label
464 			// If the caption is not null and the resources="true" attribute
465 			String label = cond.getLabel();
466 
467 			if ((label != null) && getParentForm().hasCaptionResourceSet()) {
468 				try {
469 					String message = MessageResources.getMessage(label, locale);
470 
471 					if (message != null) {
472 						label = message;
473 					}
474 				} catch (Exception e) {
475 					logCat.debug("setCaption(" + label + ") Exception : "
476 							+ e.getMessage());
477 				}
478 			}
479 
480 			// render option
481 			buf.append("\t<option value=\"" + cnt + "\" " + selected + ">"
482 					+ label + "</option>\n");
483 			cnt++;
484 		}
485 
486 		buf.append("</select>\n");
487 
488 		if (currentCond != null) {
489 			// render the current condition
490 			buf.append(currentCond.render());
491 
492 			if (!Util.isNull(setCaption)) {
493 				DbBaseHandlerFactory btn = new DbBaseHandlerFactory(
494 						this.pageContext, this, DbNavReloadButtonTag.class);
495 				((DbNavReloadButtonTag) btn.getTag()).setCaption(setCaption);
496 				((DbNavReloadButtonTag) btn.getTag()).setForceReload("true");
497 				((DbNavReloadButtonTag) btn.getTag())
498 						.setStyleClass(setStyleClass);
499 				buf.append(btn.render());
500 			}
501 
502 			if (!Util.isNull(unsetCaption)) {
503 				DbBaseHandlerFactory btn = new DbBaseHandlerFactory(
504 						this.pageContext, this, DbNavReloadButtonTag.class);
505 				((DbNavReloadButtonTag) btn.getTag()).setCaption(unsetCaption);
506 				((DbNavReloadButtonTag) btn.getTag())
507 						.setOnClick("document.dbform." + filterName + FLT_SEL
508 								+ ".selectedIndex = -1;");
509 				((DbNavReloadButtonTag) btn.getTag()).setForceReload("true");
510 				((DbNavReloadButtonTag) btn.getTag())
511 						.setStyleClass(unsetStyleClass);
512 				buf.append(btn.render());
513 			}
514 		}
515 
516 		return buf;
517 	}
518 
519 	/***
520 	 * DOCUMENT ME!
521 	 * 
522 	 * @param string
523 	 */
524 	public void setSetStyleClass(String string) {
525 		setStyleClass = string;
526 	}
527 
528 	/***
529 	 * DOCUMENT ME!
530 	 * 
531 	 * @param string
532 	 */
533 	public void setUnsetStyleClass(String string) {
534 		unsetStyleClass = string;
535 	}
536 }