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/** 
018Abstracts an HTML choice type such as choicebox or radio.
019Does <b>not</b> abstract a HTML select element for which the
020{@link Select} class should be used. Concrete subclasses
021represent a particular choice element such as {@link
022Checkbox} and {@link Radio}
023
024@author hursh jain
025**/
026public abstract class Choice extends Field
027{
028//if this choice object's original state == selected
029private boolean orig_selected;
030//can be null if not specified
031private String  value;  
032
033static class Data {
034  //the value actually submitted by the browser (could
035  //be 'on', 'ON' etc., or the value for that option (if set) 
036  private String  submit_value;
037  }
038
039/** 
040Creates a new choice object.
041
042@param  name    the field name
043@param  value   the value of this choice item (can be null
044          for unspecified)
045@param  selected  <tt>true</tt> is this choice is 
046          originally selected
047**/
048protected Choice(String name, String value, boolean selected)
049  {
050  super(name);
051  this.value = value;
052  this.orig_selected = selected;
053  }
054
055public abstract Field.Type getType();
056
057/** 
058Returns the current <b>value</b> of this field. This can be:
059<blockquote>
060  <li><tt>null</tt>: if no value is currently set which can happen if
061  this field was not selected when the parent form was submitted.
062  Browsers send nothing at all if choice type fields are not selected
063  in an HTML form.
064  <li>the value attribute of this field (if this choice field was
065  created/displayed with a value attribute) or the string "on" (which
066  is sent by browsers if there is no specific value attribute for this
067  choice field). Note, the default value should be treated as case
068  <u>in</u>sensitive, since browsers can send <tt>on</tt>, <tt>ON</tt>
069  etc.
070</blockquote>
071**/
072public String getValue(FormData fd) 
073  {
074  Choice.Data data = (Choice.Data) fd.getData(name);
075
076  if (data == null)
077    return null;
078
079  return data.submit_value;
080  }
081
082/**
083Sets the <u>selected or non-selected state</u> for this choice in the
084specified form data. Selected choices are returned by the browser as
085<tt>on, ON, oN</tt> etc., the value returned is not important as long as
086something is returned. Therefore <i>any non-null value</i> set by this method
087will have the effect of selecting this choice when it is rendered. A <tt>
088null</tt> value wil unselect this choice.
089
090@param  fd    a non-null form data object
091@param  value any non-null value
092*/
093public void setValue(FormData fd, String value)
094  {
095  Argcheck.notnull(fd, "specified fd param was null");
096  
097  if (value == null)
098    return;
099    
100  Choice.Data data = new Choice.Data();
101  fd.putData(name, data);
102  data.submit_value = value;  
103  }
104
105/**
106Convenience method that sets this choice to be selected/non-selected.
107
108@param  fd      a non-null form data object
109@param  selected  true to select this choice, false otherwise
110*/
111public void setValue(FormData fd, boolean selected)
112  {
113  Argcheck.notnull(fd, "specified fd param was null");
114    
115  if (selected) {
116    Choice.Data data = new Choice.Data();
117    fd.putData(name, data);
118    //not submit_value = value since value can be null    
119    data.submit_value = "on";
120    }
121  }
122
123/**
124Sets the value for this choice.
125*/
126public void setValue(String value, boolean selected)
127  {
128  Argcheck.notnull(value, "specified value was null");
129  
130  this.value = value; 
131  this.orig_selected = selected;
132  }
133
134/**
135Convenience method that returns the value of this 
136field as a Integer. 
137
138@throws NumberFormatException if the value could not be
139                returned as in integer. 
140*/
141public int getIntValue(FormData fd) {
142  String value = getValue(fd);
143  if (value != null)
144    value = value.trim();
145  return Integer.parseInt(value);
146  }
147
148/**
149Convenience method that returns the value of this 
150field as a Short. 
151
152@throws NumberFormatException if the value could not be
153                returned as a short.  
154*/
155public short getShortValue(FormData fd) {
156  String value = getValue(fd);
157  if (value != null)
158    value = value.trim();
159  return Short.parseShort(value);
160  }
161
162
163/**
164Convenience method that returns the value of this field as a boolean.
165<u>The returned value will be <tt>true</tt> if the submitted value is
166"true" or "on" (both case insensitive), else <tt>false</tt></u>
167*/
168public boolean getBooleanValue(FormData fd) 
169  {
170  String value = getValue(fd);
171  if (value == null)
172    return false;
173  
174  if (value.equalsIgnoreCase("on"))
175    value = "true";
176    
177  return Boolean.valueOf(value).booleanValue();
178  }
179
180//the value actually submitted by the browser (could
181//be 'on', 'ON' etc., or the value for that option if set 
182public void setValueFromSubmit(FormData fd, HttpServletRequest req) 
183throws SubmitHackedException  
184  { 
185  String submittedValue = req.getParameter(name);
186  
187  //choice was not selected 
188  if (submittedValue == null)
189    return;
190    
191  Choice.Data data = new Choice.Data();
192  fd.putData(name, data);
193  
194  if (value != null && ! value.equals(submittedValue))
195    hacklert(req, "submitted value: [" + submittedValue + "] does not equal the value [" + value + "]of this field.");
196    
197  data.submit_value = submittedValue; 
198  //log.bug(name, "setting data=", submittedValue);
199  }
200
201public void renderImpl(FormData fd, Writer writer) throws IOException
202  {
203  boolean selected = false;
204  
205  //no formdata, rendering first time
206  if (fd == null) {
207    selected = this.orig_selected;
208    }
209  else {  //submit or initial data
210    String returned_value = getValue(fd); //can be null 
211    if (returned_value != null) 
212      selected = true;
213    }
214
215  writer.write("<input type='");
216  writer.write(getType().toString());
217  writer.write("' name='");
218  writer.write(name);
219  writer.write("'");
220  
221  if (value != null) {
222    //we always write the orig. value, not the returned
223    //one (since the returned value should be the same
224    //as the original value unless hacked)
225    writer.write(" value='");
226    writer.write(value);
227    writer.write("'"); 
228    }
229
230  if (! enabled || ! isEnabled(fd)) {
231    writer.write(" disabled");
232    }
233  
234  if (selected) {
235    writer.write(" checked");
236    }
237    
238  if (renderStyleTag) {
239    writer.write(" style='");
240    writer.write(styleTag);
241    writer.write("'");
242    }
243    
244  final int arlen = arbitraryString.size();
245  for (int n = 0; n < arlen; n++) {
246    writer.write(" ");
247    writer.write(arbitraryString.get(n).toString());
248    }
249    
250  writer.write(">");
251  writer.write("</input>");
252  }
253
254
255/** 
256@return   <tt>true</tt> if this field is selected, 
257      <tt>false</tt> otherwise
258**/
259public boolean isFilled(FormData fd) 
260  {
261  return getValue(fd) != null;
262  }
263
264/**
265Sets the <b>initial</b> selection status for this field.
266
267@param  select  <tt>true</tt> if this field should be selected 
268        <tt>false</tt> otherwise.
269**/
270public void setSelected(boolean select) {
271  this.orig_selected = select;
272  } 
273
274
275public String toString() 
276  {
277  return super.toString() + 
278    "; Orig. value: [" + value + "]" +
279    "; Orig. selected: [" + orig_selected + "]";
280  } 
281
282}