feanz
3/17/2014 - 11:33 AM

Walk Object Graph

Walk Object Graph

public static class ReflectionExtensions
	{
		/// <summary>
		/// Walks an object graph performing a function for any object in the graph that implements T or any collection that implements T
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="toWalk">Item to walk from</param>
		/// <param name="snippetForObject">Function for each item of type T</param>
		/// <param name="snippetForCollection">Function for each IEnumerable of type T</param>
		/// <param name="exemptProperties">Properties to exempt</param>
		public static void WalkObjectGraph<T>(this T toWalk, Func<T, bool> snippetForObject,
			Action<IList> snippetForCollection = null,
			params string[] exemptProperties) where T : class
		{
			var visited = new List<T>();
			Action<T> walk = null;

			var exemptions = new List<string>();

			if (exemptProperties != null)
				exemptions = exemptProperties.ToList();

			walk = o =>
			{
				if (o != null && !visited.Contains(o))
				{
					visited.Add(o);

					var exitWalk = snippetForObject.Invoke(o);

					if (!exitWalk)
					{
						var properties = o.GetBrowsableProperties();

						foreach (var property in properties)
						{
							if (!exemptions.Contains(property.Name))
							{
								var propertyType = property.PropertyType;

								if (typeof(T).IsAssignableFrom(propertyType))
								{
									var subProperty = (T)property.GetValue(o, null);
									walk(subProperty);
								}

								var implementsIEnumerable = propertyType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
								if (implementsIEnumerable)
								{
									var genericArgument = propertyType.GetGenericArguments()[0];
									if (typeof(T).IsAssignableFrom(genericArgument))
									{
										var col = property.GetValue(o, null) as IList;
										if (col != null)
										{
											if (snippetForCollection != null)
												snippetForCollection.Invoke(col);

											foreach (var item in col)
											{
												walk((T)item);
											}
										}
									}
								}
							}
						}
					}
				}
			};

			walk(toWalk);
		}

		
		/// <summary>
		/// Get all public reference properties or collection types (implement IEnumerable) which are not decorated with IEnumerable
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static IEnumerable<PropertyInfo> GetBrowsableProperties(this object item)
		{
			return item.GetType()
				.GetProperties()
				.Where(info => !Attribute.IsDefined(info, typeof(NoneNavigateable)) 
					&& (info.PropertyType.IsClass || typeof(IEnumerable).IsAssignableFrom(info.PropertyType))
				&& info.PropertyType != typeof(string));
		}
	}
/// <summary>
	/// A decoration only property used to determine if a property should be walked by specific graph walking functions
	/// </summary>
	[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
	public class NoneNavigateableAttribute : Attribute{}