PageRenderTime 76ms CodeModel.GetById 46ms RepoModel.GetById 0ms app.codeStats 1ms

/src/JsonFx/Model/ModelWalker.cs

https://github.com/jrista/jsonfx
C# | 430 lines | 321 code | 56 blank | 53 comment | 48 complexity | 59dfc6e1c0009b60497b4f834ba1d538 MD5 | raw file
  1. #region License
  2. /*---------------------------------------------------------------------------------*\
  3. Distributed under the terms of an MIT-style license:
  4. The MIT License
  5. Copyright (c) 2006-2010 Stephen M. McKamey
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. \*---------------------------------------------------------------------------------*/
  22. #endregion License
  23. using System;
  24. using System.Collections;
  25. using System.Collections.Generic;
  26. using JsonFx.CodeGen;
  27. using JsonFx.Serialization;
  28. using JsonFx.Serialization.Filters;
  29. using JsonFx.Serialization.GraphCycles;
  30. using JsonFx.Serialization.Resolvers;
  31. namespace JsonFx.Model
  32. {
  33. /// <summary>
  34. /// Generates a sequence of tokens from an object graph
  35. /// </summary>
  36. public class ModelWalker : IObjectWalker<ModelTokenType>
  37. {
  38. #region Fields
  39. private readonly DataWriterSettings Settings;
  40. private readonly IEnumerable<IDataFilter<ModelTokenType>> Filters;
  41. #endregion Fields
  42. #region Init
  43. /// <summary>
  44. /// Ctor
  45. /// </summary>
  46. /// <param name="settings"></param>
  47. public ModelWalker(DataWriterSettings settings)
  48. {
  49. if (settings == null)
  50. {
  51. throw new ArgumentNullException("settings");
  52. }
  53. this.Settings = settings;
  54. var filters = new List<IDataFilter<ModelTokenType>>();
  55. if (settings.Filters != null)
  56. {
  57. foreach (var filter in settings.Filters)
  58. {
  59. if (filter != null)
  60. {
  61. filters.Add(filter);
  62. }
  63. }
  64. }
  65. this.Filters = filters;
  66. }
  67. #endregion Init
  68. #region IObjectWalker<T> Methods
  69. /// <summary>
  70. /// Generates a sequence of tokens representing the value
  71. /// </summary>
  72. /// <param name="value"></param>
  73. /// <returns></returns>
  74. public IEnumerable<Token<ModelTokenType>> GetTokens(object value)
  75. {
  76. ICycleDetector detector;
  77. switch (this.Settings.GraphCycles)
  78. {
  79. case GraphCycleType.MaxDepth:
  80. {
  81. detector = new DepthCounter(this.Settings.MaxDepth);
  82. break;
  83. }
  84. default:
  85. {
  86. detector = new ReferenceSet();
  87. break;
  88. }
  89. }
  90. List<Token<ModelTokenType>> tokens = new List<Token<ModelTokenType>>();
  91. this.GetTokens(tokens, detector, value);
  92. return tokens;
  93. }
  94. #endregion IObjectWalker<T> Methods
  95. #region Walker Methods
  96. private void GetTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, object value)
  97. {
  98. if (value == null)
  99. {
  100. tokens.Add(ModelGrammar.TokenNull);
  101. return;
  102. }
  103. // test for cycles
  104. if (detector.Add(value))
  105. {
  106. switch (this.Settings.GraphCycles)
  107. {
  108. case GraphCycleType.Reference:
  109. {
  110. // no need to remove value as was duplicate reference
  111. throw new GraphCycleException(GraphCycleType.Reference, "Graph cycle detected: repeated references");
  112. }
  113. case GraphCycleType.MaxDepth:
  114. {
  115. throw new GraphCycleException(GraphCycleType.MaxDepth, "Graph cycle potentially detected: maximum depth exceeded");
  116. }
  117. default:
  118. case GraphCycleType.Ignore:
  119. {
  120. // no need to remove value as was duplicate reference
  121. // replace cycle with null
  122. tokens.Add(ModelGrammar.TokenNull);
  123. return;
  124. }
  125. }
  126. }
  127. try
  128. {
  129. foreach (var filter in this.Filters)
  130. {
  131. IEnumerable<Token<ModelTokenType>> filterResult;
  132. if (filter.TryWrite(this.Settings, value, out filterResult))
  133. {
  134. // found a successful match
  135. tokens.AddRange(filterResult);
  136. return;
  137. }
  138. }
  139. Type type = value.GetType();
  140. // must test enumerations before other value types
  141. if (type.IsEnum)
  142. {
  143. tokens.Add(ModelGrammar.TokenPrimitive((Enum)value));
  144. return;
  145. }
  146. // Type.GetTypeCode() allows us to more efficiently switch type
  147. switch (Type.GetTypeCode(type))
  148. {
  149. case TypeCode.Boolean:
  150. {
  151. tokens.Add(true.Equals(value) ? ModelGrammar.TokenTrue : ModelGrammar.TokenFalse);
  152. return;
  153. }
  154. case TypeCode.Byte:
  155. case TypeCode.Decimal:
  156. case TypeCode.Int16:
  157. case TypeCode.Int32:
  158. case TypeCode.Int64:
  159. case TypeCode.SByte:
  160. case TypeCode.UInt16:
  161. case TypeCode.UInt32:
  162. case TypeCode.UInt64:
  163. {
  164. tokens.Add(ModelGrammar.TokenPrimitive((ValueType)value));
  165. return;
  166. }
  167. case TypeCode.Double:
  168. {
  169. double doubleVal = (double)value;
  170. if (Double.IsNaN(doubleVal))
  171. {
  172. tokens.Add(ModelGrammar.TokenNaN);
  173. }
  174. else if (Double.IsPositiveInfinity(doubleVal))
  175. {
  176. tokens.Add(ModelGrammar.TokenPositiveInfinity);
  177. }
  178. else if (Double.IsNegativeInfinity(doubleVal))
  179. {
  180. tokens.Add(ModelGrammar.TokenNegativeInfinity);
  181. }
  182. else
  183. {
  184. tokens.Add(ModelGrammar.TokenPrimitive(doubleVal));
  185. }
  186. return;
  187. }
  188. case TypeCode.Single:
  189. {
  190. float floatVal = (float)value;
  191. if (Single.IsNaN(floatVal))
  192. {
  193. // use the Double equivalent
  194. tokens.Add(ModelGrammar.TokenNaN);
  195. }
  196. else if (Single.IsPositiveInfinity(floatVal))
  197. {
  198. // use the Double equivalent
  199. tokens.Add(ModelGrammar.TokenPositiveInfinity);
  200. }
  201. else if (Single.IsNegativeInfinity(floatVal))
  202. {
  203. // use the Double equivalent
  204. tokens.Add(ModelGrammar.TokenNegativeInfinity);
  205. }
  206. else
  207. {
  208. tokens.Add(ModelGrammar.TokenPrimitive(floatVal));
  209. }
  210. return;
  211. }
  212. case TypeCode.Char:
  213. case TypeCode.DateTime:
  214. case TypeCode.String:
  215. {
  216. tokens.Add(ModelGrammar.TokenPrimitive(value));
  217. return;
  218. }
  219. case TypeCode.DBNull:
  220. case TypeCode.Empty:
  221. {
  222. tokens.Add(ModelGrammar.TokenNull);
  223. return;
  224. }
  225. }
  226. if (value is IEnumerable)
  227. {
  228. this.GetArrayTokens(tokens, detector, (IEnumerable)value);
  229. return;
  230. }
  231. if (value is Guid || value is Uri || value is Version)
  232. {
  233. tokens.Add(ModelGrammar.TokenPrimitive(value));
  234. return;
  235. }
  236. if (value is TimeSpan)
  237. {
  238. tokens.Add(ModelGrammar.TokenPrimitive((TimeSpan)value));
  239. return;
  240. }
  241. #if NET40 && !WINDOWS_PHONE
  242. if (value is System.Dynamic.DynamicObject)
  243. {
  244. // TODO: expand to all IDynamicMetaObjectProvider?
  245. this.GetObjectTokens(tokens, detector, type, (System.Dynamic.DynamicObject)value);
  246. return;
  247. }
  248. #endif
  249. // all other structs and classes
  250. this.GetObjectTokens(tokens, detector, type, value);
  251. }
  252. finally
  253. {
  254. detector.Remove(value);
  255. }
  256. }
  257. private void GetArrayTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, IEnumerable value)
  258. {
  259. DataName typeName = this.GetTypeName(value);
  260. IEnumerator enumerator = value.GetEnumerator();
  261. if (enumerator is IEnumerator<KeyValuePair<string, object>>)
  262. {
  263. this.GetObjectTokens(tokens, detector, typeName, (IEnumerator<KeyValuePair<string, object>>)enumerator);
  264. return;
  265. }
  266. if (enumerator is IDictionaryEnumerator)
  267. {
  268. this.GetObjectTokens(tokens, detector, typeName, (IDictionaryEnumerator)enumerator);
  269. return;
  270. }
  271. tokens.Add(ModelGrammar.TokenArrayBegin(typeName));
  272. while (enumerator.MoveNext())
  273. {
  274. this.GetTokens(tokens, detector, enumerator.Current);
  275. }
  276. tokens.Add(ModelGrammar.TokenArrayEnd);
  277. }
  278. private void GetObjectTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, DataName typeName, IDictionaryEnumerator enumerator)
  279. {
  280. tokens.Add(ModelGrammar.TokenObjectBegin(typeName));
  281. while (enumerator.MoveNext())
  282. {
  283. tokens.Add(ModelGrammar.TokenProperty(enumerator.Key));
  284. this.GetTokens(tokens, detector, enumerator.Value);
  285. }
  286. tokens.Add(ModelGrammar.TokenObjectEnd);
  287. }
  288. private void GetObjectTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, DataName typeName, IEnumerator<KeyValuePair<string, object>> enumerator)
  289. {
  290. tokens.Add(ModelGrammar.TokenObjectBegin(typeName));
  291. while (enumerator.MoveNext())
  292. {
  293. KeyValuePair<string, object> pair = enumerator.Current;
  294. tokens.Add(ModelGrammar.TokenProperty(pair.Key));
  295. this.GetTokens(tokens, detector, pair.Value);
  296. }
  297. tokens.Add(ModelGrammar.TokenObjectEnd);
  298. }
  299. #if NET40 && !WINDOWS_PHONE
  300. private void GetObjectTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, Type type, System.Dynamic.DynamicObject value)
  301. {
  302. DataName typeName = this.GetTypeName(value);
  303. tokens.Add(ModelGrammar.TokenObjectBegin(typeName));
  304. foreach (var memberName in value.GetDynamicMemberNames())
  305. {
  306. object propertyValue;
  307. if (!value.TryGetMember(new DynamicGetter(memberName), out propertyValue))
  308. {
  309. continue;
  310. }
  311. tokens.Add(ModelGrammar.TokenProperty(memberName));
  312. this.GetTokens(tokens, detector, propertyValue);
  313. }
  314. tokens.Add(ModelGrammar.TokenObjectEnd);
  315. }
  316. #endif
  317. private void GetObjectTokens(List<Token<ModelTokenType>> tokens, ICycleDetector detector, Type type, object value)
  318. {
  319. DataName typeName = this.GetTypeName(value);
  320. tokens.Add(ModelGrammar.TokenObjectBegin(typeName));
  321. IDictionary<string, MemberMap> maps = this.Settings.Resolver.LoadMaps(type);
  322. if (maps == null)
  323. {
  324. // TODO: verify no other valid situations here
  325. tokens.Add(ModelGrammar.TokenObjectEnd);
  326. return;
  327. }
  328. // allow the resolver to optionally sort the members
  329. IEnumerable<MemberMap> members = this.Settings.Resolver.SortMembers(maps.Values);
  330. foreach (var map in members)
  331. {
  332. if (map.IsAlternate ||
  333. map.Getter == null)
  334. {
  335. continue;
  336. }
  337. object propertyValue = map.Getter(value);
  338. if (map.IsIgnored != null &&
  339. map.IsIgnored(value, propertyValue))
  340. {
  341. continue;
  342. }
  343. tokens.Add(ModelGrammar.TokenProperty(map.DataName));
  344. this.GetTokens(tokens, detector, propertyValue);
  345. }
  346. tokens.Add(ModelGrammar.TokenObjectEnd);
  347. }
  348. #endregion Walker Methods
  349. #region Utility Methods
  350. private DataName GetTypeName(object value)
  351. {
  352. IEnumerable<DataName> typeNames = this.Settings.Resolver.LoadTypeName((value != null) ? value.GetType() : null);
  353. if (typeNames != null)
  354. {
  355. foreach (DataName n in typeNames)
  356. {
  357. if (!n.IsEmpty)
  358. {
  359. return n;
  360. }
  361. }
  362. }
  363. return DataName.Empty;
  364. }
  365. #endregion Utility Methods
  366. }
  367. }