//This function is pretty recursive //Indent = how many spaces before text //Add dictionary material support and then we guchi and never need to touch this again, just add attributes! //Example includes: listings[id],listinggroups public static bool List <T>(T element, bool verbose, string[] includes, string[] filterStrings = null, int length = int.MaxValue, int indents = 0) { //Parse the includes formatting strings. Key = Current Primary element. Tuple<lenofpelementifenumerable, subincludes> //Format: (currentElementName, (subIncludes, lenToList, filterString) Dictionary <string, Tuple <string[], int, string[]> > subIncludes = new Dictionary <string, Tuple <string[], int, string[]> >(); if (includes != null) { Dictionary <string, string[]> subIncludesTmp = ParseSearchStringToDictionary(includes); //TODO Need to manage when filter string is null Dictionary <string, string[]> filterStringsTmp = ParseSearchStringToDictionary(filterStrings); //Reformat includesDict to include specified length foreach (string key in subIncludesTmp.Keys) { string[] primaryInfo = key.Split(':'); string primaryKey = primaryInfo[0]; int sublength = (primaryInfo.Length > 1) ? Int32.Parse(primaryInfo[1]) : length; string[] filterString; //Since etsy objects will NEVER have a comparison done on them directly, we dont have to worry about reformatting any primary keys //that may have a comparison sign in them if (!filterStringsTmp.TryGetValue(primaryKey, out filterString)) { filterString = null; } Tuple <string[], int, string[]> keyInfo = new Tuple <string[], int, string[]>(subIncludesTmp[key], sublength, filterString); subIncludes.Add(primaryKey, keyInfo); } } Action <Tuple <PropertyInfo, ListableAttribute> > listElements = (propertyAttribute) => { PropertyInfo property = propertyAttribute.Item1; ListableAttribute attribute = propertyAttribute.Item2; //If included, check if ienumerable or not bool isEnumerable = typeof(System.Collections.IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string); if (isEnumerable) { //Set length for this length = subIncludes[attribute.Name].Item2; //Print header string title = string.Format("{0}:", attribute.Name); Console.WriteLine(title.PadLeft(title.Length + indents)); //Manage when stop listing int i = 0; System.Collections.IEnumerator enumerator = ((System.Collections.IEnumerable)(property.GetValue(element))).GetEnumerator(); bool hasNext = enumerator.MoveNext(); while (hasNext) { if (Filter(enumerator.Current, subIncludes[attribute.Name].Item3)) { List(enumerator.Current, verbose, subIncludes[attribute.Name].Item1, filterStrings: subIncludes[attribute.Name].Item3, indents: indents + 4); //If there is another item to print, put a spacer line in between them if (hasNext && i != length - 1) { Console.WriteLine(); } //Check if list is over i++; if (i >= length) { break; } } hasNext = enumerator.MoveNext(); } } else { //Works fine until we get a non array non basic data type passed in (Like Product or Material) string toPrint = string.Format("{0}: {1}\n", attribute.Name, property.GetValue(element)); toPrint = toPrint.PadLeft(toPrint.Length + indents); Console.Write(toPrint); } }; //Get properties that are listable MemberInfo[] properties = element.GetType().GetProperties(); properties = properties.Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(ListableAttribute))).ToArray(); //Create list of attributes for those properties List <Tuple <PropertyInfo, ListableAttribute> > attributes = new List <Tuple <PropertyInfo, ListableAttribute> >(); foreach (PropertyInfo property in properties) { ListableAttribute attribute = (ListableAttribute)Attribute.GetCustomAttribute(property, typeof(ListableAttribute)); attribute.Name = attribute.Name ?? property.Name.ToLower(); attributes.Add(new Tuple <PropertyInfo, ListableAttribute>(property, attribute)); } //Do default properties then take out of list so as to not be repeated Tuple <PropertyInfo, ListableAttribute>[] defaults = attributes.FindAll(t => t.Item2.Default).ToArray(); foreach (var def in defaults) { listElements(def); attributes.Remove(def); } //Iterate over desired //I am iterating over keys instead of properties because I want things to be listed in the order they are formated in the includes string foreach (string key in subIncludes.Keys) { Tuple <PropertyInfo, ListableAttribute> propertyAttribute = attributes.Find(t => t.Item2.Name.Equals(key)); if (propertyAttribute != null) { ListableAttribute attribute = propertyAttribute.Item2; //Use method supplied in attribute if (attribute.CallbackClass != null) { MethodInfo methodInfo = attribute.CallbackClass.GetMethod(attribute.CallbackMethodName, BindingFlags.Public | BindingFlags.Static); if (methodInfo != null) { //These parameters should work for any method supplied in an attribute, it is just required //I should probably add a check to make sure, but I don't want to so I just won't mess it up for now //since I am the only one using this object[] parameters = { element, propertyAttribute, subIncludes[attribute.Name], indents }; methodInfo = methodInfo.MakeGenericMethod(element.GetType()); methodInfo.Invoke(null, parameters); } else { Console.WriteLine("Supplied callback method in class {0} name {1} does not exist or is not public and static", attribute.CallbackClass.GetType().ToString(), attribute.CallbackMethodName); } } //Use this default list implementation else { listElements(propertyAttribute); } } } return(false); }
//Indent = how many spaces before text //Add dictionary material support and then we guchi and never need to touch this again, just add attributes! public static void List <T>(T element, bool verbose, string includes, int length = int.MaxValue, int indents = 0) { //Parse the includes formatting strings. Key = Current Primary element. Tuple<lenofpelementifenumerable, subincludes> Dictionary <string, string[]> subIncludes = new Dictionary <string, string[]>(); if (includes != null) { //Finds square bracket sections and seperate them from the primary key Regex reg = new Regex(@"\[(.*)\]"); //Splits into outermost comma seperated values, and lists those seperately //List doesn't list groups of things, so this seperates them into single items to list those Regex reg2 = new Regex(@"[\w:]+(\[+.*?\]+)*"); string[] allIncludes = (string.IsNullOrEmpty(includes)) ? null : reg.Matches(includes.ToLower()).Cast <Match>().Select(m => m.Value).ToArray(); if (allIncludes.Length > 1) { foreach (string include in allIncludes) { List(element, verbose, include, indents: indents); } } foreach (string include in allIncludes) { Match match = reg.Match(include); if (match != null) { string primaryKey = include.Remove(match.Index, match.Length); string[] primaryInfo = primaryKey.Split(':'); primaryKey = primaryInfo[0]; length = (primaryInfo.Length > 1) ? Int32.Parse(primaryInfo[1]) : length; string newIncludes = match.Groups[1].Value; //splitting based on comma doesn't work, split based off other regex expression string[] sub = (string.IsNullOrEmpty(newIncludes)) ? null : reg2.Matches(newIncludes).Cast <Match>().Select(m => m.Value).ToArray(); subIncludes.Add(primaryKey, sub); } else { subIncludes.Add(include, null); } } } Action <Tuple <PropertyInfo, ListableAttribute> > listElements = (propertyAttribute) => { PropertyInfo property = propertyAttribute.Item1; ListableAttribute attribute = propertyAttribute.Item2; //If included, check if ienumerable or not bool isEnumerable = typeof(System.Collections.IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string); if (isEnumerable) { //Print header string title = string.Format("{0}:", attribute.Name); Console.WriteLine(title.PadLeft(title.Length + indents)); //Manage when stop listing int i = 0; System.Collections.IEnumerator enumerator = ((System.Collections.IEnumerable)(property.GetValue(element))).GetEnumerator(); bool hasNext = enumerator.MoveNext(); while (hasNext) { List(enumerator.Current, verbose, subIncludes[attribute.Name], indents: indents + 4); //If there is another item to print, put a spacer line in between them if (hasNext && i != length - 1) { Console.WriteLine(); } //Check if list is over i++; if (i >= length) { break; } hasNext = enumerator.MoveNext(); } } else { //Works fine until we get a non array non basic data type passed in (Like Product or Material) string toPrint = string.Format("{0}: {1}\n", attribute.Name, property.GetValue(element)); toPrint = toPrint.PadLeft(toPrint.Length + indents); Console.Write(toPrint); } }; //Get properties that are listable MemberInfo[] properties = element.GetType().GetProperties(); properties = properties.Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(ListableAttribute))).ToArray(); //Create list of attributes for those properties List <Tuple <PropertyInfo, ListableAttribute> > attributes = new List <Tuple <PropertyInfo, ListableAttribute> >(); foreach (PropertyInfo property in properties) { ListableAttribute attribute = (ListableAttribute)Attribute.GetCustomAttribute(property, typeof(ListableAttribute)); attribute.Name = attribute.Name ?? property.Name.ToLower(); attributes.Add(new Tuple <PropertyInfo, ListableAttribute>(property, attribute)); } //Do default properties then take out of list so as to not be repeated Tuple <PropertyInfo, ListableAttribute>[] defaults = attributes.FindAll(t => t.Item2.Default).ToArray(); foreach (var def in defaults) { listElements(def); attributes.Remove(def); } //Iterate over desired //I am iterating over keys instead of properties because I want things to be listed in the order they are formated in the includes string foreach (string key in subIncludes.Keys) { Tuple <PropertyInfo, ListableAttribute> propertyAttribute = attributes.Find(t => t.Item2.Name.Equals(key)); if (propertyAttribute != null) { ListableAttribute attribute = propertyAttribute.Item2; //Use method supplied in attribute if (attribute.CallbackClass != null) { MethodInfo methodInfo = attribute.CallbackClass.GetMethod(attribute.CallbackMethodName, BindingFlags.Public | BindingFlags.Static); if (methodInfo != null) { //These parameters should work for any method supplied in an attribute, it is just required //I should probably add a check to make sure, but I don't want to so I just won't mess it up for now //since I am the only one using this object[] parameters = { element, propertyAttribute, subIncludes[attribute.Name], indents }; methodInfo = methodInfo.MakeGenericMethod(element.GetType()); methodInfo.Invoke(null, parameters); } else { Console.WriteLine("Supplied callback method in class {0} name {1} does not exist or is not public and static", attribute.CallbackClass.GetType().ToString(), attribute.CallbackMethodName); } } //Use this default list implementation else { listElements(propertyAttribute); } } } }