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