From 998a433cb6401ca7fc1b51cbc69589acd265e9fe Mon Sep 17 00:00:00 2001 From: Alexander Ignatovich Date: Tue, 18 May 2021 19:19:12 +0300 Subject: [PATCH] Extensible storage lookup fix and small improvements (#81) * fix broken schema fields values display * SpecTypeId.Custom is not a measurable spec (it represents double values), but requires UnitTypeId.Custom to get an entity field value * this allows to view extensible storage schema map fields (dictionaries) * ElementId could represent Revit model element or built in parameter id or built in category id. In the last two cases it is better to show id value instead of "" * now keyvaluepair is a trully snoopable object * remove unused using * show value if ElementId represents built-in parameters or built-in category --- CS/Snoop/CollectorExts/CollectorExtElement.cs | 30 +++++- CS/Snoop/CollectorExts/DataFactory.cs | 7 +- .../CollectorExts/ElementPropertiesStream.cs | 93 ++++++++++--------- .../ExtensibleStorageEntityContentStream.cs | 80 +++++++--------- CS/Snoop/Data/ElementId.cs | 79 ++++++---------- CS/Snoop/Data/Enumerable.cs | 2 +- 6 files changed, 148 insertions(+), 143 deletions(-) diff --git a/CS/Snoop/CollectorExts/CollectorExtElement.cs b/CS/Snoop/CollectorExts/CollectorExtElement.cs index 664f9020a..0c9bcd9c8 100644 --- a/CS/Snoop/CollectorExts/CollectorExtElement.cs +++ b/CS/Snoop/CollectorExts/CollectorExtElement.cs @@ -25,13 +25,13 @@ using System; using System.Linq; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using RevitLookup.Snoop.Collectors; using Autodesk.Revit.DB; using Autodesk.Revit.DB.ExtensibleStorage; using RevitLookup.Snoop.Data; -using String = System.String; namespace RevitLookup.Snoop.CollectorExts { @@ -52,6 +52,7 @@ public CollectorExtElement() .Where(x => Path.GetDirectoryName(x.Location) == baseDirectory) .Where(x => x.GetName().Name.ToLower().Contains("revit")) .SelectMany(x => x.GetTypes()) + .Union(new [] {typeof(KeyValuePair<,>)}) .ToArray(); } @@ -72,7 +73,7 @@ protected override void CollectEvent(object sender, CollectorEventArgs e) private void Stream(ArrayList data, object elem) { - var thisElementTypes = types.Where(x => elem.GetType().IsSubclassOf(x) || elem.GetType() == x || x.IsAssignableFrom(elem.GetType())).ToList(); + var thisElementTypes = types.Where(x => IsSnoopableType(x, elem)).ToList(); var streams = new IElementStream[] { @@ -83,9 +84,9 @@ private void Stream(ArrayList data, object elem) new ExtensibleStorageEntityContentStream(m_app.ActiveUIDocument.Document, data, elem) }; - foreach (Type type in thisElementTypes) + foreach (var type in thisElementTypes) { - data.Add(new Snoop.Data.ClassSeparator(type)); + data.Add(new ClassSeparator(type)); foreach (var elementStream in streams) elementStream.Stream(type); @@ -96,6 +97,27 @@ private void Stream(ArrayList data, object elem) StreamSimpleType(data, elem); } + private static bool IsSnoopableType(Type type, object element) + { + var elementType = element.GetType(); + + if (type == elementType || elementType.IsSubclassOf(type) || type.IsAssignableFrom(elementType)) + return true; + + return type.IsGenericType && elementType.IsGenericType && IsSubclassOfRawGeneric(type, elementType); + } + + private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) { + while (toCheck != null && toCheck != typeof(object)) { + var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; + if (generic == cur) { + return true; + } + toCheck = toCheck.BaseType; + } + return false; + } + private static void StreamElementExtensibleStorages(ArrayList data, Element elem) { var schemas = Schema.ListSchemas(); diff --git a/CS/Snoop/CollectorExts/DataFactory.cs b/CS/Snoop/CollectorExts/DataFactory.cs index 84a64a730..189ddeb8d 100644 --- a/CS/Snoop/CollectorExts/DataFactory.cs +++ b/CS/Snoop/CollectorExts/DataFactory.cs @@ -17,8 +17,13 @@ public DataFactory(UIApplication application, object elem) this.elem = elem; } - public Data.Data Create(MethodInfo methodInfo) + public Data.Data Create(MethodInfo mi) { + var methodInfo = mi.ContainsGenericParameters ? elem.GetType().GetMethod(mi.Name, mi.GetParameters().Select(x => x.ParameterType).ToArray()) : mi; + + if (methodInfo == null) + return null; + var declaringType = methodInfo.DeclaringType; if (methodInfo.IsSpecialName || declaringType == null) diff --git a/CS/Snoop/CollectorExts/ElementPropertiesStream.cs b/CS/Snoop/CollectorExts/ElementPropertiesStream.cs index ca7f429f7..508081fd6 100644 --- a/CS/Snoop/CollectorExts/ElementPropertiesStream.cs +++ b/CS/Snoop/CollectorExts/ElementPropertiesStream.cs @@ -51,55 +51,62 @@ private PropertyInfo[] GetElementProperties( Type type ) .ToArray(); } - private void AddPropertyToData( PropertyInfo pi ) + private void AddPropertyToData(PropertyInfo pi) { - Type propertyType = pi.PropertyType; + var propertyInfo = pi.PropertyType.ContainsGenericParameters ? elem.GetType().GetProperty(pi.Name) : pi; + + if (propertyInfo == null) + return; + + var propertyType = propertyInfo.PropertyType; - try - { - object propertyValue; - if( pi.Name == "Geometry" ) - propertyValue = pi.GetValue( elem, new object[ 1 ] { new Options() } ); - else if( pi.Name == "BoundingBox" ) - propertyValue = pi.GetValue( elem, new object[ 1 ] { application.ActiveUIDocument.ActiveView } ); - else if( pi.Name == "Item" ) - propertyValue = pi.GetValue( elem, new object[ 1 ] { 0 } ); - else if( pi.Name == "Parameter" ) - return; - else if( pi.Name == "PlanTopology" ) - return; - else if( pi.Name == "PlanTopologies" && pi.GetMethod.GetParameters().Length != 0 ) - return; - else - propertyValue = pi.GetValue( elem ); + try + { + object propertyValue; + if (propertyInfo.Name == "Geometry") + propertyValue = propertyInfo.GetValue(elem, new object[1] {new Options()}); + else if (propertyInfo.Name == "BoundingBox") + propertyValue = propertyInfo.GetValue(elem, new object[1] {application.ActiveUIDocument.ActiveView}); + else if (propertyInfo.Name == "Item") + propertyValue = propertyInfo.GetValue(elem, new object[1] {0}); + else if (propertyInfo.Name == "Parameter") + return; + else if (propertyInfo.Name == "PlanTopology") + return; + else if (propertyInfo.Name == "PlanTopologies" && propertyInfo.GetMethod.GetParameters().Length != 0) + return; + else if (propertyType.ContainsGenericParameters) + propertyValue = elem.GetType().GetProperty(propertyInfo.Name)?.GetValue(elem); + else + propertyValue = propertyInfo.GetValue(elem); - DataTypeInfoHelper.AddDataFromTypeInfo( application, pi, propertyType, propertyValue, elem, data ); + DataTypeInfoHelper.AddDataFromTypeInfo(application, propertyInfo, propertyType, propertyValue, elem, data); - var category = elem as Category; - if( category != null && pi.Name == "Id" && category.Id.IntegerValue < 0 ) - { - var bic = (BuiltInCategory) category.Id.IntegerValue; + var category = elem as Category; + if (category != null && propertyInfo.Name == "Id" && category.Id.IntegerValue < 0) + { + var bic = (BuiltInCategory) category.Id.IntegerValue; - data.Add( new Snoop.Data.String( "BuiltInCategory", bic.ToString() ) ); - } + data.Add(new Snoop.Data.String("BuiltInCategory", bic.ToString())); + } - } - catch( ArgumentException ex ) - { - data.Add( new Snoop.Data.Exception( pi.Name, ex ) ); - } - catch( TargetException ex ) - { - data.Add( new Snoop.Data.Exception( pi.Name, ex ) ); - } - catch( TargetInvocationException ex ) - { - data.Add( new Snoop.Data.Exception( pi.Name, ex ) ); - } - catch( TargetParameterCountException ex ) - { - data.Add( new Snoop.Data.Exception( pi.Name, ex ) ); - } + } + catch (ArgumentException ex) + { + data.Add(new Snoop.Data.Exception(propertyInfo.Name, ex)); + } + catch (TargetException ex) + { + data.Add(new Snoop.Data.Exception(propertyInfo.Name, ex)); + } + catch (TargetInvocationException ex) + { + data.Add(new Snoop.Data.Exception(propertyInfo.Name, ex)); + } + catch (TargetParameterCountException ex) + { + data.Add(new Snoop.Data.Exception(propertyInfo.Name, ex)); + } } } } diff --git a/CS/Snoop/CollectorExts/ExtensibleStorageEntityContentStream.cs b/CS/Snoop/CollectorExts/ExtensibleStorageEntityContentStream.cs index db1ec88d9..f05d99bbd 100644 --- a/CS/Snoop/CollectorExts/ExtensibleStorageEntityContentStream.cs +++ b/CS/Snoop/CollectorExts/ExtensibleStorageEntityContentStream.cs @@ -42,32 +42,32 @@ public void Stream( Type type ) StreamEntityFieldValue( field ); } - private void StreamEntityFieldValue( Field field ) + private void StreamEntityFieldValue(Field field) { - try - { - var getEntityValueMethod = GetEntityFieldValueMethod( field ); + try + { + var getEntityValueMethod = GetEntityFieldValueMethod(field); - var valueType = GetFieldValueType( field ); + var valueType = GetFieldValueType(field); - var genericGet = getEntityValueMethod.MakeGenericMethod( valueType ); + var genericGet = getEntityValueMethod.MakeGenericMethod(valueType); - //var unit = UnitUtils.GetValidDisplayUnits( field.UnitType ).First(); // 2020 + var fieldSpecType = field.GetSpecTypeId(); - var unit = UnitUtils.GetValidUnits( field.GetSpecTypeId() ).First(); // 2021 + var unit = UnitUtils.IsMeasurableSpec(fieldSpecType) ? UnitUtils.GetValidUnits(field.GetSpecTypeId()).First() : UnitTypeId.Custom; - var parameters = getEntityValueMethod.GetParameters().Length == 1 - ? new object[] { field } - : new object[] { field, unit }; + var parameters = getEntityValueMethod.GetParameters().Length == 1 + ? new object[] {field} + : new object[] {field, unit}; - var value = genericGet.Invoke( entity, parameters ); + var value = genericGet.Invoke(entity, parameters); - AddFieldValue( field, value ); - } - catch( Exception ex ) - { - data.Add( new Snoop.Data.Exception( field.FieldName, ex ) ); - } + AddFieldValue(field, value); + } + catch (Exception ex) + { + data.Add(new Data.Exception(field.FieldName, ex)); + } } private Type GetFieldValueType( Field field ) @@ -97,7 +97,7 @@ private void AddFieldValue( Field field, object value ) try { if( field.ContainerType != ContainerType.Simple ) - data.Add( new Snoop.Data.Object( field.FieldName, value ) ); + data.Add( new Snoop.Data.Enumerable( field.FieldName, value as IEnumerable ) ); else if( field.ValueType == typeof( double ) ) data.Add( new Snoop.Data.Double( field.FieldName, (double) value ) ); else if( field.ValueType == typeof( string ) ) @@ -125,40 +125,30 @@ private void AddFieldValue( Field field, object value ) } } - private MethodInfo GetEntityFieldValueMethod( Field field ) + private static MethodInfo GetEntityFieldValueMethod(Field field) { - return typeof( Entity ) - .GetMethods( BindingFlags.Public | BindingFlags.Instance ) - .Where( x => x.Name == "Get" && x.IsGenericMethod ) - .Single( x => IsGetByFieldMethod( x, field ) ); + return typeof(Entity) + .GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.Name == nameof(Entity.Get) && x.IsGenericMethod) + .Single(x => IsGetByFieldMethod(x, field)); } - private static bool IsGetByFieldMethod( MethodInfo methodInfo, Field field ) + private static bool IsGetByFieldMethod(MethodInfo methodInfo, Field field) { - var parameters = methodInfo.GetParameters(); - - if( field.ContainerType == ContainerType.Simple - && (field.ValueType == typeof( XYZ ) - || field.ValueType == typeof( double )) ) - { - //#pragma warning disable CS0618 // Revit 2021 - // warning CS0618: `DisplayUnitType` is obsolete: - // This enumeration is deprecated in Revit 2021 and may be removed in a future version of Revit. - // Please use the `ForgeTypeId` class instead. - // Use constant members of the `UnitTypeId` class to replace uses of specific values of this enumeration. + var parameters = methodInfo.GetParameters(); + + var fieldSpecType = field.GetSpecTypeId(); - if( 2 == parameters.Length ) + if (UnitUtils.IsMeasurableSpec(fieldSpecType) || fieldSpecType == SpecTypeId.Custom) { - ParameterInfo p1 = parameters.First(); - ParameterInfo p2 = parameters.Last(); - return p1.ParameterType == typeof( Field ) - && // (p2.ParameterType == typeof( DisplayUnitType ) || // Revit 2021 - p2.ParameterType == typeof( ForgeTypeId ); + var firstParameter = parameters.First(); + + var lastParameter = parameters.Last(); + + return parameters.Length == 2 && firstParameter.ParameterType == typeof(Field) && lastParameter.ParameterType == typeof(ForgeTypeId); } - //#pragma warning restore CS0618 // Revit 2021 - } - return parameters.Length == 1 && parameters.Single().ParameterType == typeof( Field ); + return parameters.Length == 1 && parameters.Single().ParameterType == typeof(Field); } } } diff --git a/CS/Snoop/Data/ElementId.cs b/CS/Snoop/Data/ElementId.cs index 7f21cd3dc..c6a7b9b52 100644 --- a/CS/Snoop/Data/ElementId.cs +++ b/CS/Snoop/Data/ElementId.cs @@ -22,63 +22,44 @@ // #endregion // Header -using System; -using System.Windows.Forms; -using System.Collections; - using Autodesk.Revit.DB; namespace RevitLookup.Snoop.Data { - /// - /// Snoop.Data class to hold and format an ElementId value. - /// - - public class ElementId : Data - { - protected Autodesk.Revit.DB.ElementId m_val; - protected Element m_elem = null; - - public - ElementId(string label, Autodesk.Revit.DB.ElementId val, Document doc) - : base(label) - { - m_val = val; - try - { - if (val != Autodesk.Revit.DB.ElementId.InvalidElementId) - m_elem = doc.GetElement(val); // TBD: strange signature! - } - catch (System.Exception) - { - m_elem = null; - } - } - - public override string - StrValue() + /// + /// Snoop.Data class to hold and format an ElementId value. + /// + + public class ElementId : Data + { + protected Autodesk.Revit.DB.ElementId m_val; + protected Element m_elem; + + public ElementId(string label, Autodesk.Revit.DB.ElementId val, Document doc) : base(label) { - return Utils.ObjToLabelStr(m_elem); + m_val = val; + + m_elem = doc.GetElement(val); } - - public override bool - HasDrillDown + + public override string StrValue() { - get { - if (m_elem == null) - return false; - else - return true; - } + if (m_elem != null) + return Utils.ObjToLabelStr(m_elem); + + return m_val != Autodesk.Revit.DB.ElementId.InvalidElementId ? m_val.ToString() : Utils.ObjToLabelStr(null); } - - public override void - DrillDown() + + public override bool HasDrillDown => m_elem != null; + + public override void DrillDown() { - if (m_elem != null) { - Snoop.Forms.Objects form = new Snoop.Forms.Objects(m_elem); - form.ShowDialog(); - } + if (m_elem == null) + return; + + var form = new Forms.Objects(m_elem); + + form.ShowDialog(); } - } + } } diff --git a/CS/Snoop/Data/Enumerable.cs b/CS/Snoop/Data/Enumerable.cs index d459bfc77..380ce6f2f 100644 --- a/CS/Snoop/Data/Enumerable.cs +++ b/CS/Snoop/Data/Enumerable.cs @@ -71,7 +71,7 @@ public class Enumerable : Data { var elementId = iter.Current as Autodesk.Revit.DB.ElementId; - if (elementId != null && doc != null) + if (elementId != null && doc != null && elementId.IntegerValue > 0) m_objs.Add(doc.GetElement(elementId)); // it's more useful for user to view element rather than element id. else m_objs.Add(iter.Current);