/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
- /*
- * Copyright (c) 2006-2009, The haXe Project Contributors
- * All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- */
- package haxe.rtti;
- import haxe.rtti.CType;
- class HtmlEditor {
- static var UID = 0;
- var id : String;
- var types : Hash<TypeTree>;
- var buf : StringBuf;
- var nfields : Int;
- public function new() {
- types = new Hash();
- }
- public function add( tl : TypeRoot ) {
- for( t in tl )
- switch(t) {
- case TPackage(_,_,subs):
- add(subs);
- case TClassdecl(c):
- types.set(c.path,t);
- case TEnumdecl(e):
- types.set(e.path,t);
- case TTypedecl(td):
- types.set(td.path,t);
- }
- }
- public function buildHTML( id : String, v : Dynamic, t : CType ) {
- this.id = id;
- nfields = 0;
- buf = new StringBuf();
- buildHTMLRec(v,t,false);
- var str = buf.toString();
- buf = null;
- return str;
- }
- function open(t) {
- buf.add("<"+t);
- }
- function close(?t) {
- buf.add(( t == null ) ? "/>" : "</"+t+">");
- }
- function genUID() {
- return "__u"+id+"_"+(UID++);
- }
- function genFieldName() {
- return "__f"+id+"_"+(nfields++);
- }
- function skipField() {
- nfields++;
- }
- function attrib(name,value) {
- buf.add(" "+name+'="'+value+'"');
- }
- function followTypeDef( name, params : List<CType> ) {
- var td = types.get(name);
- if( td == null ) throw "Missing type "+name;
- if( !params.isEmpty() ) throw "Can't apply parameters";
- return switch( td ) {
- case TTypedecl(t): t.type;
- default: throw "assert";
- };
- }
- function getEnum( name ) {
- var td = types.get(name);
- if( td == null ) throw "Missing type "+name;
- return switch( td ) { case TEnumdecl(e): e; default: throw "assert"; };
- }
- function buildNullField( checked ) {
- open("input");
- attrib("name",genFieldName());
- attrib("class","null");
- attrib("type","checkbox");
- if( checked )
- attrib("checked","checked");
- close();
- }
- function buildHTMLRec( v : Dynamic, t : CType, nullable ) {
- switch( t ) {
- case CUnknown,CDynamic(_),CFunction(_,_):
- buf.add("???");
- case CTypedef(name,params):
- var t = followTypeDef(name,params);
- buildHTMLRec(v,t,nullable || name == "Null");
- case CAnonymous(fl):
- open("table");
- attrib("class","anon");
- buf.add(">");
- for( f in fl ) {
- buf.add("<tr><th>");
- buf.add(f.name);
- buf.add("</th><td>");
- buildHTMLRec(Reflect.field(v,f.name),f.t,false);
- buf.add("</td></tr>");
- }
- close("table");
- case CClass(name,params):
- if( !params.isEmpty() ) throw "Can't use type parameters";
- switch( name ) {
- case "Int":
- open("input");
- attrib("name",genFieldName());
- attrib("class","int");
- if( v != null )
- attrib("value",v);
- close();
- case "String":
- if( nullable )
- buildNullField(v != null);
- open("input");
- attrib("name",genFieldName());
- attrib("class","string");
- if( v != null )
- attrib("value",v);
- close();
- case "Bool":
- if( nullable )
- buildNullField(v != null);
- open("input");
- attrib("name",genFieldName());
- attrib("type","checkbox");
- if( v )
- attrib("checked","checked");
- close();
- default:
- throw "Can't edit instances of "+name;
- }
- case CEnum(name,params):
- if( name == "Bool" ) {
- buildHTMLRec(v,CClass("Bool",params),nullable);
- return;
- }
- if( !params.isEmpty() ) throw "Can't use type parameters";
- var e = getEnum(name);
- var js = genUID();
- open("select");
- attrib("name",genFieldName());
- attrib("class","enum");
- attrib("onchange",js+"(this)");
- buf.add(">");
- var current = if( v == null ) null else Type.enumConstructor(v);
- if( nullable )
- buf.add("<option value=''>---- NULL ----</option>");
- var prefix = if( e.constructors.length <= 1 ) "" else e.constructors.first().name;
- for( c in e.constructors )
- while( prefix.length > 0 )
- if( c.name.substr(0,prefix.length) == prefix )
- break;
- else
- prefix = prefix.substr(0,prefix.length-1);
- for( c in e.constructors ) {
- open("option");
- attrib("value",c.name);
- if( current == c.name )
- attrib("selected","selected");
- buf.add(">");
- buf.add(c.name.substr(prefix.length));
- close("option");
- }
- close("select");
- var ids = new Array();
- for( c in e.constructors ) {
- var id = genUID();
- ids.push({ id : id, c : c });
- open("table");
- attrib("id",id);
- attrib("class","construct");
- if( current != c.name )
- attrib("style","display : none");
- buf.add(">");
- if( c.args != null ) {
- var args = if( current == c.name ) Type.enumParameters(v) else new Array();
- var i = 0;
- for( p in c.args ) {
- buf.add("<tr><th>");
- buf.add(p.name);
- buf.add("</th><td>");
- buildHTMLRec(args[i++],p.t,p.opt);
- buf.add("</td></tr>");
- }
- }
- close("table");
- }
- open("script");
- attrib("type","text/javascript");
- buf.add(">");
- buf.add("function "+js+"(s) {");
- for( c in ids )
- buf.add("document.getElementById('"+c.id+"').style.display = (s.value == '"+c.c.name+"')?'':'none';");
- buf.add("}");
- close("script");
- }
- }
- public function buildObject( id : String, params : Hash<String>, t : CType ) : Dynamic {
- this.id = id;
- nfields = 0;
- return buildObjectRec(params,t,false);
- }
- function buildObjectRec( params : Hash<String>, t : CType, nullable : Bool ) : Dynamic {
- return switch( t ) {
- case CUnknown,CDynamic(_),CFunction(_,_):
- throw Type.enumConstructor(t)+" can't be built";
- case CTypedef(name,pl):
- buildObjectRec(params,followTypeDef(name,pl),nullable || name == "Null");
- case CAnonymous(fl):
- var o = {};
- for( f in fl )
- Reflect.setField(o,f.name,buildObjectRec(params,f.t,false));
- o;
- case CClass(name,_):
- var v = params.get(genFieldName());
- var ret : Dynamic;
- switch( name ) {
- case "Int":
- if( v == null || (v == "" && !nullable) )
- throw "Missing required value";
- if( !~/^[0-9]+$/.match(v) )
- throw "Invalid int format '"+v+"'";
- ret = ( v == "" ) ? null : Std.parseInt(v);
- case "String":
- if( nullable ) {
- var str = params.get(genFieldName());
- ret = if( v == null && str == "" ) null else str;
- } else {
- if( v == null )
- throw "Missing required value";
- ret = v;
- }
- case "Bool":
- if( nullable ) {
- var b = params.exists(genFieldName());
- ret = if( v == null && !b ) null else b;
- } else
- ret = (v != null);
- default:
- throw name+" can't be built";
- }
- ret;
- case CEnum(name,_):
- if( name == "Bool" )
- buildObjectRec(params,CClass("Bool",new List()),nullable);
- else {
- var e = getEnum(name);
- var v = genFieldName();
- var current = params.get(v);
- var value = null;
- for( c in e.constructors ) {
- if( c.name == current ) {
- var args = null;
- if( c.args != null ) {
- args = new Array();
- for( a in c.args )
- args.push(buildObjectRec(params,a.t,a.opt));
- }
- value = Type.createEnum(Type.resolveEnum(name),current,args);
- } else if( c.args != null ) {
- for( a in c.args )
- skipObjectRec(a.t,a.opt);
- }
- }
- if( value == null && !nullable )
- throw name+" can't be null";
- value;
- }
- };
- }
- function skipObjectRec( t : CType, nullable ) {
- switch( t ) {
- case CUnknown,CDynamic(_),CFunction(_,_):
- // nothing
- case CTypedef(name,pl):
- skipObjectRec(followTypeDef(name,pl),nullable || name == "Null");
- case CAnonymous(fl):
- for( f in fl )
- skipObjectRec(f.t,false);
- case CEnum(name,_):
- if( name == "Bool" ) {
- skipObjectRec(CClass("Bool",new List()),nullable);
- return;
- }
- var e = getEnum(name);
- skipField();
- for( c in e.constructors ) {
- if( c.args == null ) continue;
- for( a in c.args )
- skipObjectRec(a.t,a.opt);
- }
- case CClass(name,_):
- switch( name ) {
- case "Int": skipField();
- case "String", "Bool":
- if( nullable ) skipField();
- skipField();
- default:
- }
- }
- }
- }