(this IEnumer
foreach (Type t in dependencyTypes)
{
MethodInfo generic = method.MakeGenericMethod(new Type[] { typeof(T), t });
- var list = generic.Invoke(null, new object[] { objects, adapter });
+ IList list = generic.Invoke(null, new object[] { objects, adapter }) as IList;
- dict.Add(t, list as IEnumerable);
+ if (list != null && list.Count != 0)
+ dict.Add(t, list);
}
return dict;
@@ -72,7 +76,7 @@ private static List GetDependencyObjects(this IEnumerable objects, I
{
//If adapter is provided and not null, check for dependency modules
if (adapter != null)
- {
+ {
//Look for any GetDependencyModules of the current type
List> dependencyModules = adapter.AdapterModules.OfType>().ToList();
if (dependencyModules.Count != 0)
@@ -92,3 +96,4 @@ private static List GetDependencyObjects(this IEnumerable objects, I
+
diff --git a/Adapter_Engine/Query/GetDependencySortedObjects.cs b/Adapter_Engine/Query/GetDependencySortedObjects.cs
new file mode 100644
index 00000000..188a11fc
--- /dev/null
+++ b/Adapter_Engine/Query/GetDependencySortedObjects.cs
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the Buildings and Habitats object Model (BHoM)
+ * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
+ *
+ * Each contributor holds copyright over their respective contributions.
+ * The project versioning (Git) records all such contribution source information.
+ *
+ *
+ * The BHoM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3.0 of the License, or
+ * (at your option) any later version.
+ *
+ * The BHoM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+using BH.oM.Base;
+using BH.Engine.Base;
+using BH.oM.Data;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using BH.oM.Adapter;
+using BH.oM.Base.Attributes;
+using BH.Engine.Reflection;
+using System.Reflection;
+
+namespace BH.Engine.Adapter
+{
+ public static partial class Query
+ {
+ /***************************************************/
+ /**** Push Support methods ****/
+ /***************************************************/
+ // These are support methods required by other methods in the Push process.
+
+ [Description("Collects and groups all of the obejcts and dependencies of all the objects by type and pushtype. Groups are sorted by dependency order.")]
+ [Input("objects", "Objects to group and sort by dependency order. The dependency of these objects will also be gathered recursively and included in the output.")]
+ [Input("pushType", "PushType provided in the Push.")]
+ [Input("bHoMAdapter", "The DependencyTypes that define the order of the output will be gathered from this Adapter instance.")]
+ public static List>> GetDependencySortedObjects(this IEnumerable objects, PushType pushType, IBHoMAdapter bHoMAdapter)
+ {
+ if ((!objects?.Any() ?? true) || bHoMAdapter == null)
+ return new List>>();
+
+ // Group the objects by their specific type.
+ var typeGroups = objects.GroupBy(x => x.GetType());
+
+ // Collect all objects and related dependency objects, recursively, and group them by type.
+ Dictionary, List> allObjectsPerType = GetObjectsAndRecursiveDependencies(objects, pushType, bHoMAdapter);
+
+ // Sort the groups by dependency order, so they can be pushed in the correct order.
+ List>> baseTypeGroupObjects = allObjectsPerType.Select(x => new Tuple> (x.Key.Item1, x.Key.Item2, x.Value )).ToList();
+
+ // Group per base type extracted from dependencies.
+ // This is useful to reduce the number of CRUD calls.
+ var allTypesInDependencies = bHoMAdapter.DependencyTypes.Values.SelectMany(v => v).Distinct();
+ for (int i = 0; i < baseTypeGroupObjects.Count; i++)
+ {
+ var kv = baseTypeGroupObjects.ElementAt(i);
+
+ foreach (Type baseType in kv.Item1.BaseTypes())
+ {
+ bool found = false;
+ foreach (Type dependencyType in allTypesInDependencies)
+ {
+ if (baseType != dependencyType)
+ continue;
+
+ //Find matching item in the ordered obejct, matching the base type and push type.
+ var matchingItem = baseTypeGroupObjects.FirstOrDefault(o => o.Item1 == baseType && o.Item2 == kv.Item2);
+
+ int matchingIndex;
+ //Get index of matching object if match is not null.
+ if (matchingItem != null)
+ matchingIndex = baseTypeGroupObjects.IndexOf(matchingItem);
+ else
+ matchingIndex = -1;
+
+ if (matchingIndex == -1)
+ {
+ //Nothing found. Replace the current item with base type instead of concrete type.
+ baseTypeGroupObjects[i] = new Tuple>(baseType, kv.Item2, kv.Item3);
+ }
+ else
+ {
+ //If matching base type is found, concatenate the to sets together, to be CRUD together.
+ //For example, if the pushtype for both is the same, SteelSections and AluminiumSections will be grouped under ISectionProperty if ISectionProperty is in DependencyTypes.
+ var toAdd = new Tuple>(baseType, baseTypeGroupObjects[matchingIndex].Item2, baseTypeGroupObjects[matchingIndex].Item3.Concat(kv.Item3));
+
+ int minIndex = Math.Min(i, matchingIndex);
+ int maxIndex = Math.Max(i, matchingIndex);
+
+ //Replace on minimum of the two indecies found and remove the other.
+ baseTypeGroupObjects[minIndex] = toAdd;
+ baseTypeGroupObjects.RemoveAt(maxIndex);
+ i--; //Decrement i as item has been removed from the list.
+ }
+ found = true;
+ break;
+
+ }
+
+ if (found)
+ break;
+ }
+ }
+
+ //Dictionary to store the dependency depth of each type
+ //The dependency depth indicates how many objects being pushed that depend on them
+ //This means that the types with the highest depth count should be pushed first
+ Dictionary dependecyDepth = new Dictionary();
+
+ //Method runs through all types, and recursively calls the dependecy types, and increments the depth of each type for every time it is found
+ EvaluateDependencyDepths(bHoMAdapter, baseTypeGroupObjects.Select(x => x.Item1).Distinct(), dependecyDepth);
+
+ //Sorts the types by highest to lowest depth count
+ List>> orderedObjects = baseTypeGroupObjects.OrderByDescending(x => dependecyDepth[x.Item1]).ToList();
+
+ // If two types are subject to two different CRUD operations (e.g. UpdateOnly and FullCRUD),
+ // make sure the order of CRUD operations is appropriate (e.g. UpdateOnly must happen before FullCRUD to avoid duplicates).
+ // For example, this happens when both Nodes and Bars are sent via UpdateOnly during a same Push operation,
+ // and the Nodes being updated are the same assigned to the endpoints of the Bars being updated).
+ for (int i = 0; i < orderedObjects.Count; i++)
+ {
+ var kv1 = orderedObjects[i];
+
+ for (int j = i + 1; j < orderedObjects.Count; j++)
+ {
+ var kv2 = orderedObjects[j];
+
+ if (kv1.Item1 == kv2.Item1 && kv1.Item2 == PushType.FullPush && kv2.Item2 == PushType.UpdateOnly)
+ {
+ orderedObjects.RemoveAt(j);
+ orderedObjects.Insert(i, kv2);
+ }
+ }
+ }
+
+ return orderedObjects;
+ }
+
+ /***************************************************/
+
+ [Description("Looping through all types and their dependencies and incrementally increases the depth counter for every time a type is found.")]
+ private static void EvaluateDependencyDepths(this IBHoMAdapter bHoMAdapter, IEnumerable types, Dictionary depths)
+ {
+ foreach (Type type in types)
+ {
+ if (!depths.ContainsKey(type))
+ depths[type] = 0; //First appearance of the type, either as a standalone object or as a dependecy
+ else
+ depths[type]++; //Increment depth counter for every time the dependecy is found, either as the standalone type, or as a dependency object
+
+ //Recursive call to make sure all dependecies are incremented
+ EvaluateDependencyDepths(bHoMAdapter, bHoMAdapter.GetDependencyTypes(type), depths);
+ }
+ }
+
+ /***************************************************/
+ }
+}
+
+
diff --git a/Adapter_Engine/Query/GetDependencyTypes.cs b/Adapter_Engine/Query/GetDependencyTypes.cs
index a56067cd..2f53a741 100644
--- a/Adapter_Engine/Query/GetDependencyTypes.cs
+++ b/Adapter_Engine/Query/GetDependencyTypes.cs
@@ -1,6 +1,6 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
- * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved.
+ * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
@@ -41,26 +41,30 @@ public static partial class Query
[Description("Returns the dependency types for a certain object type.")]
public static List GetDependencyTypes(this IBHoMAdapter bhomAdapter)
{
- Type type = typeof(T);
+ return GetDependencyTypes(bhomAdapter, typeof(T));
+ }
+
+ [Description("Returns the dependency types for a certain object type.")]
+ public static List GetDependencyTypes(this IBHoMAdapter bhomAdapter, Type type)
+ {
+ List dependencyTypes = new List();
if (bhomAdapter.DependencyTypes.ContainsKey(type))
- return bhomAdapter.DependencyTypes[type];
+ dependencyTypes.AddRange(bhomAdapter.DependencyTypes[type]);
- else if (type.BaseType != null && bhomAdapter.DependencyTypes.ContainsKey(type.BaseType))
- return bhomAdapter.DependencyTypes[type.BaseType];
+ if (type.BaseType != null && bhomAdapter.DependencyTypes.ContainsKey(type.BaseType))
+ dependencyTypes.AddRange(bhomAdapter.DependencyTypes[type.BaseType]);
- else
+ foreach (Type interType in type.GetInterfaces())
{
- foreach (Type interType in type.GetInterfaces())
- {
- if (bhomAdapter.DependencyTypes.ContainsKey(interType))
- return bhomAdapter.DependencyTypes[interType];
- }
+ if (bhomAdapter.DependencyTypes.ContainsKey(interType))
+ dependencyTypes.AddRange(bhomAdapter.DependencyTypes[interType]);
}
- return new List();
+ return dependencyTypes.Distinct().ToList();
}
}
}
+
diff --git a/Adapter_Engine/Query/GetFullFileName.cs b/Adapter_Engine/Query/GetFullFileName.cs
index f645ba3b..840484cd 100644
--- a/Adapter_Engine/Query/GetFullFileName.cs
+++ b/Adapter_Engine/Query/GetFullFileName.cs
@@ -1,6 +1,6 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
- * Copyright (c) 2015 - 2022, the respective contributors. All rights reserved.
+ * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
@@ -50,3 +50,4 @@ public static string GetFullFileName(this FileSettings fileSettings)
+
diff --git a/Adapter_Engine/Query/GetObjectsAndRecursiveDependencies.cs b/Adapter_Engine/Query/GetObjectsAndRecursiveDependencies.cs
new file mode 100644
index 00000000..f44a6d46
--- /dev/null
+++ b/Adapter_Engine/Query/GetObjectsAndRecursiveDependencies.cs
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the Buildings and Habitats object Model (BHoM)
+ * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
+ *
+ * Each contributor holds copyright over their respective contributions.
+ * The project versioning (Git) records all such contribution source information.
+ *
+ *
+ * The BHoM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3.0 of the License, or
+ * (at your option) any later version.
+ *
+ * The BHoM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ */
+
+using BH.Engine.Reflection;
+using BH.oM.Base;
+using BH.oM.Data.Collections;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using BH.oM.Adapter;
+using BH.oM.Base.Attributes;
+
+namespace BH.Engine.Adapter
+{
+ public static partial class Query
+ {
+ /***************************************************/
+ /**** Public Methods ****/
+ /***************************************************/
+
+ [Description("Groups all objects and their dependencies by Type and PushType.")]
+ [Input("objects", "Objects to group. The dependency of these objects will also be gathered recursively and included in the output.")]
+ [Input("pushType", "PushType provided in the Push.")]
+ [Input("adapter", "The DependencyTypes will be gathered from this Adapter instance.")]
+ public static Dictionary, List> GetObjectsAndRecursiveDependencies(this IEnumerable objects, PushType pushType, IBHoMAdapter adapter)
+ {
+ // Group the objects by their specific type.
+ var typeGroups = objects.GroupBy(x => x.GetType());
+
+ Dictionary, List> allObjectsPerType = new Dictionary, List>();
+
+ foreach (var typeGroup in typeGroups)
+ {
+ var key = new Tuple(typeGroup.Key, pushType);
+ if (allObjectsPerType.ContainsKey(key))
+ allObjectsPerType[key].AddRange(typeGroup.Cast());
+ else
+ allObjectsPerType[key] = typeGroup.Cast().ToList();
+
+ MethodInfo enumCastMethodSpecificType = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new[] { typeGroup.Key });
+ dynamic objListSpecificType = enumCastMethodSpecificType.Invoke(typeGroup, new object[] { typeGroup });
+
+ //For update only the pushtypes for the dependencies should be full crud.
+ //For all other push types, the same as the top level should be used for the dependencies.
+ PushType dependecyPushType = pushType == PushType.UpdateOnly ? PushType.FullPush : pushType;
+
+ GetDependencyObjectsRecursive(objListSpecificType, allObjectsPerType, dependecyPushType, adapter);
+ }
+
+ return allObjectsPerType;
+ }
+
+ /***************************************************/
+ /**** Private Methods ****/
+ /***************************************************/
+
+ private static void GetDependencyObjectsRecursive(this IEnumerable