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    Abstracts an HTML choice type such as choicebox or radio.
019    Does <b>not</b> abstract a HTML select element for which the
020    {@link Select} class should be used. Concrete subclasses
021    represent a particular choice element such as {@link
022    Checkbox} and {@link Radio}
023    
024    @author hursh jain
025    **/
026    public abstract class Choice extends Field
027    {
028    //if this choice object's original state == selected
029    private boolean orig_selected;
030    //can be null if not specified
031    private String  value;  
032    
033    static 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    /** 
040    Creates 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    **/
048    protected Choice(String name, String value, boolean selected)
049      {
050      super(name);
051      this.value = value;
052      this.orig_selected = selected;
053      }
054    
055    public abstract Field.Type getType();
056    
057    /** 
058    Returns 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    **/
072    public 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    /**
083    Sets the <u>selected or non-selected state</u> for this choice in the
084    specified 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
086    something is returned. Therefore <i>any non-null value</i> set by this method
087    will have the effect of selecting this choice when it is rendered. A <tt>
088    null</tt> value wil unselect this choice.
089    
090    @param  fd    a non-null form data object
091    @param  value any non-null value
092    */
093    public 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    /**
106    Convenience 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    */
111    public 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    /**
124    Sets the value for this choice.
125    */
126    public 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    /**
135    Convenience method that returns the value of this 
136    field as a Integer. 
137    
138    @throws NumberFormatException if the value could not be
139                    returned as in integer. 
140    */
141    public 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    /**
149    Convenience method that returns the value of this 
150    field as a Short. 
151    
152    @throws NumberFormatException if the value could not be
153                    returned as a short.  
154    */
155    public 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    /**
164    Convenience 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    */
168    public 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 
182    public void setValueFromSubmit(FormData fd, HttpServletRequest req) 
183    throws 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    
201    public 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    **/
259    public boolean isFilled(FormData fd) 
260      {
261      return getValue(fd) != null;
262      }
263    
264    /**
265    Sets 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    **/
270    public void setSelected(boolean select) {
271      this.orig_selected = select;
272      } 
273    
274    
275    public String toString() 
276      {
277      return super.toString() + 
278        "; Orig. value: [" + value + "]" +
279        "; Orig. selected: [" + orig_selected + "]";
280      } 
281    
282    }