123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.ComponentModel.DataAnnotations;
- using System.Globalization;
- using System.Reflection;
- using System.Runtime.Serialization;
- using System.Web.Http;
- using System.Web.Http.Description;
- using System.Xml.Serialization;
- using Newtonsoft.Json;
-
- namespace CacheControl_test.Areas.HelpPage.ModelDescriptions
- {
- /// <summary>
- /// Generates model descriptions for given types.
- /// </summary>
- public class ModelDescriptionGenerator
- {
- // Modify this to support more data annotation attributes.
- private readonly IDictionary<Type, Func<object, string>> AnnotationTextGenerator = new Dictionary<Type, Func<object, string>>
- {
- { typeof(RequiredAttribute), a => "Required" },
- { typeof(RangeAttribute), a =>
- {
- RangeAttribute range = (RangeAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
- }
- },
- { typeof(MaxLengthAttribute), a =>
- {
- MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
- }
- },
- { typeof(MinLengthAttribute), a =>
- {
- MinLengthAttribute minLength = (MinLengthAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
- }
- },
- { typeof(StringLengthAttribute), a =>
- {
- StringLengthAttribute strLength = (StringLengthAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
- }
- },
- { typeof(DataTypeAttribute), a =>
- {
- DataTypeAttribute dataType = (DataTypeAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
- }
- },
- { typeof(RegularExpressionAttribute), a =>
- {
- RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
- return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
- }
- },
- };
-
- // Modify this to add more default documentations.
- private readonly IDictionary<Type, string> DefaultTypeDocumentation = new Dictionary<Type, string>
- {
- { typeof(Int16), "integer" },
- { typeof(Int32), "integer" },
- { typeof(Int64), "integer" },
- { typeof(UInt16), "unsigned integer" },
- { typeof(UInt32), "unsigned integer" },
- { typeof(UInt64), "unsigned integer" },
- { typeof(Byte), "byte" },
- { typeof(Char), "character" },
- { typeof(SByte), "signed byte" },
- { typeof(Uri), "URI" },
- { typeof(Single), "decimal number" },
- { typeof(Double), "decimal number" },
- { typeof(Decimal), "decimal number" },
- { typeof(String), "string" },
- { typeof(Guid), "globally unique identifier" },
- { typeof(TimeSpan), "time interval" },
- { typeof(DateTime), "date" },
- { typeof(DateTimeOffset), "date" },
- { typeof(Boolean), "boolean" },
- };
-
- private Lazy<IModelDocumentationProvider> _documentationProvider;
-
- public ModelDescriptionGenerator(HttpConfiguration config)
- {
- if (config == null)
- {
- throw new ArgumentNullException("config");
- }
-
- _documentationProvider = new Lazy<IModelDocumentationProvider>(() => config.Services.GetDocumentationProvider() as IModelDocumentationProvider);
- GeneratedModels = new Dictionary<string, ModelDescription>(StringComparer.OrdinalIgnoreCase);
- }
-
- public Dictionary<string, ModelDescription> GeneratedModels { get; private set; }
-
- private IModelDocumentationProvider DocumentationProvider
- {
- get
- {
- return _documentationProvider.Value;
- }
- }
-
- public ModelDescription GetOrCreateModelDescription(Type modelType)
- {
- if (modelType == null)
- {
- throw new ArgumentNullException("modelType");
- }
-
- Type underlyingType = Nullable.GetUnderlyingType(modelType);
- if (underlyingType != null)
- {
- modelType = underlyingType;
- }
-
- ModelDescription modelDescription;
- string modelName = ModelNameHelper.GetModelName(modelType);
- if (GeneratedModels.TryGetValue(modelName, out modelDescription))
- {
- if (modelType != modelDescription.ModelType)
- {
- throw new InvalidOperationException(
- String.Format(
- CultureInfo.CurrentCulture,
- "A model description could not be created. Duplicate model name '{0}' was found for types '{1}' and '{2}'. " +
- "Use the [ModelName] attribute to change the model name for at least one of the types so that it has a unique name.",
- modelName,
- modelDescription.ModelType.FullName,
- modelType.FullName));
- }
-
- return modelDescription;
- }
-
- if (DefaultTypeDocumentation.ContainsKey(modelType))
- {
- return GenerateSimpleTypeModelDescription(modelType);
- }
-
- if (modelType.IsEnum)
- {
- return GenerateEnumTypeModelDescription(modelType);
- }
-
- if (modelType.IsGenericType)
- {
- Type[] genericArguments = modelType.GetGenericArguments();
-
- if (genericArguments.Length == 1)
- {
- Type enumerableType = typeof(IEnumerable<>).MakeGenericType(genericArguments);
- if (enumerableType.IsAssignableFrom(modelType))
- {
- return GenerateCollectionModelDescription(modelType, genericArguments[0]);
- }
- }
- if (genericArguments.Length == 2)
- {
- Type dictionaryType = typeof(IDictionary<,>).MakeGenericType(genericArguments);
- if (dictionaryType.IsAssignableFrom(modelType))
- {
- return GenerateDictionaryModelDescription(modelType, genericArguments[0], genericArguments[1]);
- }
-
- Type keyValuePairType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments);
- if (keyValuePairType.IsAssignableFrom(modelType))
- {
- return GenerateKeyValuePairModelDescription(modelType, genericArguments[0], genericArguments[1]);
- }
- }
- }
-
- if (modelType.IsArray)
- {
- Type elementType = modelType.GetElementType();
- return GenerateCollectionModelDescription(modelType, elementType);
- }
-
- if (modelType == typeof(NameValueCollection))
- {
- return GenerateDictionaryModelDescription(modelType, typeof(string), typeof(string));
- }
-
- if (typeof(IDictionary).IsAssignableFrom(modelType))
- {
- return GenerateDictionaryModelDescription(modelType, typeof(object), typeof(object));
- }
-
- if (typeof(IEnumerable).IsAssignableFrom(modelType))
- {
- return GenerateCollectionModelDescription(modelType, typeof(object));
- }
-
- return GenerateComplexTypeModelDescription(modelType);
- }
-
- // Change this to provide different name for the member.
- private static string GetMemberName(MemberInfo member, bool hasDataContractAttribute)
- {
- JsonPropertyAttribute jsonProperty = member.GetCustomAttribute<JsonPropertyAttribute>();
- if (jsonProperty != null && !String.IsNullOrEmpty(jsonProperty.PropertyName))
- {
- return jsonProperty.PropertyName;
- }
-
- if (hasDataContractAttribute)
- {
- DataMemberAttribute dataMember = member.GetCustomAttribute<DataMemberAttribute>();
- if (dataMember != null && !String.IsNullOrEmpty(dataMember.Name))
- {
- return dataMember.Name;
- }
- }
-
- return member.Name;
- }
-
- private static bool ShouldDisplayMember(MemberInfo member, bool hasDataContractAttribute)
- {
- JsonIgnoreAttribute jsonIgnore = member.GetCustomAttribute<JsonIgnoreAttribute>();
- XmlIgnoreAttribute xmlIgnore = member.GetCustomAttribute<XmlIgnoreAttribute>();
- IgnoreDataMemberAttribute ignoreDataMember = member.GetCustomAttribute<IgnoreDataMemberAttribute>();
- NonSerializedAttribute nonSerialized = member.GetCustomAttribute<NonSerializedAttribute>();
- ApiExplorerSettingsAttribute apiExplorerSetting = member.GetCustomAttribute<ApiExplorerSettingsAttribute>();
-
- bool hasMemberAttribute = member.DeclaringType.IsEnum ?
- member.GetCustomAttribute<EnumMemberAttribute>() != null :
- member.GetCustomAttribute<DataMemberAttribute>() != null;
-
- // Display member only if all the followings are true:
- // no JsonIgnoreAttribute
- // no XmlIgnoreAttribute
- // no IgnoreDataMemberAttribute
- // no NonSerializedAttribute
- // no ApiExplorerSettingsAttribute with IgnoreApi set to true
- // no DataContractAttribute without DataMemberAttribute or EnumMemberAttribute
- return jsonIgnore == null &&
- xmlIgnore == null &&
- ignoreDataMember == null &&
- nonSerialized == null &&
- (apiExplorerSetting == null || !apiExplorerSetting.IgnoreApi) &&
- (!hasDataContractAttribute || hasMemberAttribute);
- }
-
- private string CreateDefaultDocumentation(Type type)
- {
- string documentation;
- if (DefaultTypeDocumentation.TryGetValue(type, out documentation))
- {
- return documentation;
- }
- if (DocumentationProvider != null)
- {
- documentation = DocumentationProvider.GetDocumentation(type);
- }
-
- return documentation;
- }
-
- private void GenerateAnnotations(MemberInfo property, ParameterDescription propertyModel)
- {
- List<ParameterAnnotation> annotations = new List<ParameterAnnotation>();
-
- IEnumerable<Attribute> attributes = property.GetCustomAttributes();
- foreach (Attribute attribute in attributes)
- {
- Func<object, string> textGenerator;
- if (AnnotationTextGenerator.TryGetValue(attribute.GetType(), out textGenerator))
- {
- annotations.Add(
- new ParameterAnnotation
- {
- AnnotationAttribute = attribute,
- Documentation = textGenerator(attribute)
- });
- }
- }
-
- // Rearrange the annotations
- annotations.Sort((x, y) =>
- {
- // Special-case RequiredAttribute so that it shows up on top
- if (x.AnnotationAttribute is RequiredAttribute)
- {
- return -1;
- }
- if (y.AnnotationAttribute is RequiredAttribute)
- {
- return 1;
- }
-
- // Sort the rest based on alphabetic order of the documentation
- return String.Compare(x.Documentation, y.Documentation, StringComparison.OrdinalIgnoreCase);
- });
-
- foreach (ParameterAnnotation annotation in annotations)
- {
- propertyModel.Annotations.Add(annotation);
- }
- }
-
- private CollectionModelDescription GenerateCollectionModelDescription(Type modelType, Type elementType)
- {
- ModelDescription collectionModelDescription = GetOrCreateModelDescription(elementType);
- if (collectionModelDescription != null)
- {
- return new CollectionModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- ElementDescription = collectionModelDescription
- };
- }
-
- return null;
- }
-
- private ModelDescription GenerateComplexTypeModelDescription(Type modelType)
- {
- ComplexTypeModelDescription complexModelDescription = new ComplexTypeModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- Documentation = CreateDefaultDocumentation(modelType)
- };
-
- GeneratedModels.Add(complexModelDescription.Name, complexModelDescription);
- bool hasDataContractAttribute = modelType.GetCustomAttribute<DataContractAttribute>() != null;
- PropertyInfo[] properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
- foreach (PropertyInfo property in properties)
- {
- if (ShouldDisplayMember(property, hasDataContractAttribute))
- {
- ParameterDescription propertyModel = new ParameterDescription
- {
- Name = GetMemberName(property, hasDataContractAttribute)
- };
-
- if (DocumentationProvider != null)
- {
- propertyModel.Documentation = DocumentationProvider.GetDocumentation(property);
- }
-
- GenerateAnnotations(property, propertyModel);
- complexModelDescription.Properties.Add(propertyModel);
- propertyModel.TypeDescription = GetOrCreateModelDescription(property.PropertyType);
- }
- }
-
- FieldInfo[] fields = modelType.GetFields(BindingFlags.Public | BindingFlags.Instance);
- foreach (FieldInfo field in fields)
- {
- if (ShouldDisplayMember(field, hasDataContractAttribute))
- {
- ParameterDescription propertyModel = new ParameterDescription
- {
- Name = GetMemberName(field, hasDataContractAttribute)
- };
-
- if (DocumentationProvider != null)
- {
- propertyModel.Documentation = DocumentationProvider.GetDocumentation(field);
- }
-
- complexModelDescription.Properties.Add(propertyModel);
- propertyModel.TypeDescription = GetOrCreateModelDescription(field.FieldType);
- }
- }
-
- return complexModelDescription;
- }
-
- private DictionaryModelDescription GenerateDictionaryModelDescription(Type modelType, Type keyType, Type valueType)
- {
- ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType);
- ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType);
-
- return new DictionaryModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- KeyModelDescription = keyModelDescription,
- ValueModelDescription = valueModelDescription
- };
- }
-
- private EnumTypeModelDescription GenerateEnumTypeModelDescription(Type modelType)
- {
- EnumTypeModelDescription enumDescription = new EnumTypeModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- Documentation = CreateDefaultDocumentation(modelType)
- };
- bool hasDataContractAttribute = modelType.GetCustomAttribute<DataContractAttribute>() != null;
- foreach (FieldInfo field in modelType.GetFields(BindingFlags.Public | BindingFlags.Static))
- {
- if (ShouldDisplayMember(field, hasDataContractAttribute))
- {
- EnumValueDescription enumValue = new EnumValueDescription
- {
- Name = field.Name,
- Value = field.GetRawConstantValue().ToString()
- };
- if (DocumentationProvider != null)
- {
- enumValue.Documentation = DocumentationProvider.GetDocumentation(field);
- }
- enumDescription.Values.Add(enumValue);
- }
- }
- GeneratedModels.Add(enumDescription.Name, enumDescription);
-
- return enumDescription;
- }
-
- private KeyValuePairModelDescription GenerateKeyValuePairModelDescription(Type modelType, Type keyType, Type valueType)
- {
- ModelDescription keyModelDescription = GetOrCreateModelDescription(keyType);
- ModelDescription valueModelDescription = GetOrCreateModelDescription(valueType);
-
- return new KeyValuePairModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- KeyModelDescription = keyModelDescription,
- ValueModelDescription = valueModelDescription
- };
- }
-
- private ModelDescription GenerateSimpleTypeModelDescription(Type modelType)
- {
- SimpleTypeModelDescription simpleModelDescription = new SimpleTypeModelDescription
- {
- Name = ModelNameHelper.GetModelName(modelType),
- ModelType = modelType,
- Documentation = CreateDefaultDocumentation(modelType)
- };
- GeneratedModels.Add(simpleModelDescription.Name, simpleModelDescription);
-
- return simpleModelDescription;
- }
- }
- }
|