jsonifier.monkey2 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. Namespace mojo3d.jsonifier
  2. Class Jsonifier
  3. Private
  4. Class Instance
  5. Field id:Int
  6. Field obj:Variant
  7. Field ctor:Invocation
  8. Field initialState:JsonObject
  9. Operator To:String()
  10. Return "Instance(type="+obj.Type+" dynamic type="+obj.DynamicType+")"
  11. End
  12. End
  13. Field _loading:Int
  14. Field _instLoading:Int
  15. Field _instsByObj:=New Map<Object,Instance>
  16. Field _insts:=New Stack<Instance>
  17. Public
  18. Method BeginLoading()
  19. _loading+=1
  20. If _loading>1 Return
  21. _instLoading=_insts.Length
  22. End
  23. Method EndLoading()
  24. _loading-=1
  25. If _loading Return
  26. For Local i:=_instLoading Until _insts.Length
  27. Local inst:=_insts[i]
  28. Local tobj:=Cast<Object>( inst.obj )
  29. inst.initialState=JsonifyState( tobj )
  30. next
  31. End
  32. Method AddInstance( obj:Variant,ctor:Invocation )
  33. Local tobj:=Cast<Object>( obj )
  34. Local inst:=_instsByObj[tobj]
  35. If inst
  36. If inst.ctor Print "Warning: overwriting instance ctor for "+inst
  37. inst.ctor=ctor
  38. Return
  39. Endif
  40. inst=New Instance
  41. inst.id=_insts.Length
  42. inst.obj=obj
  43. _insts.Add( inst )
  44. _instsByObj[tobj]=inst
  45. If _loading Return
  46. inst.ctor=ctor
  47. inst.initialState=JsonifyState( tobj )
  48. End
  49. Method JsonifyInstances:JsonObject( assetsDir:String="" )
  50. If Not assetsDir assetsDir=AssetsDir()
  51. Local jobj:=New JsonObject
  52. jobj["assetsDir"]=New JsonString( assetsDir )
  53. Local jinsts:=New Stack<JsonValue>
  54. For Local i:=0 Until _insts.Length
  55. ' Print "Jsonifying "+i
  56. Local inst:=_insts[i]
  57. Local tobj:=Cast<Object>( inst.obj )
  58. 'compute delta state
  59. Local state:=JsonifyState( tobj )
  60. Local dstate:=New JsonObject
  61. For Local it:=Eachin state.All()
  62. Local x:=it.Value
  63. Local y:=inst.initialState.GetValue( it.Key )
  64. If CompareJson( x,y )<>0 dstate[it.Key]=x
  65. Next
  66. If Not inst.ctor And dstate.Empty Continue
  67. Local jobj:=New JsonObject
  68. jobj["type"]=New JsonString( tobj.DynamicType.Name ) 'not actually used, cool for debug
  69. jobj["id"]=New JsonNumber( inst.id )
  70. If inst.ctor jobj["ctor"]=Jsonify( inst.ctor )
  71. If Not dstate.Empty jobj["state"]=dstate
  72. jinsts.Add( jobj )
  73. Next
  74. jobj["instances"]=New JsonArray( jinsts )
  75. Return jobj
  76. End
  77. Method DejsonifyInstances( jobj:JsonObject )
  78. Local assetsDir:=AssetsDir()
  79. If jobj.Contains( "assetsDir" ) SetAssetsDir( jobj.GetString( "assetsDir" ) )
  80. Local jinsts:=jobj.GetArray( "instances" )
  81. Local jobjsById:=New IntMap<JsonObject>
  82. For Local i:=0 Until jinsts.Length
  83. Local jobj:=jinsts.GetObject( i )
  84. Assert( jobj.Contains( "id" ) )
  85. Local id:=jobj.GetNumber( "id" )
  86. Assert( Not jobjsById.Contains( id ) )
  87. jobjsById[id]=jobj
  88. Next
  89. 'copy of insts alreday created (ie: initial Scene)
  90. Local tinsts:=_insts.ToArray(),id:=0
  91. For Local i:=0 Until jinsts.Length
  92. Local jobj:=jinsts.GetObject( i )
  93. If Not jobj.Contains( "ctor" ) Continue
  94. Local ctor:=Cast<Invocation>( Dejsonify( jobj["ctor"],Typeof<Invocation> ) )
  95. ctor.Execute()
  96. For Local j:=id Until _insts.Length
  97. If Not jobjsById.Contains( j ) Continue
  98. Local jobj:=jobjsById[j]
  99. Local tobj:=Cast<Object>( _insts[j].obj )
  100. If jobj.Contains( "state" ) DejsonifyState( tobj,jobj.GetObject( "state" ),tobj.DynamicType,False )
  101. Next
  102. id=_insts.Length
  103. Next
  104. 'set reference type state - do this on a second pass 'coz of forward refs. Probably wont always work?
  105. For Local i:=0 Until _insts.Length
  106. If Not jobjsById.Contains( i ) Continue
  107. Local jobj:=jobjsById[i]
  108. Local tobj:=Cast<Object>( _insts[i].obj )
  109. If jobj.Contains( "state" ) DejsonifyState( tobj,jobj.GetObject( "state" ),tobj.DynamicType,True )
  110. Next
  111. SetAssetsDir( assetsDir )
  112. End
  113. 'ctor via ctor
  114. Method AddInstance( obj:Variant,args:Variant[] )
  115. AddInstance( obj,New Invocation( obj.DynamicType,"New",Null,args ) )
  116. end
  117. 'ctor via method call
  118. Method AddInstance( obj:Variant,decl:String,inst:Variant,args:Variant[] )
  119. AddInstance( obj,New Invocation( decl,inst,args ) )
  120. End
  121. 'ctor via function call
  122. Method AddInstance( obj:Variant,decl:String,args:Variant[] )
  123. AddInstance( obj,New Invocation( decl,Null,args ) )
  124. End
  125. Method Jsonify:JsonValue( value:Variant )
  126. If Not value Return JsonValue.NullValue
  127. Local type:=value.Type
  128. Assert( type )
  129. 'handle primitive types
  130. Select type
  131. Case Typeof<Bool>
  132. Return New JsonBool( Cast<Bool>( value ) )
  133. Case Typeof<Short>
  134. Return New JsonNumber( Cast<Short>( value ) )
  135. Case Typeof<Int>
  136. Return New JsonNumber( Cast<Int>( value ) )
  137. Case Typeof<Float>
  138. Return New JsonNumber( Cast<Float>( value ) )
  139. Case Typeof<String>
  140. Return New JsonString( Cast<String>( value ) )
  141. End
  142. 'handle enums+references
  143. Select type.Kind
  144. Case "Class"
  145. Local obj:=Cast<Object>( value )
  146. If Not obj Return JsonValue.NullValue
  147. Local inst:=_instsByObj[obj]
  148. If inst Return New JsonString( "@"+inst.id )
  149. Case "Enum"
  150. Return New JsonNumber( value.EnumValue )
  151. End
  152. 'try custom jsonifiers
  153. For Local jext:=Eachin JsonifierExt.All
  154. Local jvalue:=jext.Jsonify( value,Self )
  155. If jvalue Return jvalue
  156. Next
  157. Select type.Kind
  158. Case "Unknown"
  159. Local obj:=Cast<Object>( value )
  160. If Not obj Return JsonValue.NullValue
  161. Local inst:=_instsByObj[obj]
  162. If inst Return New JsonString( "@"+inst.id )
  163. Case "Class"
  164. Return JsonValue.NullValue
  165. Case "Array"
  166. Local n:=value.GetArrayLength()
  167. Local jarray:=New JsonArray( n )
  168. For Local i:=0 Until n
  169. jarray[i]=Jsonify( value.GetArrayElement( i ) )
  170. Next
  171. Return jarray
  172. End
  173. RuntimeError( "TODO: No jsonifier found for type '"+type+"'" )
  174. Return Null
  175. End
  176. Method Dejsonify<T>:T( jvalue:JsonValue )
  177. Return Cast<T>( Dejsonify( jvalue,Typeof<T> ) )
  178. End
  179. Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo )
  180. 'handle primitive types
  181. Select type
  182. Case Typeof<Bool>
  183. Return jvalue.ToBool()
  184. Case Typeof<Short>
  185. Return Short( jvalue.ToNumber() )
  186. Case Typeof<Int>
  187. Return Int( jvalue.ToNumber() )
  188. Case Typeof<Float>
  189. Return Float( jvalue.ToNumber() )
  190. Case Typeof<String>
  191. Return jvalue.ToString()
  192. End
  193. 'handle references
  194. Select type.Kind
  195. Case "Class"
  196. If jvalue.IsNull
  197. Return type.NullValue
  198. Elseif jvalue.IsString
  199. Local id:=Int( jvalue.ToString().Slice( 1 ) )
  200. Assert( id>=0 And id<_insts.Length,"Dejsonify error" )
  201. Return _insts[id].obj
  202. Endif
  203. Case "Enum"
  204. Return type.MakeEnum( jvalue.ToNumber() )
  205. End
  206. 'try custom jsonifiers
  207. For Local jext:=Eachin JsonifierExt.All
  208. Local value:=jext.Dejsonify( jvalue,type,Self )
  209. If value Return value
  210. Next
  211. Select type.Kind
  212. Case "Unknown"
  213. If jvalue.IsString
  214. Local id:=Int( jvalue.ToString().Slice( 1 ) )
  215. Assert( id>=0 And id<_insts.Length,"Dejsonify error" )
  216. Return _insts[id].obj
  217. Endif
  218. Case "Class"
  219. Return type.NullValue
  220. Case "Array"
  221. Local elemType:=type.ElementType
  222. Local jarray:=Cast<JsonArray>( jvalue )
  223. Local n:=jarray.Length,v:=elemType.NewArray( n )
  224. For Local i:=0 Until n
  225. Local elem:=Dejsonify( jarray[i],elemType )
  226. v.SetArrayElement( i,elem )
  227. Next
  228. Return v
  229. End
  230. RuntimeError( "No dejsonifier found for type '"+type+"'" )
  231. Return Null
  232. End
  233. Private
  234. Method JsonifyState:JsonObject( obj:Object )
  235. Local jobj:=New JsonObject
  236. JsonifyState( obj,jobj,obj.DynamicType )
  237. Return jobj
  238. End
  239. Method JsonifyState( obj:Object,jobj:JsonObject,type:TypeInfo )
  240. If type.Kind<>"Class" Return
  241. If type.SuperType JsonifyState( obj,jobj,type.SuperType )
  242. For Local d:=Eachin type.GetDecls()
  243. If d.Kind<>"Property" Continue
  244. 'Note: Add DeclInfo.Access property so we can do public fields only?
  245. If Not d.Gettable Or Not d.Settable Continue
  246. If Not Int( d.GetMetaValue( "jsonify" ) ) Continue
  247. jobj.SetValue( d.Name,Jsonify( d.Get( obj ) ) )
  248. Next
  249. End
  250. Method DejsonifyState( obj:Object,jobj:JsonObject,type:TypeInfo,insts:Bool )
  251. If type.Kind<>"Class" Return
  252. If type.SuperType DejsonifyState( obj,jobj,type.SuperType,insts )
  253. For Local d:=Eachin type.GetDecls()
  254. If d.Kind<>"Property" Continue
  255. 'Note: Add DeclInfo.Access property so we can do public fields only?
  256. If Not d.Gettable Or Not d.Settable Or Not jobj.Contains( d.Name ) Continue
  257. If Not Int( d.GetMetaValue( "jsonify" ) ) Continue
  258. Local type:=d.Type
  259. Local isinst:=type.Kind="Class" Or type.Kind="Unknown"
  260. If Not isinst And type.Kind="Array" And type.ElementType.Kind="Class" isinst=True
  261. If isinst<>insts Continue
  262. d.Set( obj,Dejsonify( jobj.GetValue( d.Name ),d.Type ) )
  263. Next
  264. End
  265. End
  266. Class JsonifierExt
  267. Const All:=New Stack<JsonifierExt>
  268. Method New()
  269. All.Add( Self )
  270. End
  271. Method Jsonify:JsonValue( value:Variant,jsonifier:Jsonifier ) Abstract
  272. Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo,jsonifier:Jsonifier ) Abstract
  273. End