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.simpleforms;
007
008import javax.servlet.*;
009import javax.servlet.http.*;
010import java.io.*;
011import java.util.*;
012
013import fc.io.*;
014
015/**
016Convenience class to store arbitrary form validation errors
017and messages. This class should be instantiated per request 
018as needed (when there are form validation errors). 
019<p>
020Note: This class is not thread-safe but that's not a concern
021since only each seperate user request is handled by at 
022most 1 thread.
023
024@author hursh jain
025**/
026public final class Errors
027{
028Log   log = Log.getDefault();
029
030List  formErrors;
031Map   fieldErrors;
032List  formWarnings;
033Map   fieldWarnings;
034
035/**
036Adds a form level error, typically associated with the form
037itself and/or multiple fields as a group.
038*/
039public void addFormError(String msg)
040  {
041  if (formErrors == null)
042    formErrors = new ArrayList();
043  
044  formErrors.add(msg);
045  }
046
047/**
048Adds a field validation error
049
050@param  fieldName the name of the field
051@param  msg     some error object, typically a string but
052          can be a list of strings (for example) if
053          there is more than 1 validation error for
054          this field
055*/
056public void addFieldError(String fieldName, Object msg)
057  {
058  if (fieldErrors == null)
059    fieldErrors = new HashMap();
060  
061  Object old = fieldErrors.put(fieldName, msg);
062  if (old != null) {
063    log.warn("Use a list to add more than 1 message. I am currently over-writing the previous error message for field: " + fieldName + " [old msg=" + old + "] [new msg=" + msg + "]");
064    }
065  }
066
067/**
068Adds an arbitrary warning message generated as part of form processing
069*/
070public void addFormWarning(String msg)
071  {
072  if (formWarnings == null)
073    formWarnings = new ArrayList();
074  
075  formWarnings.add(msg);
076  }
077
078/**
079Adds an arbitrary warning message generated as part of form 
080processing. This warning is associated with the specified field.
081*/
082public void addFieldWarning(String fieldname, Object msg)
083  {
084  if (fieldWarnings == null)
085    fieldWarnings = new HashMap();
086  
087  Object old = fieldWarnings.put(fieldname, msg);
088  if (old != null) {
089    log.warn("Use a list to add more than 1 warning. I am currently over-writing the previous warning message for field: " + fieldname + " [old msg=" + old + "] [new msg=" + msg + "]");
090    }
091  }
092
093/**
094Returns a list of all form errors or null if no errors are present.
095*/
096public List getFormErrors() {
097  return formErrors;
098  }
099
100/**
101Returns the field error for the specified fieldname or null if no 
102error was found.
103*/
104public Object getFieldError(String fieldName) 
105  {
106  if (fieldErrors == null)
107    return null;
108    
109  return fieldErrors.get(fieldName);
110  }
111
112/**
113Returns the list of all form-level warnings or <tt>null</tt> if no
114warnings exist for the form.
115*/
116public List getFormWarnings() {
117  return formWarnings;
118  }
119
120/**
121Returns the warning for the specified field or <tt>null</tt> if
122no warning exists.
123*/
124public Object getFieldWarning(String fieldname) {
125  return fieldWarnings.get(fieldname);
126  }
127
128/**
129Returns true if there are any form or field errors. (although
130warnings are allowed)
131*/
132public boolean hasError() {
133  return formErrors != null || fieldErrors != null;
134  }
135  
136/**
137Returns true if there are any warnings.
138*/
139public boolean hasWarning() {
140  return formWarnings != null || fieldWarnings != null;
141  }
142  
143/**
144Returns true if there are any warnings for the specified <u>field</u>
145*/
146public boolean hasWarning(String fieldname) {
147  return fieldWarnings != null && fieldWarnings.containsKey(fieldname);
148  }
149
150  
151/**
152Convenience method to render all the form errors (if present).
153For more control, obtain the form errors and print them manually.
154Invoking this method has the following effect:
155<blockquote><pre>
156String after = "&lt;br&gt;";
157List list = error.getFormErrors();
158if (list != null) 
159  {
160    out.write("&lt;div class='form-errmsg'&gt;");
161    out.write("&lt;ul&gt;");
162    for (int n = 0; n < list.size(); n++) {
163      out.write("&lt;li&gt;");
164        out.write(String.valueOf(list.get(n)));
165        out.write("&lt;/li&gt;");
166        }
167    out.write("&lt;/ul&gt;");
168    out.write("&lt;/div&gt;\n");
169    }
170</pre></blockquote>
171*/
172public void renderFormErrors(Writer out) throws IOException
173  {
174  if (formErrors == null) {
175    return;
176    }
177
178  if (formErrors == null)
179    return;
180    
181  out.write("<div class='form-errmsg'>");
182  out.write("\n<ul>");
183  
184  for (int n = 0; n < formErrors.size(); n++) {
185    Object obj = formErrors.get(n);
186    out.write("<li>");
187    out.write(String.valueOf(obj));
188    out.write("</li>\n");
189    }
190
191  out.write("</ul>\n"); 
192  out.write("</div>");
193  }
194
195/**
196Convenience method to render a field error. Invoking this
197method is a shorthand for saying (where <tt>error</tt> is
198an instance of this class):
199<blockquote><pre>
200Object obj = error.getFieldError("some_field_name");
201if (str != null) {
202    out.write("&lt;span class='field-errmsg'&gt;");
203    out.write (String.valueOf(obj));
204    out.write("&lt;/span&gt;\n");
205    out.write("&lt;br&gt;"); 
206    }
207</pre></blockquote>
208The above is the same as:
209<blockquote><pre>
210error.<font color=blue>render</font>(out, "some_field_name");
211</pre></blockquote>
212Note: The object representing the error for the field is written as
213is. Typically for strings, this works fine. However, for more complex
214objects (like say a list holding more than 1 error for the same field),
215the list is printed as-is. For more formatting options for complex
216objects, obtain and print the error manually.
217*/
218public void render(Writer out, String fieldName) throws IOException
219  {
220  if (fieldErrors == null) {
221    return;
222    }
223
224  Object obj = fieldErrors.get(fieldName);
225  if (obj == null)
226    return;
227
228  out.write("<span class='field-errmsg'>");
229  out.write(String.valueOf(obj));
230  out.write("</span>");
231  out.write("<br>");
232  }
233
234
235/**
236Convenience method to render a field error. Invoking this
237method is a shorthand for saying (where <tt>error</tt> is
238an instance of this class):
239<blockquote><pre>
240Object obj = error.getFieldError("some_field_name");
241if (str != null) {
242    out.write("&lt;span class='field-errmsg'&gt;");
243    out.write (String.valueOf(obj));
244    out.write(<font color=blue>"inside"</font>);
245    out.write("&lt;/span&gt;\n");
246    out.write(<font color=blue>"outside"</font>); 
247    }
248</pre></blockquote>
249The above is the same as (for example):
250<blockquote><pre>
251error.<font color=blue>render</font>(out, "some_field_name", <font color=blue>"inside"</font>, <font color=blue>"outside"</font>);
252</pre></blockquote>
253Note: The object representing the error for the field is written as
254is. Typically for strings, this works fine. However, for more complex
255objects (like say a list holding more than 1 error for the same field),
256the list is printed as-is. For more formatting options for complex
257objects, obtain and print the error manually.
258
259@param inside   this string is written before the span tag is closed
260@param outside    this string is written right after the span tag is closed.
261*/
262public void render(
263 Writer out, String fieldName, String inside, String outside) 
264 throws IOException
265  {
266  if (fieldErrors == null) {
267    return;
268    }
269
270  Object obj = fieldErrors.get(fieldName);
271  if (obj == null)
272    return;
273
274  out.write("<span class='field-errmsg'>");
275  out.write(String.valueOf(obj));
276  out.write(inside);
277  out.write("</span>");
278  out.write(outside);
279  }
280
281
282
283public static void main (String args[]) throws Exception
284  {
285  Errors e = new Errors();
286  e.addFieldError("a", "some field error");
287  List list = new ArrayList();
288  list.add("item1");
289  list.add("item2");
290  e.addFieldError("b", list);
291  System.out.println("Has warnings: [should be false]: " + e.hasWarning());
292  System.out.println("Has errors: [should be true]:    " + e.hasError());
293  e.addFormError("some form error");
294  System.out.println("Has errors: [should be true]:    " + e.hasError());
295  PrintWriter pw = new PrintWriter(System.out);
296  pw.println("\n--------- form errors ---------");
297  e.renderFormErrors(pw);
298  pw.println("\n--------- field errors [a]--------");
299  e.render(pw, "a");
300  pw.println("\n--------- field errors [b]--------");
301  e.render(pw, "b");
302  pw.println("\n--------- field errors [c/non-existent]--------");
303  e.render(pw, "c");
304  pw.flush();
305  }
306
307}