// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Reflection; namespace Microsoft.AspNet.Mvc.ModelBinding { // Class does not override ComputeIsComplexType() because value calculated in ModelMetadata's base implementation // is correct. public class CachedDataAnnotationsModelMetadata : CachedModelMetadata { private static readonly string HtmlName = DataType.Html.ToString(); private bool _isEditFormatStringFromCache; public CachedDataAnnotationsModelMetadata(CachedDataAnnotationsModelMetadata prototype, Func modelAccessor) : base(prototype, modelAccessor) { } public CachedDataAnnotationsModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Type modelType, string propertyName, IEnumerable attributes) : base(provider, containerType, modelType, propertyName, new CachedDataAnnotationsMetadataAttributes(attributes)) { } protected override bool ComputeConvertEmptyStringToNull() { return PrototypeCache.DisplayFormat != null ? PrototypeCache.DisplayFormat.ConvertEmptyStringToNull : base.ComputeConvertEmptyStringToNull(); } protected override string ComputeNullDisplayText() { return PrototypeCache.DisplayFormat != null ? PrototypeCache.DisplayFormat.NullDisplayText : base.ComputeNullDisplayText(); } /// /// Calculate based on presence of a /// and its method. /// /// /// Calculated value. /// value if a exists. /// "Html" if a exists with its /// value false. null otherwise. /// protected override string ComputeDataTypeName() { if (PrototypeCache.DataType != null) { return PrototypeCache.DataType.GetDataTypeName(); } if (PrototypeCache.DisplayFormat != null && !PrototypeCache.DisplayFormat.HtmlEncode) { return HtmlName; } return base.ComputeDataTypeName(); } protected override string ComputeDescription() { return PrototypeCache.Display != null ? PrototypeCache.Display.GetDescription() : base.ComputeDescription(); } /// /// Calculate based on presence of an /// and its value. /// /// /// Calculated value. /// if an exists. /// null otherwise. /// protected override string ComputeDisplayFormatString() { return PrototypeCache.DisplayFormat != null ? PrototypeCache.DisplayFormat.DataFormatString : base.ComputeEditFormatString(); } protected override string ComputeDisplayName() { // DisplayName may be provided by DisplayAttribute. // If that does not supply a name, then we fall back to the property name (in base.GetDisplayName()). if (PrototypeCache.Display != null) { // DisplayAttribute doesn't require you to set a name, so this could be null. var name = PrototypeCache.Display.GetName(); if (name != null) { return name; } } return base.ComputeDisplayName(); } /// /// Calculate based on presence of an /// and its and /// values. /// /// /// Calculated value. /// if an exists and /// its is true; null otherwise. /// /// /// Subclasses overriding this method should also override to /// ensure the two calculations remain consistent. /// protected override string ComputeEditFormatString() { if (PrototypeCache.DisplayFormat != null && PrototypeCache.DisplayFormat.ApplyFormatInEditMode) { _isEditFormatStringFromCache = true; return PrototypeCache.DisplayFormat.DataFormatString; } return base.ComputeEditFormatString(); } /// /// Calculate based on /// and presence of and /// . /// /// /// Calculated value. true if /// is non-null, non-empty, and came from the cache (was /// not set directly). In addition the applied must not have come from an /// applied . false otherwise. /// protected override bool ComputeHasNonDefaultEditFormat() { // Following calculation ignores possibility something (an IModelMetadataProvider) set EditFormatString // directly. if (!string.IsNullOrEmpty(EditFormatString) && _isEditFormatStringFromCache) { // Have a non-empty EditFormatString based on [DisplayFormat] from our cache. if (PrototypeCache.DataType == null) { // Attributes include no [DataType]; [DisplayFormat] was applied directly. return true; } if (PrototypeCache.DataType.DisplayFormat != PrototypeCache.DisplayFormat) { // Attributes include separate [DataType] and [DisplayFormat]; [DisplayFormat] provided override. return true; } if (PrototypeCache.DataType.GetType() != typeof(DataTypeAttribute)) { // Attributes include [DisplayFormat] copied from [DataType] and [DataType] was of a subclass. // Assume the [DataType] constructor used the protected DisplayFormat setter to override its // default. That is derived [DataType] provided override. return true; } } return base.ComputeHasNonDefaultEditFormat(); } /// /// Calculate based on presence of an /// and its value. /// /// Calculated value. true if an /// exists and its value is /// false; false otherwise. protected override bool ComputeHideSurroundingHtml() { if (PrototypeCache.HiddenInput != null) { return !PrototypeCache.HiddenInput.DisplayValue; } return base.ComputeHideSurroundingHtml(); } protected override bool ComputeIsReadOnly() { if (PrototypeCache.Editable != null) { return !PrototypeCache.Editable.AllowEdit; } return base.ComputeIsReadOnly(); } protected override bool ComputeIsRequired() { return (PrototypeCache.Required != null) || base.ComputeIsRequired(); } protected override string ComputeSimpleDisplayText() { if (Model != null && PrototypeCache.DisplayColumn != null && !string.IsNullOrEmpty(PrototypeCache.DisplayColumn.DisplayColumn)) { var displayColumnProperty = ModelType.GetTypeInfo().GetDeclaredProperty( PrototypeCache.DisplayColumn.DisplayColumn); ValidateDisplayColumnAttribute(PrototypeCache.DisplayColumn, displayColumnProperty, ModelType); var simpleDisplayTextValue = displayColumnProperty.GetValue(Model, null); if (simpleDisplayTextValue != null) { return simpleDisplayTextValue.ToString(); } } return base.ComputeSimpleDisplayText(); } protected override bool ComputeShowForDisplay() { return PrototypeCache.ScaffoldColumn != null ? PrototypeCache.ScaffoldColumn.Scaffold : base.ComputeShowForDisplay(); } protected override bool ComputeShowForEdit() { return PrototypeCache.ScaffoldColumn != null ? PrototypeCache.ScaffoldColumn.Scaffold : base.ComputeShowForEdit(); } private static void ValidateDisplayColumnAttribute(DisplayColumnAttribute displayColumnAttribute, PropertyInfo displayColumnProperty, Type modelType) { if (displayColumnProperty == null) { throw new InvalidOperationException( Resources.FormatDataAnnotationsModelMetadataProvider_UnknownProperty( modelType.FullName, displayColumnAttribute.DisplayColumn)); } if (displayColumnProperty.GetGetMethod() == null) { throw new InvalidOperationException( Resources.FormatDataAnnotationsModelMetadataProvider_UnreadableProperty( modelType.FullName, displayColumnAttribute.DisplayColumn)); } } } }