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);