/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. */
  25. package haxe.rtti;
  26. import haxe.rtti.CType;
  27. class HtmlEditor {
  28. static var UID = 0;
  29. var id : String;
  30. var types : Hash<TypeTree>;
  31. var buf : StringBuf;
  32. var nfields : Int;
  33. public function new() {
  34. types = new Hash();
  35. }
  36. public function add( tl : TypeRoot ) {
  37. for( t in tl )
  38. switch(t) {
  39. case TPackage(_,_,subs):
  40. add(subs);
  41. case TClassdecl(c):
  42. types.set(c.path,t);
  43. case TEnumdecl(e):
  44. types.set(e.path,t);
  45. case TTypedecl(td):
  46. types.set(td.path,t);
  47. }
  48. }
  49. public function buildHTML( id : String, v : Dynamic, t : CType ) {
  50. this.id = id;
  51. nfields = 0;
  52. buf = new StringBuf();
  53. buildHTMLRec(v,t,false);
  54. var str = buf.toString();
  55. buf = null;
  56. return str;
  57. }
  58. function open(t) {
  59. buf.add("<"+t);
  60. }
  61. function close(?t) {
  62. buf.add(( t == null ) ? "/>" : "</"+t+">");
  63. }
  64. function genUID() {
  65. return "__u"+id+"_"+(UID++);
  66. }
  67. function genFieldName() {
  68. return "__f"+id+"_"+(nfields++);
  69. }
  70. function skipField() {
  71. nfields++;
  72. }
  73. function attrib(name,value) {
  74. buf.add(" "+name+'="'+value+'"');
  75. }
  76. function followTypeDef( name, params : List<CType> ) {
  77. var td = types.get(name);
  78. if( td == null ) throw "Missing type "+name;
  79. if( !params.isEmpty() ) throw "Can't apply parameters";
  80. return switch( td ) {
  81. case TTypedecl(t): t.type;
  82. default: throw "assert";
  83. };
  84. }
  85. function getEnum( name ) {
  86. var td = types.get(name);
  87. if( td == null ) throw "Missing type "+name;
  88. return switch( td ) { case TEnumdecl(e): e; default: throw "assert"; };
  89. }
  90. function buildNullField( checked ) {
  91. open("input");
  92. attrib("name",genFieldName());
  93. attrib("class","null");
  94. attrib("type","checkbox");
  95. if( checked )
  96. attrib("checked","checked");
  97. close();
  98. }
  99. function buildHTMLRec( v : Dynamic, t : CType, nullable ) {
  100. switch( t ) {
  101. case CUnknown,CDynamic(_),CFunction(_,_):
  102. buf.add("???");
  103. case CTypedef(name,params):
  104. var t = followTypeDef(name,params);
  105. buildHTMLRec(v,t,nullable || name == "Null");
  106. case CAnonymous(fl):
  107. open("table");
  108. attrib("class","anon");
  109. buf.add(">");
  110. for( f in fl ) {
  111. buf.add("<tr><th>");
  112. buf.add(f.name);
  113. buf.add("</th><td>");
  114. buildHTMLRec(Reflect.field(v,f.name),f.t,false);
  115. buf.add("</td></tr>");
  116. }
  117. close("table");
  118. case CClass(name,params):
  119. if( !params.isEmpty() ) throw "Can't use type parameters";
  120. switch( name ) {
  121. case "Int":
  122. open("input");
  123. attrib("name",genFieldName());
  124. attrib("class","int");
  125. if( v != null )
  126. attrib("value",v);
  127. close();
  128. case "String":
  129. if( nullable )
  130. buildNullField(v != null);
  131. open("input");
  132. attrib("name",genFieldName());
  133. attrib("class","string");
  134. if( v != null )
  135. attrib("value",v);
  136. close();
  137. case "Bool":
  138. if( nullable )
  139. buildNullField(v != null);
  140. open("input");
  141. attrib("name",genFieldName());
  142. attrib("type","checkbox");
  143. if( v )
  144. attrib("checked","checked");
  145. close();
  146. default:
  147. throw "Can't edit instances of "+name;
  148. }
  149. case CEnum(name,params):
  150. if( name == "Bool" ) {
  151. buildHTMLRec(v,CClass("Bool",params),nullable);
  152. return;
  153. }
  154. if( !params.isEmpty() ) throw "Can't use type parameters";
  155. var e = getEnum(name);
  156. var js = genUID();
  157. open("select");
  158. attrib("name",genFieldName());
  159. attrib("class","enum");
  160. attrib("onchange",js+"(this)");
  161. buf.add(">");
  162. var current = if( v == null ) null else Type.enumConstructor(v);
  163. if( nullable )
  164. buf.add("<option value=''>---- NULL ----</option>");
  165. var prefix = if( e.constructors.length <= 1 ) "" else e.constructors.first().name;
  166. for( c in e.constructors )
  167. while( prefix.length > 0 )
  168. if( c.name.substr(0,prefix.length) == prefix )
  169. break;
  170. else
  171. prefix = prefix.substr(0,prefix.length-1);
  172. for( c in e.constructors ) {
  173. open("option");
  174. attrib("value",c.name);
  175. if( current == c.name )
  176. attrib("selected","selected");
  177. buf.add(">");
  178. buf.add(c.name.substr(prefix.length));
  179. close("option");
  180. }
  181. close("select");
  182. var ids = new Array();
  183. for( c in e.constructors ) {
  184. var id = genUID();
  185. ids.push({ id : id, c : c });
  186. open("table");
  187. attrib("id",id);
  188. attrib("class","construct");
  189. if( current != c.name )
  190. attrib("style","display : none");
  191. buf.add(">");
  192. if( c.args != null ) {
  193. var args = if( current == c.name ) Type.enumParameters(v) else new Array();
  194. var i = 0;
  195. for( p in c.args ) {
  196. buf.add("<tr><th>");
  197. buf.add(p.name);
  198. buf.add("</th><td>");
  199. buildHTMLRec(args[i++],p.t,p.opt);
  200. buf.add("</td></tr>");
  201. }
  202. }
  203. close("table");
  204. }
  205. open("script");
  206. attrib("type","text/javascript");
  207. buf.add(">");
  208. buf.add("function "+js+"(s) {");
  209. for( c in ids )
  210. buf.add("document.getElementById('"+c.id+"').style.display = (s.value == '"+c.c.name+"')?'':'none';");
  211. buf.add("}");
  212. close("script");
  213. }
  214. }
  215. public function buildObject( id : String, params : Hash<String>, t : CType ) : Dynamic {
  216. this.id = id;
  217. nfields = 0;
  218. return buildObjectRec(params,t,false);
  219. }
  220. function buildObjectRec( params : Hash<String>, t : CType, nullable : Bool ) : Dynamic {
  221. return switch( t ) {
  222. case CUnknown,CDynamic(_),CFunction(_,_):
  223. throw Type.enumConstructor(t)+" can't be built";
  224. case CTypedef(name,pl):
  225. buildObjectRec(params,followTypeDef(name,pl),nullable || name == "Null");
  226. case CAnonymous(fl):
  227. var o = {};
  228. for( f in fl )
  229. Reflect.setField(o,f.name,buildObjectRec(params,f.t,false));
  230. o;
  231. case CClass(name,_):
  232. var v = params.get(genFieldName());
  233. var ret : Dynamic;
  234. switch( name ) {
  235. case "Int":
  236. if( v == null || (v == "" && !nullable) )
  237. throw "Missing required value";
  238. if( !~/^[0-9]+$/.match(v) )
  239. throw "Invalid int format '"+v+"'";
  240. ret = ( v == "" ) ? null : Std.parseInt(v);
  241. case "String":
  242. if( nullable ) {
  243. var str = params.get(genFieldName());
  244. ret = if( v == null && str == "" ) null else str;
  245. } else {
  246. if( v == null )
  247. throw "Missing required value";
  248. ret = v;
  249. }
  250. case "Bool":
  251. if( nullable ) {
  252. var b = params.exists(genFieldName());
  253. ret = if( v == null && !b ) null else b;
  254. } else
  255. ret = (v != null);
  256. default:
  257. throw name+" can't be built";
  258. }
  259. ret;
  260. case CEnum(name,_):
  261. if( name == "Bool" )
  262. buildObjectRec(params,CClass("Bool",new List()),nullable);
  263. else {
  264. var e = getEnum(name);
  265. var v = genFieldName();
  266. var current = params.get(v);
  267. var value = null;
  268. for( c in e.constructors ) {
  269. if( c.name == current ) {
  270. var args = null;
  271. if( c.args != null ) {
  272. args = new Array();
  273. for( a in c.args )
  274. args.push(buildObjectRec(params,a.t,a.opt));
  275. }
  276. value = Type.createEnum(Type.resolveEnum(name),current,args);
  277. } else if( c.args != null ) {
  278. for( a in c.args )
  279. skipObjectRec(a.t,a.opt);
  280. }
  281. }
  282. if( value == null && !nullable )
  283. throw name+" can't be null";
  284. value;
  285. }
  286. };
  287. }
  288. function skipObjectRec( t : CType, nullable ) {
  289. switch( t ) {
  290. case CUnknown,CDynamic(_),CFunction(_,_):
  291. // nothing
  292. case CTypedef(name,pl):
  293. skipObjectRec(followTypeDef(name,pl),nullable || name == "Null");
  294. case CAnonymous(fl):
  295. for( f in fl )
  296. skipObjectRec(f.t,false);
  297. case CEnum(name,_):
  298. if( name == "Bool" ) {
  299. skipObjectRec(CClass("Bool",new List()),nullable);
  300. return;
  301. }
  302. var e = getEnum(name);
  303. skipField();
  304. for( c in e.constructors ) {
  305. if( c.args == null ) continue;
  306. for( a in c.args )
  307. skipObjectRec(a.t,a.opt);
  308. }
  309. case CClass(name,_):
  310. switch( name ) {
  311. case "Int": skipField();
  312. case "String", "Bool":
  313. if( nullable ) skipField();
  314. skipField();
  315. default:
  316. }
  317. }
  318. }
  319. }