PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/java/fanx/serial/ObjEncoder.java

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