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    
013    import fc.jdbc.*;
014    import fc.io.*;
015    import fc.util.*;
016    
017    /** 
018    Represents an HTML hidden field. If this object is created with an array of
019    string values, then each string value will be written out as a seperate
020    hidden field and all such written hidden fields will have the same name.
021    When the form is submitted, all values can be retrieved as an array of
022    strings.
023    
024    @author hursh jain
025    **/
026    public final class Hidden extends Field
027    {
028    static String[] empty_arr = new String[] { "" };
029    
030    final static class Data {
031      String[] values;
032      }
033      
034    String[] orig_values;
035    boolean  dynamicallyAdded = false;
036    
037    /** 
038    Creates a new text element with the initial value set to an empty string.
039    This is useful in cases where we are interested in the presense/absense of
040    the name of the hidden field and don't care about a seperate value.
041    **/
042    public Hidden(String name)
043      {
044      this(name, "");
045      }
046      
047    /** 
048    Creates a new hidden element with the specified initial value.
049    If the initial value is specified as <tt>null</tt>, it is set to be
050    an empty string.
051    **/
052    public Hidden(String name, String value)
053      {
054      super(name);
055      if (value == null) 
056        value = "";
057    
058      orig_values = new String[] { value };
059      }
060    
061    /** 
062    Convenience constructor to creates a new hidden element with the 
063    specified initial integer value (converted to a string).
064    **/
065    public Hidden(String name, int value)
066      {
067      super(name);
068      orig_values = new String[] { String.valueOf(value) };
069      }
070      
071    /** 
072    Creates a new hidden element with the specified initial values.
073    **/
074    public Hidden(String name, String[] values) 
075      {
076      super(name);
077      if (values == null) {
078        log.warn("Specified values were null, defaulting to \"\"");
079        orig_values = empty_arr;
080        }
081      else
082        orig_values = values;
083      }
084    
085    /** 
086    Creates a new hidden element with the specified initial values. Each
087    element in the List will be added by invoking <tt>toString</tt> on it.
088    **/
089    public Hidden(String name, final List values) 
090      {
091      super(name);
092      if (values == null) {
093        log.warn("Specified values were null, defaulting to \"\"");
094        orig_values = empty_arr;
095        }
096      else{
097        //was: orig_values = (String[]) values.toArray(new String[0]);
098        //this is more foolproof:
099        final int size = values.size();
100        orig_values = new String[size];
101        for (int n = 0; n < size; n++) {
102          orig_values[n] = values.get(n).toString();
103          }
104        }
105      }
106    
107    public Field.Type getType() {
108      return Field.Type.HIDDEN;
109      }
110    
111    public void renderImpl(FormData fd, Writer writer) throws IOException 
112      {
113      /*
114      We maintain the submit state even for hidden fields because it is 
115      possible that some javascript on the client side might modify the 
116      contents of a hidden field.
117      */
118      String[] values = getRenderValues(fd);
119      
120      for (int n = 0; n < values.length; n++) 
121        {
122        writer.write("<input type='");
123        writer.write(getType().toString());
124        writer.write("' name='");
125        writer.write(name);
126        writer.write("'");
127        
128        if (values != null) {
129          writer.write(" value='");
130          writer.write(values[n]);
131          writer.write("'"); 
132          }
133    
134        final int arlen = arbitraryString.size();
135        for (int k = 0; k < arlen; k++) {
136          writer.write(" ");
137          writer.write(arbitraryString.get(k).toString());
138          }
139      
140        writer.write("></input>\n"); 
141        }
142      }
143          
144    /** 
145    Returns a String representing the value of this field or <tt>null</tt>
146    if there is no current value. If more than one value exists for this
147    hidden field, use the {@link getValues} method. If called when more
148    than one value exists, this method will return any 1 arbitrary value.
149    */
150    public String getValue(FormData fd) 
151      {
152      if (dynamicallyAdded) {
153        return orig_values[0];
154        }
155        
156      Data data = (Data) fd.getData(name);
157    
158      if (data == null)
159        return null;
160          
161      return data.values[0];
162      }
163    
164    /**
165    Returns a String[] containing all values associated with this field.
166    */
167    public String[] getValues(FormData fd) 
168      {
169      if (dynamicallyAdded)
170        return orig_values;
171    
172      Data data = (Data) fd.getData(name);
173      
174      if (data == null)
175        return null;
176          
177      return data.values;
178      }
179    
180    
181    /**
182    Convenience method that returns one value of this field as a String with
183    null values being returned as an empty string). <b>If more than one value
184    exists, use the {@link #getValues} method instead.</b>
185    
186    @throws NumberFormatException if the value could not be
187                    returned as in integer. 
188    */
189    public String getStringValue(FormData fd)
190      {
191      String  s = getValue(fd);
192      if (s == null)
193        s = "";
194      return s;
195      }
196    
197    /**
198    Convenience method that returns one value of this field as a Integer. <b>If
199    more than one value exists, use the {@link #getValues} method instead.</b>
200    
201    @throws NumberFormatException if the value could not be
202                    returned as an integer. 
203    */
204    public int getIntValue(FormData fd) {
205      String value = getValue(fd);
206      if (value != null)
207        value = value.trim();
208      return Integer.parseInt(value);
209      }
210    
211    /**
212    Convenience method that returns one value of this field as a Short. <b>If
213    more than one value exists, use the {@link #getValues} method instead.</b>
214    
215    @throws NumberFormatException if the value could not be
216                    returned as a short.  
217    */
218    public short getShortValue(FormData fd) {
219      String value = getValue(fd);
220      if (value != null)
221        value = value.trim();
222      return Short.parseShort(value);
223      }
224    
225    /**
226    Convenience method that returns one value of this field as a boolean. The
227    value is converted into a boolean as per the {@link
228    Boolean.valueOf(String)} method. <b>If more than one value exists, use the
229    {@link #getValues} method instead.</b>
230    */
231    public boolean getBooleanValue(FormData fd) {
232      return Boolean.valueOf(getValue(fd)).booleanValue();
233      }
234    
235    /**
236    The values to render this field with. If the form has not been shown to the
237    user at all or if the specified fd object is null, returns the original
238    values. Also if this field was dynamically added to the formdata, the
239    original values will be returned (since there will never be any submitted
240    values).
241    */
242    String[] getRenderValues(FormData fd) 
243      {
244      if (dynamicallyAdded)
245        return orig_values;
246      
247      if (fd != null) {
248        Data data = (Data) fd.getData(name);  
249        if (data == null)
250          return empty_arr;
251        else  
252          return data.values;
253        }
254      else { //fd == null, no form data, showing form for first time
255        return orig_values;
256        }
257      }
258    
259    public void setValueFromSubmit(FormData fd, HttpServletRequest req) 
260    throws SubmitHackedException
261      {
262      String[] values = req.getParameterValues(name);
263      
264      if (values == null) 
265        {  //client was buggy or hacked  [or field = disabled]
266        if (! enabled || ! isEnabled(fd)) {  
267          hacklert(req, "Bug/hack alert: did not find ["+name+"] field in the request (but expected to), defaulting to original values");
268          }
269        values = empty_arr;
270        }
271          
272      Data data = new Data();
273      fd.putData(name, data);
274      data.values = values;
275      }
276    
277    /** 
278    Sets the <b>initial</b> values of this text element.
279    **/
280    public void setValue(String[] values) 
281      {
282      if (values == null) {
283        log.warn("specified values param was null, defaulting to \"\"");
284        values = empty_arr;
285        }
286        
287      this.orig_values = values;
288      } 
289    
290    /** 
291    Sets the <b>initial</b> values of this text element.
292    **/
293    public void setValue(String value) 
294      {
295      if (value == null) {
296        log.warn("specified values param was null, defaulting to \"\"");
297        value = "";
298        }
299      
300      this.orig_values = new String[] { value };
301      } 
302    
303    public boolean isFilled(FormData fd) 
304      {
305      Data data = (Data) fd.getData(name);
306      
307      if (data == null)
308        return false;
309        
310      String[] values = data.values;
311      
312      if (values == null) {
313        log.error("Internal error: unexpected state");
314        }
315      
316      //since it's always at least some string -- possibly
317      //all spaces but it'll be non-null
318      return true; 
319      }
320    
321    public void reset(FormData fd) {
322      Data data = (Data) fd.getData(name);
323      data.values = orig_values;
324      }
325    
326    public String toString() 
327      {
328      return super.toString() + "; Orig. value: [" + Arrays.toString(orig_values) + "]"; 
329      } 
330    
331    }          //~class Hidden