PageRenderTime 83ms CodeModel.GetById 66ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

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