PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/dotnet/fanx/serial/ObjEncoder.cs

https://bitbucket.org/bedlaczech/fan-1.0
C# | 381 lines | 275 code | 47 blank | 59 comment | 51 complexity | f1ac78d860c104f05bf820f286966e73 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2007, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 11 Sep 07 Andy Frank Creation
  7. //
  8. using System.Collections;
  9. using Fan.Sys;
  10. using Fanx.Util;
  11. namespace Fanx.Serial
  12. {
  13. /// <summary>
  14. /// ObjEncoder serializes an object to an output stream.
  15. /// </summary>
  16. public class ObjEncoder
  17. {
  18. //////////////////////////////////////////////////////////////////////////
  19. // Static
  20. //////////////////////////////////////////////////////////////////////////
  21. public static string encode(object obj)
  22. {
  23. StrBufOutStream @out = new StrBufOutStream();
  24. new ObjEncoder(@out, null).writeObj(obj);
  25. return @out.@string();
  26. }
  27. //////////////////////////////////////////////////////////////////////////
  28. // Constructor
  29. //////////////////////////////////////////////////////////////////////////
  30. public ObjEncoder(OutStream @out, Map options)
  31. {
  32. this.@out = @out;
  33. if (options != null) initOptions(options);
  34. }
  35. //////////////////////////////////////////////////////////////////////////
  36. // Write
  37. //////////////////////////////////////////////////////////////////////////
  38. public void writeObj(object obj)
  39. {
  40. if (obj == null)
  41. {
  42. w("null");
  43. return;
  44. }
  45. if (obj.GetType().FullName[0] == 'S')
  46. {
  47. if (obj is bool && (bool)obj) { w("true"); return; }
  48. if (obj is bool && !(bool)obj) { w("false"); return; }
  49. if (obj is double) { FanFloat.encode((double)obj, this); return; }
  50. if (obj is long) { FanInt.encode((long)obj, this); return; }
  51. if (obj is string) { wStrLiteral(obj.ToString(), '"'); return; }
  52. }
  53. if (obj.GetType().FullName[0] == 'F')
  54. {
  55. if (obj is Boolean && (obj as Boolean).booleanValue()) { w("true"); return; }
  56. if (obj is Boolean && !(obj as Boolean).booleanValue()) { w("false"); return; }
  57. if (obj is Double) { FanFloat.encode((obj as Double).doubleValue(), this); return; }
  58. if (obj is Long) { FanInt.encode((obj as Long).longValue(), this); return; }
  59. if (obj is BigDecimal) { FanDecimal.encode((BigDecimal)obj, this); return; }
  60. }
  61. if (obj is Literal)
  62. {
  63. ((Literal)obj).encode(this);
  64. return;
  65. }
  66. Type type = FanObj.@typeof(obj);
  67. Serializable ser = (Serializable)type.facet(Sys.SerializableType, false);
  68. if (ser != null)
  69. {
  70. if (ser.m_simple)
  71. writeSimple(type, obj);
  72. else
  73. writeComplex(type, obj, ser);
  74. }
  75. else
  76. {
  77. if (skipErrors)
  78. w("null /* Not serializable: ").w(type.qname()).w(" */");
  79. else
  80. throw IOErr.make("Not serializable: " + type).val;
  81. }
  82. }
  83. //////////////////////////////////////////////////////////////////////////
  84. // Simple
  85. //////////////////////////////////////////////////////////////////////////
  86. private void writeSimple(Type type, object obj)
  87. {
  88. wType(type).w('(').wStrLiteral(FanObj.toStr(obj), '"').w(')');
  89. }
  90. //////////////////////////////////////////////////////////////////////////
  91. // Complex
  92. //////////////////////////////////////////////////////////////////////////
  93. private void writeComplex(Type type, object obj, Serializable ser)
  94. {
  95. wType(type);
  96. bool first = true;
  97. object defObj = null;
  98. if (skipDefaults) defObj = FanObj.@typeof(obj).make();
  99. List fields = type.fields();
  100. for (int i=0; i<fields.sz(); ++i)
  101. {
  102. Field f = (Field)fields.get(i);
  103. // skip static, transient, and synthetic (once) fields
  104. if (f.isStatic() || f.isSynthetic() || f.hasFacet(Sys.TransientType))
  105. continue;
  106. // get the value
  107. object val = f.get(obj);
  108. // if skipping defaults
  109. if (defObj != null)
  110. {
  111. object defVal = f.get(defObj);
  112. if (OpUtil.compareEQ(val, defVal)) continue;
  113. }
  114. // if first then open braces
  115. if (first) { w('\n').wIndent().w('{').w('\n'); level++; first = false; }
  116. // field name =
  117. wIndent().w(f.name()).w('=');
  118. // field value
  119. curFieldType = f.type().toNonNullable();
  120. writeObj(val);
  121. curFieldType = null;
  122. w('\n');
  123. }
  124. // if collection
  125. if (ser.m_collection)
  126. first = writeCollectionItems(type, obj, first);
  127. // if we output fields, then close braces
  128. if (!first) { level--; wIndent().w('}'); }
  129. }
  130. //////////////////////////////////////////////////////////////////////////
  131. // Collection (@collection)
  132. //////////////////////////////////////////////////////////////////////////
  133. private bool writeCollectionItems(Type type, object obj, bool first)
  134. {
  135. // lookup each method
  136. Method m = type.method("each", false);
  137. if (m == null) throw IOErr.make("Missing " + type.qname() + ".each").val;
  138. // call each(it)
  139. EachIterator it = new EachIterator(this, first);
  140. m.invoke(obj, new object[] { it });
  141. return it.first;
  142. }
  143. static FuncType eachIteratorType = new FuncType(new Type[] { Sys.ObjType }, Sys.VoidType);
  144. internal class EachIterator : Func.Indirect1
  145. {
  146. internal EachIterator(ObjEncoder encoder, bool first)
  147. : base(eachIteratorType)
  148. {
  149. this.encoder = encoder;
  150. this.first = first;
  151. }
  152. public override object call(object obj)
  153. {
  154. if (first) { encoder.w('\n').wIndent().w('{').w('\n'); encoder.level++; first = false; }
  155. encoder.wIndent();
  156. encoder.writeObj(obj);
  157. encoder.w(',').w('\n');
  158. return null;
  159. }
  160. private ObjEncoder encoder;
  161. internal bool first;
  162. }
  163. //////////////////////////////////////////////////////////////////////////
  164. // List
  165. //////////////////////////////////////////////////////////////////////////
  166. public void writeList(List list)
  167. {
  168. // get of type
  169. Type of = list.of();
  170. // decide if we're going output as single or multi-line format
  171. bool nl = isMultiLine(of);
  172. // figure out if we can use an inferred type
  173. bool inferred = false;
  174. if (curFieldType != null && curFieldType.fits(Sys.ListType))
  175. {
  176. inferred = true;
  177. }
  178. // clear field type, so it doesn't get used for inference again
  179. curFieldType = null;
  180. // if we don't have an inferred type, then prefix of type
  181. if (!inferred) wType(of);
  182. // handle empty list
  183. int size = list.sz();
  184. if (size == 0) { w("[,]"); return; }
  185. // items
  186. if (nl) w('\n').wIndent();
  187. w('[');
  188. level++;
  189. for (int i=0; i<size; ++i)
  190. {
  191. if (i > 0) w(',');
  192. if (nl) w('\n').wIndent();
  193. writeObj(list.get(i));
  194. }
  195. level--;
  196. if (nl) w('\n').wIndent();
  197. w(']');
  198. }
  199. //////////////////////////////////////////////////////////////////////////
  200. // Map
  201. //////////////////////////////////////////////////////////////////////////
  202. public void writeMap(Map map)
  203. {
  204. // get k,v type
  205. MapType t = (MapType)map.@typeof();
  206. // decide if we're going output as single or multi-line format
  207. bool nl = isMultiLine(t.m_k) || isMultiLine(t.m_v);
  208. // figure out if we can use an inferred type
  209. bool inferred = false;
  210. if (curFieldType != null && curFieldType.fits(Sys.MapType))
  211. {
  212. inferred = true;
  213. }
  214. // clear field type, so it doesn't get used for inference again
  215. curFieldType = null;
  216. // if we don't have an inferred type, then prefix of type
  217. if (!inferred) wType(t);
  218. // handle empty map
  219. if (map.isEmpty()) { w("[:]"); return; }
  220. // items
  221. level++;
  222. w('[');
  223. bool first = true;
  224. IDictionaryEnumerator en = map.pairsIterator();
  225. while (en.MoveNext())
  226. {
  227. if (first) first = false; else w(',');
  228. if (nl) w('\n').wIndent();
  229. object key = en.Key;
  230. object val = en.Value;
  231. writeObj(key); w(':'); writeObj(val);
  232. }
  233. w(']');
  234. level--;
  235. }
  236. private bool isMultiLine(Type t)
  237. {
  238. return t.pod() != Sys.m_sysPod;
  239. }
  240. //////////////////////////////////////////////////////////////////////////
  241. // Output
  242. //////////////////////////////////////////////////////////////////////////
  243. public ObjEncoder wType(Type t)
  244. {
  245. return w(t.signature());
  246. }
  247. public ObjEncoder wStrLiteral(string s, char quote)
  248. {
  249. int len = s.Length;
  250. w(quote);
  251. // NOTE: these escape sequences are duplicated in FanStr.toCode()
  252. for (int i=0; i<len; ++i)
  253. {
  254. char c = s[i];
  255. switch (c)
  256. {
  257. case '\n': w('\\').w('n'); break;
  258. case '\r': w('\\').w('r'); break;
  259. case '\f': w('\\').w('f'); break;
  260. case '\t': w('\\').w('t'); break;
  261. case '\\': w('\\').w('\\'); break;
  262. case '"': if (quote == '"') w('\\').w('"'); else w(c); break;
  263. case '`': if (quote == '`') w('\\').w('`'); else w(c); break;
  264. case '$': w('\\').w('$'); break;
  265. default: w(c); break;
  266. }
  267. }
  268. return w(quote);
  269. }
  270. public ObjEncoder wIndent()
  271. {
  272. int num = level*indent;
  273. for (int i=0; i<num; ++i) w(' ');
  274. return this;
  275. }
  276. public ObjEncoder w(string s)
  277. {
  278. int len = s.Length;
  279. for (int i=0; i<len; ++i)
  280. @out.writeChar(s[i]);
  281. return this;
  282. }
  283. public ObjEncoder w(char ch)
  284. {
  285. @out.writeChar(ch);
  286. return this;
  287. }
  288. //////////////////////////////////////////////////////////////////////////
  289. // Options
  290. //////////////////////////////////////////////////////////////////////////
  291. private void initOptions(Map options)
  292. {
  293. indent = option(options, "indent", indent);
  294. skipDefaults = option(options, "skipDefaults", skipDefaults);
  295. skipErrors = option(options, "skipErrors", skipErrors);
  296. }
  297. private static int option(Map options, string name, int def)
  298. {
  299. Long val = (Long)options.get(name);
  300. if (val == null) return def;
  301. return val.intValue();
  302. }
  303. private static bool option(Map options, string name, bool def)
  304. {
  305. Boolean val = (Boolean)options.get(name);
  306. if (val == null) return def;
  307. return val.booleanValue();
  308. }
  309. //////////////////////////////////////////////////////////////////////////
  310. // Fields
  311. //////////////////////////////////////////////////////////////////////////
  312. OutStream @out;
  313. int level = 0;
  314. int indent = 0;
  315. bool skipDefaults = false;
  316. bool skipErrors = false;
  317. Type curFieldType;
  318. }
  319. }