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.*;
012
013import fc.jdbc.*;
014import fc.io.*;
015import fc.util.*;
016
017/** 
018Represents an HTML hidden field. If this object is created with an array of
019string values, then each string value will be written out as a seperate
020hidden field and all such written hidden fields will have the same name.
021When the form is submitted, all values can be retrieved as an array of
022strings.
023
024@author hursh jain
025**/
026public final class Hidden extends Field
027{
028static String[] empty_arr = new String[] { "" };
029
030final static class Data {
031  String[] values;
032  }
033  
034String[] orig_values;
035boolean  dynamicallyAdded = false;
036
037/** 
038Creates a new text element with the initial value set to an empty string.
039This is useful in cases where we are interested in the presense/absense of
040the name of the hidden field and don't care about a seperate value.
041**/
042public Hidden(String name)
043  {
044  this(name, "");
045  }
046  
047/** 
048Creates a new hidden element with the specified initial value.
049If the initial value is specified as <tt>null</tt>, it is set to be
050an empty string.
051**/
052public 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/** 
062Convenience constructor to creates a new hidden element with the 
063specified initial integer value (converted to a string).
064**/
065public Hidden(String name, int value)
066  {
067  super(name);
068  orig_values = new String[] { String.valueOf(value) };
069  }
070  
071/** 
072Creates a new hidden element with the specified initial values.
073**/
074public 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/** 
086Creates a new hidden element with the specified initial values. Each
087element in the List will be added by invoking <tt>toString</tt> on it.
088**/
089public 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
107public Field.Type getType() {
108  return Field.Type.HIDDEN;
109  }
110
111public 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/** 
145Returns a String representing the value of this field or <tt>null</tt>
146if there is no current value. If more than one value exists for this
147hidden field, use the {@link getValues} method. If called when more
148than one value exists, this method will return any 1 arbitrary value.
149*/
150public 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/**
165Returns a String[] containing all values associated with this field.
166*/
167public 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/**
182Convenience method that returns one value of this field as a String with
183null values being returned as an empty string). <b>If more than one value
184exists, use the {@link #getValues} method instead.</b>
185
186@throws NumberFormatException if the value could not be
187                returned as in integer. 
188*/
189public String getStringValue(FormData fd)
190  {
191  String  s = getValue(fd);
192  if (s == null)
193    s = "";
194  return s;
195  }
196
197/**
198Convenience method that returns one value of this field as a Integer. <b>If
199more 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*/
204public 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/**
212Convenience method that returns one value of this field as a Short. <b>If
213more 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*/
218public 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/**
226Convenience method that returns one value of this field as a boolean. The
227value is converted into a boolean as per the {@link
228Boolean.valueOf(String)} method. <b>If more than one value exists, use the
229{@link #getValues} method instead.</b>
230*/
231public boolean getBooleanValue(FormData fd) {
232  return Boolean.valueOf(getValue(fd)).booleanValue();
233  }
234
235/**
236The values to render this field with. If the form has not been shown to the
237user at all or if the specified fd object is null, returns the original
238values. Also if this field was dynamically added to the formdata, the
239original values will be returned (since there will never be any submitted
240values).
241*/
242String[] 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
259public void setValueFromSubmit(FormData fd, HttpServletRequest req) 
260throws 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/** 
278Sets the <b>initial</b> values of this text element.
279**/
280public 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/** 
291Sets the <b>initial</b> values of this text element.
292**/
293public 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
303public 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
321public void reset(FormData fd) {
322  Data data = (Data) fd.getData(name);
323  data.values = orig_values;
324  }
325
326public String toString() 
327  {
328  return super.toString() + "; Orig. value: [" + Arrays.toString(orig_values) + "]"; 
329  } 
330
331}          //~class Hidden