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.simpleforms;
007    
008    import javax.servlet.*;
009    import javax.servlet.http.*;
010    import java.io.*;
011    import java.util.*;
012    
013    import fc.io.*;
014    
015    /**
016    Convenience class to store arbitrary form validation errors
017    and messages. This class should be instantiated per request 
018    as needed (when there are form validation errors). 
019    <p>
020    Note: This class is not thread-safe but that's not a concern
021    since only each seperate user request is handled by at 
022    most 1 thread.
023    
024    @author hursh jain
025    **/
026    public final class Errors
027    {
028    Log   log = Log.getDefault();
029    
030    List  formErrors;
031    Map   fieldErrors;
032    List  formWarnings;
033    Map   fieldWarnings;
034    
035    /**
036    Adds a form level error, typically associated with the form
037    itself and/or multiple fields as a group.
038    */
039    public void addFormError(String msg)
040      {
041      if (formErrors == null)
042        formErrors = new ArrayList();
043      
044      formErrors.add(msg);
045      }
046    
047    /**
048    Adds 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    */
056    public 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    /**
068    Adds an arbitrary warning message generated as part of form processing
069    */
070    public void addFormWarning(String msg)
071      {
072      if (formWarnings == null)
073        formWarnings = new ArrayList();
074      
075      formWarnings.add(msg);
076      }
077    
078    /**
079    Adds an arbitrary warning message generated as part of form 
080    processing. This warning is associated with the specified field.
081    */
082    public 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    /**
094    Returns a list of all form errors or null if no errors are present.
095    */
096    public List getFormErrors() {
097      return formErrors;
098      }
099    
100    /**
101    Returns the field error for the specified fieldname or null if no 
102    error was found.
103    */
104    public Object getFieldError(String fieldName) 
105      {
106      if (fieldErrors == null)
107        return null;
108        
109      return fieldErrors.get(fieldName);
110      }
111    
112    /**
113    Returns the list of all form-level warnings or <tt>null</tt> if no
114    warnings exist for the form.
115    */
116    public List getFormWarnings() {
117      return formWarnings;
118      }
119    
120    /**
121    Returns the warning for the specified field or <tt>null</tt> if
122    no warning exists.
123    */
124    public Object getFieldWarning(String fieldname) {
125      return fieldWarnings.get(fieldname);
126      }
127    
128    /**
129    Returns true if there are any form or field errors. (although
130    warnings are allowed)
131    */
132    public boolean hasError() {
133      return formErrors != null || fieldErrors != null;
134      }
135      
136    /**
137    Returns true if there are any warnings.
138    */
139    public boolean hasWarning() {
140      return formWarnings != null || fieldWarnings != null;
141      }
142      
143    /**
144    Returns true if there are any warnings for the specified <u>field</u>
145    */
146    public boolean hasWarning(String fieldname) {
147      return fieldWarnings != null && fieldWarnings.containsKey(fieldname);
148      }
149    
150      
151    /**
152    Convenience method to render all the form errors (if present).
153    For more control, obtain the form errors and print them manually.
154    Invoking this method has the following effect:
155    <blockquote><pre>
156    String after = "&lt;br&gt;";
157    List list = error.getFormErrors();
158    if (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    */
172    public 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    /**
196    Convenience method to render a field error. Invoking this
197    method is a shorthand for saying (where <tt>error</tt> is
198    an instance of this class):
199    <blockquote><pre>
200    Object obj = error.getFieldError("some_field_name");
201    if (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>
208    The above is the same as:
209    <blockquote><pre>
210    error.<font color=blue>render</font>(out, "some_field_name");
211    </pre></blockquote>
212    Note: The object representing the error for the field is written as
213    is. Typically for strings, this works fine. However, for more complex
214    objects (like say a list holding more than 1 error for the same field),
215    the list is printed as-is. For more formatting options for complex
216    objects, obtain and print the error manually.
217    */
218    public 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    /**
236    Convenience method to render a field error. Invoking this
237    method is a shorthand for saying (where <tt>error</tt> is
238    an instance of this class):
239    <blockquote><pre>
240    Object obj = error.getFieldError("some_field_name");
241    if (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>
249    The above is the same as (for example):
250    <blockquote><pre>
251    error.<font color=blue>render</font>(out, "some_field_name", <font color=blue>"inside"</font>, <font color=blue>"outside"</font>);
252    </pre></blockquote>
253    Note: The object representing the error for the field is written as
254    is. Typically for strings, this works fine. However, for more complex
255    objects (like say a list holding more than 1 error for the same field),
256    the list is printed as-is. For more formatting options for complex
257    objects, 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    */
262    public 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    
283    public 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    }