001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org)
002 // The Molly framework is freely distributable under the terms of an
003 // MIT-style license. For details, see the molly pages web site at:
004 // http://www.mollypages.org/. Use, modify, have fun !
005
006 package fc.web.forms;
007
008 import javax.servlet.*;
009 import javax.servlet.http.*;
010 import java.io.*;
011 import java.util.*;
012 import java.sql.*;
013
014 import fc.jdbc.*;
015 import fc.io.*;
016 import fc.util.*;
017
018 /**
019 An object that contains form data submitted by a user. This object
020 represents the data submitted by a browser upon each time the form is
021 submitted. This object can then be passed along to various other methods
022 in the form framework as needed. This object can also be created manually
023 before the form is shown if default/initial values must differ per user
024 (say, when user must initially see their last filled value which is
025 retreived from a database).
026
027 @see {@link Form#handleSubmit handleSubmit}.
028 @author hursh jain
029 */
030 public final class FormData
031 {
032 //The following 2 store the same info, one all the
033 //error messages, the other validator-->error message
034 //
035 //list of of form errors
036 List formValidateErrorsList;
037 //form-validator-->error message
038 Map formValidateErrorsMap;
039 //
040 //fieldname->list of errors
041 Map fieldValidateErrors;
042 //submitted value of each field, fieldname->value
043 Map fieldValues;
044 //
045 Set disabledFields;
046 //dynamically added fields
047 Map dynamicFields;
048 //
049 boolean dependencyUpdated = false;
050 //
051 Log log;
052 //
053 Connection connection; /*remains null typically until set*/
054 //
055 Map messages; //usually null
056 //
057 Map validatedData; //mostly null
058
059 public FormData(Log log) {
060 this.log = log;
061 fieldValues = new HashMap();
062 formValidateErrorsList = new ArrayList();
063 formValidateErrorsMap = new HashMap();
064 fieldValidateErrors = new HashMap();
065 disabledFields = new HashSet();
066 }
067
068 public FormData() {
069 this(Log.getDefault());
070 }
071
072 /*
073 Adds a new validation error for the specified field
074 */
075 List createFieldErrorList(Field field)
076 {
077 String name = field.name;
078 List list = (List) fieldValidateErrors.get(name);
079 if (list == null) {
080 list = new ArrayList();
081 fieldValidateErrors.put(name, list);
082 }
083 return list;
084 }
085
086 /**
087 Returns a map containing all validation errors for all
088 fields [map: field->List_of_errors]
089 */
090 Map getFieldErrors() {
091 return fieldValidateErrors;
092 }
093
094 /**
095 Returns a list containing all validation errors for the
096 specified field. If
097 */
098 List getFieldErrors(Field field) {
099 return (List) fieldValidateErrors.get(field.name);
100 }
101
102 void addFormError(String validatorName, String errmsg)
103 {
104 formValidateErrorsList.add(errmsg);
105
106 if (validatorName != null)
107 {
108 List list = (List) formValidateErrorsMap.get(validatorName);
109 if (list == null) {
110 list = new ArrayList();
111 formValidateErrorsMap.put(validatorName, list);
112 }
113 list.add(errmsg);
114 }
115 }
116
117 /**
118 Returns a list containing all form level validation errors
119 */
120 Collection getFormErrorsCollection() {
121 return formValidateErrorsList;
122 }
123
124 /**
125 Returns a map containing all form level validation errors keyed by the
126 validator name. The value in the map is a List containing all errors for
127 that validator. If the error was added using Form.addError(fd, null, "foo"),
128 then the MAP won't have any data (since the key was null) but the form error
129 LIST will have the error.
130 */
131 Map getFormErrorsMap() {
132 return formValidateErrorsMap;
133 }
134
135 void putData(String fieldname, Object obj) {
136 fieldValues.put(fieldname, obj);
137 }
138
139 //set since i keep forgetting whether it's set or put
140 void setData(String fieldname, Object obj) {
141 fieldValues.put(fieldname, obj);
142 }
143
144 Object getData(String fieldname) {
145 return fieldValues.get(fieldname);
146 }
147
148 void enableField(String fieldname, boolean enable)
149 {
150 if (enable) {
151 if (disabledFields.remove(fieldname));
152 }
153 else {
154 disabledFields.add(fieldname);
155 }
156 }
157
158 boolean isEnabled(String fieldname)
159 {
160 return ! disabledFields.contains(fieldname);
161 }
162
163
164 void addMessage(String key, String value)
165 {
166 //no need to synchronize this, form processing is sequential/per-thread
167 //for each fd.
168 if (messages == null)
169 messages = new HashMap();
170
171 messages.put(key, value);
172 }
173
174 String getMessage(String key)
175 {
176 if (messages == null || ! messages.containsKey(key))
177 return "";
178
179 return (String) messages.get(key);
180 }
181
182 //================== Public methods =============================
183 //For convenience. These can go in the form itself if need be and made
184 //private here.
185
186 /**
187 Useful if strings entered by the user in the browser form are converted
188 to dates/times etc as part of the validation. Validators can then put
189 those date/time/whatever objects here and then those same conversions
190 don't have to be repeated at a later stage while saving the form out to
191 a database.
192 */
193 public void putValidatedData(String fieldname, Object obj)
194 {
195 if (validatedData == null)
196 validatedData = new HashMap();
197
198 validatedData.put(fieldname, obj);
199 }
200
201 public Object getValidatedData(String fieldname)
202 {
203 return validatedData.get(fieldname);
204 }
205
206
207
208 /**
209 Sets a connection to be used during this request. Meant primarily for
210 refresher/dependent fields.
211 */
212 public void setConnection(Connection con) {
213 this.connection = con;
214 }
215
216 public Connection getConnection() {
217 return connection;
218 }
219
220 /**
221 Adds a {@link Hidden} field to this form. This field exists only for the
222 duration of this form data object. This field can be rendered.
223 */
224 public void addDynamicField(Hidden hidden) {
225 //no need to synchronize this, form processing is sequential & mem-vis
226 //not a concern... i guess
227 if (dynamicFields == null)
228 dynamicFields = new HashMap();
229
230 hidden.dynamicallyAdded = true;
231 dynamicFields.put(hidden.getName(), hidden);
232 }
233
234 /**
235 Gets a {@link Hidden} field added to this formdata object. Returns
236 <tt>null</tt> if that field does not exist.
237 */
238 public Hidden getDynamicField(String fieldName)
239 {
240 if (dynamicFields == null) {
241 log.warn("Cannot obtain field:", fieldName, " from this formdata. This field is only available after being added to the formdata and is lost when the formdata object is lost, typically after the form is rendered.");
242 return null;
243 }
244
245 return (Hidden) dynamicFields.get(fieldName);
246 }
247
248 /**
249 Dependencies should call this method to set the dependencyUpdated flag to
250 true anytime there is a dependency change.
251 */
252 public void setDependencyUpdated()
253 {
254 dependencyUpdated = true;
255 }
256
257 public String toString()
258 {
259 return new ToString(this, ToString.Style.VisibleLevel.PRIVATE)
260 .reflect().render();
261 }
262
263 }