preliminary experiments with a wiki tag
[org.ibex.xt.git] / src / org / ibex / xt / Form.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.xt;
6 import org.ibex.js.*;
7 import org.ibex.util.*;
8 import org.ibex.io.*;
9 import java.lang.reflect.*;
10 import java.io.*;
11 import java.net.*;
12 import java.util.*;
13 import javax.servlet.*;
14 import javax.servlet.http.*;
15
16 // tabbing order?  accessability key?
17 // the 'name' property is taken from the field name, with underscores converted to spaces
18 // the way I'm doing Radio() { ... } right now is how multi-select menus ought to work
19 // autogenerate <LABEL> elements?
20 //     <TD><LABEL for="fname">First Name</LABEL>
21 //     <TD><INPUT type="text" name="firstname" id="fname">
22
23 public abstract class Form extends HttpServlet {
24
25     final String action;
26     public Form(String action) { this.action = action; }
27
28     public static class TestForm extends Form {
29         public TestForm() { super("/foo"); }
30         public boolean[]  poo        = new boolean[] { true, false, true, true };
31         public String[]   poo3       = new String[] { "a", "b", "c" };
32         public boolean    poo2       = true;
33         public String     myName     = "blarg";
34         public Radio      day        = new Radio() { public boolean sun, mon, tue, wed, thu, fri, sat; };
35         public CheckBox   vegetarian = new CheckBox();                     // label taken from field name
36         public CheckBox   vegan      = new CheckBox("are you vegan?");
37         public Text       firstName  = new Text("First Name");
38         public Text       lastName   = new Text();
39         public Password   pass;
40         public CheckBox[] checkboxes = new CheckBox[] {
41             new CheckBox("foo"),
42             new CheckBox("bar"),
43             new CheckBox("baz")
44         };
45         //public Option[] options    = new Option[] { new Option("foo"); }
46     }
47     public static void main(String[] s) throws Exception {
48         Form sample = new TestForm();
49         System.out.println(sample.emit());
50     }
51
52     //public void sendRedirect(HttpServletResponse response) throws IOException {
53     //response.sendRedirect(uri);
54     //}
55     public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response); }
56     public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
57         Field[] fields = this.getClass().getFields();
58         for(int i=0; i<fields.length; i++) {
59             Field f     = fields[i];
60             Class c     = f.getType();
61             String name = f.getName();
62             Log.warn("param", name + " = " + request.getParameter(name));
63         }        
64     }
65
66     //public abstract void validate(HttpServletRequest req);
67     //public abstract void go(HttpServletRequest req, HttpServletResponse resp);
68
69     public JS fields() {
70         JSArray ret = new JSArray();
71         Field[] fields = this.getClass().getFields();
72         for(int i=0; i<fields.length; i++) {
73             Field f     = fields[i];
74             String name = f.getName();
75             ret.add(JSU.S(name));
76         }
77         return ret;
78     }
79
80     public String emit() throws Exception { return emit(null); }
81     public String emit(String fieldName) throws Exception {
82         Field[] fields = this.getClass().getFields();
83         StringBuffer ret = new StringBuffer();
84         if (fieldName==null) ret.append("<form xmlns='http://www.w3.org/1999/xhtml'"+q("action",action)+">\n");
85         for(int i=0; i<fields.length; i++) {
86             Field f     = fields[i];
87             Class c     = f.getType();
88             String name = f.getName();
89             if (fieldName != null && !fieldName.equals(name)) continue;
90             if (c == Boolean.TYPE) {
91                 boolean b = ((Boolean)f.get(this)).booleanValue();
92                 ret.append("    <input type='checkbox'"+q("name",name)+(b?" checked='on'":"")+"/>\n");
93             } else if (c == String.class) {
94                 ret.append("    ");
95                 Text t = new Text(f.getName());
96                 t.value = ((String)f.get(this));
97                 ret.append(t.emit());
98                 ret.append('\n');
99             } else if (c.isArray()) {
100                 Class c2 = c.getComponentType();
101                 int len = Array.getLength(f.get(this));
102                 for(int j=0; j<len; j++) {
103                     ret.append("      ");
104                     if (c2 == Boolean.TYPE) {
105                         boolean b = ((Boolean)Array.get(f.get(this),j)).booleanValue();
106                         ret.append("    <input type='checkbox'"+q("name",name+"_"+j)+(b?" checked='on'":"")+"/>\n");
107                     } else if (c2 == String.class) {
108                         Text t = new Text(f.getName()+"_"+j);
109                         t.value = (String)Array.get(f.get(this),j);
110                         ret.append(t.emit());
111                     } else {
112                         Element[] o = (Element[])f.get(this);
113                         o[j].name = name+"_"+j;
114                         ret.append(o[j].emit());
115                     }
116                     ret.append('\n');
117                 }
118             } else if (!Element.class.isAssignableFrom(c)) {  // nothing
119             } else {
120                 Element e = (Element)f.get(this);
121                 if (e==null) {
122                     e = (Element)c.newInstance();
123                     f.set(this, e);
124                 }
125                 if (e.name==null) e.name = name;
126                 ret.append("    ");
127                 ret.append(e.emit());
128                 ret.append('\n');
129             }
130         }
131         if (fieldName==null) ret.append("</form>\n");
132         return ret.toString();
133     }
134
135     public static String q(String key, String val)  { return val==null ? "" : " "+key+"='"+val+"'"; }
136     public static String q(String key, int val)     { return key+" ='"+val+"'"; }
137     public static String q(String key, boolean val) { return val ? " "+key+"=true" : ""; }
138     public static String escape(String s) { return s.replaceAll("&","&amp;").replaceAll("<","&gt;").replaceAll("'","&apos;"); }
139
140     public static abstract class Element {
141         public String  name     = null;
142         public boolean disabled = false;
143         public String  id       = null;
144         public int     size     = -1;
145         public String  alt      = null;
146         public Element() { }
147         public Element(String name) { this.name = name; }
148         public String fields() { return q("id",id)+q("disabled", disabled)+q("alt", alt); }
149         public String emit() { return "<input"+q("name",name)+q("type",type())+fields()+"/>"; }
150         public String type() {
151             String t = this.getClass().getName().toLowerCase();
152             if (t.indexOf('.') != -1) t = t.substring(t.lastIndexOf('.')+1);
153             if (t.indexOf('$') != -1) t = t.substring(t.lastIndexOf('$')+1);
154             return t;
155         }
156     }
157
158     public static class Text extends Element{
159         public String  value     = null;
160         public boolean ro        = false;
161         public char    maxLength = 0;
162         public String  fields(){return super.fields()+q("value",value)+q("readonly",ro)+(maxLength>0?q("maxlength",maxLength):"");}
163         public Text()                { super(); }
164         public Text(String name)     { super(name); }
165     }
166     public static class Password extends Text { }
167     public static class Hidden   extends Text { }
168     public static class TextArea extends Text {
169         int rows; // relation to 'size'?
170         int cols;
171         private TextArea() { }
172         public  TextArea(int rows, int cols) { this.rows=rows; this.cols=cols; }
173         public  String fields() { return super.fields()+q("rows",rows)+q("cols",cols); }
174         public  String emit()   { return "<textarea"+fields()+">\n"+escape(value)+"</textarea>"; }
175     }
176
177     public static class Radio extends Element {
178         public String emit() {
179             StringBuffer ret = new StringBuffer();
180             try {
181                 Class c = this.getClass();
182                 Field[] f = c.getFields();
183                 for(int i=0; i<f.length; i++) {
184                     if (f[i].getType() != Boolean.TYPE) continue;
185                     if (f[i].getDeclaringClass().isAssignableFrom(Radio.class)) continue;
186                     boolean b = ((Boolean)f[i].get(this)).booleanValue();
187                     ret.append("        ");
188                     ret.append("<input type='radio'"+q("name",name)+q("value",f[i].getName())+(b?" checked='true'":"")+"/>");
189                     ret.append('\n');
190                 }
191             } catch (Exception e) { e.printStackTrace(); }
192             return ret.toString();
193         }
194     }
195
196     public static class Submit extends Element { public Submit(String label) { super(label); } }
197     public static class Reset extends Element  { public Reset(String label)  { super(label); } }
198     public static class Upload extends Element {
199         String filename;
200         public Upload(String label) { super(label); }
201         public Upload(String label, String filename) { super(label); this.filename=filename; }
202     }
203
204     public static class CheckBox extends Element {
205         boolean on = false;
206         public String fields()        { return super.fields()+q("on",on); }
207         public CheckBox()             { super(); }
208         public CheckBox(String label) { super(label); }
209     }
210
211     /*
212     public static class Image extends Element {
213         // usemap, ismap
214         private Image() { }
215         public  Image(String url, String alt) { }
216         public  String url;
217         public  int x;
218         public  int y;
219     }
220
221
222     // Option[] -> Menu
223
224     // size == number of visible rows
225     public static class Menu extends Element {
226         // force a pre-selected option
227         public static class Item {
228             String label;
229             String value;
230             boolean selected = false;
231         }
232         public static class Group extends Item { }
233         public Menu(Item[] items) { }
234         public boolean multipleSelect;
235     }
236     */
237 }