#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #endregion using System; using System.Collections.Generic; using System.Reflection; using System.Collections; using System.Linq; using System.Globalization; using System.Runtime.Serialization.Formatters; namespace Newtonsoft.Json.Utilities { internal static class ReflectionUtils { public static Type GetObjectType(object v) { return (v != null) ? v.GetType() : null; } public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat) { switch (assemblyFormat) { case FormatterAssemblyStyle.Simple: return t.FullName + ", " + t.Assembly.GetName().Name; case FormatterAssemblyStyle.Full: return t.AssemblyQualifiedName; default: throw new ArgumentOutOfRangeException(); } } public static bool IsInstantiatableType(Type t) { ValidationUtils.ArgumentNotNull(t, "t"); if (t.IsAbstract || t.IsInterface || t.IsArray || t.IsGenericTypeDefinition || t == typeof(void)) return false; if (!HasDefaultConstructor(t)) return false; return true; } public static bool HasDefaultConstructor(Type t) { return HasDefaultConstructor(t, false); } public static bool HasDefaultConstructor(Type t, bool nonPublic) { ValidationUtils.ArgumentNotNull(t, "t"); if (t.IsValueType) return true; return (GetDefaultConstructor(t, nonPublic) != null); } public static ConstructorInfo GetDefaultConstructor(Type t) { return GetDefaultConstructor(t, false); } public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic) { BindingFlags accessModifier = BindingFlags.Public; if (nonPublic) accessModifier = accessModifier | BindingFlags.NonPublic; return t.GetConstructor(accessModifier | BindingFlags.Instance, null, new Type[0], null); } public static bool IsNullable(Type t) { ValidationUtils.ArgumentNotNull(t, "t"); if (t.IsValueType) return IsNullableType(t); return true; } public static bool IsNullableType(Type t) { ValidationUtils.ArgumentNotNull(t, "t"); return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)); } //public static bool IsValueTypeUnitializedValue(ValueType value) //{ // if (value == null) // return true; // return value.Equals(CreateUnitializedValue(value.GetType())); //} public static bool IsUnitializedValue(object value) { if (value == null) { return true; } else { object unitializedValue = CreateUnitializedValue(value.GetType()); return value.Equals(unitializedValue); } } public static object CreateUnitializedValue(Type type) { ValidationUtils.ArgumentNotNull(type, "type"); if (type.IsGenericTypeDefinition) throw new ArgumentException("Type {0} is a generic type definition and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, type), "type"); if (type.IsClass || type.IsInterface || type == typeof(void)) return null; else if (type.IsValueType) return Activator.CreateInstance(type); else throw new ArgumentException("Type {0} cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, type), "type"); } public static bool IsPropertyIndexed(PropertyInfo property) { ValidationUtils.ArgumentNotNull(property, "property"); return !CollectionUtils.IsNullOrEmpty(property.GetIndexParameters()); } public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition) { Type implementingType; return ImplementsGenericDefinition(type, genericInterfaceDefinition, out implementingType); } public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition, out Type implementingType) { ValidationUtils.ArgumentNotNull(type, "type"); ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, "genericInterfaceDefinition"); if (!genericInterfaceDefinition.IsInterface || !genericInterfaceDefinition.IsGenericTypeDefinition) throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition)); if (type.IsInterface) { if (type.IsGenericType) { Type interfaceDefinition = type.GetGenericTypeDefinition(); if (genericInterfaceDefinition == interfaceDefinition) { implementingType = type; return true; } } } foreach (Type i in type.GetInterfaces()) { if (i.IsGenericType) { Type interfaceDefinition = i.GetGenericTypeDefinition(); if (genericInterfaceDefinition == interfaceDefinition) { implementingType = i; return true; } } } implementingType = null; return false; } public static bool AssignableToTypeName(this Type type, string fullTypeName, out Type match) { Type current = type; while (current != null) { if (string.Equals(current.FullName, fullTypeName, StringComparison.Ordinal)) { match = current; return true; } current = current.BaseType; } foreach (Type i in type.GetInterfaces()) { if (string.Equals(i.Name, fullTypeName, StringComparison.Ordinal)) { match = type; return true; } } match = null; return false; } public static bool AssignableToTypeName(this Type type, string fullTypeName) { Type match; return type.AssignableToTypeName(fullTypeName, out match); } public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition) { Type implementingType; return InheritsGenericDefinition(type, genericClassDefinition, out implementingType); } public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition, out Type implementingType) { ValidationUtils.ArgumentNotNull(type, "type"); ValidationUtils.ArgumentNotNull(genericClassDefinition, "genericClassDefinition"); if (!genericClassDefinition.IsClass || !genericClassDefinition.IsGenericTypeDefinition) throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition)); return InheritsGenericDefinitionInternal(type, genericClassDefinition, out implementingType); } private static bool InheritsGenericDefinitionInternal(Type currentType, Type genericClassDefinition, out Type implementingType) { if (currentType.IsGenericType) { Type currentGenericClassDefinition = currentType.GetGenericTypeDefinition(); if (genericClassDefinition == currentGenericClassDefinition) { implementingType = currentType; return true; } } if (currentType.BaseType == null) { implementingType = null; return false; } return InheritsGenericDefinitionInternal(currentType.BaseType, genericClassDefinition, out implementingType); } /// /// Gets the type of the typed collection's items. /// /// The type. /// The type of the typed collection's items. public static Type GetCollectionItemType(Type type) { ValidationUtils.ArgumentNotNull(type, "type"); Type genericListType; if (type.IsArray) { return type.GetElementType(); } else if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out genericListType)) { if (genericListType.IsGenericTypeDefinition) throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); return genericListType.GetGenericArguments()[0]; } else if (typeof(IEnumerable).IsAssignableFrom(type)) { return null; } else { throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); } } public static void GetDictionaryKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) { ValidationUtils.ArgumentNotNull(dictionaryType, "type"); Type genericDictionaryType; if (ImplementsGenericDefinition(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType)) { if (genericDictionaryType.IsGenericTypeDefinition) throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); Type[] dictionaryGenericArguments = genericDictionaryType.GetGenericArguments(); keyType = dictionaryGenericArguments[0]; valueType = dictionaryGenericArguments[1]; return; } else if (typeof(IDictionary).IsAssignableFrom(dictionaryType)) { keyType = null; valueType = null; return; } else { throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); } } public static Type GetDictionaryValueType(Type dictionaryType) { Type keyType; Type valueType; GetDictionaryKeyValueTypes(dictionaryType, out keyType, out valueType); return valueType; } public static Type GetDictionaryKeyType(Type dictionaryType) { Type keyType; Type valueType; GetDictionaryKeyValueTypes(dictionaryType, out keyType, out valueType); return keyType; } /// /// Tests whether the list's items are their unitialized value. /// /// The list. /// Whether the list's items are their unitialized value public static bool ItemsUnitializedValue(IList list) { ValidationUtils.ArgumentNotNull(list, "list"); Type elementType = GetCollectionItemType(list.GetType()); if (elementType.IsValueType) { object unitializedValue = CreateUnitializedValue(elementType); for (int i = 0; i < list.Count; i++) { if (!list[i].Equals(unitializedValue)) return false; } } else if (elementType.IsClass) { for (int i = 0; i < list.Count; i++) { object value = list[i]; if (value != null) return false; } } else { throw new Exception("Type {0} is neither a ValueType or a Class.".FormatWith(CultureInfo.InvariantCulture, elementType)); } return true; } /// /// Gets the member's underlying type. /// /// The member. /// The underlying type of the member. public static Type GetMemberUnderlyingType(MemberInfo member) { ValidationUtils.ArgumentNotNull(member, "member"); switch (member.MemberType) { case MemberTypes.Field: return ((FieldInfo)member).FieldType; case MemberTypes.Property: return ((PropertyInfo)member).PropertyType; case MemberTypes.Event: return ((EventInfo)member).EventHandlerType; default: throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or EventInfo", "member"); } } /// /// Determines whether the member is an indexed property. /// /// The member. /// /// true if the member is an indexed property; otherwise, false. /// public static bool IsIndexedProperty(MemberInfo member) { ValidationUtils.ArgumentNotNull(member, "member"); PropertyInfo propertyInfo = member as PropertyInfo; if (propertyInfo != null) return IsIndexedProperty(propertyInfo); else return false; } /// /// Determines whether the property is an indexed property. /// /// The property. /// /// true if the property is an indexed property; otherwise, false. /// public static bool IsIndexedProperty(PropertyInfo property) { ValidationUtils.ArgumentNotNull(property, "property"); return (property.GetIndexParameters().Length > 0); } /// /// Gets the member's value on the object. /// /// The member. /// The target object. /// The member's value on the object. public static object GetMemberValue(MemberInfo member, object target) { ValidationUtils.ArgumentNotNull(member, "member"); ValidationUtils.ArgumentNotNull(target, "target"); switch (member.MemberType) { case MemberTypes.Field: return ((FieldInfo)member).GetValue(target); case MemberTypes.Property: try { return ((PropertyInfo)member).GetValue(target, null); } catch (TargetParameterCountException e) { throw new ArgumentException("MemberInfo '{0}' has index parameters".FormatWith(CultureInfo.InvariantCulture, member.Name), e); } default: throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, member.Name), "member"); } } /// /// Sets the member's value on the target object. /// /// The member. /// The target. /// The value. public static void SetMemberValue(MemberInfo member, object target, object value) { ValidationUtils.ArgumentNotNull(member, "member"); ValidationUtils.ArgumentNotNull(target, "target"); switch (member.MemberType) { case MemberTypes.Field: ((FieldInfo)member).SetValue(target, value); break; case MemberTypes.Property: ((PropertyInfo)member).SetValue(target, value, null); break; default: throw new ArgumentException("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), "member"); } } /// /// Determines whether the specified MemberInfo can be read. /// /// The MemberInfo to determine whether can be read. /// /// if set to true then allow the member to be gotten non-publicly. /// /// true if the specified MemberInfo can be read; otherwise, false. /// public static bool CanReadMemberValue(MemberInfo member, bool nonPublic) { switch (member.MemberType) { case MemberTypes.Field: FieldInfo fieldInfo = (FieldInfo)member; if (nonPublic) return true; else if (fieldInfo.IsPublic) return true; return false; case MemberTypes.Property: PropertyInfo propertyInfo = (PropertyInfo) member; if (!propertyInfo.CanRead) return false; if (nonPublic) return true; return (propertyInfo.GetGetMethod(nonPublic) != null); default: return false; } } /// /// Determines whether the specified MemberInfo can be set. /// /// The MemberInfo to determine whether can be set. /// if set to true then allow the member to be set non-publicly. /// /// true if the specified MemberInfo can be set; otherwise, false. /// public static bool CanSetMemberValue(MemberInfo member, bool nonPublic) { switch (member.MemberType) { case MemberTypes.Field: FieldInfo fieldInfo = (FieldInfo)member; if (fieldInfo.IsInitOnly) return false; if (nonPublic) return true; else if (fieldInfo.IsPublic) return true; return false; case MemberTypes.Property: PropertyInfo propertyInfo = (PropertyInfo)member; if (!propertyInfo.CanWrite) return false; if (nonPublic) return true; return (propertyInfo.GetSetMethod(nonPublic) != null); default: return false; } } public static List GetFieldsAndProperties(BindingFlags bindingAttr) { return GetFieldsAndProperties(typeof(T), bindingAttr); } public static List GetFieldsAndProperties(Type type, BindingFlags bindingAttr) { List targetMembers = new List(); targetMembers.AddRange(GetFields(type, bindingAttr)); targetMembers.AddRange(GetProperties(type, bindingAttr)); // for some reason .NET returns multiple members when overriding a generic member on a base class // http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/ // filter members to only return the override on the topmost class // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... List distinctMembers = new List(targetMembers.Count); var groupedMembers = targetMembers.GroupBy(m => m.Name).Select(g => new { Count = g.Count(), Members = g.Cast() }); foreach (var groupedMember in groupedMembers) { if (groupedMember.Count == 1) { distinctMembers.Add(groupedMember.Members.First()); } else { var members = groupedMember.Members.Where(m => !IsOverridenGenericMember(m, bindingAttr) || m.Name == "Item"); distinctMembers.AddRange(members); } } return distinctMembers; } private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr) { if (memberInfo.MemberType != MemberTypes.Field && memberInfo.MemberType != MemberTypes.Property) throw new ArgumentException("Member must be a field or property."); Type declaringType = memberInfo.DeclaringType; if (!declaringType.IsGenericType) return false; Type genericTypeDefinition = declaringType.GetGenericTypeDefinition(); if (genericTypeDefinition == null) return false; MemberInfo[] members = genericTypeDefinition.GetMember(memberInfo.Name, bindingAttr); if (members.Length == 0) return false; Type memberUnderlyingType = GetMemberUnderlyingType(members[0]); if (!memberUnderlyingType.IsGenericParameter) return false; return true; } public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute { return GetAttribute(attributeProvider, true); } public static T GetAttribute(ICustomAttributeProvider attributeProvider, bool inherit) where T : Attribute { T[] attributes = GetAttributes(attributeProvider, inherit); return CollectionUtils.GetSingleItem(attributes, true); } public static T[] GetAttributes(ICustomAttributeProvider attributeProvider, bool inherit) where T : Attribute { ValidationUtils.ArgumentNotNull(attributeProvider, "attributeProvider"); return (T[])attributeProvider.GetCustomAttributes(typeof(T), inherit); } public static string GetNameAndAssessmblyName(Type t) { ValidationUtils.ArgumentNotNull(t, "t"); return t.FullName + ", " + t.Assembly.GetName().Name; } public static Type MakeGenericType(Type genericTypeDefinition, params Type[] innerTypes) { ValidationUtils.ArgumentNotNull(genericTypeDefinition, "genericTypeDefinition"); ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes"); ValidationUtils.ArgumentConditionTrue(genericTypeDefinition.IsGenericTypeDefinition, "genericTypeDefinition", "Type {0} is not a generic type definition.".FormatWith(CultureInfo.InvariantCulture, genericTypeDefinition)); return genericTypeDefinition.MakeGenericType(innerTypes); } public static object CreateGeneric(Type genericTypeDefinition, Type innerType, params object[] args) { return CreateGeneric(genericTypeDefinition, new [] { innerType }, args); } public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, params object[] args) { return CreateGeneric(genericTypeDefinition, innerTypes, (t, a) => CreateInstance(t, a.ToArray()), args); } public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, Func, object> instanceCreator, params object[] args) { ValidationUtils.ArgumentNotNull(genericTypeDefinition, "genericTypeDefinition"); ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes"); ValidationUtils.ArgumentNotNull(instanceCreator, "createInstance"); Type specificType = MakeGenericType(genericTypeDefinition, innerTypes.ToArray()); return instanceCreator(specificType, args); } public static bool IsCompatibleValue(object value, Type type) { if (value == null) return IsNullable(type); if (type.IsAssignableFrom(value.GetType())) return true; return false; } public static object CreateInstance(Type type, params object[] args) { ValidationUtils.ArgumentNotNull(type, "type"); #if !PocketPC return Activator.CreateInstance(type, args); #else // CF doesn't have a Activator.CreateInstance overload that takes args // lame if (type.IsValueType && CollectionUtils.IsNullOrEmpty(args)) return Activator.CreateInstance(type); ConstructorInfo[] constructors = type.GetConstructors(); ConstructorInfo match = constructors.Where(c => { ParameterInfo[] parameters = c.GetParameters(); if (parameters.Length != args.Length) return false; for (int i = 0; i < parameters.Length; i++) { ParameterInfo parameter = parameters[i]; object value = args[i]; if (!IsCompatibleValue(value, parameter.ParameterType)) return false; } return true; }).FirstOrDefault(); if (match == null) throw new Exception("Could not create '{0}' with given parameters.".FormatWith(CultureInfo.InvariantCulture, type)); return match.Invoke(args); #endif } public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName) { int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); if (assemblyDelimiterIndex != null) { typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.Value).Trim(); assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.Value + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.Value - 1).Trim(); } else { typeName = fullyQualifiedTypeName; assemblyName = null; } } private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) { // we need to get the first comma following all surrounded in brackets because of generic types // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 int scope = 0; for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { char current = fullyQualifiedTypeName[i]; switch (current) { case '[': scope++; break; case ']': scope--; break; case ',': if (scope == 0) return i; break; } } return null; } public static IEnumerable GetFields(Type targetType, BindingFlags bindingAttr) { ValidationUtils.ArgumentNotNull(targetType, "targetType"); List fieldInfos = new List(targetType.GetFields(bindingAttr)); // Type.GetFields doesn't return inherited private fields // manually find private fields from base class GetChildPrivateFields(fieldInfos, targetType, bindingAttr); return fieldInfos.Cast(); } private static void GetChildPrivateFields(IList initialFields, Type targetType, BindingFlags bindingAttr) { // fix weirdness with private FieldInfos only being returned for the current Type // find base type fields and add them to result if ((bindingAttr & BindingFlags.NonPublic) != 0) { // modify flags to not search for public fields BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); while ((targetType = targetType.BaseType) != null) { // filter out protected fields IEnumerable childPrivateFields = targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate).Cast(); initialFields.AddRange(childPrivateFields); } } } public static IEnumerable GetProperties(Type targetType, BindingFlags bindingAttr) { ValidationUtils.ArgumentNotNull(targetType, "targetType"); List propertyInfos = new List(targetType.GetProperties(bindingAttr)); GetChildPrivateProperties(propertyInfos, targetType, bindingAttr); return propertyInfos.Cast(); } public static BindingFlags RemoveFlag(this BindingFlags bindingAttr, BindingFlags flag) { return ((bindingAttr & flag) == flag) ? bindingAttr ^ flag : bindingAttr; } private static void GetChildPrivateProperties(IList initialProperties, Type targetType, BindingFlags bindingAttr) { // fix weirdness with private PropertyInfos only being returned for the current Type // find base type properties and add them to result if ((bindingAttr & BindingFlags.NonPublic) != 0) { // modify flags to not search for public fields BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); while ((targetType = targetType.BaseType) != null) { foreach (PropertyInfo propertyInfo in targetType.GetProperties(nonPublicBindingAttr)) { PropertyInfo nonPublicProperty = propertyInfo; // have to test on name rather than reference because instances are different // depending on the type that GetProperties was called on int index = initialProperties.IndexOf(p => p.Name == nonPublicProperty.Name); if (index == -1) { initialProperties.Add(nonPublicProperty); } else { // replace nonpublic properties for a child, but gotten from // the parent with the one from the child // the property gotten from the child will have access to private getter/setter initialProperties[index] = nonPublicProperty; } } } } } } }