From 1d93914fb8ecf04e10a89db63ccb3d10f3d34327 Mon Sep 17 00:00:00 2001 From: Joel Martinez Date: Thu, 23 May 2019 17:16:50 -0400 Subject: [PATCH] Attached properties will now accept a Property DP. Closes #426 --- .../Util/AttachedEntitiesHelper.cs | 64 +++++++++++++------ .../Util/AttachedPropertyDefinition.cs | 43 +++++++++---- .../Util/AttachedPropertyReference.cs | 16 ++++- 3 files changed, 91 insertions(+), 32 deletions(-) diff --git a/mdoc/Mono.Documentation/Util/AttachedEntitiesHelper.cs b/mdoc/Mono.Documentation/Util/AttachedEntitiesHelper.cs index f711f90bf..2defb1666 100644 --- a/mdoc/Mono.Documentation/Util/AttachedEntitiesHelper.cs +++ b/mdoc/Mono.Documentation/Util/AttachedEntitiesHelper.cs @@ -114,34 +114,62 @@ private static IEnumerable GetAttachedProperties(Type if (IsAttachedProperty(field, methods)) yield return new AttachedPropertyReference(field); } + foreach (var prop in type.Properties) + { + if (IsAttachedProperty(prop, methods)) + yield return new AttachedPropertyReference(prop); + } } private static bool IsAttachedProperty(FieldDefinition field, Dictionary> methods) { - // https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview - // https://github.com/mono/api-doc-tools/issues/63#issuecomment-328995418 - if (!field.Name.EndsWith(PropertyConst, StringComparison.Ordinal)) + string fieldName = field.Name; + TypeReference fieldType = field.FieldType; + TypeDefinition declaringType = field?.DeclaringType; + bool isPublic = field.IsPublic; + bool isStatic = field.IsStatic; + bool isInitOnly = field.IsInitOnly; + return IsAttachedPropertyCore(methods, fieldName, fieldType, declaringType, isPublic, isStatic, isInitOnly); + + } + + private static bool IsAttachedProperty(PropertyDefinition prop, Dictionary> methods) + { + string fieldName = prop.Name; + TypeReference fieldType = prop.PropertyType; + TypeDefinition declaringType = prop?.DeclaringType; + bool isPublic = prop.GetMethod.IsPublic; + bool isStatic = prop.GetMethod.IsStatic; + bool isInitOnly = prop.SetMethod == null || !prop.SetMethod.IsPublic; + return IsAttachedPropertyCore(methods, fieldName, fieldType, declaringType, isPublic, isStatic, isInitOnly); + + } + + private static bool IsAttachedPropertyCore(Dictionary> methods, string fieldName, TypeReference fieldType, TypeDefinition declaringType, bool isPublic, bool isStatic, bool isInitOnly) + { + // https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview + // https://github.com/mono/api-doc-tools/issues/63#issuecomment-328995418 + if (!fieldName.EndsWith(PropertyConst, StringComparison.Ordinal)) return false; - var propertyName = GetPropertyName(field.Name); + var propertyName = GetPropertyName(fieldName); var getMethodName = $"Get{propertyName}"; var setMethodName = $"Set{propertyName}"; - var hasExistingProperty = field?.DeclaringType?.Properties.Any (p => p.Name.Equals (propertyName, System.StringComparison.Ordinal)); - var hasExistingField = field?.DeclaringType?.Fields.Any (f => f.Name.Equals (propertyName, System.StringComparison.Ordinal)); + var hasExistingProperty = declaringType?.Properties.Any(p => p.Name.Equals(propertyName, System.StringComparison.Ordinal)); + var hasExistingField = declaringType?.Fields.Any(f => f.Name.Equals(propertyName, System.StringComparison.Ordinal)); - return !hasExistingProperty.IsTrue () && !hasExistingField.IsTrue () && - // Class X has a static field of type DependencyProperty [Name]Property - (field.FieldType.FullName == Consts.DependencyPropertyFullName || field.FieldType.FullName == Consts.DependencyPropertyFullNameXaml) - && field.IsPublic - && field.IsStatic - && field.IsInitOnly - - // Class X also has static methods with the following names: Get[Name] and Set[Name] + return !hasExistingProperty.IsTrue() && !hasExistingField.IsTrue() && + // Class X has a static field of type DependencyProperty [Name]Property + (fieldType.FullName == Consts.DependencyPropertyFullName || fieldType.FullName == Consts.DependencyPropertyFullNameXaml) + && isPublic + && isStatic + && isInitOnly + + // Class X also has static methods with the following names: Get[Name] and Set[Name] && ((methods.ContainsKey(getMethodName) && methods[getMethodName].Any(IsAttachedPropertyGetMethod)) - || (methods.ContainsKey(setMethodName) && methods[setMethodName].Any(IsAttachedPropertySetMethod))); - - } - + || (methods.ContainsKey(setMethodName) && methods[setMethodName].Any(IsAttachedPropertySetMethod))); + } + private static bool IsAttachedPropertyGetMethod(MethodDefinition method) { return method.Parameters.Count == 1 diff --git a/mdoc/Mono.Documentation/Util/AttachedPropertyDefinition.cs b/mdoc/Mono.Documentation/Util/AttachedPropertyDefinition.cs index 5b72e0086..35ad25d2c 100644 --- a/mdoc/Mono.Documentation/Util/AttachedPropertyDefinition.cs +++ b/mdoc/Mono.Documentation/Util/AttachedPropertyDefinition.cs @@ -5,46 +5,65 @@ namespace Mono.Documentation.Util { public class AttachedPropertyDefinition : AttachedPropertyReference, IMemberDefinition { - private readonly FieldDefinition fieldDefinition; + string defName; + Collection defAttributes; + bool defHasAttributes, defIsSpecialName, defIsRuntimeSpecialName; + TypeDefinition defDeclaringType; + public AttachedPropertyDefinition(FieldDefinition fieldDefinition, MetadataToken metadataToken) : base(fieldDefinition) { - this.fieldDefinition = fieldDefinition; MetadataToken = metadataToken; + defName = fieldDefinition.Name; + defAttributes = fieldDefinition.CustomAttributes; + defHasAttributes = fieldDefinition.HasCustomAttributes; + defIsSpecialName = fieldDefinition.IsSpecialName; + defIsRuntimeSpecialName = fieldDefinition.IsRuntimeSpecialName; + defDeclaringType = fieldDefinition.DeclaringType; + } + public AttachedPropertyDefinition(PropertyDefinition propDefinition, MetadataToken metadataToken) : base(propDefinition) + { + MetadataToken = metadataToken; + defName = propDefinition.Name; + defAttributes = propDefinition.CustomAttributes; + defHasAttributes = propDefinition.HasCustomAttributes; + defIsSpecialName = propDefinition.IsSpecialName; + defIsRuntimeSpecialName = propDefinition.IsRuntimeSpecialName; + defDeclaringType = propDefinition.DeclaringType; } public MemberReference GetMethod { get => this.DeclaringType.GetMember( - $"Get{AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name)}", + $"Get{AttachedEntitiesHelper.GetPropertyName(defName)}", m => (m as MethodReference)?.Parameters.Count == 1); } public MemberReference SetMethod { get => this.DeclaringType.GetMember( - $"Set{AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name)}", + $"Set{AttachedEntitiesHelper.GetPropertyName(defName)}", m => (m as MethodReference)?.Parameters.Count == 2); } - public Collection CustomAttributes => fieldDefinition.CustomAttributes; - public bool HasCustomAttributes => fieldDefinition.HasCustomAttributes; + public Collection CustomAttributes => defAttributes; + public bool HasCustomAttributes => defHasAttributes; public bool IsSpecialName { - get { return fieldDefinition.IsSpecialName; } - set { fieldDefinition.IsSpecialName = value; } + get { return defIsSpecialName; } + set { defIsSpecialName = value; } } public bool IsRuntimeSpecialName { - get { return fieldDefinition.IsRuntimeSpecialName; } - set { fieldDefinition.IsRuntimeSpecialName = value; } + get { return defIsRuntimeSpecialName; } + set { defIsRuntimeSpecialName = value; } } public new TypeDefinition DeclaringType { - get { return fieldDefinition.DeclaringType; } - set { fieldDefinition.DeclaringType = value; } + get { return defDeclaringType; } + set { defDeclaringType = value; } } } } \ No newline at end of file diff --git a/mdoc/Mono.Documentation/Util/AttachedPropertyReference.cs b/mdoc/Mono.Documentation/Util/AttachedPropertyReference.cs index 60480f06b..ed135d367 100644 --- a/mdoc/Mono.Documentation/Util/AttachedPropertyReference.cs +++ b/mdoc/Mono.Documentation/Util/AttachedPropertyReference.cs @@ -5,17 +5,29 @@ namespace Mono.Documentation.Util public class AttachedPropertyReference : FieldReference { private readonly FieldDefinition fieldDefinition; + private readonly PropertyDefinition propDefinition; private AttachedPropertyDefinition definition; public AttachedPropertyReference(FieldDefinition fieldDefinition) : base(AttachedEntitiesHelper.GetPropertyName(fieldDefinition.Name), fieldDefinition.FieldType, fieldDefinition.DeclaringType) { this.fieldDefinition = fieldDefinition; } + public AttachedPropertyReference(PropertyDefinition propDefinition) : base(AttachedEntitiesHelper.GetPropertyName(propDefinition.Name), propDefinition.PropertyType, propDefinition.DeclaringType) + { + this.propDefinition = propDefinition; + } protected override IMemberDefinition ResolveDefinition() { - return definition ?? - (definition = new AttachedPropertyDefinition(fieldDefinition, MetadataToken)); + if (definition == null) + { + if (fieldDefinition != null) + definition = new AttachedPropertyDefinition(fieldDefinition, MetadataToken); + + if (propDefinition != null) + definition = new AttachedPropertyDefinition(propDefinition, MetadataToken); + } + return definition; } } } \ No newline at end of file