jsonifier.monkey2 8.9 KB

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