View Javadoc

1   /*
2    * $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/taglib/DbFormTag.java,v 1.164 2006/03/25 15:38:24 hkollmann Exp $
3    * $Revision: 1.164 $
4    * $Date: 2006/03/25 15:38:24 $
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  package org.dbforms.taglib;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.commons.validator.ValidatorResources;
29  
30  import org.dbforms.config.Constants;
31  import org.dbforms.config.DbFormsErrors;
32  import org.dbforms.config.Field;
33  import org.dbforms.config.FieldTypes;
34  import org.dbforms.config.FieldValue;
35  import org.dbforms.config.FieldValues;
36  import org.dbforms.config.GrantedPrivileges;
37  import org.dbforms.config.ResultSetVector;
38  import org.dbforms.config.Table;
39  import org.dbforms.config.MultipleValidationException;
40  
41  import org.dbforms.event.AbstractNavEventFactory;
42  import org.dbforms.event.NavEventFactoryImpl;
43  import org.dbforms.event.AbstractNavigationEvent;
44  import org.dbforms.event.AbstractWebEvent;
45  import org.dbforms.event.eventtype.EventType;
46  import org.dbforms.interfaces.DbEventInterceptorData;
47  import org.dbforms.interfaces.IDbEventInterceptor;
48  
49  import org.dbforms.util.MessageResources;
50  import org.dbforms.util.MessageResourcesInternal;
51  import org.dbforms.util.ParseUtil;
52  import org.dbforms.util.SqlUtil;
53  import org.dbforms.util.TimeUtil;
54  import org.dbforms.util.Util;
55  import org.dbforms.util.external.FileUtil;
56  
57  import org.dbforms.validation.DbFormsValidatorUtil;
58  import org.dbforms.validation.ValidatorConstants;
59  
60  import org.dbforms.interfaces.ISqlFilter;
61  
62  import java.io.IOException;
63  
64  import java.sql.Connection;
65  import java.sql.SQLException;
66  
67  import java.util.ArrayList;
68  import java.util.Date;
69  import java.util.Enumeration;
70  import java.util.Hashtable;
71  import java.util.Locale;
72  import java.util.Map;
73  import java.util.StringTokenizer;
74  import java.util.Vector;
75  
76  import javax.servlet.http.HttpServletRequest;
77  import javax.servlet.http.HttpServletResponse;
78  import javax.servlet.jsp.JspException;
79  import javax.servlet.jsp.JspWriter;
80  import javax.servlet.jsp.tagext.Tag;
81  import javax.servlet.jsp.tagext.TryCatchFinally;
82  
83  /***
84   * This is the root element of a data manipulation form
85   * 
86   * @author Joachim Peer
87   */
88  public class DbFormTag extends AbstractScriptHandlerTag implements ISqlFilter, TryCatchFinally {
89     /*** logging category for this class */
90     private static Log                     logCat                      = LogFactory.getLog(DbFormTag.class.getName());
91  
92     /*** NavigationEvent factory */
93     private static AbstractNavEventFactory navEventFactory             = NavEventFactoryImpl.instance();
94  
95     /*** reference to a parent DBFormTag (if any) */
96     private transient DbFormTag            parentForm;
97  
98     private transient FieldValues          orderFields                 = null;
99  
100    /*** List of all child field name with assosciate generated name. */
101 
102    /* Ex: "champ1" : "f_0_3@root_3" */
103    private transient Hashtable            childFieldNames             = new Hashtable();
104 
105    /*** holds the list of fields of the sub forms (2003-02-04 HKK) */
106    private transient Hashtable            fieldNames;
107 
108    private transient Hashtable            gotoHt                      = null;
109 
110    /*** Used to avoid creation of same javascript function. */
111    private transient Hashtable            javascriptDistinctFunctions = new Hashtable();
112 
113    private transient Hashtable            validationFields;
114 
115    /***
116     * Holds the locale the form is created with. Can be readed from other tags
117     */
118    private Locale                         locale                      = null;
119 
120    /*** the data to be rendered */
121    private transient ResultSetVector      resultSetVector;
122 
123    /*** form's action attribute */
124    private String                         action;
125 
126    /***
127     * if "true", at every action (navigation, insert, update, etc.) all input
128     * fields of ALL currently rendered rowsets are parsed and updated. Many rows
129     * may be affected. If "false", updates are only performed if an explicite
130     * "update"- action is launched (normally by hittig the updateAction-button).
131     * Up to 1 row may be affected. <br>
132     * Default is: "false"
133     */
134    private String                         autoUpdate                  = "false";
135 
136    /*** represents the HTML form tag attribute AUTOCOMPLETE. */
137    private String                         autocomplete                = null;
138 
139    /*** support caption name resolution with ApplicationResources. */
140    private String                         captionResource             = "false";
141 
142    /***
143     * used in sub-form: field(s) in this forme that is/are linked to the parent
144     * form
145     */
146    private String                         childField;
147 
148    private String                         dbConnectionName            = null;
149 
150    /*** filter string */
151    private String                         filter;
152 
153    /*
154     * If set to true, resultSet is obtained only if search filters have been
155     * specified
156     */
157    private String                         searchFilterRequired        = "false";
158 
159    /***
160     * site to be invoked after action - nota bene: this followUp may be
161     * overruled by "followUp"-attributes of actionButtons
162     */
163    private String                         followUp;
164 
165    /***
166     * site to be invoked after action if previous form contained errors- nota
167     * bene: this followUp may be overruled by "followUp"-attributes of
168     * actionButtons
169     */
170    private String                         followUpOnError;
171 
172    /*** the form name to map with valdation.xml form name. */
173    private String                         formValidatorName;
174 
175    private String                         gotoPrefix;
176 
177    /*** support caption name resolution with ApplicationResources. */
178    private String                         javascriptFieldsArray       = "false";
179 
180    /*** support caption name resolution with ApplicationResources. */
181    private String                         javascriptValidation        = "false";
182 
183    /***
184     * File of validation javascript for include &lt;SCRIPT src="..."&gt;
185     * &lt;/SCRIPT&gt; For better performance. Else it's the webserver will
186     * generate it each time.
187     */
188    private String                         javascriptValidationSrcFile;
189 
190    private String                         localWebEvent;
191 
192    /*** count of this form (n ||) */
193    private String                         maxRows;
194 
195    /*** is either "true" or "false" */
196    private String                         multipart;
197 
198    /*** holds name attribute */
199    private String                         name                        = null;
200 
201    /*** onSubmit form field (20020703-HKK) */
202    private String                         onSubmit;
203 
204    /*** SQL order by string */
205    private String                         orderBy;
206 
207    /*** true if update should be performed always */
208    private String                         overrideFieldCheck          = null;
209 
210    /***
211     * used in sub-form: field(s) in the main form that is/are linked to this
212     * form
213     */
214    private String                         parentField;
215 
216    /***
217     * holds information about how many times the body of this tag has been
218     * rendered
219     */
220    private String                         positionPath;
221 
222    /*
223     * important in subforms: 5th row of subform in 2nd row of main form has
224     * path: "5@2"; in mainforms
225     */
226    private String                         positionPathCore;
227 
228    /*** Indicate if the form is in read-only mode */
229    private String                         readOnly                    = "false";
230 
231    /*** redisplayFieldsOnError flag */
232    private String                         redisplayFieldsOnError      = "false";
233 
234    /*** SQL filter string */
235    private String                         sqlFilter                   = null;
236 
237    /*** Supply table name list */
238    private String                         tableList;
239 
240    /*** the name of the underlying table */
241    private String                         tableName;
242 
243    /***
244     * pedant to the html-target attribute in html-form tag: the target frame to
245     * jump to
246     */
247    private String                         target;
248 
249    /*** Free-form select query */
250    private String                         whereClause;
251 
252    /*** #fixme: description */
253    private StringBuffer                   childElementOutput;
254 
255    /*** the underlying table */
256    private transient Table                table;
257 
258    private Vector                         overrulingOrderFields;
259 
260    /*** holds the list of sub forms to validate (2003-02-04 HKK) */
261    private Vector                         validationForms;
262 
263    /*** Keep trace of witch event is use */
264    private transient AbstractWebEvent     webEvent                    = null;
265 
266    /***
267     * used in sub-form: this data structure holds the linked childfield(s) and
268     * their current values it/they derive from the main form
269     */
270    private transient FieldValue[]         childFieldValues;
271 
272    /***
273     * if rendering of header and body of the form is completed and only the
274     * footer needs to be rendered yet
275     */
276    private boolean                        footerReached               = false;
277 
278    /***
279     * count (multiplicity, view-mode) of this form (n || -1), whereby n E N
280     * (==1,2..z)
281     */
282    private int                            count;
283 
284    /***
285     * holds information about how many times the body of this tag has been
286     * rendered
287     */
288    private int                            currentCount                = 0;
289 
290    /*** the id of the underlying table */
291    private int                            tableId                     = -1;
292 
293    /***
294     * Set the action attribute value.
295     * 
296     * @param newAction
297     *           the new attribute value
298     */
299    public void setAction(java.lang.String newAction) {
300       action = newAction;
301    }
302 
303    /***
304     * Get the action attribute value.
305     * 
306     * @return the action attribute value
307     */
308    public java.lang.String getAction() {
309       return action;
310    }
311 
312    /***
313     * Sets the autoUpdate attribute of the DbFormTag object
314     * 
315     * @param autoUpdate
316     *           The new autoUpdate value
317     */
318    public void setAutoUpdate(String autoUpdate) {
319       this.autoUpdate = autoUpdate;
320    }
321 
322    /***
323     * Gets the autoUpdate attribute of the DbFormTag object
324     * 
325     * @return The autoUpdate value
326     */
327    public String getAutoUpdate() {
328       return autoUpdate;
329    }
330 
331    /***
332     * DOCUMENT ME!
333     * 
334     * @param string
335     */
336    public void setAutocomplete(String string) {
337       autocomplete = string;
338    }
339 
340    /***
341     * DOCUMENT ME!
342     * 
343     * @return autocomplete
344     */
345    public String getAutocomplete() {
346       return autocomplete;
347    }
348 
349    /***
350     * Sets the captionResource attribute of the DbFormTag object
351     * 
352     * @param res
353     *           The new captionResource value
354     */
355    public void setCaptionResource(String res) {
356       this.captionResource = res;
357    }
358 
359    /***
360     * Sets the childField attribute of the DbFormTag object
361     * 
362     * @param childField
363     *           The new childField value
364     */
365    public void setChildField(String childField) {
366       this.childField = childField;
367    }
368 
369    /***
370     * Gets the childField attribute of the DbFormTag object
371     * 
372     * @return The childField value
373     */
374    public String getChildField() {
375       return childField;
376    }
377 
378    /***
379     * Gets the count attribute of the DbFormTag object
380     * 
381     * @return The count value
382     */
383    public int getCount() {
384       return count;
385    }
386 
387    /***
388     * Gets the currentCount attribute of the DbFormTag object
389     * 
390     * @return The currentCount value
391     */
392    public int getCurrentCount() {
393       return currentCount;
394    }
395 
396    /***
397     * Sets the dbConnectionName attribute of the DbFormTag object
398     * 
399     * @param dbConnectionName
400     *           The new dbConnectionName value
401     */
402    public void setDbConnectionName(String dbConnectionName) {
403       this.dbConnectionName = dbConnectionName;
404    }
405 
406    /***
407     * Gets the dbConnectionName attribute of the DbFormTag object
408     * 
409     * @return The dbConnectionName value
410     */
411    public String getDbConnectionName() {
412       return dbConnectionName;
413    }
414 
415    /***
416     * Sets the filter attribute of the DbFormTag object
417     * 
418     * @param filter
419     *           The new filter value
420     */
421    public void setFilter(String filter) {
422       this.filter = filter;
423    }
424 
425    /***
426     * Gets the filter attribute of the DbFormTag object
427     * 
428     * @return The filter value
429     */
430    public String getFilter() {
431       return filter;
432    }
433 
434    /***
435     * Sets the followUp attribute of the DbFormTag object
436     * 
437     * @param followUp
438     *           The new followUp value
439     */
440    public void setFollowUp(String followUp) {
441       this.followUp = followUp;
442    }
443 
444    /***
445     * Gets the followUp attribute of the DbFormTag object
446     * 
447     * @return The followUp value
448     */
449    public String getFollowUp() {
450       return followUp;
451    }
452 
453    /***
454     * Sets the followUpOnError attribute
455     * 
456     * @param followUpOnError
457     *           The followUpOnError to set
458     */
459    public void setFollowUpOnError(String followUpOnError) {
460       this.followUpOnError = followUpOnError;
461    }
462 
463    /***
464     * Gets the followUpOnError
465     * 
466     * @return Returns a String
467     */
468    public String getFollowUpOnError() {
469       return followUpOnError;
470    }
471 
472    /***
473     * Sets the footerReached attribute of the DbFormTag object
474     * 
475     * @param footerReached
476     *           The new footerReached value
477     */
478    public void setFooterReached(boolean footerReached) {
479       this.footerReached = footerReached;
480    }
481 
482    /***
483     * Gets the footerReached attribute of the DbFormTag object
484     * 
485     * @return The footerReached value
486     */
487    public boolean isFooterReached() {
488       return footerReached;
489    }
490 
491    /***
492     * Sets the formValidatorName attribute of the DbFormTag object
493     * 
494     * @param fv
495     *           The new formValidatorName value
496     */
497    public void setFormValidatorName(String fv) {
498       this.formValidatorName = fv;
499    }
500 
501    /***
502     * Gets the formValidatorName attribute of the DbFormTag object
503     * 
504     * @return The formValidatorName value
505     */
506    public String getFormValidatorName() {
507       return formValidatorName;
508    }
509 
510    /***
511     * Sets the gotoHt attribute of the DbFormTag object
512     * 
513     * @param gotoHt
514     *           The new gotoHt value
515     */
516    public void setGotoHt(Hashtable gotoHt) {
517       this.gotoHt = gotoHt;
518    }
519 
520    /***
521     * Gets the gotoHt attribute of the DbFormTag object
522     * 
523     * @return The gotoHt value
524     */
525    public Hashtable getGotoHt() {
526       return gotoHt;
527    }
528 
529    /***
530     * Sets the gotoPrefix attribute of the DbFormTag object
531     * 
532     * @param gotoPrefix
533     *           The new gotoPrefix value
534     */
535    public void setGotoPrefix(String gotoPrefix) {
536       this.gotoPrefix = gotoPrefix;
537    }
538 
539    /***
540     * Gets the gotoPrefix attribute of the DbFormTag object
541     * 
542     * @return The gotoPrefix value
543     */
544    public String getGotoPrefix() {
545       return gotoPrefix;
546    }
547 
548    /***
549     * Sets the javascriptFieldsArray attribute of the DbFormTag object
550     * 
551     * @param jfa
552     *           The new javascriptFieldsArray value
553     */
554    public void setJavascriptFieldsArray(String jfa) {
555       this.javascriptFieldsArray = jfa;
556    }
557 
558    /***
559     * Sets the javascriptValidation attribute of the DbFormTag object
560     * 
561     * @param jsv
562     *           The new javascriptValidation value
563     */
564    public void setJavascriptValidation(String jsv) {
565       this.javascriptValidation = jsv;
566    }
567 
568    /***
569     * Sets the javascriptFieldsArray attribute of the DbFormTag object
570     * 
571     * @param jsvs
572     *           The javascriptFieldsArray value
573     */
574    public void setJavascriptValidationSrcFile(String jsvs) {
575       this.javascriptValidationSrcFile = jsvs;
576    }
577 
578    /***
579     * Gets the javascriptValidationSrcFile attribute of the DbFormTag object
580     * 
581     * @return The javascriptValidationSrcFile value
582     */
583    public String getJavascriptValidationSrcFile() {
584       return javascriptValidationSrcFile;
585    }
586 
587    /***
588     * Sets the localWebEvent attribute of the DbFormTag object
589     * 
590     * @param localWebEvent
591     *           The new localWebEvent value
592     */
593    public void setLocalWebEvent(String localWebEvent) {
594       this.localWebEvent = localWebEvent;
595    }
596 
597    /***
598     * Gets the localWebEvent attribute of the DbFormTag object
599     * 
600     * @return The localWebEvent value
601     */
602    public String getLocalWebEvent() {
603       return localWebEvent;
604    }
605 
606    /***
607     * Returns the locale the form is created with. Can be readed from other tags
608     * 
609     * @return DOCUMENT ME!
610     */
611    public Locale getLocale() {
612       return locale;
613    }
614 
615    /***
616     * Sets the maxRows attribute of the DbFormTag object
617     * 
618     * @param maxRows
619     *           The new maxRows value
620     */
621    public void setMaxRows(String maxRows) {
622       this.maxRows = maxRows;
623 
624       if (maxRows.trim().equals("*")) {
625          this.count = 0;
626       } else {
627          this.count = Integer.parseInt(maxRows);
628       }
629    }
630 
631    /***
632     * Gets the maxRows attribute of the DbFormTag object
633     * 
634     * @return The maxRows value
635     */
636    public String getMaxRows() {
637       return maxRows;
638    }
639 
640    /***
641     * Sets the multipart attribute of the DbFormTag object
642     * 
643     * @param value
644     *           The new multipart value
645     */
646    public void setMultipart(String value) {
647       this.multipart = value;
648    }
649 
650    /***
651     * DOCUMENT ME!
652     * 
653     * @param string
654     */
655    public void setName(String string) {
656       name = string;
657    }
658 
659    /***
660     * DOCUMENT ME!
661     * 
662     * @return
663     */
664    public String getName() {
665       return (name != null) ? name : tableName;
666    }
667 
668    /***
669     * Set the onSubmit attribute value
670     * 
671     * @param newonSubmit
672     *           the new onSubmit value
673     */
674    public void setOnSubmit(String newonSubmit) {
675       onSubmit = newonSubmit;
676    }
677 
678    /***
679     * Get the onSubmit attribute.
680     * 
681     * @return the onSubmit attribute
682     */
683    public java.lang.String getOnSubmit() {
684       return onSubmit;
685    }
686 
687    /***
688     * Sets the orderBy attribute of the DbFormTag object
689     * 
690     * @param orderBy
691     *           The new orderBy value
692     */
693    public void setOrderBy(String orderBy) {
694       this.orderBy = orderBy;
695    }
696 
697    /***
698     * Gets the orderBy attribute of the DbFormTag object
699     * 
700     * @return The orderBy value
701     */
702    public String getOrderBy() {
703       return orderBy;
704    }
705 
706    /***
707     * DOCUMENT ME!
708     * 
709     * @return DOCUMENT ME!
710     */
711    public FieldValues getOrderFields() {
712       return orderFields;
713    }
714 
715    /***
716     * DOCUMENT ME!
717     * 
718     * @param string
719     */
720    public void setOverrideFieldCheck(String string) {
721       overrideFieldCheck = string;
722    }
723 
724    /***
725     * DOCUMENT ME!
726     * 
727     * @return
728     */
729    public String getOverrideFieldCheck() {
730       return overrideFieldCheck;
731    }
732 
733    /***
734     * Set the parent tag
735     * 
736     * @param p
737     *           the parent tag
738     */
739    public void setParent(Tag p) {
740       super.setParent(p);
741       this.parentForm = (DbFormTag) findAncestorWithClass(this, DbFormTag.class);
742    }
743 
744    /***
745     * Sets the parentField attribute of the DbFormTag object
746     * 
747     * @param parentField
748     *           The new parentField value
749     */
750    public void setParentField(String parentField) {
751       this.parentField = parentField;
752    }
753 
754    /***
755     * Gets the parentField attribute of the DbFormTag object
756     * 
757     * @return The parentField value
758     */
759    public String getParentField() {
760       return parentField;
761    }
762 
763    /***
764     * Gets the positionPath attribute of the DbFormTag object
765     * 
766     * @return The positionPath value
767     */
768    public String getPositionPath() {
769       return positionPath;
770    }
771 
772    /***
773     * Gets the positionPathCore attribute of the DbFormTag object.
774     * 
775     * @return The positionPathCore value
776     */
777 
778    /* eg. "root", "123@35@root" */
779    public String getPositionPathCore() {
780       return positionPathCore;
781    }
782 
783    /***
784     * Sets the readOnly attribute of the DbFormTag object
785     * 
786     * @param readOnly
787     *           The new readOnly value
788     */
789    public void setReadOnly(String readOnly) {
790       this.readOnly = readOnly;
791    }
792 
793    /***
794     * Set the redisplayFieldsOnError attribute
795     * 
796     * @param newRedisplayFieldsOnError
797     *           the new redisplayFieldsOnError value
798     */
799    public void setRedisplayFieldsOnError(java.lang.String newRedisplayFieldsOnError) {
800       redisplayFieldsOnError = newRedisplayFieldsOnError;
801    }
802 
803    /***
804     * Gets the resultSetVector attribute of the DbFormTag object
805     * 
806     * @return The resultSetVector value
807     */
808    public ResultSetVector getResultSetVector() {
809       return resultSetVector;
810    }
811 
812    /***
813     * DOCUMENT ME!
814     * 
815     * @param string
816     */
817    public void setSqlFilter(String string) {
818       sqlFilter = string;
819    }
820 
821    /***
822     * DOCUMENT ME!
823     * 
824     * @return
825     */
826    public String getSqlFilter() {
827       return sqlFilter;
828    }
829 
830    /***
831     * Gets the subForm attribute of the DbFormTag object
832     * 
833     * @return The subForm value
834     */
835    public boolean isSubForm() {
836       return parentForm != null;
837    }
838 
839    /***
840     * Gets the parentForm DbFormTag object
841     * 
842     * @return The parentForm value
843     */
844    public DbFormTag getParentForm() {
845       return parentForm;
846    }
847 
848    /***
849     * Gets the table attribute of the DbFormTag object
850     * 
851     * @return The table value
852     */
853    public Table getTable() {
854       return table;
855    }
856 
857    /***
858     * Set the table list attribute value
859     * 
860     * @param tableList
861     *           the new tableList value
862     */
863    public void setTableList(String tableList) {
864       this.tableList = tableList;
865    }
866 
867    /***
868     * Get the table list attribute.
869     * 
870     * @return the table list string
871     */
872    public String getTableList() {
873       return tableList;
874    }
875 
876    /***
877     * Sets the tableName attribute of the DbFormTag object
878     * 
879     * @param tableName
880     *           The new tableName value
881     * 
882     * @throws Exception
883     *            DOCUMENT ME!
884     */
885    public void setTableName(String tableName) throws Exception {
886       this.tableName = tableName;
887       this.table = getConfig().getTableByName(tableName);
888 
889       if (getTable() == null) {
890          throw new Exception("Table " + tableName + " not found in configuration");
891       }
892 
893       this.tableId = getTable().getId();
894    }
895 
896    /***
897     * Gets the tableName attribute of the DbFormTag object
898     * 
899     * @return The tableName value
900     */
901    public String getTableName() {
902       return tableName;
903    }
904 
905    /***
906     * Sets the target attribute of the DbFormTag object
907     * 
908     * @param target
909     *           The new target value
910     */
911    public void setTarget(String target) {
912       this.target = target;
913    }
914 
915    /***
916     * Gets the target attribute of the DbFormTag object
917     * 
918     * @return The target value
919     */
920    public String getTarget() {
921       return target;
922    }
923 
924    /***
925     * DOCUMENT ME!
926     * 
927     * @return
928     */
929    public AbstractWebEvent getWebEvent() {
930       return webEvent;
931    }
932 
933    /***
934     * Sets the whereClause attribute of the DbFormTag object
935     * 
936     * @param wc
937     *           The new whereClause value
938     */
939    public void setWhereClause(String wc) {
940       this.whereClause = wc;
941    }
942 
943    /***
944     * Gets the whereClause attribute of the DbFormTag object
945     * 
946     * @return The whereClause value
947     */
948    public String getWhereClause() {
949       return whereClause;
950    }
951 
952    /***
953     * Adds a feature to the ChildName attribute of the DbFormTag object
954     * 
955     * @param tableFieldName
956     *           The feature to be added to the ChildName attribute
957     * @param dbFormGeneratedName
958     *           The feature to be added to the ChildName attribute
959     */
960    public void addChildName(String tableFieldName, String dbFormGeneratedName) {
961       childFieldNames.put(dbFormGeneratedName, tableFieldName);
962    }
963 
964    /***
965     * Adds a feature to the JavascriptFunction attribute of the DbFormTag object
966     * 
967     * @param jsFctName
968     *           The feature to be added to the JavascriptFunction attribute
969     * @param jsFct
970     *           The feature to be added to the JavascriptFunction attribute
971     */
972    public void addJavascriptFunction(String jsFctName, StringBuffer jsFct) {
973       if (!existJavascriptFunction(jsFctName)) {
974          javascriptDistinctFunctions.put(jsFctName, jsFct);
975       }
976    }
977 
978    /***
979     * DOCUMENT ME!
980     * 
981     * @param formName
982     *           DOCUMENT ME!
983     * @param childFields
984     *           DOCUMENT ME!
985     */
986    public void addValidationForm(String formName, Hashtable childFields) {
987       if (validationForms == null) {
988          validationForms = new Vector();
989       }
990 
991       validationForms.add(formName);
992 
993       if (validationFields == null) {
994          validationFields = new Hashtable();
995       }
996 
997       validationFields.putAll(childFields);
998    }
999 
1000    /***
1001     * Append the input string to the childElementOutput stringBuffer.
1002     * 
1003     * @param str
1004     *           the string to append
1005     */
1006    public void appendToChildElementOutput(String str) {
1007       if (!Util.isNull(str)) {
1008          this.childElementOutput.append(str);
1009       }
1010    }
1011 
1012    /***
1013     * DOCUMENT ME!
1014     * 
1015     * @return DOCUMENT ME!
1016     * 
1017     * @throws JspException
1018     *            DOCUMENT ME!
1019     */
1020    public int doAfterBody() throws JspException {
1021       if (ResultSetVector.isNull(resultSetVector)) {
1022          setFooterReached(true);
1023 
1024          return SKIP_BODY;
1025       }
1026 
1027       boolean renderPage = !isFooterReached();
1028 
1029       currentCount++;
1030 
1031       int pCount = getCount(); // pCount==-1 => endless form, else max nr.
1032       // of eval.loops is pCount
1033 
1034       logCat.info("we are talking about=" + getTableName() + " pcount=" + pCount + " pcurrent=" + currentCount);
1035 
1036       // field values that have not been rendered by html tags but that is
1037       // determinated by field
1038       // mapping between main- and subform are rendered now:
1039       if (isSubForm() && !isFooterReached()) {
1040          appendToChildElementOutput(produceLinkedTags()); // print
1041          // hidden-fields
1042          // for missing
1043          // insert-fields
1044          // we can
1045          // determinated
1046       }
1047 
1048       if (((pCount != -1) && (currentCount == pCount)) // if the max-count
1049             // is reached
1050             || (ResultSetVector.isNull(getResultSetVector())) || (getResultSetVector().isLast())) {
1051          logCat.info("setting footerreached to true");
1052          setFooterReached(true); // tell parent form that there are no more
1053          // loops do go and the only thing remaining
1054          // to be rendered is this footerTag and its
1055          // subelements
1056       }
1057 
1058       getResultSetVector().moveNext();
1059 
1060       // rsv may be null in empty-forms (where not tableName attribute is
1061       // provided)
1062       if (renderPage) {
1063          return EVAL_BODY_BUFFERED;
1064       } else {
1065          return SKIP_BODY;
1066       }
1067    }
1068 
1069    /***
1070     * D O E N D T A G
1071     * 
1072     * @return Description of the Return Value
1073     * 
1074     * @exception JspException
1075     *               Description of the Exception
1076     */
1077    public int doEndTag() throws JspException {
1078       JspWriter jspOut = pageContext.getOut();
1079 
1080       // avoid to call getOut each time (Demeter law)
1081       try {
1082          if (bodyContent != null) {
1083             bodyContent.writeOut(bodyContent.getEnclosingWriter());
1084             bodyContent.clearBody();
1085 
1086             // 2002116-HKK: workaround for duplicate rows in Tomcat 4.1
1087          }
1088 
1089          logCat.debug("pageContext.getOut()=" + pageContext.getOut());
1090          logCat.debug("childElementOutput=" + childElementOutput);
1091 
1092          // hidden fields and other stuff coming from child elements get
1093          // written out
1094          if (childElementOutput != null) {
1095             jspOut.println(childElementOutput.toString());
1096          }
1097 
1098          if (parentForm == null) {
1099             jspOut.println("</form>");
1100          }
1101 
1102          /* Generate Javascript validation methods & calls */
1103          if (!Util.isNull(getFormValidatorName()) && hasJavascriptValidationSet()) {
1104             jspOut.println(generateJavascriptValidation());
1105          }
1106 
1107          /*
1108           * Generate Javascript array of fields. To help developper to work with
1109           * DbForms fields name.
1110           * 
1111           * Ex: champ1 => f_0_1@root_4
1112           */
1113          if (hasJavascriptFieldsArraySet()) {
1114             jspOut.println(generateJavascriptFieldsArray());
1115          }
1116 
1117          /* Write generic Javascript functions created from childs tag */
1118          if (javascriptDistinctFunctions.size() > 0) {
1119             jspOut.println("\n<SCRIPT language=\"javascript\">\n");
1120 
1121             Enumeration e = javascriptDistinctFunctions.keys();
1122 
1123             while (e.hasMoreElements()) {
1124                String aKey = (String) e.nextElement();
1125                StringBuffer sbFonction = (StringBuffer) javascriptDistinctFunctions.get(aKey);
1126 
1127                jspOut.println(sbFonction);
1128             }
1129 
1130             jspOut.println("\n</SCRIPT>\n");
1131          }
1132       } catch (IOException ioe) {
1133          logCat.error("::doEndTag - IOException", ioe);
1134       }
1135 
1136       logCat.info("end reached of " + tableName);
1137 
1138       return EVAL_PAGE;
1139    }
1140 
1141    /***
1142     * DOCUMENT ME!
1143     */
1144    public void doFinally() {
1145       logCat.info("doFinally called");
1146 
1147       if (validationForms != null) {
1148          validationForms.clear();
1149       }
1150 
1151       if (validationFields != null) {
1152          validationFields.clear();
1153       }
1154 
1155       if (fieldNames != null) {
1156          fieldNames.clear();
1157       }
1158 
1159       if (childFieldNames != null) {
1160          childFieldNames.clear();
1161       }
1162 
1163       sqlFilter = null;
1164       orderFields = null;
1165       overrideFieldCheck = null;
1166    }
1167 
1168    /***
1169     * Philip Grunikiewicz 2001-08-17 Added an attribute to allow developer to
1170     * bypass navigation: If we are not going to use navigation, eliminate the
1171     * creation of, and execution of "fancy!" queries. Henner Kollmann 2002-07-03
1172     * Added onSubmit support Philip Grunikiewicz 2001-10-22 Sometimes we use
1173     * dbForms to simply display information on screen but wish to call another
1174     * servlet to process the post. I've added the 'action' attribute to allow a
1175     * developer to set the wanted action instead of defaulting to the dbforms
1176     * control servlet. Important: Use a fully qualified path. Philip
1177     * Grunikiewicz 2001-11-28 Introducing Free-form select queries. Using the
1178     * whereClause attribute, developers are now able to specify the ending of an
1179     * actual DbForms query. Dbforms generates the "Select From" part, and the
1180     * developers have the responsibility of completing the query. ie: adding the
1181     * whereClause, an orderBy, etc... Note that using this function renders
1182     * navigation impossible!
1183     * 
1184     * @return Description of the Return Value
1185     */
1186    public int doStartTag() throws JspException {
1187       try {
1188          Connection con = getConfig().getConnection(dbConnectionName);
1189 
1190          // *************************************************************
1191          // Part I - checking user access right, processing interceptor
1192          // *************************************************************
1193          HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
1194          HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
1195 
1196          locale = MessageResources.getLocale(request);
1197 
1198          logCat.info("servlet path = " + request.getServletPath());
1199          logCat.info("servlet getPathInfo = " + request.getPathInfo());
1200 
1201          logCat.info("servlet getContextPath = " + request.getContextPath());
1202          logCat.info("servlet getRequestURI = " + request.getRequestURI());
1203 
1204          String strFollowUp = getFollowUp();
1205          if (Util.isNull(strFollowUp)) {
1206             strFollowUp = request.getRequestURI();
1207             String contextPath = request.getContextPath();
1208             if (!Util.isNull(contextPath)) {
1209                strFollowUp = strFollowUp.substring(contextPath.length(), strFollowUp.length());
1210             }
1211             if (!Util.isNull(request.getQueryString())) {
1212                strFollowUp += ("?" + request.getQueryString());
1213             }
1214          }
1215 
1216          logCat.debug("pos1");
1217          // part I/a - security
1218          JspWriter out = pageContext.getOut();
1219 
1220          logCat.debug("pos2");
1221 
1222          // check user privilege
1223          if ((getTable() != null) && !getTable().hasUserPrivileg(request, GrantedPrivileges.PRIVILEG_SELECT)) {
1224             logCat.debug("pos3");
1225 
1226             String str = MessageResourcesInternal.getMessage("dbforms.events.view.nogrant", getLocale(), new String[] { getTable().getName() });
1227             logCat.warn(str);
1228             out.println(str);
1229 
1230             return SKIP_BODY;
1231          }
1232 
1233          logCat.debug("pos4");
1234 
1235          DbEventInterceptorData interceptorData = new DbEventInterceptorData(request, getConfig(), con, getTable());
1236          interceptorData.setAttribute(DbEventInterceptorData.CONNECTIONNAME, dbConnectionName);
1237          interceptorData.setAttribute(DbEventInterceptorData.PAGECONTEXT, pageContext);
1238          interceptorData.setAttribute(DbEventInterceptorData.FORMTAG, this);
1239          interceptorData.setSqlFilterTag(this);
1240 
1241          PresetFormValuesTag.presetFormValues(interceptorData);
1242          // part II/b - processing interceptors
1243          if ((getTable() != null) && getTable().hasInterceptors()) {
1244             try {
1245                logCat.debug("pos5");
1246                getTable().processInterceptors(IDbEventInterceptor.PRE_SELECT, interceptorData);
1247             } catch (Exception sqle) {
1248                logCat.error("pos6");
1249                logCat.error(sqle.getMessage(), sqle);
1250                out.println(sqle.getMessage());
1251 
1252                return SKIP_BODY;
1253             }
1254          }
1255          interceptorData.setSqlFilterTag(null);
1256 
1257          logCat.debug("pos7");
1258 
1259          // *************************************************************
1260          // Part II - WebEvent infrastructural stuff ----
1261          // write out data for the controller so that it
1262          // knows what it deals with if user hits a button
1263          // *************************************************************
1264          StringBuffer tagBuf = new StringBuffer();
1265 
1266          // explicitly re-set all instance variables which get changed during
1267          // evaluation loops
1268          // and which are not reset by the jsp container trough setXxx()
1269          // methods and
1270          logCat.info("resetting values of tag");
1271          currentCount = 0;
1272          footerReached = false;
1273          resultSetVector = null;
1274          childElementOutput = new StringBuffer();
1275          logCat.debug("first steps finished");
1276 
1277          // if main form
1278          // we write out the form-tag which points to the controller-servlet
1279          if (!isSubForm()) {
1280             tagBuf.append("<form ");
1281 
1282             if (!Util.isNull(getId())) {
1283                tagBuf.append("id=\"");
1284                tagBuf.append(getId());
1285                tagBuf.append("\" ");
1286             }
1287 
1288             tagBuf.append("name=\"dbform\" action=\"");
1289 
1290             // Check if developer has overriden action
1291             if ((this.getAction() != null) && (this.getAction().trim().length() > 0)) {
1292                tagBuf.append(this.getAction());
1293             } else {
1294                tagBuf.append(response.encodeURL(FileUtil.normalize(request.getContextPath()) + "/servlet/control"));
1295             }
1296 
1297             tagBuf.append("\"");
1298 
1299             // JFM 20040309: additional attribute autocomplete
1300             if (getAutocomplete() != null) {
1301                tagBuf.append(" autocomplete=\"" + getAutocomplete() + "\"");
1302             }
1303 
1304             // append target element
1305             if (target != null) {
1306                tagBuf.append(" target=\"");
1307                tagBuf.append(target);
1308                tagBuf.append("\"");
1309             }
1310 
1311             tagBuf.append(" method=\"post\"");
1312 
1313             if (hasMultipartSet()) {
1314                tagBuf.append(" enctype=\"multipart/form-data\"");
1315             }
1316 
1317             String validationFct = null;
1318 
1319             if (hasJavascriptValidationSet()) {
1320                validationFct = getFormValidatorName();
1321 
1322                if (!Util.isNull(validationFct)) {
1323                   validationFct = Character.toUpperCase(validationFct.charAt(0)) + validationFct.substring(1, validationFct.length());
1324                   validationFct = "validate" + validationFct + "(this)";
1325                }
1326             }
1327 
1328             if (!Util.isNull(validationFct) && !Util.isNull(getOnSubmit())) {
1329                boolean found = false;
1330                String s = getOnSubmit();
1331                String[] cmds = StringUtils.split(s, ';');
1332 
1333                for (int i = 0; i < cmds.length; i++) {
1334                   cmds[i] = cmds[i].trim();
1335 
1336                   if (cmds[i].startsWith("return")) {
1337                      cmds[i] = cmds[i].substring("return".length());
1338                      cmds[i] = "return " + validationFct + " && " + cmds[i];
1339                      found = true;
1340 
1341                      break;
1342                   }
1343                }
1344 
1345                s = "";
1346 
1347                for (int i = 0; i < cmds.length; i++) {
1348                   s = s + cmds[i] + ";";
1349                }
1350 
1351                if (!found) {
1352                   s = s + "return " + validationFct + ";";
1353                }
1354 
1355                tagBuf.append(" onSubmit=\"");
1356                tagBuf.append(s);
1357                tagBuf.append("\" ");
1358             } else if (!Util.isNull(validationFct)) {
1359                tagBuf.append(" onSubmit=\"");
1360                tagBuf.append("return " + validationFct);
1361                tagBuf.append("\" ");
1362             } else if (!Util.isNull(getOnSubmit())) {
1363                tagBuf.append(" onSubmit=\"");
1364                tagBuf.append(getOnSubmit());
1365                tagBuf.append("\" ");
1366             }
1367 
1368             tagBuf.append(">");
1369 
1370             // supports RFC 1867 - multipart upload, if some database-fields
1371             // represent filedata
1372             if (tableName == null) {
1373                appendSource(request, tagBuf);
1374                tagBuf.append("<input type=\"hidden\" name=\"fu_" + tableId + "\" value=\"" + strFollowUp + "\"/>");
1375 
1376                // if form is an emptyform -> we've fineshed yet - cancel
1377                // all further activities!
1378                out.println(tagBuf.toString());
1379 
1380                return EVAL_BODY_BUFFERED;
1381             }
1382 
1383             positionPathCore = "root";
1384          } else {
1385             // if sub-form, we dont write out html tags; this has been done
1386             // already by a parent form
1387             positionPathCore = parentForm.getPositionPath();
1388 
1389             // If whereClause is not supplied by developer
1390             // determine the value(s) of the linked field(s)
1391             if (Util.isNull(getWhereClause())) {
1392                initChildFieldValues();
1393 
1394                if (childFieldValues == null) {
1395                   return SKIP_BODY;
1396                }
1397             }
1398          }
1399 
1400          // write out involved table
1401          tagBuf.append("<input type=\"hidden\" name=\"invtable\" value=\"" + tableId + "\"/>");
1402 
1403          // write out the name of the involved dbconnection.
1404          tagBuf.append("<input type='hidden' name='invname_" + tableId + "' value='" + (Util.isNull(dbConnectionName) ? "" : dbConnectionName) + "'/>");
1405 
1406          // write out the autoupdate-policy of this form
1407          tagBuf.append("<input type=\"hidden\" name=\"autoupdate_" + tableId + "\" value=\"" + autoUpdate + "\"/>");
1408 
1409          // write out the followup-default for this table
1410          tagBuf.append("<input type=\"hidden\" name=\"fu_" + tableId + "\" value=\"" + strFollowUp + "\"/>");
1411 
1412          // write out the locale
1413          tagBuf.append("<input type=\"hidden\" name=\"lang" + "\" value=\"" + locale.getLanguage() + "\"/>");
1414          tagBuf.append("<input type=\"hidden\" name=\"country" + "\" value=\"" + locale.getCountry() + "\"/>");
1415 
1416          // write out the followupOnError-default for this table
1417          if (!Util.isNull(getFollowUpOnError())) {
1418             tagBuf.append("<input type=\"hidden\" name=\"fue_" + tableId + "\" value=\"" + getFollowUpOnError() + "\"/>");
1419          }
1420 
1421          if (!Util.isNull(getOverrideFieldCheck())) {
1422             tagBuf.append("<input type=\"hidden\" name=\"" + Constants.FIELDNAME_OVERRIDEFIELDTEST + tableId + "\" value=\"" + getOverrideFieldCheck() + "\"/>");
1423          }
1424 
1425          // write out the formValidatorName
1426          if (!Util.isNull(getFormValidatorName())) {
1427             tagBuf.append("<input type=\"hidden\" name=\"" + ValidatorConstants.FORM_VALIDATOR_NAME + "_" + tableId + "\" value=\"" + getFormValidatorName() + "\"/>");
1428          }
1429 
1430          appendSource(request, tagBuf);
1431 
1432          // Allow to send action dynamicaly from javascript
1433          tagBuf.append("<input type=\"hidden\" name=\"customEvent\"/>");
1434 
1435          // *************************************************************
1436          // Part III - fetching Data. This data is provided to all sub-
1437          // elements of this form.
1438          // *************************************************************
1439          // III/1:
1440          // initialize all of the different filters
1441          // retrieve sqlFilters
1442          String sqlFilterString = "";
1443          String requestSqlFilterString = DbFilterTag.getSqlFilter(request, this.getTable().getId());
1444          FieldValue[] sqlFilterParams = null;
1445 
1446          if (!Util.isNull(getSqlFilter()) && !Util.isNull(requestSqlFilterString)) {
1447             sqlFilterString = " ( " + requestSqlFilterString + " ) AND ( " + getSqlFilter() + " ) ";
1448          } else if (!Util.isNull(getSqlFilter())) {
1449             sqlFilterString = getSqlFilter();
1450          } else if (!Util.isNull(requestSqlFilterString)) {
1451             sqlFilterString = requestSqlFilterString;
1452          }
1453 
1454          logCat.debug("filter to apply : " + sqlFilterString);
1455 
1456          if (!Util.isNull(requestSqlFilterString)) {
1457             sqlFilterParams = DbFilterTag.getSqlFilterParams(request, this.getTable().getId());
1458          }
1459 
1460          // overrules other default declarations eventually done in XML
1461          // config;
1462          FieldValue[] overrulingOrder = initOverrulingOrder(request);
1463 
1464          // III/1: first we must determinate the ORDER clause to apply to the
1465          // query
1466          // the order may be specified in the dbforms-config.xml file or in
1467          // the current JSP file.
1468          // tells us which definintion of field order (global or local) to
1469          // use
1470          FieldValue[] orderConstraint;
1471 
1472          // if developer provided orderBy - Attribute in <db:dbform> - tag
1473          if ((overrulingOrder != null) && (overrulingOrder.length > 0)) {
1474             orderConstraint = overrulingOrder;
1475             logCat.info("using OverrulingOrder (dbform tag attribute)");
1476          }
1477          // if developer provided orderBy - Attribute globally in
1478          // dbforms-config.xml - tag
1479          else {
1480             FieldValue[] tmpOrderConstraint = getTable().getDefaultOrder();
1481             orderConstraint = new FieldValue[tmpOrderConstraint.length];
1482 
1483             // cloning is necessary to keep things thread-safe!
1484             // (we manipulate some fields in this structure.)
1485             for (int i = 0; i < tmpOrderConstraint.length; i++) {
1486                orderConstraint[i] = (FieldValue) tmpOrderConstraint[i].clone();
1487             }
1488 
1489             logCat.info("using DefaultOrder (from config file)");
1490          }
1491 
1492          // an orderBY - clause is a MUST. we can't query well without it.
1493          if (orderConstraint == null) {
1494             throw new IllegalArgumentException("OrderBy-Clause must be specified either in table-element in config.xml or in dbform-tag on jsp view");
1495          }
1496 
1497          FieldValue[] filterFieldValues = null;
1498 
1499          if (!Util.isNull(filter)) {
1500             filterFieldValues = getTable().getFilterFieldArray(filter, locale);
1501          }
1502 
1503          FieldValue[] mergedFieldValues = null;
1504 
1505          if (childFieldValues == null) {
1506             mergedFieldValues = filterFieldValues;
1507          } else if (filterFieldValues == null) {
1508             mergedFieldValues = childFieldValues;
1509          } else {
1510             mergedFieldValues = new FieldValue[childFieldValues.length + filterFieldValues.length];
1511             System.arraycopy(childFieldValues, 0, mergedFieldValues, 0, childFieldValues.length);
1512             System.arraycopy(filterFieldValues, 0, mergedFieldValues, childFieldValues.length, filterFieldValues.length);
1513          }
1514 
1515          // if we just habe a search request we do not need any other
1516          // constraints
1517          FieldValue[] searchFieldValues = initSearchFieldValues();
1518 
1519          if (searchFieldValues != null) {
1520             mergedFieldValues = searchFieldValues;
1521          }
1522 
1523          // III/2:
1524          // is there a POSITION we are supposed to navigate to?
1525          // positions are key values: for example "2", oder "2~454"
1526          // String position =
1527          // pageContext.getRequest().getParameter("pos_"+tableId);
1528          String firstPosition = Util.decode(ParseUtil.getParameter(request, "firstpos_" + tableId), pageContext.getRequest().getCharacterEncoding());
1529          String lastPosition = Util.decode(ParseUtil.getParameter(request, "lastpos_" + tableId), pageContext.getRequest().getCharacterEncoding());
1530 
1531          if (firstPosition == null) {
1532             firstPosition = lastPosition;
1533          }
1534 
1535          if (lastPosition == null) {
1536             lastPosition = firstPosition;
1537          }
1538 
1539          // if we are in a subform we must check if the fieldvalue-list
1540          // provided in the
1541          // position strings is valid in the current state;
1542          // it might be invalid if the position of the parent form has been
1543          // changed (by a navigation event)
1544          // (=> the position-strings of childforms arent valid anymore)
1545          if ((childFieldValues != null) && (!Util.isNull(firstPosition))) {
1546             if (!checkLinkage(childFieldValues, firstPosition)) {
1547                // checking one of the 2 strings is sufficient
1548                // the position info is out of date. we dont use it.
1549                firstPosition = null;
1550                lastPosition = null;
1551             }
1552          }
1553 
1554          logCat.info("firstposition " + firstPosition);
1555          logCat.info("lastPosition " + lastPosition);
1556 
1557          /*
1558           * in the code above we examined lots of information which determinates
1559           * _which_ resultset gets retrieved and the way this operation will be
1560           * done.
1561           * 
1562           * which are the "parameters" we have (eventually!) retrieved
1563           * ============================================================ )
1564           * WebEvent Object in request: if the jsp containing this tag was
1565           * invoked by the controller, then there is a Event which has been
1566           * processed (DatebasEvents) or which waits to be processed
1567           * (NavigationEvents, including GotoEvent) ) firstPos, lastPos: Strings
1568           * containing key-fieldValues and indicating a line to go to if we have
1569           * no other information. this may happen if a subform gets navigated.
1570           * the parentForm is not involved in the operation but must be able to
1571           * "navigate" to its new old position. [#checkme: risk of wrong
1572           * interpreation if jsp calls jsp - compare source tags?] )
1573           * mergedFieldValues: this is a cumulation of all rules which restrict
1574           * the result set in any way. it is build of - childFieldValues:
1575           * restricting a set in a subform that all "childFields" in the
1576           * resultset match their respective "parentFields" in main form. (for
1577           * instance if customerID == 100, we only want to select orders from
1578           * orders-table involving customerID 100) - filterFieldValues: if a
1579           * filter is applied to the resultset we always need to select the
1580           * _filtered_ resultset - searchFieldValues: if a search is performed
1581           * we just want to show fields belonging to the search result
1582           * (naturally ;=) ) orderConstraint: this is a cumulation of rules for
1583           * ordering (sorting) and restricting fields.
1584           * 
1585           * one part of it is built either from a) orderBy - clause of dbform
1586           * element b) orderbY - definition in xml config (XPath:
1587           * dbform-config/table/field) this part tells dbforms which
1588           * orderby-clause to create
1589           * 
1590           * but if we combine this "order constraint" with actual values (the
1591           * keys of a row, for example through "firstPos") then we can build
1592           * very powerful queries allowing us to select exectly what we need.
1593           * the order plays an important role in this game, because the "order
1594           * constraint" serves us as tool to make decisions if a row has to be
1595           * BEFORE or AFTER an other. (compare the rather complex methods
1596           * FieldValue.getWhereAfterClause(),
1597           * FieldValue.populateWhereAfterClause() and
1598           * FieldValue.fillWithValues() which are doing most of that stuff
1599           * describe above) ) count: this is a property of DbFormTag. Its
1600           * relevance is that certain operations need to be performed
1601           * differently if count==0, which means the form is an "endless form".
1602           */
1603          // III/3: fetching data (compare description above)
1604          // this code is still expermintal, pre alpha! We need to put this
1605          // logic into
1606          // etter (more readable, maintainable) code (evt. own method or
1607          // class)!
1608          // is there a NAVIGATION event (like "nav to first row" or "nav to
1609          // previous row", or "nav back 4 rows"?
1610          // if so...
1611          webEvent = (AbstractWebEvent) request.getAttribute("webEvent");
1612 
1613          // set actual request to webEvent. Otherwise webEvent will not
1614          // reflect current requestURI!
1615          if (webEvent != null) {
1616             webEvent.setRequest(request);
1617          }
1618 
1619          // if there comes no web event from controller,
1620          // then create a new NAVIGATION event;
1621          //
1622          // # 2002.11.xx-fossato added an event factory
1623          // # 20030320-HKK: Rewrite to use navEvent only
1624          // 2004-06-14 moretti: local event take precedence over NOOP
1625          if (((webEvent == null) || webEvent instanceof org.dbforms.event.NoopEvent) && (getLocalWebEvent() != null)) {
1626             webEvent = navEventFactory.createEvent(localWebEvent, request, getConfig(), getTable());
1627 
1628             // Setted with localWebEvent attribute.
1629             if (webEvent != null) {
1630                request.setAttribute("webEvent", webEvent);
1631             }
1632          }
1633 
1634          // will be used to cast the webEvent to a navigation event;
1635          AbstractNavigationEvent navEvent = null;
1636 
1637          //
1638          // 1. possibility: webEvent is a navigation event ?
1639          //
1640          if ((webEvent != null) && webEvent instanceof AbstractNavigationEvent) {
1641             navEvent = (AbstractNavigationEvent) webEvent;
1642 
1643             if ((navEvent.getTable() == null) || (navEvent.getTable().getId() != tableId)) {
1644                // navigation event is not for this table,
1645                // then just navigate to a position (if it exists) or just
1646                // select all data
1647                // (if no pos or if endless form).
1648                // best way to do this is to delete navEvent. Then a new
1649                // event will be created!
1650                navEvent = null;
1651             }
1652          }
1653 
1654          //
1655          // 2. possibility: webEvent is a goto event ?
1656          //
1657          if (navEvent == null) {
1658             // we need to parse request using the given goto prefix
1659             if (!Util.isNull(gotoPrefix)) {
1660                logCat.info(" NAV GOTO ");
1661 
1662                Vector v = ParseUtil.getParametersStartingWith(request, gotoPrefix);
1663                gotoHt = new Hashtable();
1664 
1665                for (int i = 0; i < v.size(); i++) {
1666                   String paramName = (String) v.elementAt(i);
1667                   String fieldName = paramName.substring(gotoPrefix.length());
1668                   logCat.debug("fieldName=" + fieldName);
1669 
1670                   String fieldValue = ParseUtil.getParameter(request, paramName);
1671                   logCat.debug("fieldValue=" + fieldValue);
1672 
1673                   if ((fieldName != null) && (fieldValue != null)) {
1674                      gotoHt.put(fieldName, fieldValue);
1675                   }
1676                }
1677             }
1678 
1679             // try to create a new GOTO event
1680             if ((gotoHt != null) && (gotoHt.size() > 0)) {
1681                String positionString = getTable().getPositionString(gotoHt);
1682                navEvent = navEventFactory.createGotoEvent(getTable(), request, getConfig(), positionString);
1683             }
1684          }
1685 
1686          //
1687          // 3. a) error in insert event ?
1688          // b) create a GOTO event using a whereClause (free form select)
1689          // c) create a GOTO event using.. another constructor ;^)
1690          //
1691          Vector errors = (Vector) request.getAttribute("errors");
1692          if ((errors != null) && (errors.size() > 0)) {
1693             navEvent = null;
1694          }
1695 
1696          if (navEvent == null) {
1697             // philip grunikiewicz
1698             // 2003-12-16
1699             // Commented code to fix the following problem:
1700             // Insert invoked in sub-form, validation error, suppose to
1701             // re-populate all fields (parent and child)
1702             // Commented code changes all field names to INSERT type -
1703             // making repopulate impossible!
1704             // Henner.Kollmann@gmx.de
1705             // 2004-01-29
1706             // Just commenting out do not work - insert errors on single
1707             // forms are not displayed then!
1708             // Check if table of event and table of form matches should do
1709             // the work!
1710 
1711             if ((webEvent != null) && EventType.EVENT_DATABASE_INSERT.equals(webEvent.getType()) && (errors != null) && (errors.size() > 0) && (webEvent.getTable().getId() == getTable().getId())) {
1712                // error in insert event, nothing to do!
1713                navEvent = null;
1714                resultSetVector = null;
1715                setFooterReached(true);
1716             } else if (!Util.isNull(getWhereClause())) {
1717                // We should do a free form select
1718                navEvent = navEventFactory.createGotoEvent(getTable(), request, getConfig(), whereClause, getTableList());
1719             } else {
1720                String myPosition = (count == 0) ? null : firstPosition;
1721 
1722                if ((webEvent != null)
1723                // && (webEvent instanceof
1724                      // org.dbforms.event.DatabaseEvent)
1725                      && (webEvent instanceof org.dbforms.event.NoopEvent)) {
1726                   myPosition = null;
1727                }
1728 
1729                navEvent = navEventFactory.createGotoEvent(getTable(), request, getConfig(), myPosition);
1730             }
1731          }
1732 
1733          // Now we have a NAVIGATION event to process
1734          logCat.info(" NAV/I ");
1735 
1736          if (navEvent != null) {
1737             logCat.info("about to process nav event:" + navEvent.getClass().getName());
1738             /*
1739              * Philip Grunikiewicz 2005-12-08
1740              * 
1741              * Added a new attribute which allows the developer to control if
1742              * data should be retrieved when search filters are empty.
1743              * searchFilterRequired="true" verifies if the user has specified
1744              * search filter(s). If so, the query is allowed to proceed, if not,
1745              * return empty data
1746              * 
1747              * A check is made to see if: a searchTag contains a value the
1748              * filter attribute (dbforms tag) contains a value a filter tag is
1749              * specified
1750              */
1751 
1752             if (Util.getTrue(getSearchFilterRequired()) && searchFieldValues == null && Util.isNull(filter) && Util.isNull(sqlFilterString)) {
1753                // ResultSet is going to be hardcoded empty
1754                resultSetVector = new ResultSetVector(getTable());
1755             } else {
1756                // Business as usual
1757                resultSetVector = navEvent.processEvent(mergedFieldValues, orderConstraint, sqlFilterString, sqlFilterParams, count, firstPosition, lastPosition, interceptorData);
1758             }
1759 
1760             if (ResultSetVector.isNull(resultSetVector)) {
1761                setFooterReached(true);
1762             }
1763 
1764             orderFields = new FieldValues(orderConstraint);
1765          }
1766 
1767          // *** DONE! ***
1768          // We have now the underlying data, and this data is accessible to
1769          // all sub-elements
1770          // (labels, textFields, etc. of this form
1771          //
1772          // *************************************************************
1773          // Part IV - Again, some WebEvent infrastructural stuff:
1774          // write out data indicating the position we have
1775          // navigated to withing the rowset
1776          // *************************************************************
1777          // we process interceptor again (post-select)
1778          // #checkme: is the overhead of a POST_SELECT interceptor necessary
1779          // or a luxury? => use cases!
1780          if ((getTable() != null) && getTable().hasInterceptors()) {
1781             // process the interceptors associated to this table
1782             interceptorData.setAttribute(DbEventInterceptorData.RESULTSET, resultSetVector);
1783             getTable().processInterceptors(IDbEventInterceptor.POST_SELECT, interceptorData);
1784          }
1785 
1786          // End of interceptor processing
1787          // determinate new position-strings (== value of the first and the
1788          // last row of the current view)
1789          if (!ResultSetVector.isNull(resultSetVector)) {
1790             resultSetVector.moveFirst();
1791             firstPosition = getTable().getPositionString(resultSetVector);
1792             resultSetVector.moveLast();
1793             lastPosition = getTable().getPositionString(resultSetVector);
1794             resultSetVector.moveFirst();
1795          }
1796 
1797          if (!footerReached) {
1798             // if not in insert mode
1799             if (firstPosition != null) {
1800                tagBuf.append("<input type=\"hidden\" name=\"firstpos_" + tableId + "\" value=\"" + Util.encode(firstPosition, pageContext.getRequest().getCharacterEncoding()) + "\"/>");
1801             }
1802 
1803             if (lastPosition != null) {
1804                tagBuf.append("<input type=\"hidden\" name=\"lastpos_" + tableId + "\" value=\"" + Util.encode(lastPosition, pageContext.getRequest().getCharacterEncoding()) + "\"/>");
1805             }
1806          }
1807 
1808          if (!isSubForm()) {
1809             pageContext.setAttribute("dbforms", new Hashtable());
1810          }
1811 
1812          Map dbforms = (Map) pageContext.getAttribute("dbforms");
1813          DbFormContext dbContext = new DbFormContext(getTable().getNamesHashtable(Constants.FIELDNAME_SEARCH), getTable().getNamesHashtable(Constants.FIELDNAME_SEARCHMODE), getTable().getNamesHashtable(Constants.FIELDNAME_SEARCHALGO), resultSetVector);
1814 
1815          if (!ResultSetVector.isNull(resultSetVector)) {
1816             dbContext.setCurrentRow(resultSetVector.getCurrentRowAsMap());
1817             dbContext.setPosition(Util.encode(getTable().getPositionString(resultSetVector), pageContext.getRequest().getCharacterEncoding()));
1818          }
1819 
1820          dbforms.put(getName(), dbContext);
1821 
1822          // construct TEI variables for access from JSP
1823          // # jp 27-06-2001: replacing "." by "_", so that SCHEMATA can be
1824          // used
1825          pageContext.setAttribute("searchFieldNames_" + tableName.replace('.', '_'), getTable().getNamesHashtable(Constants.FIELDNAME_SEARCH));
1826          pageContext.setAttribute("searchFieldModeNames_" + tableName.replace('.', '_'), getTable().getNamesHashtable(Constants.FIELDNAME_SEARCHMODE));
1827          pageContext.setAttribute("searchFieldAlgorithmNames_" + tableName.replace('.', '_'), getTable().getNamesHashtable(Constants.FIELDNAME_SEARCHALGO));
1828 
1829          // #fixme:
1830          // this is a weired crazy workaround [this code is also used in
1831          // DbBodyTag!!]
1832          // why?
1833          // #fixme: explaination! -> initBody, spec, jsp container
1834          // synchronizing variables, etc.
1835          if (!ResultSetVector.isNull(resultSetVector)) {
1836             pageContext.setAttribute("rsv_" + tableName.replace('.', '_'), resultSetVector);
1837             pageContext.setAttribute("currentRow_" + tableName.replace('.', '_'), resultSetVector.getCurrentRowAsMap());
1838             pageContext.setAttribute("position_" + tableName.replace('.', '_'), Util.encode(getTable().getPositionString(resultSetVector), pageContext.getRequest().getCharacterEncoding()));
1839          }
1840 
1841          out.println(tagBuf.toString());
1842          SqlUtil.closeConnection(con);
1843       } catch (MultipleValidationException e) {
1844          logCat.error("::doStartTag - MultipleValidationException", e);
1845          return SKIP_BODY;
1846 
1847       } catch (IOException e) {
1848          logCat.error("::doStartTag - IOException", e);
1849          return SKIP_BODY;
1850 
1851       } catch (SQLException ne) {
1852          SqlUtil.logSqlException(ne);
1853          return SKIP_BODY;
1854       }
1855 
1856       return EVAL_BODY_BUFFERED;
1857    }
1858 
1859    /***
1860     * Check if the input name of the JavaScript function exists.
1861     * 
1862     * @param jsFctName
1863     *           the name of the JS function
1864     * 
1865     * @return true if the function exists, false otherwise
1866     */
1867    public boolean existJavascriptFunction(String jsFctName) {
1868       return javascriptDistinctFunctions.containsKey(jsFctName);
1869    }
1870 
1871    /***
1872     * Gets the captionResource attribute of the DbFormTag object
1873     * 
1874     * @return The captionResource value
1875     */
1876    public boolean hasCaptionResourceSet() {
1877       return Util.getTrue(captionResource);
1878    }
1879 
1880    /***
1881     * Gets the javascriptFieldsArray attribute of the DbFormTag object
1882     * 
1883     * @return The javascriptFieldsArray value
1884     */
1885    public boolean hasJavascriptFieldsArraySet() {
1886       return Util.getTrue(javascriptFieldsArray);
1887    }
1888 
1889    /***
1890     * Gets the javascriptValidation attribute of the DbFormTag object
1891     * 
1892     * @return The javascriptValidation value
1893     */
1894    public boolean hasJavascriptValidationSet() {
1895       return Util.getTrue(javascriptValidation);
1896    }
1897 
1898    /***
1899     * Description of the Method
1900     * 
1901     * @return Description of the Return Value
1902     */
1903    public boolean hasMultipartSet() {
1904       return Util.getTrue(multipart);
1905    }
1906 
1907    /***
1908     * Gets the readOnly attribute of the DbFormTag object
1909     * 
1910     * @return The readOnly value
1911     */
1912    public boolean hasReadOnlySet() {
1913       return Util.getTrue(readOnly);
1914    }
1915 
1916    /***
1917     * Get the redisplayFieldsOnError attribute. <br>
1918     * author: philip grunikiewicz<br>
1919     * creation date: (2001-05-31 13:11:00) <br>
1920     * 
1921     * @return the redisplayFieldsOnError
1922     */
1923    public boolean hasRedisplayFieldsOnErrorSet() {
1924       return Util.getTrue(redisplayFieldsOnError);
1925    }
1926 
1927    /***
1928     * DOCUMENT ME!
1929     * 
1930     * @return DOCUMENT ME!
1931     */
1932    public String produceLinkedTags() {
1933       StringBuffer buf = new StringBuffer();
1934 
1935       // childFieldValues may be null, if we have
1936       // a free form select using attribute whereClause
1937       if (childFieldValues != null) {
1938          for (int i = 0; i < childFieldValues.length; i++) {
1939             if (childFieldValues[i].getRenderHiddenHtmlTag()) {
1940                TextFormatterUtil formatter = new TextFormatterUtil(childFieldValues[i].getField(), getLocale(), null, childFieldValues[i].getFieldValueAsObject());
1941                buf.append("<input type=\"hidden\" name=\"");
1942                buf.append(formatter.getFormFieldName(this));
1943                buf.append("\" value=\"");
1944                buf.append(formatter.getFormattedFieldValue());
1945                buf.append("\" />");
1946                buf.append(formatter.renderOldValueHtmlInputField());
1947                buf.append(formatter.renderPatternHtmlInputField());
1948             }
1949          }
1950       }
1951 
1952       return buf.toString();
1953    }
1954 
1955    /***
1956     * This method gets called by input-tags like "DbTextFieldTag" and others.
1957     * they signalize that _they_ will generate the tag for the controller, not
1958     * this form. <br>
1959     * see produceLinkedTags(). <br>
1960     * This method is only used if this class is instantiated as sub-form (==
1961     * embedded in another form's body tag)
1962     * 
1963     * @param f
1964     *           the Field object
1965     */
1966    public void strikeOut(Field f) {
1967       // childFieldValues may be null, if we have
1968       // a free form select using attribute whereClause
1969       if (childFieldValues != null) {
1970          for (int i = 0; i < childFieldValues.length; i++) {
1971             if ((f == childFieldValues[i].getField()) && childFieldValues[i].getRenderHiddenHtmlTag()) {
1972                childFieldValues[i].setRenderHiddenHtmlTag(false);
1973                logCat.info("stroke out field:" + f.getName());
1974 
1975                return;
1976             }
1977          }
1978       }
1979    }
1980 
1981    /***
1982     * Update the position path.
1983     */
1984 
1985    /* eg: "5" + "@" + "root", "5" + "@" + "123@35@root" */
1986    public void updatePositionPath() {
1987       StringBuffer positionPathBuf = new StringBuffer();
1988       positionPathBuf.append(this.currentCount);
1989       positionPathBuf.append("@");
1990       positionPathBuf.append(this.positionPathCore);
1991       this.positionPath = positionPathBuf.toString();
1992    }
1993 
1994    /***
1995     * DOCUMENT ME!
1996     * 
1997     * @param fields
1998     *           DOCUMENT ME!
1999     */
2000    private void addFieldNames(Hashtable fields) {
2001       if (fieldNames == null) {
2002          fieldNames = new Hashtable();
2003       }
2004 
2005       fieldNames.putAll(fields);
2006    }
2007 
2008    private void appendSource(HttpServletRequest request, StringBuffer tagBuf) {
2009       tagBuf.append("<input type=\"hidden\" name=\"source\" value=\"");
2010 
2011       // J.Peer 03-19-2004: in template driven sites, we may need another
2012       // way of retrieving the source page, because getRequestURI may deliver
2013       // the template name and NOT the actual JSP...
2014       // to cicumvent the problem, the applicatoin may set a request attribute
2015       // "dbforms.source" for each templated page requested
2016       String reqSource = (String) request.getAttribute("dbforms.source");
2017 
2018       // if not set, everything works as usual
2019       if (Util.isNull(reqSource)) {
2020          tagBuf.append(FileUtil.normalize(request.getRequestURI()));
2021       }
2022       // if set, we use this value instead of getRequestURI()
2023       else {
2024          tagBuf.append(reqSource);
2025       }
2026 
2027       if (!Util.isNull(request.getQueryString())) {
2028          tagBuf.append("?").append(request.getQueryString());
2029       }
2030 
2031       tagBuf.append("\"/>");
2032    }
2033 
2034    /***
2035     * if we are in a subform we must check if the fieldvalue-list provided in
2036     * the position strings is valid in the current state it might be invalid if
2037     * the position of the parent form has been changed (by a navigation event)
2038     * (=> the position-strings of childforms arent valid anymore)
2039     * 
2040     * @param childFieldValues
2041     *           Description of the Parameter
2042     * @param aPosition
2043     *           Description of the Parameter
2044     * 
2045     * @return Description of the Return Value
2046     * 
2047     * @throws IllegalArgumentException
2048     *            DOCUMENT ME!
2049     */
2050    private boolean checkLinkage(FieldValue[] achildFieldValues, String aPosition) {
2051       // at first build a hashtable of the provided values
2052       // 2003-03-29 HKK: Change from Hashtable to FieldValueTable
2053       FieldValues ht = getTable().getFieldValues(aPosition);
2054 
2055       for (int i = 0; i < achildFieldValues.length; i++) {
2056          String actualValue = achildFieldValues[i].getFieldValue();
2057          logCat.debug("actualValue=" + actualValue);
2058 
2059          Field f = achildFieldValues[i].getField();
2060          logCat.debug("f.getName=" + f.getName());
2061          logCat.debug("f.getId=" + f.getId());
2062 
2063          FieldValue aFieldValue = ht.get(f.getName());
2064 
2065          if (aFieldValue == null) {
2066             throw new IllegalArgumentException("ERROR: Make sure that field " + f.getName() + " is a KEY or a SORTFIELD of the table " + getTable().getName() + "! Otherwise you can not use it as PARENT/CHILD LINK argument!");
2067          }
2068 
2069          String valueInPos = aFieldValue.getFieldValue();
2070 
2071          logCat.info("comparing " + actualValue + " TO " + valueInPos);
2072 
2073          if (!actualValue.trim().equals(valueInPos.trim())) {
2074             return false;
2075          }
2076       }
2077 
2078       return true;
2079    }
2080 
2081    /***
2082     * Generate Javascript Array of Original field name et DbForm generated name
2083     * Generate Array for each field name. Ex: dbFormFields
2084     * 
2085     * @return Description of the Return Value
2086     */
2087    private StringBuffer generateJavascriptFieldsArray() {
2088       // This section looks hard to understand, but to avoid using
2089       // synchronized object like keySet ... don't want to alter
2090       // childFieldNames hashtable.
2091       // We use different step of enumeration.
2092       StringBuffer result = new StringBuffer();
2093       String key = null;
2094       String val = null;
2095       String values = "";
2096 
2097       Hashtable fields = new Hashtable();
2098       Enumeration e = childFieldNames.keys();
2099 
2100       //
2101       // Loop in each keys "f_0_0@root_2" and create hashtable of unique
2102       // fieldnames
2103       //
2104       while (e.hasMoreElements()) {
2105          key = (String) e.nextElement();
2106          val = (String) childFieldNames.get(key);
2107          values = "";
2108 
2109          if (fields.containsKey(val)) {
2110             values = (String) fields.get(val);
2111          }
2112 
2113          fields.put(val, values + ";" + key);
2114       }
2115 
2116       if (isSubForm()) {
2117          parentForm.addFieldNames(fields);
2118       } else {
2119          if (fieldNames != null) {
2120             fields.putAll(fieldNames);
2121          }
2122 
2123          result.append("<SCRIPT language=\"javascript\">\n");
2124          result.append("<!-- \n\n");
2125          result.append("    var dbFormFields = new Array();\n");
2126          e = fields.keys();
2127 
2128          //
2129          // Loop for each fieldname and generate text for javascript Array
2130          //
2131          // Ex: dbFormFields["DESCRIPTIONDEMANDE"] = new
2132          // Array("f_0_0@root_4", "f_0_1@root_4", "f_0_insroot_4");
2133          //
2134          while (e.hasMoreElements()) {
2135             key = (String) e.nextElement();
2136             val = (String) fields.get(key);
2137             result.append("    dbFormFields[\"").append(key).append("\"] = new Array(");
2138 
2139             // Sort the delimited string and return an ArrayList of it.
2140             ArrayList arrValues = sortFields(val);
2141 
2142             if (arrValues.size() == 1) {
2143                result.append("\"").append((String) arrValues.get(0)).append("\"");
2144             } else {
2145                for (int i = 0; i <= (arrValues.size() - 1); i++) {
2146                   result.append("\"").append((String) arrValues.get(i)).append("\"");
2147 
2148                   if (i != (arrValues.size() - 1)) {
2149                      result.append(", ");
2150                   }
2151                }
2152             }
2153 
2154             result.append(");\n");
2155          }
2156 
2157          result.append("\n    function getDbFormFieldName(name){ \n");
2158          result.append("      return getDbFormFieldName(name,null); \n");
2159          result.append("    }\n\n");
2160          result.append("\n    function getDbFormFieldName(name,pos){ \n");
2161          result.append("      var result = dbFormFields[name]; \n");
2162          result.append("      if(pos==null) return result[result.length-1]; \n");
2163          result.append("      return result[pos]; \n");
2164          result.append("    }\n");
2165          result.append("--></SCRIPT> \n");
2166       }
2167 
2168       return result;
2169    }
2170 
2171    /***
2172     * Generate the Javascript of Validation fields
2173     * 
2174     * @return Description of the Return Value
2175     */
2176    private StringBuffer generateJavascriptValidation() {
2177       if (isSubForm()) {
2178          parentForm.addValidationForm(getFormValidatorName(), childFieldNames);
2179 
2180          return new StringBuffer();
2181       } else {
2182          ValidatorResources vr = (ValidatorResources) pageContext.getServletContext().getAttribute(ValidatorConstants.VALIDATOR);
2183          DbFormsErrors errors = (DbFormsErrors) pageContext.getServletContext().getAttribute(DbFormsErrors.ERRORS);
2184          addValidationForm(getFormValidatorName(), childFieldNames);
2185 
2186          return DbFormsValidatorUtil.getJavascript(validationForms, MessageResources.getLocale((HttpServletRequest) pageContext.getRequest()), validationFields, vr, getJavascriptValidationSrcFile(), errors);
2187       }
2188    }
2189 
2190    /***
2191     * Initialize child values
2192     */
2193    private void initChildFieldValues() {
2194       // if parent form has no data, we can not render a subform!
2195       if (ResultSetVector.isNull(parentForm.getResultSetVector())) {
2196          childFieldValues = null;
2197 
2198          // childFieldValues remains null
2199          return;
2200       }
2201 
2202       String aPosition = parentForm.getTable().getPositionString(parentForm.getResultSetVector());
2203 
2204       if (Util.isNull(aPosition)) {
2205          childFieldValues = null;
2206 
2207          // childFieldValues remains null
2208          return;
2209       }
2210 
2211       childFieldValues = getTable().mapChildFieldValues(parentForm.getTable(), parentField, childField, aPosition).toArray();
2212    }
2213 
2214    // ------------------------ business, helper & utility methods
2215    // --------------------------------
2216 
2217    /***
2218     * Initialise datastructures containing informations about how table should
2219     * be ordered. The information is specified in the JSP this tags lives in.
2220     * This declaration OVERRULES other default declarations eventually done in
2221     * XML config! (compara Table.java !)
2222     * 
2223     * @param request
2224     *           the request object
2225     * 
2226     * @return DOCUMENT ME!
2227     */
2228    private FieldValue[] initOverrulingOrder(HttpServletRequest request) {
2229       // if page A links to page B (via a gotoButton, for instance) then we do
2230       // not
2231       // want A's order constraints get applied to B
2232       if (request != null) {
2233          String refSource = request.getRequestURI();
2234 
2235          if (request.getQueryString() != null) {
2236             refSource += ("?" + request.getQueryString());
2237          }
2238 
2239          String sourceTag = ParseUtil.getParameter(request, "source");
2240          logCat.info("!comparing page " + refSource + " TO " + sourceTag);
2241 
2242          // if (!Util.isNull(sourceTag) && !refSource.equals(sourceTag)) {
2243          // request = null;
2244          // }
2245       }
2246 
2247       logCat.debug("orderBy=" + orderBy);
2248 
2249       // if we have neither an orderby clause nor a request we may use then we
2250       // cant create orderconstraint
2251       if ((orderBy == null) && (request == null)) {
2252          return null;
2253       }
2254 
2255       // otherwise we can:
2256       FieldValue[] overrulingOrder = getTable().createOrderFieldValues(orderBy, request, false);
2257       overrulingOrderFields = new Vector();
2258 
2259       if (overrulingOrder != null) {
2260          for (int i = 0; i < overrulingOrder.length; i++)
2261             overrulingOrderFields.addElement(overrulingOrder[i].getField());
2262       }
2263 
2264       return overrulingOrder;
2265    }
2266 
2267    /***
2268     * Initialize the value of the search fields.
2269     * 
2270     * @return the field values array
2271     * 
2272     * @todo Whats when there is more then one search field whith the same name?
2273     *       Maybe we should parse all of them ....
2274     */
2275    private FieldValue[] initSearchFieldValues() {
2276       FieldValue[] fieldValues;
2277       HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
2278       Vector searchFieldNames = ParseUtil.getParametersStartingWith(request, Constants.FIELDNAME_SEARCH);
2279 
2280       if ((searchFieldNames == null) || (searchFieldNames.size() == 0)) {
2281          return null;
2282       }
2283 
2284       Vector mode_and = new Vector();
2285       Vector mode_or = new Vector();
2286 
2287       for (int i = 0; i < searchFieldNames.size(); i++) {
2288          String searchFieldName = (String) searchFieldNames.elementAt(i);
2289          String aSearchFieldPattern = ParseUtil.getParameter(request, Constants.FIELDNAME_PATTERNTAG + searchFieldName);
2290          String aSearchFieldValue = ParseUtil.getParameter(request, searchFieldName);
2291          aSearchFieldValue = aSearchFieldValue.trim();
2292 
2293          // ie. search_1_12 is mapped to "john"
2294          if (!Util.isNull(aSearchFieldValue)) {
2295             int firstUnderscore = searchFieldName.indexOf('_');
2296             int secondUnderscore = searchFieldName.indexOf('_', firstUnderscore + 1);
2297             int ptableId = Integer.parseInt(searchFieldName.substring(firstUnderscore + 1, secondUnderscore));
2298             int fieldId = Integer.parseInt(searchFieldName.substring(secondUnderscore + 1));
2299 
2300             Table ptable = getConfig().getTable(ptableId);
2301             Field f = ptable.getField(fieldId);
2302 
2303             if (f != null) {
2304                String aSearchMode = ParseUtil.getParameter(request, f.getSearchModeName());
2305                int mode = ("or".equals(aSearchMode)) ? Constants.SEARCHMODE_OR : Constants.SEARCHMODE_AND;
2306                String aSearchAlgorithm = ParseUtil.getParameter(request, f.getSearchAlgoName());
2307 
2308                if (ptable.getId() != getTable().getId()) {
2309                   if (isSubForm()) {
2310                      f = null;
2311                   } else {
2312                      // Table from request is different to table of form
2313                      // Try to find field in the current table
2314                      Field fTest = f;
2315                      f = getTable().getFieldByName(fTest.getName());
2316 
2317                      if (f != null) {
2318                         // There is a field with the same name - now
2319                         // check fieldtype!
2320                         if (fTest.getType() != f.getType()) {
2321                            f = null;
2322                            // Do this only if there is no original
2323                            // searchfield in the
2324                            // request
2325                         } else if (searchFieldNames.contains(f.getSearchFieldName())) {
2326                            f = null;
2327                         } else {
2328                            // put the value into the request so that
2329                            // the SearchTag will find
2330                            // it there with the changed value!
2331                            request.setAttribute(f.getSearchFieldName(), aSearchFieldValue);
2332                         }
2333                      }
2334                   }
2335                }
2336 
2337                if (f != null) {
2338                   // Check for operator
2339                   int algorithm = Constants.SEARCH_ALGO_SHARP;
2340                   int operator = Constants.FILTER_EQUAL;
2341 
2342                   if (!Util.isNull(aSearchAlgorithm)) {
2343                      if (aSearchAlgorithm.startsWith("sharpLT")) {
2344                         operator = Constants.FILTER_SMALLER_THEN;
2345                      } else if (aSearchAlgorithm.startsWith("sharpLE")) {
2346                         operator = Constants.FILTER_SMALLER_THEN_EQUAL;
2347                      } else if (aSearchAlgorithm.startsWith("sharpGT")) {
2348                         operator = Constants.FILTER_GREATER_THEN;
2349                      } else if (aSearchAlgorithm.startsWith("sharpGE")) {
2350                         operator = Constants.FILTER_GREATER_THEN_EQUAL;
2351                      } else if (aSearchAlgorithm.startsWith("sharpNE")) {
2352                         operator = Constants.FILTER_NOT_EQUAL;
2353                      } else if (aSearchAlgorithm.startsWith("sharpNULL")) {
2354                         operator = Constants.FILTER_NULL;
2355                      } else if (aSearchAlgorithm.startsWith("sharpNOTNULL")) {
2356                         operator = Constants.FILTER_NOT_NULL;
2357                      } else if (aSearchAlgorithm.startsWith("weakStartEnd")) {
2358                         algorithm = Constants.SEARCH_ALGO_WEAK_START_END;
2359                         operator = Constants.FILTER_LIKE;
2360                      } else if (aSearchAlgorithm.startsWith("weakStart")) {
2361                         algorithm = Constants.SEARCH_ALGO_WEAK_START;
2362                         operator = Constants.FILTER_LIKE;
2363                      } else if (aSearchAlgorithm.startsWith("weakEnd")) {
2364                         algorithm = Constants.SEARCH_ALGO_WEAK_END;
2365                         operator = Constants.FILTER_LIKE;
2366                      } else if (aSearchAlgorithm.startsWith("weak")) {
2367                         algorithm = Constants.SEARCH_ALGO_WEAK;
2368                         operator = Constants.FILTER_LIKE;
2369                      }
2370                   }
2371 
2372                   if ((aSearchAlgorithm == null) || (aSearchAlgorithm.toLowerCase().indexOf("extended") == -1)) {
2373                      // Extended not found, only append field
2374                      FieldValue fv = FieldValue.createFieldValueForSearching(f, aSearchFieldValue, getLocale(), operator, mode, algorithm, false);
2375 
2376                      if (!Util.isNull(aSearchFieldPattern)) {
2377                         fv.setPattern(aSearchFieldPattern);
2378                      }
2379 
2380                      if (mode == Constants.SEARCHMODE_AND) {
2381                         mode_and.addElement(fv);
2382                      } else {
2383                         mode_or.addElement(fv);
2384                      }
2385                   } else if (aSearchFieldValue.indexOf("-") != -1) {
2386                      // is extended searching and delimiter found in
2387                      // SearchFieldValue
2388                      // create 2 searchfields
2389                      algorithm = Constants.SEARCH_ALGO_EXTENDED;
2390 
2391                      StringTokenizer st = new StringTokenizer(" " + aSearchFieldValue + " ", "-");
2392                      int tokenCounter = 0;
2393 
2394                      StringBuffer merkeDate = new StringBuffer();
2395                      StringBuffer merkeTime = new StringBuffer();
2396 
2397                      while (st.hasMoreTokens()) {
2398                         aSearchFieldValue = st.nextToken().trim();
2399                         tokenCounter++;
2400 
2401                         if (aSearchFieldValue.length() > 0) {
2402                            switch (tokenCounter) {
2403                            case 1:
2404                               operator = Constants.FILTER_GREATER_THEN_EQUAL;
2405                               TimeUtil.splitDate(aSearchFieldValue, merkeDate, merkeTime);
2406                               break;
2407 
2408                            case 2:
2409                               operator = Constants.FILTER_SMALLER_THEN_EQUAL;
2410                               StringBuffer mDate = new StringBuffer();
2411                               StringBuffer mTime = new StringBuffer();
2412                               TimeUtil.splitDate(aSearchFieldValue, mDate, mTime);
2413                               if (mDate.length() == 0) {
2414                                  mDate.append(merkeDate);
2415                               }
2416                               mDate.append(" ");
2417                               mDate.append(mTime);
2418                               aSearchFieldValue = mDate.toString();
2419 
2420                               break;
2421 
2422                            default:
2423                               operator = -1;
2424 
2425                               break;
2426                            }
2427 
2428                            if (operator != -1) {
2429                               FieldValue fv = FieldValue.createFieldValueForSearching(f, aSearchFieldValue, getLocale(), operator, mode, algorithm, false);
2430 
2431                               if (!Util.isNull(aSearchFieldPattern)) {
2432                                  fv.setPattern(aSearchFieldPattern);
2433                               }
2434 
2435                               if (mode == Constants.SEARCHMODE_AND) {
2436                                  mode_and.addElement(fv);
2437                               } else {
2438                                  mode_or.addElement(fv);
2439                               }
2440                            }
2441                         }
2442                      }
2443                   } else {
2444                      // parse special chars in SearchFieldValue
2445                      int jump = 0;
2446 
2447                      // Check for Not Equal
2448                      if (aSearchFieldValue.startsWith("<>")) {
2449                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2450                         operator = Constants.FILTER_NOT_EQUAL;
2451                         jump = 2;
2452 
2453                         // Check for not equal
2454                      } else if (aSearchFieldValue.startsWith("!=")) {
2455                         // GreaterThenEqual found! - Store the operation
2456                         // for
2457                         // use later on
2458                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2459                         operator = Constants.FILTER_NOT_EQUAL;
2460                         jump = 2;
2461 
2462                         // Check for GreaterThanEqual
2463                      } else if (aSearchFieldValue.startsWith(">=")) {
2464                         // GreaterThenEqual found! - Store the operation
2465                         // for
2466                         // use later on
2467                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2468                         operator = Constants.FILTER_GREATER_THEN_EQUAL;
2469                         jump = 2;
2470 
2471                         // Check for GreaterThan
2472                      } else if (aSearchFieldValue.startsWith(">")) {
2473                         // GreaterThen found! - Store the operation for
2474                         // use
2475                         // later on
2476                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2477                         operator = Constants.FILTER_GREATER_THEN;
2478 
2479                         // Check for SmallerThenEqual
2480                      } else if (aSearchFieldValue.startsWith("<=")) {
2481                         // SmallerThenEqual found! - Store the operation
2482                         // for
2483                         // use later on
2484                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2485                         operator = Constants.FILTER_SMALLER_THEN_EQUAL;
2486                         jump = 2;
2487 
2488                         // Check for SmallerThen
2489                      } else if (aSearchFieldValue.startsWith("<")) {
2490                         // SmallerThen found! - Store the operation for
2491                         // use
2492                         // later on
2493                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2494                         operator = Constants.FILTER_SMALLER_THEN;
2495                         jump = 1;
2496 
2497                         // Check for equal
2498                      } else if (aSearchFieldValue.startsWith("=")) {
2499                         // Equal found! - Store the operator for use
2500                         // later
2501                         // on
2502                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2503                         operator = Constants.FILTER_EQUAL;
2504                         jump = 1;
2505                      } else if (aSearchFieldValue.startsWith("[NULL]")) {
2506                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2507                         operator = Constants.FILTER_NULL;
2508                         aSearchFieldValue = "";
2509                         jump = 0;
2510                      } else if (aSearchFieldValue.startsWith("[!NULL]")) {
2511                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2512                         operator = Constants.FILTER_NOT_NULL;
2513                         aSearchFieldValue = "";
2514                         jump = 0;
2515                      } else if (aSearchFieldValue.startsWith("[EMPTY]")) {
2516                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2517                         operator = Constants.FILTER_EMPTY;
2518                         aSearchFieldValue = "";
2519                         jump = 0;
2520                      } else if (aSearchFieldValue.startsWith("[!EMPTY]")) {
2521                         algorithm = Constants.SEARCH_ALGO_EXTENDED;
2522                         operator = Constants.FILTER_NOT_EMPTY;
2523                         aSearchFieldValue = "";
2524                         jump = 0;
2525                      }
2526 
2527                      if (jump > 0) {
2528                         aSearchFieldValue = aSearchFieldValue.substring(jump).trim();
2529                      }
2530 
2531                      Vector errors = (Vector) request.getAttribute("errors");
2532 
2533                      if ((operator == Constants.FILTER_EQUAL) && (jump == 0) && (f.getType() == FieldTypes.TIMESTAMP)) {
2534                         // found a single timestamp value. Extend it to
2535                         // >value and <end of day of value
2536                         operator = Constants.FILTER_GREATER_THEN_EQUAL;
2537 
2538                         FieldValue fv = FieldValue.createFieldValueForSearching(f, aSearchFieldValue, getLocale(), operator, mode, algorithm, false);
2539 
2540                         if (!Util.isNull(aSearchFieldPattern)) {
2541                            fv.setPattern(aSearchFieldPattern);
2542                         }
2543 
2544                         Date d = (Date) fv.getFieldValueAsObject();
2545 
2546                         if (d == null) {
2547                            errors.add(new Exception(MessageResourcesInternal.getMessage("dbforms.error.filter.invalid.date", getLocale())));
2548                         } else {
2549                            if (mode == Constants.SEARCHMODE_AND) {
2550                               mode_and.addElement(fv);
2551                            } else {
2552                               mode_or.addElement(fv);
2553                            }
2554 
2555                            operator = Constants.FILTER_SMALLER_THEN_EQUAL;
2556                            d = TimeUtil.findEndOfDay(d);
2557                            aSearchFieldValue = d.toString();
2558 
2559                            fv = FieldValue.createFieldValueForSearching(f, aSearchFieldValue, getLocale(), operator, mode, algorithm, false);
2560 
2561                            if (!Util.isNull(aSearchFieldPattern)) {
2562                               fv.setPattern(aSearchFieldPattern);
2563                            }
2564 
2565                            if (mode == Constants.SEARCHMODE_AND) {
2566                               mode_and.addElement(fv);
2567                            } else {
2568                               mode_or.addElement(fv);
2569                            }
2570                         }
2571                      } else {
2572                         FieldValue fv = FieldValue.createFieldValueForSearching(f, aSearchFieldValue, getLocale(), operator, mode, algorithm, false);
2573 
2574                         if (!Util.isNull(aSearchFieldPattern)) {
2575                            fv.setPattern(aSearchFieldPattern);
2576                         }
2577 
2578                         Object obj = fv.getFieldValueAsObject();
2579 
2580                         if (obj == null) {
2581                            errors.add(new Exception(MessageResourcesInternal.getMessage("dbforms.error.filter.invalid", getLocale())));
2582                         } else {
2583                            if (mode == Constants.SEARCHMODE_AND) {
2584                               mode_and.addElement(fv);
2585                            } else {
2586                               mode_or.addElement(fv);
2587                            }
2588                         }
2589                      }
2590                   }
2591                }
2592             }
2593          }
2594       }
2595 
2596       int andBagSize = mode_and.size();
2597       int orBagSize = mode_or.size();
2598       int criteriaFieldCount = andBagSize + orBagSize;
2599       logCat.info("criteriaFieldCount=" + criteriaFieldCount);
2600 
2601       if (criteriaFieldCount == 0) {
2602          return null;
2603       }
2604 
2605       // now we construct the fieldValues array
2606       // we ensure that the searchmodes are not mixed up
2607       fieldValues = new FieldValue[criteriaFieldCount];
2608 
2609       int i = 0;
2610 
2611       for (i = 0; i < andBagSize; i++) {
2612          fieldValues[i] = (FieldValue) mode_and.elementAt(i);
2613       }
2614 
2615       for (int j = 0; j < orBagSize; j++) {
2616          fieldValues[j + i] = (FieldValue) mode_or.elementAt(j);
2617       }
2618 
2619       return fieldValues;
2620    }
2621 
2622    /***
2623     * Use by generateJavascriptFieldsArray() to sort the order of field name.
2624     * 
2625     * @param str
2626     *           Description of the Parameter
2627     * 
2628     * @return Description of the Return Value
2629     */
2630    private ArrayList sortFields(String str) {
2631       /*
2632        * Sort delimited string of DbForms field, and return ArraList of this
2633        * result.
2634        * 
2635        * Ex: "f_0_1@root_1;f_0_insroot_1;f_0_0@root_1;"
2636        * 
2637        * result : "f_0_0@root_1" "f_0_1@root_1" "f_0_insroot_1;"
2638        */
2639       ArrayList arr = new ArrayList();
2640       String tmp = "";
2641       String tmp1 = null;
2642       String tmp2 = null;
2643       String insroot = null;
2644       int ident1 = 0;
2645       int ident2 = 0;
2646       StringTokenizer token = new StringTokenizer(str, ";");
2647 
2648       while (token.hasMoreTokens()) {
2649          tmp = token.nextToken();
2650 
2651          if (tmp.indexOf("@root") != -1) {
2652             arr.add(tmp);
2653          } else {
2654             insroot = tmp;
2655          }
2656       }
2657 
2658       if (insroot != null) {
2659          arr.add(insroot);
2660       }
2661 
2662       // String[] result = (String[]) arr.toArray(new String[arr.size()]);
2663       if (arr.size() == 1) {
2664          return arr;
2665       }
2666 
2667       for (int i = 0; i <= (arr.size() - 2); i++) {
2668          tmp1 = (String) arr.get(i);
2669 
2670          for (int j = i + 1; j <= (arr.size() - 1); j++) {
2671             tmp2 = (String) arr.get(j);
2672 
2673             if ((tmp1.indexOf("@root") != -1) && (tmp2.indexOf("@root") != -1)) {
2674                try {
2675                   ident1 = Integer.parseInt(tmp1.substring(tmp1.indexOf("_", 2) + 1, tmp1.indexOf("@")));
2676                   ident2 = Integer.parseInt(tmp2.substring(tmp2.indexOf("_", 2) + 1, tmp2.indexOf("@")));
2677                } catch (Exception e) {
2678                   ident1 = -1;
2679                   ident2 = -1;
2680                }
2681 
2682                if (ident2 < ident1) {
2683                   arr.set(i, tmp2);
2684                   arr.set(j, tmp1);
2685                   tmp1 = tmp2;
2686                }
2687             }
2688          }
2689       }
2690 
2691       return arr;
2692    }
2693 
2694    public String getSearchFilterRequired() {
2695       return searchFilterRequired;
2696    }
2697 
2698    public void setSearchFilterRequired(String searchFilterRequired) {
2699       this.searchFilterRequired = searchFilterRequired;
2700    }
2701 }