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 }