//----------------------------------------------------------------------- // // Copyright (c) Andrew Arnott. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOAuth.Messaging { using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Xml; using System.Xml.Linq; /// /// A sorting tool to arrange fields in an order expected by the . /// internal class DataContractMemberComparer : IComparer { /// /// The cached calculated inheritance ranking of every [DataMember] member of a type. /// private Dictionary ranking; /// /// Initializes a new instance of the class. /// /// The data contract type that will be deserialized to. internal DataContractMemberComparer(Type dataContractType) { // The elements must be serialized in inheritance rank and alphabetical order // so the DataContractSerializer will see them. this.ranking = GetDataMemberInheritanceRanking(dataContractType); } #region IComparer Members /// /// Compares to fields and decides what order they should appear in. /// /// The first field. /// The second field. /// -1 if the first field should appear first, 0 if it doesn't matter, 1 if it should appear last. public int Compare(string field1, string field2) { int rank1, rank2; bool field1Valid = this.ranking.TryGetValue(field1, out rank1); bool field2Valid = this.ranking.TryGetValue(field2, out rank2); // If both fields are invalid, we don't care about the order. if (!field1Valid && !field2Valid) { return 0; } // If exactly one is valid, put that one first. if (field1Valid ^ field2Valid) { return field1Valid ? -1 : 1; } // First compare their inheritance ranking. if (rank1 != rank2) { // We want DESCENDING rank order, putting the members defined in the most // base class first. return -rank1.CompareTo(rank2); } // Finally sort alphabetically with case sensitivity. return string.CompareOrdinal(field1, field2); } #endregion /// /// Generates a dictionary of field name and inheritance rankings for a given DataContract type. /// /// The type to generate member rankings for. /// The generated dictionary. private static Dictionary GetDataMemberInheritanceRanking(Type type) { Debug.Assert(type != null, "type == null"); var ranking = new Dictionary(); // TODO: review partial trust scenarios and this NonPublic flag. Type currentType = type; int rank = 0; do { foreach (MemberInfo member in currentType.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) { if (member is PropertyInfo || member is FieldInfo) { DataMemberAttribute dataMemberAttribute = member.GetCustomAttributes(typeof(DataMemberAttribute), true).OfType().FirstOrDefault(); if (dataMemberAttribute != null) { string name = dataMemberAttribute.Name ?? member.Name; ranking.Add(name, rank); } } } rank++; currentType = currentType.BaseType; } while (currentType != null); return ranking; } } }