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 006package fc.web.forms; 007 008import javax.servlet.*; 009import javax.servlet.http.*; 010import java.io.*; 011import java.util.*; 012import java.sql.*; 013 014import fc.jdbc.*; 015import fc.io.*; 016import fc.util.*; 017 018/** 019An object that contains form data submitted by a user. This object 020represents the data submitted by a browser upon each time the form is 021submitted. This object can then be passed along to various other methods 022in the form framework as needed. This object can also be created manually 023before 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 025retreived from a database). 026 027@see {@link Form#handleSubmit handleSubmit}. 028@author hursh jain 029*/ 030public 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 036List formValidateErrorsList; 037//form-validator-->error message 038Map formValidateErrorsMap; 039// 040//fieldname->list of errors 041Map fieldValidateErrors; 042//submitted value of each field, fieldname->value 043Map fieldValues; 044// 045Set disabledFields; 046//dynamically added fields 047Map dynamicFields; 048// 049boolean dependencyUpdated = false; 050// 051Log log; 052// 053Connection connection; /*remains null typically until set*/ 054// 055Map messages; //usually null 056// 057Map validatedData; //mostly null 058 059public 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 068public FormData() { 069 this(Log.getDefault()); 070 } 071 072/* 073Adds a new validation error for the specified field 074*/ 075List 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/** 087Returns a map containing all validation errors for all 088fields [map: field->List_of_errors] 089*/ 090Map getFieldErrors() { 091 return fieldValidateErrors; 092 } 093 094/** 095Returns a list containing all validation errors for the 096specified field. If 097*/ 098List getFieldErrors(Field field) { 099 return (List) fieldValidateErrors.get(field.name); 100 } 101 102void 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/** 118Returns a list containing all form level validation errors 119*/ 120Collection getFormErrorsCollection() { 121 return formValidateErrorsList; 122 } 123 124/** 125Returns a map containing all form level validation errors keyed by the 126validator name. The value in the map is a List containing all errors for 127that validator. If the error was added using Form.addError(fd, null, "foo"), 128then the MAP won't have any data (since the key was null) but the form error 129LIST will have the error. 130*/ 131Map getFormErrorsMap() { 132 return formValidateErrorsMap; 133 } 134 135void putData(String fieldname, Object obj) { 136 fieldValues.put(fieldname, obj); 137 } 138 139//set since i keep forgetting whether it's set or put 140void setData(String fieldname, Object obj) { 141 fieldValues.put(fieldname, obj); 142 } 143 144Object getData(String fieldname) { 145 return fieldValues.get(fieldname); 146 } 147 148void enableField(String fieldname, boolean enable) 149 { 150 if (enable) { 151 if (disabledFields.remove(fieldname)); 152 } 153 else { 154 disabledFields.add(fieldname); 155 } 156 } 157 158boolean isEnabled(String fieldname) 159 { 160 return ! disabledFields.contains(fieldname); 161 } 162 163 164void 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 174String 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/** 187Useful if strings entered by the user in the browser form are converted 188to dates/times etc as part of the validation. Validators can then put 189those date/time/whatever objects here and then those same conversions 190don't have to be repeated at a later stage while saving the form out to 191a database. 192*/ 193public void putValidatedData(String fieldname, Object obj) 194 { 195 if (validatedData == null) 196 validatedData = new HashMap(); 197 198 validatedData.put(fieldname, obj); 199 } 200 201public Object getValidatedData(String fieldname) 202 { 203 return validatedData.get(fieldname); 204 } 205 206 207 208/** 209Sets a connection to be used during this request. Meant primarily for 210refresher/dependent fields. 211*/ 212public void setConnection(Connection con) { 213 this.connection = con; 214 } 215 216public Connection getConnection() { 217 return connection; 218 } 219 220/** 221Adds a {@link Hidden} field to this form. This field exists only for the 222duration of this form data object. This field can be rendered. 223*/ 224public 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/** 235Gets a {@link Hidden} field added to this formdata object. Returns 236<tt>null</tt> if that field does not exist. 237*/ 238public 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/** 249Dependencies should call this method to set the dependencyUpdated flag to 250true anytime there is a dependency change. 251*/ 252public void setDependencyUpdated() 253 { 254 dependencyUpdated = true; 255 } 256 257public String toString() 258 { 259 return new ToString(this, ToString.Style.VisibleLevel.PRIVATE) 260 .reflect().render(); 261 } 262 263}