/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Amazon.Util.Internal; using System.Globalization; using System.Collections; using Amazon.DynamoDBv2.DocumentModel; namespace Amazon.DynamoDBv2.DataModel { internal static class Utils { #region Type methods private static readonly Type[] primitiveTypesArray = new Type[] { typeof(Boolean), typeof(Byte), typeof(Char), typeof(DateTime), typeof(Decimal), typeof(Double), typeof(int), typeof(long), typeof(SByte), typeof(short), typeof(Single), typeof(String), typeof(uint), typeof(ulong), typeof(ushort), typeof(Guid), typeof(byte[]), typeof(MemoryStream), typeof(Primitive) }; public static readonly IEnumerable<Type> PrimitiveTypes = new HashSet<Type>(primitiveTypesArray); private static readonly HashSet<ITypeInfo> PrimitiveTypeInfos = new HashSet<ITypeInfo>(primitiveTypesArray .Select(p => TypeFactory.GetTypeInfo(p))); public static bool IsPrimitive(Type type) { var typeWrapper = TypeFactory.GetTypeInfo(type); return PrimitiveTypeInfos.Any(ti => typeWrapper.IsAssignableFrom(ti)); } public static bool IsPrimitive<T>() { return IsPrimitive(typeof(T)); } public static void ValidatePrimitiveType(Type type) { if (!Utils.IsPrimitive(type)) throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, "{0} is not a supported Primitive type", type.FullName)); } public static void ValidatePrimitiveType<T>() { ValidatePrimitiveType(typeof(T)); } public static void ValidateVersionType(Type memberType) { var memberTypeWrapper = TypeFactory.GetTypeInfo(memberType); if (memberTypeWrapper.IsGenericType && memberTypeWrapper.GetGenericTypeDefinition() == typeof(Nullable<>) && (memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(Byte))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(SByte))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(int))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(uint))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(long))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(ulong))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(short))) || memberTypeWrapper.IsAssignableFrom(TypeFactory.GetTypeInfo(typeof(ushort))))) { return; } throw new InvalidOperationException("Version property must be of primitive, numeric, integer, nullable type (e.g. int?, long?, byte?)"); } public static Type GetPrimitiveElementType(Type collectionType) { var elementType = Utils.GetElementType(collectionType); if (elementType != null) { Utils.ValidatePrimitiveType(elementType); return elementType; } throw new InvalidOperationException("Unable to determine element type"); } public static Type GetElementType(Type collectionType) { var elementType = collectionType.GetElementType(); if (elementType == null) { var collectionTypeInfo = TypeFactory.GetTypeInfo(collectionType); var genericArguments = collectionTypeInfo.GetGenericArguments(); if (genericArguments != null && genericArguments.Length == 1) elementType = genericArguments[0]; } // elementType may be null at this point, meaning that the collectionType isn't a collectionType return elementType; } public static bool ItemsToCollection(Type targetType, IEnumerable<object> items, out object result) { return targetType.IsArray ? ItemsToArray(targetType, items, out result): //targetType is Array ItemsToIList(targetType, items, out result); //targetType is IList or has Add method. } private static bool ItemsToIList(Type targetType, IEnumerable<object> items, out object result) { result = Utils.Instantiate(targetType); var ilist = result as IList; if (ilist != null) { foreach (var item in items) ilist.Add(item); return true; } var targetTypeInfo = TypeFactory.GetTypeInfo(targetType); var addMethod = targetTypeInfo.GetMethod("Add"); if (addMethod != null) { foreach (var item in items) addMethod.Invoke(result, new object[] { item }); return true; } result = null; return false; } private static bool ItemsToArray(Type targetType, IEnumerable<object> items, out object result) { var itemlist = items.ToList(); var array = (Array)InstantiateArray(targetType, itemlist.Count); for (int i = 0; i < itemlist.Count; i++) { var item = itemlist[i]; array.SetValue(item, i); } result = array; return true; } #endregion #region Attribute methods public static DynamoDBTableAttribute GetTableAttribute(ITypeInfo targetTypeInfo) { DynamoDBTableAttribute tableAttribute = GetAttribute(targetTypeInfo) as DynamoDBTableAttribute; if (tableAttribute == null) return null; return tableAttribute; } public static DynamoDBAttribute GetAttribute(ITypeInfo targetTypeInfo) { if (targetTypeInfo == null) throw new ArgumentNullException("targetTypeInfo"); object[] attributes = targetTypeInfo.GetCustomAttributes(TypeFactory.GetTypeInfo(typeof(DynamoDBAttribute)), true); return GetSingleDDBAttribute(attributes); } public static DynamoDBAttribute GetAttribute(MemberInfo targetMemberInfo) { object[] attributes = GetAttributeObjects(targetMemberInfo); return GetSingleDDBAttribute(attributes); } public static List<DynamoDBAttribute> GetAttributes(MemberInfo targetMemberInfo) { object[] attObjects = GetAttributeObjects(targetMemberInfo) ?? new object[0]; var attributes = new List<DynamoDBAttribute>(); foreach (var attObj in attObjects) { var attribute = attObj as DynamoDBAttribute; if (attribute != null) attributes.Add(attribute); } return attributes; } private static DynamoDBAttribute GetSingleDDBAttribute(object[] attributes) { if (attributes.Length == 0) return null; if (attributes.Length == 1) return (attributes[0] as DynamoDBAttribute); throw new InvalidOperationException("Cannot have multiple DynamoDBAttributes on a single member"); } private static object[] GetAttributeObjects(MemberInfo targetMemberInfo) { if (targetMemberInfo == null) throw new ArgumentNullException("targetMemberInfo"); #if NETSTANDARD object[] attributes = CustomAttributeExtensions.GetCustomAttributes(targetMemberInfo, typeof(DynamoDBAttribute), true).ToArray<object>(); #else object[] attributes = Attribute.GetCustomAttributes(targetMemberInfo, typeof(DynamoDBAttribute), true).ToArray<object>(); #endif return attributes; } #endregion #region Non-DynamoDB utilities public static string ToLowerCamelCase(string value) { if (string.IsNullOrEmpty(value) || char.IsLower(value[0])) return value; StringBuilder sb = new StringBuilder(value); sb[0] = char.ToLowerInvariant(sb[0]); return sb.ToString(); } private static ITypeInfo[][] validConstructorInputs = new ITypeInfo[][] { TypeFactory.EmptyTypes, }; private static ITypeInfo[][] validArrayConstructorInputs = new ITypeInfo[][] { //supports one dimension Array only new ITypeInfo[] { TypeFactory.GetTypeInfo(typeof(int)) } }; private static ITypeInfo[][] validConverterConstructorInputs = new ITypeInfo[][] { TypeFactory.EmptyTypes, new ITypeInfo[] { TypeFactory.GetTypeInfo(typeof(DynamoDBContext)) } }; public static object InstantiateConverter(Type objectType, IDynamoDBContext context) { return InstantiateHelper(objectType, validConverterConstructorInputs, new object[] { context }); } public static object InstantiateArray(Type objectType,int length) { return InstantiateHelper(objectType, validArrayConstructorInputs, new object[] { length }); } public static object Instantiate(Type objectType) { return InstantiateHelper(objectType, validConstructorInputs, null); } private static object InstantiateHelper(Type objectType, ITypeInfo[][] validConstructorInputs, object[] optionalInput = null) { if (objectType == null) throw new ArgumentNullException("objectType"); if (!CanInstantiateHelper(objectType, validConstructorInputs)) throw new InvalidOperationException("Cannot instantiate type " + objectType.FullName); var objectTypeWrapper = TypeFactory.GetTypeInfo(objectType); var constructors = GetConstructors(objectTypeWrapper, validConstructorInputs).ToList(); if (constructors != null && constructors.Count > 0) { foreach (var constructor in constructors) { var inputs = constructor.GetParameters(); object[] constructorParameters = inputs.Length == 0 ? null : optionalInput; object instance = constructor.Invoke(constructorParameters); return instance; } } throw new InvalidOperationException("Unable to find valid constructor for type " + objectType.FullName); } private static IEnumerable<ConstructorInfo> GetConstructors(ITypeInfo typeInfo, ITypeInfo[][] validConstructorInputs) { foreach(var inputTypes in validConstructorInputs) { var constructor = typeInfo.GetConstructor(inputTypes); if (constructor != null) yield return constructor; } } public static bool CanInstantiate(Type objectType) { return CanInstantiateHelper(objectType, validConstructorInputs); } public static bool CanInstantiateArray(Type objectType) { return objectType.IsArray && CanInstantiateHelper(objectType, validArrayConstructorInputs); } public static bool CanInstantiateConverter(Type objectType) { return CanInstantiateHelper(objectType, validConverterConstructorInputs); } private static bool CanInstantiateHelper(Type objectType, ITypeInfo[][] validConstructorInputs) { var objectTypeWrapper = TypeFactory.GetTypeInfo(objectType); bool candidate = //objectType.IsPublic && objectTypeWrapper.IsClass && !objectTypeWrapper.IsInterface && !objectTypeWrapper.IsAbstract && !objectTypeWrapper.IsGenericTypeDefinition && !objectTypeWrapper.ContainsGenericParameters; if (!candidate) return false; // check valid constructor inputs var constructors = GetConstructors(objectTypeWrapper, validConstructorInputs).ToList(); if (constructors.Count == 0) return false; return true; } public static Type GetType(MemberInfo member) { var pi = member as PropertyInfo; var fi = member as FieldInfo; if (pi == null && fi == null) throw new ArgumentOutOfRangeException("member", "member must be of type PropertyInfo or FieldInfo"); return (pi != null ? pi.PropertyType : fi.FieldType); } public static bool IsReadWrite(MemberInfo member) { PropertyInfo property = member as PropertyInfo; FieldInfo field = member as FieldInfo; if (property != null) { return (property.CanRead && property.CanWrite); } else if (field != null) { return (field.IsPublic && !field.IsLiteral && !field.IsInitOnly); } else { throw new ArgumentOutOfRangeException("member", "Member must be FieldInfo or PropertyInfo"); } } public static bool ImplementsInterface(Type targetType, Type interfaceType) { var targetTypeWrapper = TypeFactory.GetTypeInfo(targetType); var interfaceTypeWrapper = TypeFactory.GetTypeInfo(interfaceType); if (!interfaceTypeWrapper.IsInterface) throw new ArgumentOutOfRangeException("interfaceType", "Type is not an interface"); foreach (var inter in targetTypeWrapper.GetInterfaces()) { var interWrapper = TypeFactory.GetTypeInfo(inter); if (object.Equals(interWrapper, interfaceTypeWrapper)) return true; if (interfaceTypeWrapper.IsGenericTypeDefinition && interWrapper.IsGenericType) { var generic = interWrapper.GetGenericTypeDefinition(); if (generic == interfaceType) return true; } } return false; } #endregion } }