PageRenderTime 33ms CodeModel.GetById 12ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/bin/std/haxe/rtti/HtmlEditor.hx

http://github.com/Yoomee/clippy
Haxe | 339 lines | 294 code | 20 blank | 25 comment | 97 complexity | 76a319768e1945cb2f0d024422c1dfa4 MD5 | raw file
  1/*
  2 * Copyright (c) 2006-2009, The haXe Project Contributors
  3 * All rights reserved.
  4 * Redistribution and use in source and binary forms, with or without
  5 * modification, are permitted provided that the following conditions are met:
  6 *
  7 *   - Redistributions of source code must retain the above copyright
  8 *     notice, this list of conditions and the following disclaimer.
  9 *   - Redistributions in binary form must reproduce the above copyright
 10 *     notice, this list of conditions and the following disclaimer in the
 11 *     documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 16 * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
 17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 23 * DAMAGE.
 24 */
 25package haxe.rtti;
 26import haxe.rtti.CType;
 27
 28class HtmlEditor {
 29
 30	static var UID = 0;
 31
 32	var id : String;
 33	var types : Hash<TypeTree>;
 34	var buf : StringBuf;
 35	var nfields : Int;
 36
 37	public function new() {
 38		types = new Hash();
 39	}
 40
 41	public function add( tl : TypeRoot ) {
 42		for( t in tl )
 43			switch(t) {
 44			case TPackage(_,_,subs):
 45				add(subs);
 46			case TClassdecl(c):
 47				types.set(c.path,t);
 48			case TEnumdecl(e):
 49				types.set(e.path,t);
 50			case TTypedecl(td):
 51				types.set(td.path,t);
 52			}
 53	}
 54
 55	public function buildHTML( id : String, v : Dynamic, t : CType ) {
 56		this.id = id;
 57		nfields = 0;
 58		buf = new StringBuf();
 59		buildHTMLRec(v,t,false);
 60		var str = buf.toString();
 61		buf = null;
 62		return str;
 63	}
 64
 65	function open(t) {
 66		buf.add("<"+t);
 67	}
 68
 69	function close(?t) {
 70		buf.add(( t == null ) ? "/>" : "</"+t+">");
 71	}
 72
 73	function genUID() {
 74		return "__u"+id+"_"+(UID++);
 75	}
 76
 77	function genFieldName() {
 78		return "__f"+id+"_"+(nfields++);
 79	}
 80
 81	function skipField() {
 82		nfields++;
 83	}
 84
 85	function attrib(name,value) {
 86		buf.add(" "+name+'="'+value+'"');
 87	}
 88
 89	function followTypeDef( name, params : List<CType> ) {
 90		var td = types.get(name);
 91		if( td == null ) throw "Missing type "+name;
 92		if( !params.isEmpty() ) throw "Can't apply parameters";
 93		return switch( td ) {
 94			case TTypedecl(t): t.type;
 95			default: throw "assert";
 96		};
 97	}
 98
 99	function getEnum( name ) {
100		var td = types.get(name);
101		if( td == null ) throw "Missing type "+name;
102		return switch( td ) { case TEnumdecl(e): e; default: throw "assert"; };
103	}
104
105	function buildNullField( checked ) {
106		open("input");
107		attrib("name",genFieldName());
108		attrib("class","null");
109		attrib("type","checkbox");
110		if( checked )
111			attrib("checked","checked");
112		close();
113	}
114
115	function buildHTMLRec( v : Dynamic, t : CType, nullable ) {
116		switch( t ) {
117		case CUnknown,CDynamic(_),CFunction(_,_):
118			buf.add("???");
119		case CTypedef(name,params):
120			var t = followTypeDef(name,params);
121			buildHTMLRec(v,t,nullable || name == "Null");
122		case CAnonymous(fl):
123			open("table");
124			attrib("class","anon");
125			buf.add(">");
126			for( f in fl ) {
127				buf.add("<tr><th>");
128				buf.add(f.name);
129				buf.add("</th><td>");
130				buildHTMLRec(Reflect.field(v,f.name),f.t,false);
131				buf.add("</td></tr>");
132			}
133			close("table");
134		case CClass(name,params):
135			if( !params.isEmpty() ) throw "Can't use type parameters";
136			switch( name ) {
137			case "Int":
138				open("input");
139				attrib("name",genFieldName());
140				attrib("class","int");
141				if( v != null )
142					attrib("value",v);
143				close();
144			case "String":
145				if( nullable )
146					buildNullField(v != null);
147				open("input");
148				attrib("name",genFieldName());
149				attrib("class","string");
150				if( v != null )
151					attrib("value",v);
152				close();
153			case "Bool":
154				if( nullable )
155					buildNullField(v != null);
156				open("input");
157				attrib("name",genFieldName());
158				attrib("type","checkbox");
159				if( v )
160					attrib("checked","checked");
161				close();
162			default:
163				throw "Can't edit instances of "+name;
164			}
165		case CEnum(name,params):
166			if( name == "Bool" ) {
167				buildHTMLRec(v,CClass("Bool",params),nullable);
168				return;
169			}
170			if( !params.isEmpty() ) throw "Can't use type parameters";
171			var e = getEnum(name);
172			var js = genUID();
173			open("select");
174			attrib("name",genFieldName());
175			attrib("class","enum");
176			attrib("onchange",js+"(this)");
177			buf.add(">");
178			var current = if( v == null ) null else Type.enumConstructor(v);
179			if( nullable )
180				buf.add("<option value=''>---- NULL ----</option>");
181			var prefix = if( e.constructors.length <= 1 ) "" else e.constructors.first().name;
182			for( c in e.constructors )
183				while( prefix.length > 0 )
184					if( c.name.substr(0,prefix.length) == prefix )
185						break;
186					else
187						prefix = prefix.substr(0,prefix.length-1);
188			for( c in e.constructors ) {
189				open("option");
190				attrib("value",c.name);
191				if( current == c.name )
192					attrib("selected","selected");
193				buf.add(">");
194				buf.add(c.name.substr(prefix.length));
195				close("option");
196			}
197			close("select");
198			var ids = new Array();
199			for( c in e.constructors ) {
200				var id = genUID();
201				ids.push({ id : id, c : c });
202				open("table");
203				attrib("id",id);
204				attrib("class","construct");
205				if( current != c.name )
206					attrib("style","display : none");
207				buf.add(">");
208				if( c.args != null ) {
209					var args = if( current == c.name ) Type.enumParameters(v) else new Array();
210					var i = 0;
211					for( p in c.args ) {
212						buf.add("<tr><th>");
213						buf.add(p.name);
214						buf.add("</th><td>");
215						buildHTMLRec(args[i++],p.t,p.opt);
216						buf.add("</td></tr>");
217					}
218				}
219				close("table");
220			}
221			open("script");
222			attrib("type","text/javascript");
223			buf.add(">");
224			buf.add("function "+js+"(s) {");
225			for( c in ids )
226				buf.add("document.getElementById('"+c.id+"').style.display = (s.value == '"+c.c.name+"')?'':'none';");
227			buf.add("}");
228			close("script");
229		}
230	}
231
232	public function buildObject( id : String, params : Hash<String>, t : CType ) : Dynamic {
233		this.id = id;
234		nfields = 0;
235		return buildObjectRec(params,t,false);
236	}
237
238	function buildObjectRec( params : Hash<String>, t : CType, nullable : Bool ) : Dynamic {
239		return switch( t ) {
240		case CUnknown,CDynamic(_),CFunction(_,_):
241			throw Type.enumConstructor(t)+" can't be built";
242		case CTypedef(name,pl):
243			buildObjectRec(params,followTypeDef(name,pl),nullable || name == "Null");
244		case CAnonymous(fl):
245			var o = {};
246			for( f in fl )
247				Reflect.setField(o,f.name,buildObjectRec(params,f.t,false));
248			o;
249		case CClass(name,_):
250			var v = params.get(genFieldName());
251			var ret : Dynamic;
252			switch( name ) {
253			case "Int":
254				if( v == null || (v == "" && !nullable) )
255					throw "Missing required value";
256				if( !~/^[0-9]+$/.match(v) )
257					throw "Invalid int format '"+v+"'";
258				ret = ( v == "" ) ? null : Std.parseInt(v);
259			case "String":
260				if( nullable ) {
261					var str = params.get(genFieldName());
262					ret = if( v == null && str == "" ) null else str;
263				} else {
264					if( v == null )
265						throw "Missing required value";
266					ret = v;
267				}
268			case "Bool":
269				if( nullable ) {
270					var b = params.exists(genFieldName());
271					ret = if( v == null && !b ) null else b;
272				} else
273					ret = (v != null);
274			default:
275				throw name+" can't be built";
276			}
277			ret;
278		case CEnum(name,_):
279			if( name == "Bool" )
280				buildObjectRec(params,CClass("Bool",new List()),nullable);
281			else {
282				var e = getEnum(name);
283				var v = genFieldName();
284				var current = params.get(v);
285				var value = null;
286				for( c in e.constructors ) {
287					if( c.name == current ) {
288						var args = null;
289						if( c.args != null ) {
290							args = new Array();
291							for( a in c.args )
292								args.push(buildObjectRec(params,a.t,a.opt));
293						}
294						value = Type.createEnum(Type.resolveEnum(name),current,args);
295					} else if( c.args != null ) {
296						for( a in c.args )
297							skipObjectRec(a.t,a.opt);
298					}
299				}
300				if( value == null && !nullable )
301					throw name+" can't be null";
302				value;
303			}
304		};
305	}
306
307	function skipObjectRec( t : CType, nullable ) {
308		switch( t ) {
309		case CUnknown,CDynamic(_),CFunction(_,_):
310			// nothing
311		case CTypedef(name,pl):
312			skipObjectRec(followTypeDef(name,pl),nullable || name == "Null");
313		case CAnonymous(fl):
314			for( f in fl )
315				skipObjectRec(f.t,false);
316		case CEnum(name,_):
317			if( name == "Bool" ) {
318				skipObjectRec(CClass("Bool",new List()),nullable);
319				return;
320			}
321			var e = getEnum(name);
322			skipField();
323			for( c in e.constructors ) {
324				if( c.args == null ) continue;
325				for( a in c.args )
326					skipObjectRec(a.t,a.opt);
327			}
328		case CClass(name,_):
329			switch( name ) {
330			case "Int": skipField();
331			case "String", "Bool":
332				if( nullable ) skipField();
333				skipField();
334			default:
335			}
336		}
337	}
338
339}