diff --git a/.ci/unit-tests/BHoM_Adapter_Tests.sln b/.ci/unit-tests/BHoM_Adapter_Tests.sln new file mode 100644 index 00000000..23c80008 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32922.545 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BHoM_Adapter_Tests", "BHoM_Adapter_Tests\BHoM_Adapter_Tests.csproj", "{2693899C-1398-4486-BED2-02809FB43BE9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adapter_oM", "..\..\Adapter_oM\Adapter_oM.csproj", "{C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adapter_Engine", "..\..\Adapter_Engine\Adapter_Engine.csproj", "{CE56A0D0-EB5B-4A49-B20F-0432E1832937}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BHoM_Adapter", "..\..\BHoM_Adapter\BHoM_Adapter.csproj", "{C389BC62-717D-4639-BACD-864CF97336BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Structure_AdapterModules", "..\..\Structure_AdapterModules\Structure_AdapterModules.csproj", "{3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Test|Any CPU = Test|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2693899C-1398-4486-BED2-02809FB43BE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2693899C-1398-4486-BED2-02809FB43BE9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2693899C-1398-4486-BED2-02809FB43BE9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2693899C-1398-4486-BED2-02809FB43BE9}.Release|Any CPU.Build.0 = Release|Any CPU + {2693899C-1398-4486-BED2-02809FB43BE9}.Test|Any CPU.ActiveCfg = Test|Any CPU + {2693899C-1398-4486-BED2-02809FB43BE9}.Test|Any CPU.Build.0 = Test|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Release|Any CPU.Build.0 = Release|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Test|Any CPU.ActiveCfg = Test|Any CPU + {C62FB82E-FDAB-4FF4-98B6-5EBD695B579F}.Test|Any CPU.Build.0 = Test|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Release|Any CPU.Build.0 = Release|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Test|Any CPU.ActiveCfg = Test|Any CPU + {CE56A0D0-EB5B-4A49-B20F-0432E1832937}.Test|Any CPU.Build.0 = Test|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Release|Any CPU.Build.0 = Release|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Test|Any CPU.ActiveCfg = Test|Any CPU + {C389BC62-717D-4639-BACD-864CF97336BB}.Test|Any CPU.Build.0 = Test|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Release|Any CPU.Build.0 = Release|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Test|Any CPU.ActiveCfg = Test|Any CPU + {3E8F16BF-4EB2-47C8-B5D6-9A370A55B902}.Test|Any CPU.Build.0 = Test|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {763D45C4-AF11-4244-B502-01ADEC9953BC} + EndGlobalSection +EndGlobal diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/BHoM_Adapter_Tests.csproj b/.ci/unit-tests/BHoM_Adapter_Tests/BHoM_Adapter_Tests.csproj new file mode 100644 index 00000000..d7ed3d90 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/BHoM_Adapter_Tests.csproj @@ -0,0 +1,67 @@ + + + + net6.0 + enable + enable + + false + + Debug;Release;Test + + + + + + + + + + + + + + + + + + + + + + C:\ProgramData\BHoM\Assemblies\Analytical_oM.dll + + + C:\ProgramData\BHoM\Assemblies\BHoM.dll + + + C:\ProgramData\BHoM\Assemblies\BHoM_Engine.dll + + + C:\ProgramData\BHoM\Assemblies\Dimensional_oM.dll + + + C:\ProgramData\BHoM\Assemblies\Geometry_Engine.dll + + + C:\ProgramData\BHoM\Assemblies\Geometry_oM.dll + + + C:\ProgramData\BHoM\Assemblies\Physical_oM.dll + + + C:\ProgramData\BHoM\Assemblies\Spatial_oM.dll + + + C:\ProgramData\BHoM\Assemblies\Structure_Engine.dll + + + C:\ProgramData\BHoM\Assemblies\Structure_oM.dll + + + + + + + + diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObject.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObject.cs new file mode 100644 index 00000000..2e51b424 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObject.cs @@ -0,0 +1,43 @@ +/* + * 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.Base; +using BH.oM.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Adapter.Tests +{ + public static partial class Create + { + public static T RandomObject() where T : IObject + { + if (typeof(T).IsInterface) + return default(T); + + return (T)BH.Engine.Base.Create.RandomObject(typeof(T)); + } + } +} \ No newline at end of file diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObjects.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObjects.cs new file mode 100644 index 00000000..a130b113 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Create/RandomObjects.cs @@ -0,0 +1,87 @@ +/* + * 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.Base; +using BH.oM.Base; +using BH.Tests.Adapter; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BH.Adapter.Tests +{ + public static partial class Create + { + public static List RandomObjects(int count = 100, bool assignIdFragmentWithProgressiveId = false, bool assignObjectName = false, string objectNamePrefix = "bar_") where T : IObject + { + return RandomBHoMObjects(typeof(T), count, assignIdFragmentWithProgressiveId, assignObjectName, objectNamePrefix).OfType().ToList(); + } + + public static List RandomBHoMObjects(Type t, int count = 100, bool assignIdFragmentWithProgressiveId = false, bool assignObjectName = false, string objectNamePrefix = "bar_") + { + List objs = new List(); + + for (int i = 0; i < count; i++) + { + IObject obj = BH.Engine.Base.Create.RandomObject(t); + + if (obj == null) + throw new ArgumentException($"Could not create a Random Object of type {t.FullName}."); + + if (assignIdFragmentWithProgressiveId) + { + IBHoMObject bhomObj = obj as IBHoMObject; + TestFragment testIdFragment = new TestFragment() { SomeProp = i }; + bhomObj = bhomObj.AddFragment(testIdFragment); + obj = bhomObj; + } + + if (assignObjectName) + { + IBHoMObject bhomObj = obj as IBHoMObject; + bhomObj.Name = objectNamePrefix + i.ToString(); + obj = bhomObj; + } + + objs.Add(obj as dynamic); + } + + return objs; + } + + public static List RandomIObjects(Type t, int count = 100) + { + List objs = new List(); + + for (int i = 0; i < count; i++) + { + IObject obj = BH.Engine.Base.Create.RandomObject(t); + + objs.Add(obj); + } + + return objs; + } + } +} \ No newline at end of file diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapter.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapter.cs new file mode 100644 index 00000000..bcde4e66 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapter.cs @@ -0,0 +1,212 @@ +/* + * 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.Adapter; +using BH.Adapter.Tests; +using BH.Engine.Structure; +using BH.oM.Adapter; +using BH.oM.Base; +using BH.oM.Structure.Constraints; +using BH.oM.Structure.Elements; +using BH.oM.Structure.Loads; +using BH.oM.Structure.MaterialFragments; +using BH.oM.Structure.Offsets; +using BH.oM.Structure.SectionProperties; +using BH.oM.Structure.SurfaceProperties; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; +using BH.Engine.Adapter; +using BH.Engine.Base; + +namespace BH.Tests.Adapter +{ + public class StructuralAdapter : BHoMAdapter + { + public List>> Created { get; set; } = new List>>(); + public List> ReadTypes { get; set; } = new List>(); + public List>> Updated { get; set; } = new List>>(); + public List>> Deleted { get; set; } = new List>>(); + + [Description("Useful e.g. to compare how many calls to Create are done with/without the caching mechanism.")] + public Dictionary CallsToCreatePerType { get; set; } = new Dictionary(); + [Description("Useful e.g. to compare how many calls to Create are done with/without the caching mechanism.")] + public Dictionary CallsToReadPerType { get; set; } = new Dictionary(); + [Description("Useful e.g. to compare how many calls to Create are done with/without the caching mechanism.")] + public Dictionary CallsToUpdatePerType { get; set; } = new Dictionary(); + [Description("Useful e.g. to compare how many calls to Create are done with/without the caching mechanism.")] + public Dictionary CallsToDeletePerType { get; set; } = new Dictionary(); + + public StructuralAdapter(bool cacheCRUDobjects = true) + { + m_AdapterSettings = new AdapterSettings() + { + UseAdapterId = true, + OnlyUpdateChangedObjects = true, + CacheCRUDobjects = cacheCRUDobjects + }; + + DependencyTypes = new Dictionary> + { + {typeof(Bar), new List { typeof(ISectionProperty), typeof(Node), typeof(BarRelease), typeof(Offset)}}, + {typeof(ISectionProperty), new List { typeof(IMaterialFragment) } }, + {typeof(Node), new List { typeof(Constraint6DOF) } }, + {typeof(ILoad), new List { typeof(Loadcase) } }, + {typeof(LoadCombination), new List { typeof(Loadcase) } }, + {typeof(Panel), new List { typeof(ISurfaceProperty) , typeof(Opening), typeof(Edge)} }, + {typeof(Opening), new List {typeof(Edge) } }, + {typeof(Edge), new List { typeof(Constraint6DOF), typeof(Constraint4DOF) } }, + {typeof(ISurfaceProperty), new List { typeof(IMaterialFragment) } }, + {typeof(RigidLink), new List { typeof(LinkConstraint), typeof(Node) } }, + {typeof(FEMesh), new List { typeof(Node), typeof(ISurfaceProperty)} }, + { typeof(IElementLoad), new List{ typeof(Bar)} }, + { typeof(IElementLoad), new List{ typeof(Node)} }, + { typeof(GravityLoad), new List{ typeof(Bar), typeof(Panel), typeof(FEMesh)} } + }; + + AdapterComparers = new Dictionary + { + {typeof(Bar), new BarEndNodesDistanceComparer(3) }, + {typeof(Node), new NodeDistanceComparer(3) }, + {typeof(ISectionProperty), new NameOrDescriptionComparer() }, + {typeof(ISurfaceProperty), new NameOrDescriptionComparer() }, + {typeof(IMaterialFragment), new NameOrDescriptionComparer() }, + {typeof(LinkConstraint), new NameOrDescriptionComparer() }, + {typeof(Constraint6DOF), new NameOrDescriptionComparer() }, + {typeof(Offset), new NameOrDescriptionComparer() }, + {typeof(BarRelease), new NameOrDescriptionComparer() } + }; + + + BH.Adapter.Modules.Structure.ModuleLoader.LoadModules(this); + AdapterIdFragmentType = typeof(StructuralAdapterId); + } + + protected override bool ICreate(IEnumerable objects, ActionConfig actionConfig = null) + { + + ValidateCreateObjects(objects as dynamic); + + Created.Add(new Tuple>(typeof(T), objects.OfType())); + + if (!CallsToCreatePerType.TryGetValue(typeof(T), out int n)) + CallsToCreatePerType[typeof(T)] = 1; + else + CallsToCreatePerType[typeof(T)] = n + 1; + + return true; + } + + private void ValidateCreateObjects(IEnumerable objects) + { + + } + + private void ValidateCreateObjects(IEnumerable> objects) where T : IBHoMObject + { + foreach (IElementLoad load in objects) + { + foreach (IBHoMObject bhObj in load.Objects.Elements) + { + StructuralAdapterId id = bhObj.FindFragment(); + if (id == null) + throw new Exception("Elements on loads do not contain required Ids."); + } + } + } + + protected override IEnumerable IRead(Type type, IList ids, ActionConfig actionConfig = null) + { + ReadTypes.Add(new Tuple(type, ids)); + + List modelObjects = Created.Where(x => x.Item1.IsAssignableFrom(type)).SelectMany(x => x.Item2).ToList(); + + List dependencyTypes = this.GetDependencyTypes(type); + + MethodInfo readCached = typeof(BHoMAdapter).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetGenericArguments().Length == 1).FirstOrDefault(x => x.Name == nameof(GetCachedOrRead)); + + foreach (Type t in dependencyTypes) + { + MethodInfo generic = readCached.MakeGenericMethod(t); + generic.Invoke(this, new object[] { null, null, actionConfig }); + } + + if (!CallsToReadPerType.TryGetValue(type, out int n)) + CallsToReadPerType[type] = 1; + else + CallsToReadPerType[type] = n + 1; + + return modelObjects; + } + + protected override bool IUpdate(IEnumerable objects, ActionConfig actionConfig = null) + { + Updated.Add(new Tuple>(typeof(T), objects.OfType())); + + if (!CallsToUpdatePerType.TryGetValue(typeof(T), out int n)) + CallsToUpdatePerType[typeof(T)] = 1; + else + CallsToUpdatePerType[typeof(T)] = n + 1; + + return true; + } + + protected override int IDelete(Type type, IEnumerable ids, ActionConfig actionConfig = null) + { + Deleted.Add(new Tuple>(type, ids)); + + if (!CallsToDeletePerType.TryGetValue(type, out int n)) + CallsToDeletePerType[type] = 1; + else + CallsToDeletePerType[type] = n + 1; + + return 0; + } + + protected override object NextFreeId(Type objectType, bool refresh = false) + { + if (refresh || !m_nextId.ContainsKey(objectType)) + { + int nextId = Created.Where(x => x.Item1 == objectType).SelectMany(x => x.Item2).Count(); + m_nextId[objectType] = nextId; + return nextId; + } + else + { + int prev = m_nextId[objectType]; + int next = prev + 1; + m_nextId[objectType] = next; + return next; + } + } + + Dictionary m_nextId = new Dictionary(); + } +} \ No newline at end of file diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapterId.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapterId.cs new file mode 100644 index 00000000..04225f3a --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/StructuralAdapterId.cs @@ -0,0 +1,46 @@ +/* + * 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 System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace BH.Tests.Adapter +{ + [Description("")] + public class StructuralAdapterId : IAdapterId + { + /***************************************************/ + /**** Properties ****/ + /***************************************************/ + + [Description("")] + public virtual object Id { get; set; } + + + /***************************************************/ + + } +} + + diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestContainer.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestContainer.cs new file mode 100644 index 00000000..572114ea --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestContainer.cs @@ -0,0 +1,36 @@ +/* + * 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; + +namespace BH.Tests.Adapter +{ + // Must be unpackable + public class TestContainer : BHoMObject, IContainer + { + public T? SomeObject { get; set; } + public IEnumerable ListOfObjects { get; set; } = new List(); + public IEnumerable> ListOfLists { get; set; } = new List>(); + } +} + diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestFragment.cs b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestFragment.cs new file mode 100644 index 00000000..0c4f63fb --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/Objects/TestFragment.cs @@ -0,0 +1,33 @@ +/* + * 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; + +namespace BH.Tests.Adapter +{ + public class TestFragment : IFragment + { + public object SomeProp { get; set; } + } +} + diff --git a/.ci/unit-tests/BHoM_Adapter_Tests/PushTests.cs b/.ci/unit-tests/BHoM_Adapter_Tests/PushTests.cs new file mode 100644 index 00000000..e0be67d4 --- /dev/null +++ b/.ci/unit-tests/BHoM_Adapter_Tests/PushTests.cs @@ -0,0 +1,580 @@ +/* + * 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 NUnit.Framework; +using BH.Adapter.Tests; +using BH.oM.Base; +using BH.oM.Structure.Constraints; +using BH.oM.Structure.Elements; +using BH.oM.Structure.Loads; +using BH.oM.Structure.MaterialFragments; +using BH.oM.Structure.SectionProperties; +using BH.oM.Structure.SurfaceProperties; +using BH.oM.Adapter; +using System.Diagnostics.Contracts; +using AutoBogus; +using Shouldly; +using BH.oM.Geometry; + +namespace BH.Tests.Adapter.Structure +{ + public class PushTests + { + StructuralAdapter sa; + [SetUp] + public void Setup() + { + sa = new StructuralAdapter(); + BH.Engine.Base.Compute.ClearCurrentEvents(); + } + + private static IEnumerable GetTestContainers() + { + // BH.Engine.Base.Create.RandomObject() can't deal with generics. Using AutoFaker instead. Example: + // AutoFaker creates 1 objects of the requested type per each IEnumerable property. + // E.g. Container will have 1 + 1 + 1 = 3 Bars. + yield return new TestCaseData(new AutoFaker>().Generate(), 5, 50); + } + + + [Test] + [TestCaseSource(nameof(GetTestContainers))] + public void UnpackObjsDuringPush(TestContainer container, int numberOfTypes, int minTotalObjects) + { + sa.Push(new List() { container }); + + IEnumerable? sectionProperties = sa.Created.Where(c => c.Item1 == typeof(ISectionProperty)).FirstOrDefault()?.Item2 ?? new List(); + sa.Created.Count.ShouldBe(numberOfTypes); + sa.Created.SelectMany(kv => kv.Item2).Count().ShouldBeGreaterThan(minTotalObjects); + } + + [Test] + public void GroupByParentInterface() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + sa.Push(inputObjects); + + IEnumerable? sectionProperties = sa.Created.Where(c => c.Item1 == typeof(ISectionProperty)).FirstOrDefault()?.Item2 ?? new List(); + Assert.IsTrue(sectionProperties.OfType().Any() && sectionProperties.OfType().Any(), + "Section properties should include both the input SteelSections and the AluminiumSection generated via RandomObject()."); + } + + + [Test] + public void DependencyOrder_BarLoads() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + sa.Push(inputObjects); + + string correctOrder = "BH.oM.Structure.MaterialFragments.IMaterialFragment, " + + "BH.oM.Structure.Constraints.Constraint6DOF, " + + "BH.oM.Structure.SectionProperties.ISectionProperty, " + + "BH.oM.Structure.Elements.Node, " + + "BH.oM.Structure.Constraints.BarRelease, " + + "BH.oM.Structure.Offsets.Offset, " + + "BH.oM.Structure.Elements.Bar, " + + "BH.oM.Structure.Loads.Loadcase, " + + "BH.oM.Structure.Loads.BarUniformlyDistributedLoad"; + + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + Assert.AreEqual(correctOrder, createdOrder); + } + + + [Test] + public void DependencyOrder_MostStructuralObjects() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + sa.Push(inputObjects); + + string correctOrder = "BH.oM.Structure.Constraints.Constraint6DOF, " + + "BH.oM.Structure.MaterialFragments.IMaterialFragment, " + + "BH.oM.Structure.Constraints.Constraint4DOF, " + + "BH.oM.Structure.Elements.Node, " + + "BH.oM.Structure.Elements.Edge, " + + "BH.oM.Structure.SectionProperties.ISectionProperty, " + + "BH.oM.Structure.Constraints.BarRelease, " + + "BH.oM.Structure.Offsets.Offset, " + + "BH.oM.Structure.SurfaceProperties.ISurfaceProperty, " + + "BH.oM.Structure.Elements.Bar, " + + "BH.oM.Structure.Loads.Loadcase, " + + "BH.oM.Structure.Elements.Opening, " + + "BH.oM.Structure.Elements.FEMesh, " + + "BH.oM.Structure.Elements.Panel, " + + "BH.oM.Structure.Loads.BarUniformlyDistributedLoad"; + + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + Assert.AreEqual(correctOrder, createdOrder); + } + + [Test] + public void Dependecies_UpdateOnly() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + for (int i = 0; i < inputObjects.Count; i++) + { + BH.Engine.Adapter.Modify.SetAdapterId(inputObjects[i], typeof(StructuralAdapterId), i); + } + + sa.Push(inputObjects, "", BH.oM.Adapter.PushType.UpdateOnly); + + string correctOrderCreated = "BH.oM.Structure.Constraints.Constraint6DOF, " + + "BH.oM.Structure.MaterialFragments.IMaterialFragment, " + + "BH.oM.Structure.SectionProperties.ISectionProperty, " + + "BH.oM.Structure.Elements.Node, " + + "BH.oM.Structure.Constraints.BarRelease, " + + "BH.oM.Structure.Offsets.Offset"; + string correctOrderUpdated = "BH.oM.Structure.Elements.Node, BH.oM.Structure.SectionProperties.ISectionProperty, BH.oM.Structure.Elements.Bar"; + + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + string updateOrder = string.Join(", ", sa.Updated.Select(c => c.Item1.FullName)); + Assert.AreEqual(correctOrderCreated, createdOrder); + Assert.AreEqual(correctOrderUpdated, updateOrder); + } + + [Test] + public void DependencyOrder_MultipleSectionTypes() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + sa.Push(inputObjects); + + string correctOrder = "BH.oM.Structure.MaterialFragments.IMaterialFragment, BH.oM.Structure.SectionProperties.ISectionProperty"; + + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + + Assert.AreEqual(correctOrder, createdOrder); + + List correctCreatedSectionTypes = inputObjects.Select(x => x.GetType()).Distinct().ToList(); + + Assert.IsTrue(sa.Created.Count == 2, "Wrong number of created object types."); + Assert.IsTrue(sa.Created[1].Item1 == typeof(ISectionProperty), "Sections not created as second item."); + + List createdSectionTypes = sa.Created[1].Item2.Select(x => x.GetType()).Distinct().ToList(); + + Assert.AreEqual(correctCreatedSectionTypes, createdSectionTypes); + } + + [Test] + public void DependencyOrder_UpdateAndFullPush() + { + List inputObjects = new List(); + Node n = Create.RandomObject(); + Bar bar = Create.RandomObject(); + bar.StartNode = n; + inputObjects.Add(bar); + inputObjects.Add(n); + inputObjects.Add(Create.RandomObject()); // this should be moved up before the Bar's AluminiumSection's FullPush. + inputObjects.Add(new Aluminium()); + inputObjects.Add(new Constraint6DOF()); // this should not "jump ahead" + + var orderedObjects = Engine.Adapter.Query.GetDependencySortedObjects(inputObjects.OfType().ToList(), BH.oM.Adapter.PushType.UpdateOnly, sa); + + var onlyNodes = orderedObjects.Where(t => t.Item1 == typeof(Node)); + + Assert.IsTrue(onlyNodes.Any(t => t.Item2 == PushType.UpdateOnly), "Missing UpdateOnly in the list of pushed nodes."); + Assert.IsTrue(onlyNodes.Any(t => t.Item2 == PushType.FullPush), "Missing FullPush in the list of pushed nodes."); + + Assert.IsTrue(orderedObjects.Where(t => t.Item1 == typeof(Node)).First().Item2 == PushType.UpdateOnly, + "For Node objects, UpdateOnly should have come before FullPush."); + } + + [Test] + public void DependencyOrder_CreateLoadNoObjectsWithIds() + { + List loads = Create.RandomObjects(3); + + sa.Push(loads); + + string correctOrder = "BH.oM.Structure.MaterialFragments.IMaterialFragment, " + + "BH.oM.Structure.Constraints.Constraint6DOF, " + + "BH.oM.Structure.SectionProperties.ISectionProperty, " + + "BH.oM.Structure.Elements.Node, " + + "BH.oM.Structure.Constraints.BarRelease, " + + "BH.oM.Structure.Offsets.Offset, " + + "BH.oM.Structure.Elements.Bar, " + + "BH.oM.Structure.Loads.Loadcase, " + + "BH.oM.Structure.Loads.BarUniformlyDistributedLoad"; + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + + Assert.AreEqual(correctOrder, createdOrder); + } + + [Test] + public void DependencyOrder_CreateLoadAllObjectsWithIds() + { + List bars = Create.RandomObjects(10); + for (int i = 0; i < bars.Count; i++) + { + Engine.Adapter.Modify.SetAdapterId(bars[i], new StructuralAdapterId { Id = i + 1 }); + } + BarUniformlyDistributedLoad load = Create.RandomObject(); + load.Objects.Elements = bars; + + sa.Push(new List { load }); + + string correctOrder = "BH.oM.Structure.Loads.Loadcase, BH.oM.Structure.Loads.BarUniformlyDistributedLoad"; //All bars contain Ids, hence no bars should be created even if there is a dependency on the bars + string createdOrder = string.Join(", ", sa.Created.Select(c => c.Item1.FullName)); + + Assert.AreEqual(correctOrder, createdOrder); + } + + [Test] + public void DependencyOrder_CreateLoadHalfObjectsWithIds() + { + int objectCount = 10; + List bars = Create.RandomObjects(objectCount); + int withIdCount = objectCount / 2; + int withoutIdCount = objectCount - withIdCount; + for (int i = 0; i < withIdCount; i++) + { + Engine.Adapter.Modify.SetAdapterId(bars[i], new StructuralAdapterId { Id = i + 1 }); + } + + //Shuffle the order fo the bars. + //Doing this to test that the order of bars with and without Id does not matter + Random random = new Random(2); + bars = bars.OrderBy(x => random.Next()).ToList(); + + BarUniformlyDistributedLoad load = Create.RandomObject(); + load.Objects.Elements = bars; + + sa.Push(new List { load }); + + Assert.IsTrue(sa.Created.Any(x => x.Item1 == typeof(Bar)), "No bars created."); + int barCreationCount = sa.Created.First(x => x.Item1 == typeof(Bar)).Item2.Count(); + + Assert.AreEqual(withoutIdCount, barCreationCount, "Wrong number of bars created."); + } + + [Test] + public void DependencyOrder_CreateGravityLoadHalfObjectsWithIds() + { + int objectCount = 10; + List bars = Create.RandomObjects(objectCount); + List panels = Create.RandomObjects(objectCount); + int withIdCount = objectCount / 2; + int withoutIdCount = objectCount - withIdCount; + for (int i = 0; i < withIdCount; i++) + { + Engine.Adapter.Modify.SetAdapterId(bars[i], new StructuralAdapterId { Id = i + 1 }); + Engine.Adapter.Modify.SetAdapterId(panels[i], new StructuralAdapterId { Id = i + 1 }); + } + + //Shuffle the order fo the bars. + //Doing this to test that the order of bars with and without Id does not matter + Random random = new Random(2); + bars = bars.OrderBy(x => random.Next()).ToList(); + panels = panels.OrderBy(x => random.Next()).ToList(); + + GravityLoad load = Create.RandomObject(); + load.Objects.Elements = bars.Cast().Concat(panels).ToList(); + + sa.Push(new List { load }); + + Assert.IsTrue(sa.Created.Any(x => x.Item1 == typeof(Bar)), "No bars created."); + int barCreationCount = sa.Created.First(x => x.Item1 == typeof(Bar)).Item2.Count(); + + Assert.AreEqual(withoutIdCount, barCreationCount, "Wrong number of bars created."); + + Assert.IsTrue(sa.Created.Any(x => x.Item1 == typeof(Panel)), "No Panels created."); + int panelsCreationCount = sa.Created.First(x => x.Item1 == typeof(Panel)).Item2.Count(); + + Assert.AreEqual(withoutIdCount, panelsCreationCount, "Wrong number of Panels created."); + } + + [Test] + public void UpdateOnlyChanged() + { + //Create some random objects + int objectCount = 10; + + List bars = new List(); + List nodes = new List(); + List sectionProperties = new List(); + List steels = new List(); + + //Using methodology below to ensure the same random obejcts are created each set of the run. + //For some edge cases not using the methodology, some strings turned out the same/empty leading to the test failing + for (int i = 0; i < objectCount; i++) + { + bars.Add(BH.Engine.Base.Create.RandomObject(typeof(Bar), (i + 1) * 3) as Bar); + nodes.Add(BH.Engine.Base.Create.RandomObject(typeof(Node), (i + 1) * 7) as Node); + sectionProperties.Add(BH.Engine.Base.Create.RandomObject(typeof(SteelSection), (i + 1) * 37) as SteelSection); + steels.Add(BH.Engine.Base.Create.RandomObject(typeof(Steel), (i + 1) * 13) as Steel); + } + + + List allObjects = bars.Cast().Concat(nodes).Concat(sectionProperties).Concat(steels).ToList(); + + sa.Push(allObjects); + + int changeCount = objectCount / 2; + + HashSet randomIds = new HashSet(); + Random random = new Random(2); + //Generate random ids to change + while (randomIds.Count < changeCount) + { + randomIds.Add((int)Math.Floor(random.NextDouble() * objectCount)); + } + + //Update the random obejcts with the random ids + //The update is ensured to not change the part of the object used by the comparer to identify the objects as the same + //Using methodology below to ensure the same random obejcts are created each set of the run. + //For some edge cases not using the methodology, some strings turned out the same/empty leading to the test failing + foreach (int i in randomIds) + { + bars[i].SectionProperty = BH.Engine.Base.Create.RandomObject(typeof(SteelSection), (i + 1) * 17) as SteelSection; + nodes[i].Support = BH.Engine.Base.Create.RandomObject(typeof(Constraint6DOF), (i + 1) * 19) as Constraint6DOF; + SteelSection newSection = BH.Engine.Base.Create.RandomObject(typeof(SteelSection), (i + 1) * 23) as SteelSection; + newSection.Name = sectionProperties[i].Name; + sectionProperties[i] = newSection; + Steel newMaterial = BH.Engine.Base.Create.RandomObject(typeof(Steel), (i + 1) * 31) as Steel; + newMaterial.Name = steels[i].Name; + steels[i] = newMaterial; + } + + allObjects = bars.Cast().Concat(nodes).Concat(sectionProperties).Concat(steels).ToList(); + + //Push the updated objects again + sa.Push(allObjects); + + Assert.IsTrue(sa.Updated.Any(x => x.Item1 == typeof(Bar)), "No Bars Updated."); + int barUpdateCount = sa.Updated.First(x => x.Item1 == typeof(Bar)).Item2.Count(); + + Assert.AreEqual(changeCount, barUpdateCount, "Wrong number of Bars Updated."); + + Assert.IsTrue(sa.Updated.Any(x => x.Item1 == typeof(Node)), "No Nodes Updated."); + int nodeUpdateCount = sa.Updated.First(x => x.Item1 == typeof(Node)).Item2.Count(); + + Assert.AreEqual(changeCount, nodeUpdateCount, "Wrong number of Nodes Updated."); + + Assert.IsTrue(sa.Updated.Any(x => x.Item1 == typeof(ISectionProperty)), "No SectionProperty Updated."); + int sectionUpdateCount = sa.Updated.First(x => x.Item1 == typeof(ISectionProperty)).Item2.Count(); + + Assert.AreEqual(changeCount, sectionUpdateCount, "Wrong number of ISectionProperties Updated."); + + Assert.IsTrue(sa.Updated.Any(x => x.Item1 == typeof(IMaterialFragment)), "No Materials Updated."); + int materialUpdateCount = sa.Updated.First(x => x.Item1 == typeof(IMaterialFragment)).Item2.Count(); + + Assert.AreEqual(changeCount, materialUpdateCount, "Wrong number of Materials Updated."); + } + + [Test] + public void CountCRUDCallsPerType() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + sa.Push(inputObjects); + + Assert.IsTrue(sa.CallsToCreatePerType.All(kv => kv.Value == 1), "Calls to Create should be done once per each type."); + Assert.IsTrue(sa.CallsToReadPerType.All(kv => kv.Value == 1), "Calls to Read should be done once per each type."); + } + + [Test] + public void CountCRUDCallsPerType_UpdateOnly() + { + List inputObjects = new List(); + inputObjects.AddRange(Create.RandomObjects(10)); + inputObjects.AddRange(Create.RandomObjects(10)); + + for (int i = 0; i < inputObjects.Count; i++) + { + BH.Engine.Adapter.Modify.SetAdapterId(inputObjects[i], typeof(StructuralAdapterId), i); + } + + sa.Push(inputObjects); + sa.Push(inputObjects, "", PushType.UpdateOnly); + + Assert.IsTrue(sa.CallsToCreatePerType.All(kv => kv.Value == 1), "Calls to Create should be done once per each type."); + Assert.IsTrue(sa.CallsToReadPerType.Where(kv => kv.Key == typeof(Bar)).First().Value == 1, "The Bar should be read only once."); + Assert.IsTrue(sa.CallsToReadPerType.Where(kv => kv.Key != typeof(Bar)).All(kv => kv.Value == 2)); + Assert.IsTrue(sa.CallsToUpdatePerType.All(kv => kv.Value == 1), "Calls to Update should be done once per each type."); + } + + [Test] + public void Preprocess_PanelLoads() + { + List inputObjects = new List(); + List panels = Create.RandomObjects(10); + AreaUniformlyDistributedLoad load = Create.RandomObject(); + load.Objects.Elements = panels.Cast().ToList(); + + inputObjects.Add(load); + inputObjects.AddRange(panels); + + sa.Push(inputObjects); + //No duplicate ids, hence no warning should be raised + Engine.Base.Query.CurrentEvents().ShouldNotContain(x => x.Message.StartsWith("Some objects pushed have duplicate BHoM_Guids")); + } + + + [Test] + public void Preprocess_PushTranslatedBars() + { + List inputObjects = new List(); + + Bar bar = Create.RandomObject(); + int barCount = 10; + + for (int i = 0; i < barCount; i++) + { + //Tranforming bars does not replace the Guids + Bar translated = BH.Engine.Structure.Modify.Transform(bar, Engine.Geometry.Create.TranslationMatrix(new oM.Geometry.Vector { Z = i })); + inputObjects.Add(translated); + } + + //Culling only happens when there are loads + PointLoad load = Create.RandomObject(); + inputObjects.Add(load); + + + sa.Push(inputObjects); + + + sa.Created.Where(x => x.Item1 == typeof(Bar)).SelectMany(x => x.Item2).Count().ShouldBe(barCount); + //Duplicate Ids irrelevant hence no warning should be raised + Engine.Base.Query.CurrentEvents().ShouldNotContain(x => x.Message.StartsWith("Some objects pushed have duplicate BHoM_Guids")); + } + + [Test] + public void Preprocess_PanelLoadsDuplicateIds() + { + //All panels are set to have the same Guid, hence should not be able to rely on the replace mechanism + List inputObjects = new List(); + List panels = Create.RandomObjects(10); + Guid guid = Guid.NewGuid(); + panels.ForEach(x => x.BHoM_Guid = guid); + + AreaUniformlyDistributedLoad load = Create.RandomObject(); + load.Objects.Elements = panels.Cast().ToList(); + + inputObjects.Add(load); + inputObjects.AddRange(panels); + + //Expecting this to crash on ValidateCreateObjects, and warning to be raised to inform that replacement could not happen + try + { + sa.Push(inputObjects); + } + catch (Exception e) + { + if (e.Message == "Elements on loads do not contain required Ids.") + { + BH.Engine.Base.Query.CurrentEvents().ShouldContain(x => x.Message.StartsWith("Some objects pushed have duplicate BHoM_Guids")); + } + else + throw; + } + + + } + + [Test] + [Description("Tests that objects being pushed are correctly 'merged' by calls to CopyProperties modules. \n" + + "Two nodes pushed in the same location, one with a support and one without, the adapter should make sure the final node being sent for creation should contain the support.")] + public void CopyProperties_NodesReplaced() + { + //Create bar from line. Nodes will have null-constraints on the Bar + Line line = new Line { Start = new Point { X = 0 }, End = new Point { X = 10 } }; + Bar bar = BH.Engine.Structure.Create.Bar(line, null, 0); + + //Create nodes in the same position + Node node1 = new Node { Position = line.Start, Support = Create.RandomObject() }; + Node node2 = new Node { Position = line.End, Support = Create.RandomObject() }; + + //Push with bar before the nodes + List inputObjs = new List { bar, node1, node2 }; + sa.Push(inputObjs); + + //Make sure the nodes in the model contain the supports + var supports = sa.Created.Where(x => x.Item1 == typeof(Node)).SelectMany(x => x.Item2).Cast().Select(x => x.Support).Where(x => x != null); + supports.ShouldContain(x => x.Name == node1.Support.Name); + supports.ShouldContain(x => x.Name == node2.Support.Name); + } + + [Test] + [Description("Tests that all objects sent to the push have AdapterIds assigned, even though some have been identified as duplicates and hence culled out.")] + public void DuplicateObjects_EnsureAllOutputHaveIds() + { + //Create duplicate elements + Steel steel1 = Create.RandomObject(); + Steel steel2 = Create.RandomObject(); + + steel1.Name = "MatName"; + steel2.Name = steel1.Name; + + SteelSection section1 = Create.RandomObject(); + SteelSection section2 = Create.RandomObject(); + section1.Material = steel1; + section2.Material = steel2; + section1.Name = "SecName"; + section2.Name = section1.Name; + + Line line = new Line { Start = new Point { X = 0 }, End = new Point { X = 10 } }; + Bar bar1 = BH.Engine.Structure.Create.Bar(line, section1, 0); + Bar bar2 = BH.Engine.Structure.Create.Bar(line, section1, 0); + + Node node1 = new Node { Position = line.Start }; + Node node2 = new Node { Position = line.End }; + + //Push duplicates + List inputObjs = new List { bar1, bar2, node1, node2, section1, section2, steel1, steel2 }; + List pushed = sa.Push(inputObjs).OfType().ToList(); + + //Make sure correct number of items has been created to ensure comparers work. + //If this does not work, the check of all objects having assigned Ids is pointless + sa.Created.Where(x => x.Item1 != typeof(Node)).ShouldAllBe(x => x.Item2.Count() == 1); + sa.Created.Where(x => x.Item1 == typeof(Node)).ShouldAllBe(x => x.Item2.Count() == 2); + + pushed.ShouldAllBe(x => BH.Engine.Base.Query.FindFragment(x, typeof(StructuralAdapterId)) != null, "At least one of the pushed objects did not contain an AdapterId Fragment."); + } + } +} \ No newline at end of file diff --git a/Adapter_Engine/Adapter_Engine.csproj b/Adapter_Engine/Adapter_Engine.csproj index 13683644..fa6265ee 100644 --- a/Adapter_Engine/Adapter_Engine.csproj +++ b/Adapter_Engine/Adapter_Engine.csproj @@ -3,22 +3,31 @@ netstandard2.0 6.0.0.0 - https://github.com/BHoM/BHoM_Adapter + https://github.com/BHoM/BHoM_Adapter 5.0.0 BHoM Copyright © https://github.com/BHoM BH.Engine.Adapter - 6.0.0.0 + 6.1.0.0 + Debug;Release;Test ..\Build\ + + ..\Build\ + + - + + + + + diff --git a/Adapter_Engine/Create/BHoMAdapter.cs b/Adapter_Engine/Create/BHoMAdapter.cs index e6f08bf8..3263c596 100644 --- a/Adapter_Engine/Create/BHoMAdapter.cs +++ b/Adapter_Engine/Create/BHoMAdapter.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. @@ -162,3 +162,4 @@ public static IBHoMAdapter BHoMAdapter(Type adapterType, List parameters } } + diff --git a/Adapter_Engine/Modify/SetAdapterId.cs b/Adapter_Engine/Modify/SetAdapterId.cs index c252bf2c..829a9564 100644 --- a/Adapter_Engine/Modify/SetAdapterId.cs +++ b/Adapter_Engine/Modify/SetAdapterId.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. @@ -82,3 +82,4 @@ public static void SetAdapterId(this IBHoMObject bHoMObject, IAdapterId id) } + diff --git a/Adapter_Engine/Query/AdapterId.cs b/Adapter_Engine/Query/AdapterId.cs index 34421fd8..75a03e35 100644 --- a/Adapter_Engine/Query/AdapterId.cs +++ b/Adapter_Engine/Query/AdapterId.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. @@ -78,3 +78,4 @@ public static T AdapterId(this IBHoMObject bHoMObject, Type adapterIdFragment } } + diff --git a/Adapter_Engine/Query/AdapterIds.cs b/Adapter_Engine/Query/AdapterIds.cs index 05ced8eb..cb4e56fc 100644 --- a/Adapter_Engine/Query/AdapterIds.cs +++ b/Adapter_Engine/Query/AdapterIds.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. @@ -74,3 +74,4 @@ public static object AdapterIds(this IBHoMObject bHoMObject, Type adapterIdFragm } + diff --git a/Adapter_Engine/Query/AdapterMethods.cs b/Adapter_Engine/Query/AdapterMethods.cs index d1fd5c58..c22bb816 100644 --- a/Adapter_Engine/Query/AdapterMethods.cs +++ b/Adapter_Engine/Query/AdapterMethods.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. @@ -177,3 +177,4 @@ private static bool IsOfAllowedTypes(this Type type) + diff --git a/Adapter_Engine/Query/GetComparerForType.cs b/Adapter_Engine/Query/GetComparerForType.cs index 9bc428bf..ef147b42 100644 --- a/Adapter_Engine/Query/GetComparerForType.cs +++ b/Adapter_Engine/Query/GetComparerForType.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. @@ -64,3 +64,4 @@ public static IEqualityComparer GetComparerForType(this IBHoMAdapter bHoMA + diff --git a/Adapter_Engine/Query/GetCopyPropertiesModules.cs b/Adapter_Engine/Query/GetCopyPropertiesModules.cs index 99a4d137..ae1254e0 100644 --- a/Adapter_Engine/Query/GetCopyPropertiesModules.cs +++ b/Adapter_Engine/Query/GetCopyPropertiesModules.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. @@ -32,7 +32,8 @@ namespace BH.Engine.Adapter { public static partial class Query { - public static List> GetCopyPropertiesModules(this IBHoMAdapter adapter) where T : class, IBHoMObject + [Description("Gets any adapter module on the adapter for copying properties of an object of type T to another object of type T.")] + public static List> GetCopyPropertiesModules(this IBHoMAdapter adapter) where T : IBHoMObject { return adapter.AdapterModules.OfType>().ToList(); } @@ -41,3 +42,4 @@ public static List> GetCopyPropertiesModules(this IB + diff --git a/Adapter_Engine/Query/GetDependencyObjects.cs b/Adapter_Engine/Query/GetDependencyObjects.cs index f1a1f5c8..18575d54 100644 --- a/Adapter_Engine/Query/GetDependencyObjects.cs +++ b/Adapter_Engine/Query/GetDependencyObjects.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,6 +41,9 @@ public static partial class Query /***************************************************/ [Description("Fetches all dependancy objects of types provided from the list of the objects. Firsts checks for any DependencyModules, if no present matching the type, tries to scan any property returning the types.")] + [Input("objects", "Objects to get the dependency objects for.")] + [Input("dependencyTypes", "List of types that have dependencies.")] + [Input("adapter", "Adapter for which dependencies must be gathered.")] public static Dictionary GetDependencyObjects(this IEnumerable objects, List dependencyTypes, IBHoMAdapter adapter = null) where T : IBHoMObject { if (objects == null || !objects.Any() || dependencyTypes == null || !dependencyTypes.Any()) @@ -56,9 +59,10 @@ public static Dictionary GetDependencyObjects(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 objects, Dictionary, List> gatheredDependecies, PushType pushType, IBHoMAdapter adapter) where T : IBHoMObject + { + List dependencies = GetDependencyTypes(adapter); + Dictionary dependencyObjects = GetDependencyObjects(objects, dependencies, adapter); + + foreach (var depObj in dependencyObjects) + { + var key = new Tuple(depObj.Key, pushType); + if (gatheredDependecies.ContainsKey(key)) + gatheredDependecies[key].AddRange(depObj.Value.Cast()); + else + gatheredDependecies[key] = depObj.Value.Cast().ToList(); + + GetDependencyObjectsRecursive(depObj.Value as dynamic, gatheredDependecies, pushType, adapter); + } + } + + /***************************************************/ + } +} \ No newline at end of file diff --git a/Adapter_Engine/Query/HasAdapterIdFragment.cs b/Adapter_Engine/Query/HasAdapterIdFragment.cs index a6858899..e2e3007c 100644 --- a/Adapter_Engine/Query/HasAdapterIdFragment.cs +++ b/Adapter_Engine/Query/HasAdapterIdFragment.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. @@ -60,3 +60,4 @@ public static bool HasAdapterIdFragment(this IBHoMObject iBHoMObject, Type fragm + diff --git a/Adapter_oM/Adapter_oM.csproj b/Adapter_oM/Adapter_oM.csproj index 0aadd7ed..6727b517 100644 --- a/Adapter_oM/Adapter_oM.csproj +++ b/Adapter_oM/Adapter_oM.csproj @@ -3,20 +3,25 @@ netstandard2.0 6.0.0.0 - https://github.com/BHoM/BHoM_Adapter + https://github.com/BHoM/BHoM_Adapter 5.0.0 BHoM Copyright © https://github.com/BHoM BH.oM.Adapter - 6.0.0.0 + 6.1.0.0 + Debug;Release;Test ..\Build\ + + ..\Build\ + + - + diff --git a/Adapter_oM/Enums/PullType.cs b/Adapter_oM/Enums/PullType.cs index b33b1988..cd2912d7 100644 --- a/Adapter_oM/Enums/PullType.cs +++ b/Adapter_oM/Enums/PullType.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. @@ -39,3 +39,4 @@ public enum PullType + diff --git a/Adapter_oM/Enums/PushType.cs b/Adapter_oM/Enums/PushType.cs index 5cbc5f6c..86fdfc2b 100644 --- a/Adapter_oM/Enums/PushType.cs +++ b/Adapter_oM/Enums/PushType.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. @@ -51,3 +51,4 @@ namespace BH.oM.Adapter } + diff --git a/Adapter_oM/ExecuteCommands/Analyse.cs b/Adapter_oM/ExecuteCommands/Analyse.cs index 1b531ed0..3fed9d85 100644 --- a/Adapter_oM/ExecuteCommands/Analyse.cs +++ b/Adapter_oM/ExecuteCommands/Analyse.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. @@ -37,3 +37,4 @@ public class Analyse : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/AnalyseLoadCases.cs b/Adapter_oM/ExecuteCommands/AnalyseLoadCases.cs index e96df41f..f2c38dfa 100644 --- a/Adapter_oM/ExecuteCommands/AnalyseLoadCases.cs +++ b/Adapter_oM/ExecuteCommands/AnalyseLoadCases.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. @@ -40,3 +40,4 @@ public class AnalyseLoadCases : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/ClearResults.cs b/Adapter_oM/ExecuteCommands/ClearResults.cs index 6f62c0bc..5d33dea0 100644 --- a/Adapter_oM/ExecuteCommands/ClearResults.cs +++ b/Adapter_oM/ExecuteCommands/ClearResults.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. @@ -37,3 +37,4 @@ public class ClearResults : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/Close.cs b/Adapter_oM/ExecuteCommands/Close.cs index 19a5fff2..1f811489 100644 --- a/Adapter_oM/ExecuteCommands/Close.cs +++ b/Adapter_oM/ExecuteCommands/Close.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. @@ -38,3 +38,4 @@ public class Close : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/CustomCommand.cs b/Adapter_oM/ExecuteCommands/CustomCommand.cs index efe21724..274e3080 100644 --- a/Adapter_oM/ExecuteCommands/CustomCommand.cs +++ b/Adapter_oM/ExecuteCommands/CustomCommand.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,3 +41,4 @@ public class CustomCommand : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/Exit.cs b/Adapter_oM/ExecuteCommands/Exit.cs index 865e9f96..fd08ea57 100644 --- a/Adapter_oM/ExecuteCommands/Exit.cs +++ b/Adapter_oM/ExecuteCommands/Exit.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. @@ -39,3 +39,4 @@ public class Exit : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/NewModel.cs b/Adapter_oM/ExecuteCommands/NewModel.cs index f158621e..b30c2314 100644 --- a/Adapter_oM/ExecuteCommands/NewModel.cs +++ b/Adapter_oM/ExecuteCommands/NewModel.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. @@ -37,3 +37,4 @@ public class NewModel : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/Open.cs b/Adapter_oM/ExecuteCommands/Open.cs index a4a1a0ac..5b3e61fe 100644 --- a/Adapter_oM/ExecuteCommands/Open.cs +++ b/Adapter_oM/ExecuteCommands/Open.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. @@ -38,3 +38,4 @@ public class Open : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/Save.cs b/Adapter_oM/ExecuteCommands/Save.cs index 93e22b78..40794bb4 100644 --- a/Adapter_oM/ExecuteCommands/Save.cs +++ b/Adapter_oM/ExecuteCommands/Save.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. @@ -37,3 +37,4 @@ public class Save : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/SaveAs.cs b/Adapter_oM/ExecuteCommands/SaveAs.cs index 09614d6d..1dadeb74 100644 --- a/Adapter_oM/ExecuteCommands/SaveAs.cs +++ b/Adapter_oM/ExecuteCommands/SaveAs.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. @@ -38,3 +38,4 @@ public class SaveAs : IExecuteCommand + diff --git a/Adapter_oM/ExecuteCommands/_IExecuteCommand.cs b/Adapter_oM/ExecuteCommands/_IExecuteCommand.cs index 83e8bcbc..280d02cf 100644 --- a/Adapter_oM/ExecuteCommands/_IExecuteCommand.cs +++ b/Adapter_oM/ExecuteCommands/_IExecuteCommand.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. @@ -38,3 +38,4 @@ public interface IExecuteCommand : IObject + diff --git a/Adapter_oM/FileSettings.cs b/Adapter_oM/FileSettings.cs index e98bd5ae..89f2d42f 100644 --- a/Adapter_oM/FileSettings.cs +++ b/Adapter_oM/FileSettings.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. @@ -42,3 +42,4 @@ public class FileSettings : BHoMObject } + diff --git a/Adapter_oM/IBHoMAdapter.cs b/Adapter_oM/IBHoMAdapter.cs index 45662c35..24f6be7a 100644 --- a/Adapter_oM/IBHoMAdapter.cs +++ b/Adapter_oM/IBHoMAdapter.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. @@ -49,3 +49,4 @@ public interface IBHoMAdapter + diff --git a/Adapter_oM/Module/IAdapterModule.cs b/Adapter_oM/Module/IAdapterModule.cs index 8c097144..05962be3 100644 --- a/Adapter_oM/Module/IAdapterModule.cs +++ b/Adapter_oM/Module/IAdapterModule.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. @@ -35,3 +35,4 @@ public interface IAdapterModule + diff --git a/Adapter_oM/Module/ICopyPropertiesModule.cs b/Adapter_oM/Module/ICopyPropertiesModule.cs index 7a2a34a9..9049040c 100644 --- a/Adapter_oM/Module/ICopyPropertiesModule.cs +++ b/Adapter_oM/Module/ICopyPropertiesModule.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. @@ -37,3 +37,4 @@ public interface ICopyPropertiesModule : IAdapterModule where T : IBHoMObj + diff --git a/Adapter_oM/Module/IGetDependencyModule.cs b/Adapter_oM/Module/IGetDependencyModule.cs index f734427c..55ec3860 100644 --- a/Adapter_oM/Module/IGetDependencyModule.cs +++ b/Adapter_oM/Module/IGetDependencyModule.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. @@ -40,3 +40,4 @@ public interface IGetDependencyModule : IAdapterModule where T : IB + diff --git a/Adapter_oM/Module/IPushPreProcessModule.cs b/Adapter_oM/Module/IPushPreProcessModule.cs new file mode 100644 index 00000000..9c602cc1 --- /dev/null +++ b/Adapter_oM/Module/IPushPreProcessModule.cs @@ -0,0 +1,37 @@ +/* + * 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 System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.Adapter.Module +{ + [Description("Module for any additional pre-processing to be done to the objects beofre being pushed. This is called after all other preprocessing, such as cloning is done.")] + public interface IPushPreProcessModule : IAdapterModule + { + [Description("Method called during ProcessObjectsForPush.")] + IEnumerable PreprocessObjects(IEnumerable objects); + } +} diff --git a/Adapter_oM/Module/ModuleSet.cs b/Adapter_oM/Module/ModuleSet.cs index 2ed0a235..2d3b0439 100644 --- a/Adapter_oM/Module/ModuleSet.cs +++ b/Adapter_oM/Module/ModuleSet.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. @@ -42,3 +42,4 @@ public ModuleSet() : + diff --git a/Adapter_oM/ObjectWrapper.cs b/Adapter_oM/ObjectWrapper.cs index 27ec3756..82d39767 100644 --- a/Adapter_oM/ObjectWrapper.cs +++ b/Adapter_oM/ObjectWrapper.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. @@ -37,3 +37,4 @@ public class ObjectWrapper : BHoMObject + diff --git a/Adapter_oM/Settings-Config/ActionConfig.cs b/Adapter_oM/Settings-Config/ActionConfig.cs index 6c5b637e..5fa91b70 100644 --- a/Adapter_oM/Settings-Config/ActionConfig.cs +++ b/Adapter_oM/Settings-Config/ActionConfig.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. @@ -49,3 +49,4 @@ public class ActionConfig : IObject + diff --git a/Adapter_oM/Settings-Config/AdapterSettings.cs b/Adapter_oM/Settings-Config/AdapterSettings.cs index d2a06a49..36c98e3e 100644 --- a/Adapter_oM/Settings-Config/AdapterSettings.cs +++ b/Adapter_oM/Settings-Config/AdapterSettings.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. @@ -61,6 +61,12 @@ public class AdapterSettings : IObject public virtual bool UseAdapterId { get; set; } = true; public virtual bool UseHashComparerAsDefault { get; set; } = false; public virtual bool ProcessInMemory { get; set; } = false; + [Description("If true, Objects found to be the same according to the AdapterComparer for the provided type are checked for full equality using the HashComparer of the type (which by default checks every property except BHoM_Guid). If equal according to the HashComparer, they are not updated.\n" + + "Otherwise, Objects found identical according to the AdapterComparer for the provided type are sent to the Update method.")] + public virtual bool OnlyUpdateChangedObjects { get; set; } = true; + + [Description("If true, CRUD operations will attempt read/write cache objects to speed things during a same Action.")] + public virtual bool CacheCRUDobjects { get; set; } = true; /****************************************/ /**** CreateOnly settings *****/ @@ -75,3 +81,4 @@ public class AdapterSettings : IObject + diff --git a/BHoM_Adapter.sln b/BHoM_Adapter.sln index 3df90704..fabffeef 100644 --- a/BHoM_Adapter.sln +++ b/BHoM_Adapter.sln @@ -1,15 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31205.134 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32922.545 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BHoM_Adapter", "BHoM_Adapter\BHoM_Adapter.csproj", "{41FE6552-45DE-4B79-A11E-B2724077EFFF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adapter_Engine", "Adapter_Engine\Adapter_Engine.csproj", "{06E1FCF3-B637-4902-80B9-7176EBFAD757}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adapter_Engine", "Adapter_Engine\Adapter_Engine.csproj", "{06E1FCF3-B637-4902-80B9-7176EBFAD757}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adapter_oM", "Adapter_oM\Adapter_oM.csproj", "{59752259-D2F6-46DE-A203-027F6FA1C1EA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Adapter_oM", "Adapter_oM\Adapter_oM.csproj", "{59752259-D2F6-46DE-A203-027F6FA1C1EA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Structure_AdapterModules", "Structure_AdapterModules\Structure_AdapterModules.csproj", "{48BE209E-658F-48FE-B45B-BB4D7E6CDD03}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Structure_AdapterModules", "Structure_AdapterModules\Structure_AdapterModules.csproj", "{48BE209E-658F-48FE-B45B-BB4D7E6CDD03}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/BHoM_Adapter/AdapterActions/Execute.cs b/BHoM_Adapter/AdapterActions/Execute.cs index 2d5320dd..6197cf2b 100644 --- a/BHoM_Adapter/AdapterActions/Execute.cs +++ b/BHoM_Adapter/AdapterActions/Execute.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. @@ -58,10 +58,9 @@ public virtual bool SetupExecuteConfig(ActionConfig actionConfig, out ActionConf "Item2 = A boolean indicating if the Execute was successful.")] public virtual Output, bool> Execute(IExecuteCommand command, ActionConfig actionConfig = null) { + BH.Engine.Base.Compute.RecordError($"Execute is not implemented in {this.GetType().Name}."); + return new Output, bool> { Item1 = null, Item2 = false}; } } -} - - - +} \ No newline at end of file diff --git a/BHoM_Adapter/AdapterActions/Move.cs b/BHoM_Adapter/AdapterActions/Move.cs index ffc1242c..b5dd724b 100644 --- a/BHoM_Adapter/AdapterActions/Move.cs +++ b/BHoM_Adapter/AdapterActions/Move.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. @@ -61,3 +61,4 @@ public virtual bool Move(IBHoMAdapter to, IRequest request, + diff --git a/BHoM_Adapter/AdapterActions/Pull.cs b/BHoM_Adapter/AdapterActions/Pull.cs index 8823fe1e..29e43ccc 100644 --- a/BHoM_Adapter/AdapterActions/Pull.cs +++ b/BHoM_Adapter/AdapterActions/Pull.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. @@ -72,6 +72,7 @@ public virtual bool SetupPullConfig(ActionConfig actionConfig, out ActionConfig [Description("Pulls objects from an external software using the basic CRUD/Read method as appropriate.")] public virtual IEnumerable Pull(IRequest request, PullType pullType = PullType.AdapterDefault, ActionConfig actionConfig = null) { + this.ClearCache(); // `(this as dynamic)` casts the abstract BHoMAdapter to its instance type (e.g. Speckle_Adapter), so all public ReadResults methods are captured if (request is IResultRequest) return (this as dynamic).ReadResults(request as dynamic, actionConfig); @@ -80,9 +81,12 @@ public virtual IEnumerable Pull(IRequest request, PullType pullType = Pu if (request is IRequest) return (this as dynamic).Read(request as dynamic, actionConfig); - return IRead(null, null, actionConfig); + IEnumerable read = IRead(null, null, actionConfig); + this.ClearCache(); + return read; } } } + diff --git a/BHoM_Adapter/AdapterActions/Push.cs b/BHoM_Adapter/AdapterActions/Push.cs index 53013ea8..0d7f68bd 100644 --- a/BHoM_Adapter/AdapterActions/Push.cs +++ b/BHoM_Adapter/AdapterActions/Push.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. @@ -22,6 +22,7 @@ using BH.oM.Base; using BH.oM.Adapter; +using BH.Engine.Reflection; using BH.Engine.Base; using BH.Engine.Adapter; using System; @@ -30,6 +31,7 @@ using System.Linq; using System.Reflection; using System.ComponentModel; +using System.Collections.ObjectModel; namespace BH.Adapter { @@ -65,6 +67,7 @@ public virtual bool SetupPushType(PushType inputPushType, out PushType pushType) [Description("Pushes input objects using either the 'Full CRUD', 'CreateOnly' or 'UpdateOnly', depending on the PushType.")] public virtual List Push(IEnumerable objects, string tag = "", PushType pushType = PushType.AdapterDefault, ActionConfig actionConfig = null) { + this.ClearCache(); bool success = true; // ----------------------------------------// @@ -80,7 +83,7 @@ public virtual List Push(IEnumerable objects, string tag = "", P if (objectsToPush.Count() == 0) { - BH.Engine.Base.Compute.RecordError("Input objects were invalid."); + BH.Engine.Base.Compute.RecordError("Input objects were invalid. Check that they are of a type supported by this Adapter's Push."); return new List(); } @@ -88,27 +91,46 @@ public virtual List Push(IEnumerable objects, string tag = "", P // ACTUAL PUSH // // ----------------------------------------// - // Group the objects by their specific type. - var typeGroups = objectsToPush.GroupBy(x => x.GetType()); + List>> orderedObjects; - foreach (var typeGroup in typeGroups) + if(m_AdapterSettings.HandleDependencies) + orderedObjects = Engine.Adapter.Query.GetDependencySortedObjects(objectsToPush, pushType, this); + else + orderedObjects = objectsToPush.GroupBy(x => x.GetType()).Select(x => new Tuple>(x.Key, pushType, x.Cast())).ToList(); + + // We now have objects grouped per type, and the groups are sorted following the dependency order. + foreach (var group in orderedObjects) { - // Cast the objects to their specific types - MethodInfo enumCastMethod_specificType = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new[] { typeGroup.Key }); - var objList_specificType = enumCastMethod_specificType.Invoke(typeGroup, new object[] { typeGroup }); + // Cast the IEnumerable to an IEnumerable where T is the the specific type it contains. + // This is used to dynamically dispatch to the right type-specific CRUD method. + MethodInfo enumCastMethod_specificType = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(new[] { group.Item1 }); + object objList_specificType = enumCastMethod_specificType.Invoke(group.Item2, new object[] { group.Item3.ToList() }); - if (pushType == PushType.FullPush || pushType == PushType.CreateNonExisting || pushType == PushType.UpdateOrCreateOnly) - success &= FullCRUD(objList_specificType as dynamic, pushType, tag, actionConfig); - else if (pushType == PushType.CreateOnly) - { - success &= CreateOnly(objList_specificType as dynamic, tag, actionConfig); - } - else if (pushType == PushType.UpdateOnly) + switch (group.Item2) { - success &= UpdateOnly(objList_specificType as dynamic, tag, actionConfig); + case PushType.FullPush: + case PushType.UpdateOrCreateOnly: + case PushType.CreateNonExisting: + success &= FullCRUD(objList_specificType as dynamic, group.Item2, tag, actionConfig); + break; + case PushType.CreateOnly: + success &= CreateOnly(objList_specificType as dynamic, tag, actionConfig); + break; + + case PushType.UpdateOnly: + success &= UpdateOnly(objList_specificType as dynamic, tag, actionConfig); + break; + + case PushType.DeleteThenCreate: + case PushType.AdapterDefault: + default: + Engine.Base.Compute.RecordError($"Push type {group.Item2} not implemented in {this.GetType().Name}."); + break; } + } + this.ClearCache(); return success ? objectsToPush.Cast().ToList() : new List(); } } @@ -116,3 +138,4 @@ public virtual List Push(IEnumerable objects, string tag = "", P + diff --git a/BHoM_Adapter/AdapterActions/Remove.cs b/BHoM_Adapter/AdapterActions/Remove.cs index 1cad6df9..b5eec1e9 100644 --- a/BHoM_Adapter/AdapterActions/Remove.cs +++ b/BHoM_Adapter/AdapterActions/Remove.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. @@ -60,6 +60,7 @@ public virtual bool SetupRemoveConfig(ActionConfig actionConfig, out ActionConfi "The base implementation just calls Delete. It can be overridden in Toolkits.")] public virtual int Remove(IRequest request, ActionConfig actionConfig = null) { + this.ClearCache(); return Delete(request as dynamic, actionConfig); } } @@ -67,3 +68,4 @@ public virtual int Remove(IRequest request, ActionConfig actionConfig = null) + diff --git a/BHoM_Adapter/AdapterActions/_PullMethods/ReadResults.cs b/BHoM_Adapter/AdapterActions/_PullMethods/ReadResults.cs index e2e6f89e..fc56f7ae 100644 --- a/BHoM_Adapter/AdapterActions/_PullMethods/ReadResults.cs +++ b/BHoM_Adapter/AdapterActions/_PullMethods/ReadResults.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. @@ -67,3 +67,4 @@ protected virtual IEnumerable ReadResults(IRequest request, ActionConfi + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/AssignId.cs b/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/AssignId.cs index 85060252..a5898ba8 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/AssignId.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/AssignId.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. @@ -56,3 +56,4 @@ protected virtual void AssignNextFreeId(IEnumerable objects) where T : IBH } + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/NextFreeId.cs b/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/NextFreeId.cs index 3f2c9506..8ec76172 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/NextFreeId.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/AdapterId/NextFreeId.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. @@ -45,3 +45,4 @@ protected virtual object NextFreeId(Type objectType, bool refresh = false) } + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/CreateOnly.cs b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/CreateOnly.cs index 3da1b3ae..fdfc7d89 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/CreateOnly.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/CreateOnly.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. @@ -47,47 +47,48 @@ protected virtual bool CreateOnly(IEnumerable objectsToPush, string tag = { bool callDistinct = objectLevel == 0 ? m_AdapterSettings.CreateOnly_DistinctObjects : m_AdapterSettings.CreateOnly_DistinctDependencies; - List newObjects = !callDistinct ? objectsToPush.ToList() : objectsToPush.Distinct(Engine.Adapter.Query.GetComparerForType(this)).ToList(); + List newObjects; + IEnumerable> distinctGroups = null; + if (!callDistinct) + newObjects = objectsToPush.ToList(); + else + { + distinctGroups = GroupAndCopyProperties(objectsToPush, actionConfig); + newObjects = distinctGroups.Select(x => x.Key).ToList(); + } // Tag the objects, if tag is given. if (tag != "") newObjects.ForEach(x => x.Tags.Add(tag)); - // We may treat dependencies differently: like calling distinct only for them. - if (m_AdapterSettings.HandleDependencies) - { - var dependencyTypes = Engine.Adapter.Query.GetDependencyTypes(this); - var dependencyObjects = Engine.Adapter.Query.GetDependencyObjects(objectsToPush, dependencyTypes, this); //first-level dependencies - - foreach (var kv in dependencyObjects) - { - if (!CreateOnly(kv.Value as dynamic, tag, actionConfig, objectLevel++)) - return false; - } - } - // Assign Id if required if (m_AdapterSettings.UseAdapterId) AssignNextFreeId(newObjects); // Create objects - if (!ICreate(newObjects, actionConfig)) + if (m_AdapterSettings.CacheCRUDobjects) + { + if (!CreateAndCache(newObjects, actionConfig)) + return false; + } + else if(!ICreate(newObjects, actionConfig)) return false; - if (callDistinct && m_AdapterSettings.UseAdapterId) + if (callDistinct && m_AdapterSettings.UseAdapterId && distinctGroups != null) { // Map Ids to the original set of objects (before we extracted the distincts elements from it). // If some objects of the original set were not Created (because e.g. they were already existing in the external model and had already an id, // therefore no new id was assigned to them) they will not get mapped, so the original set will be left with them intact. - IEqualityComparer comparer = Engine.Adapter.Query.GetComparerForType(this); - foreach (T item in objectsToPush) + foreach (var group in distinctGroups) { - // Fetch any existing IAdapterId fragment and assign it to the item. - // This preserves any additional property other than `Id` that may be in the fragment. - IFragment fragment; - newObjects.First(x => comparer.Equals(x, item)).Fragments.TryGetValue(AdapterIdFragmentType, out fragment); - - item.SetAdapterId(fragment as IAdapterId); + IFragment idFragment; + if (group.Key.Fragments.TryGetValue(AdapterIdFragmentType, out idFragment)) + { + foreach (T item in group.Skip(1)) //Skip 1 as first instance is the key + { + item.SetAdapterId(idFragment as IAdapterId); + } + } } } @@ -99,3 +100,4 @@ protected virtual bool CreateOnly(IEnumerable objectsToPush, string tag = + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/FullCRUD.cs b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/FullCRUD.cs index 4c466b0d..c9e18fb2 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/FullCRUD.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/FullCRUD.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. @@ -31,6 +31,7 @@ using BH.oM.Diffing; using BH.oM.Adapter; using BH.Engine.Base; +using BH.Engine.Base.Objects; namespace BH.Adapter { @@ -42,10 +43,14 @@ public abstract partial class BHoMAdapter // These methods dispatch calls to different CRUD methods as required by the Push. [Description("Performs the full CRUD, calling the single CRUD methods as appropriate.")] - protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = PushType.AdapterDefault, string tag = "", ActionConfig actionConfig = null) where T : class, IBHoMObject + protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = PushType.AdapterDefault, string tag = "", ActionConfig actionConfig = null) where T : IBHoMObject { - // Make sure objects are distinct - List newObjects = objectsToPush.Distinct(Engine.Adapter.Query.GetComparerForType(this)).ToList(); + if (objectsToPush == null || !objectsToPush.Any()) + return true; + + // Make sure objects are distinct and that any copy-proeprty module for the type is run + IEnumerable> distinctGroups = GroupAndCopyProperties(objectsToPush, actionConfig); + List newObjects = distinctGroups.Select(x => x.Key).ToList(); // Add the tag if provided if (!string.IsNullOrWhiteSpace(tag)) @@ -53,24 +58,18 @@ protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = Pus //Read all the objects of that type from the external model IEnumerable readObjects; - if (tag != "" || Engine.Adapter.Query.GetComparerForType(this) != EqualityComparer.Default) - readObjects = Read(typeof(T), "", actionConfig)?.Where(x => x != null && x is T).Cast(); + if (tag != "" || Engine.Adapter.Query.GetComparerForType(this, actionConfig) != EqualityComparer.Default) + { + if (m_AdapterSettings.CacheCRUDobjects) + readObjects = GetCachedOrRead(null, "", actionConfig)?.Where(x => x != null); + else + readObjects = Read(typeof(T), "", actionConfig)?.Where(x => x != null && x is T).Cast(); + } else readObjects = new List(); readObjects = readObjects ?? new List(); // null guard for readObjects - // Merge and push the dependencies - if (m_AdapterSettings.HandleDependencies) - { - var dependencyTypes = Engine.Adapter.Query.GetDependencyTypes(this); - var dependencyObjects = Engine.Adapter.Query.GetDependencyObjects(objectsToPush, dependencyTypes, this); - - foreach (var depObj in dependencyObjects) - if (!FullCRUD(depObj.Value as dynamic, pushType, tag, actionConfig)) - return false; - } - // Replace objects that overlap and define the objects that still have to be pushed IEnumerable objectsToCreate; @@ -79,12 +78,17 @@ protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = Pus // All objects read from the model are to be deleted. // Note that this means that only objects of the same type of the objects being pushed will be deleted. if (readObjects.Any()) - IDelete(typeof(T), readObjects.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + { + if (m_AdapterSettings.CacheCRUDobjects) + DeleteIncludingCache(readObjects.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + else + IDelete(typeof(T), readObjects.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + } objectsToCreate = newObjects; } else if (m_AdapterSettings.ProcessInMemory) - objectsToCreate = ReplaceInMemory(newObjects, readObjects, tag); + objectsToCreate = ReplaceInMemory(newObjects, readObjects, tag, actionConfig); else objectsToCreate = ReplaceThroughAPI(newObjects, readObjects, tag, actionConfig, pushType); @@ -92,24 +96,34 @@ protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = Pus if (m_AdapterSettings.UseAdapterId) AssignNextFreeId(objectsToCreate); - // Create objects - if (!ICreate(objectsToCreate, actionConfig)) - return false; + // Create objects if there are any to be created. + if (objectsToCreate.Any()) + { + if (m_AdapterSettings.CacheCRUDobjects) + { + if (!CreateAndCache(objectsToCreate, actionConfig)) + return false; + } + else if (!ICreate(objectsToCreate, actionConfig)) + return false; + } + // Needs to be done even if there are no objectsToCreate as this could include updated objects if (m_AdapterSettings.UseAdapterId) { // Map Ids to the original set of objects (before we extracted the distincts elements from it). // If some objects of the original set were not Created (because e.g. they were already existing in the external model and had already an id, // therefore no new id was assigned to them) they will not get mapped, so the original set will be left with them intact. - IEqualityComparer comparer = Engine.Adapter.Query.GetComparerForType(this); - foreach (T item in objectsToPush) + foreach (var group in distinctGroups) { - // Fetch any existing IAdapterId fragment and assign it to the item. - // This preserves any additional property other than `Id` that may be in the fragment. - IFragment fragment; - newObjects.First(x => comparer.Equals(x, item)).Fragments.TryGetValue(AdapterIdFragmentType, out fragment); - - item.SetAdapterId(fragment as IAdapterId); + IFragment idFragment; + if (group.Key.Fragments.TryGetValue(AdapterIdFragmentType, out idFragment)) + { + foreach (T item in group.Skip(1)) //Skip 1 as first instance is the key + { + item.SetAdapterId(idFragment as IAdapterId); + } + } } } @@ -118,7 +132,7 @@ protected bool FullCRUD(IEnumerable objectsToPush, PushType pushType = Pus /***************************************************/ - protected IEnumerable ReplaceInMemory(IEnumerable newObjects, IEnumerable existingOjects, string tag, bool mergeWithComparer = false) where T : class, IBHoMObject + protected IEnumerable ReplaceInMemory(IEnumerable newObjects, IEnumerable existingOjects, string tag, ActionConfig actionConfig, bool mergeWithComparer = false) where T : IBHoMObject { // Separate objects based on tags List multiTaggedObjects = existingOjects.Where(x => x.Tags.Contains(tag) && x.Tags.Count > 1).ToList(); @@ -132,7 +146,7 @@ protected IEnumerable ReplaceInMemory(IEnumerable newObjects, IEnumerab { VennDiagram diagram = Engine.Data.Create.VennDiagram( newObjects, multiTaggedObjects.Concat(nonTaggedObjects), - Engine.Adapter.Query.GetComparerForType(this)); + Engine.Adapter.Query.GetComparerForType(this, actionConfig)); List> copyPropertiesModules = this.GetCopyPropertiesModules(); @@ -152,9 +166,9 @@ protected IEnumerable ReplaceInMemory(IEnumerable newObjects, IEnumerab /***************************************************/ - protected IEnumerable ReplaceThroughAPI(IEnumerable objsToPush, IEnumerable readObjs, string tag, ActionConfig actionConfig, PushType pushType) where T : class, IBHoMObject + protected IEnumerable ReplaceThroughAPI(IEnumerable objsToPush, IEnumerable readObjs, string tag, ActionConfig actionConfig, PushType pushType) where T : IBHoMObject { - IEqualityComparer comparer = Engine.Adapter.Query.GetComparerForType(this); + IEqualityComparer comparer = Engine.Adapter.Query.GetComparerForType(this, actionConfig); VennDiagram diagram = Engine.Data.Create.VennDiagram(objsToPush, readObjs, comparer); // Objects to push that do not have any overlap with the read ones @@ -175,13 +189,29 @@ protected IEnumerable ReplaceThroughAPI(IEnumerable objsToPush, IEnumer // Extract the adapterIds from the toBeDeleted and call Delete() for all of them. if (pushType != PushType.UpdateOrCreateOnly && toBeDeleted != null && toBeDeleted.Any()) - IDelete(typeof(T), toBeDeleted.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + { + if (m_AdapterSettings.CacheCRUDobjects) + DeleteIncludingCache(toBeDeleted.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + else + IDelete(typeof(T), toBeDeleted.Select(obj => obj.AdapterIds(AdapterIdFragmentType)), actionConfig); + } // Update the tags for the rest of the existing objects in the model - IUpdateTags(typeof(T), - readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.AdapterIds(AdapterIdFragmentType)), - readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.Tags), - actionConfig); + if (m_AdapterSettings.CacheCRUDobjects) + { + UpdateTagsIncludingCache( + readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.AdapterIds(AdapterIdFragmentType)), + readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.Tags), + actionConfig); + } + else + { + IUpdateTags( + typeof(T), + readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.AdapterIds(AdapterIdFragmentType)), + readObjs_exclusive.Where(x => x.Tags.Count > 0).Select(x => x.Tags), + actionConfig); + } // For the objects that have an overlap between existing and pushed // (e.g. an end Node of a Bar being pushed is overlapping with the End Node of a Bar already in the model) @@ -201,26 +231,40 @@ protected IEnumerable ReplaceThroughAPI(IEnumerable objsToPush, IEnumer { // Update the overlapping objects (between read and toPush), with the now ported properties. if (diagram.Intersection != null && diagram.Intersection.Any()) - IUpdate(diagram.Intersection.Select(x => x.Item1), actionConfig); + { + List objectsToUpdate; + if (this.m_AdapterSettings.OnlyUpdateChangedObjects) //If true, make use of the IdentityComparers to scan for objects not fully identical, and filter out objects that are + { + IEqualityComparer fullyEqualComparer = new HashComparer(actionConfig?.DiffingConfig?.ComparisonConfig ?? new ComparisonConfig()); + objectsToUpdate = diagram.Intersection.Where(x => !fullyEqualComparer.Equals(x.Item1, x.Item2)).Select(x => x.Item1).ToList(); //Filter out objects not identical according to the HashComparer + } + else + objectsToUpdate = diagram.Intersection.Select(x => x.Item1).ToList(); + + if (objectsToUpdate.Any()) + { + if (this.m_AdapterSettings.CacheCRUDobjects) + UpdateIncludingCache(objectsToUpdate, actionConfig); + else + IUpdate(objectsToUpdate, actionConfig); + } + } } else if(pushType == PushType.CreateNonExisting) { //For CreateNonExisting, the overlap objects are just kept, and not updated. To make sure tag functionality works though, //The obejcts need to get their tags (if any) updated. - IUpdateTags(typeof(T), - diagram.Intersection.Where(x => x.Item1.Tags.Count > 0).Select(x => x.Item1.AdapterIds(AdapterIdFragmentType)), + UpdateTagsIncludingCache(diagram.Intersection.Where(x => x.Item1.Tags.Count > 0).Select(x => x.Item1.AdapterIds(AdapterIdFragmentType)), diagram.Intersection.Where(x => x.Item1.Tags.Count > 0).Select(x => x.Item1.Tags), actionConfig); } - // Return the objectsToPush that do not have any overlap with the existing ones; those will need to be created return objsToPush_exclusive; } - - } } + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/UpdateOnly.cs b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/UpdateOnly.cs index 15171ccf..5215bbc3 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/UpdateOnly.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/CRUDDispatchers/UpdateOnly.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. @@ -44,26 +44,14 @@ protected virtual bool UpdateOnly(IEnumerable objectsToPush, string tag = { List newObjects = objectsToPush.ToList(); - // Make sure objects are tagged + // Make sure objects are tagged if (tag != "") newObjects.ForEach(x => x.Tags.Add(tag)); - - // Merge and push the dependencies - if (m_AdapterSettings.HandleDependencies) - { - var dependencyTypes = Engine.Adapter.Query.GetDependencyTypes(this); - var dependencyObjects = Engine.Adapter.Query.GetDependencyObjects(objectsToPush, dependencyTypes, this); //first-level dependencies - - foreach (var depObj in dependencyObjects) - if (!FullCRUD(depObj.Value as dynamic, PushType.FullPush, tag, actionConfig)) - return false; - } + + if (m_AdapterSettings.CacheCRUDobjects) + return UpdateIncludingCache(newObjects, actionConfig); return IUpdate(newObjects, actionConfig); } - - } } - - diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/ProcessObjectsForPush.cs b/BHoM_Adapter/AdapterActions/_PushMethods/ProcessObjectsForPush.cs index 16b5aedb..146d9855 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/ProcessObjectsForPush.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/ProcessObjectsForPush.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. @@ -28,6 +28,8 @@ using System.ComponentModel; using System.Linq; using BH.oM.Adapter; +using IContainer = BH.oM.Base.IContainer; +using BH.oM.Adapter.Module; namespace BH.Adapter { @@ -51,36 +53,61 @@ protected virtual IEnumerable ProcessObjectsForPush(IEnumerable objectsIncludingUnpacked = new List(); + + foreach (var obj in objects) + { + if (obj is IContainer container) + { + objectsIncludingUnpacked.AddRange(container.Unpack()); + } + else + objectsIncludingUnpacked.Add(obj); + } + // -------------------------------- // // CHECKS // // -------------------------------- // // Verify that the input objects are IBHoMObjects. - if (objects.OfType().Count() != objects.Count() & !wrapNonBHoMObjects) + if (objectsIncludingUnpacked.OfType().Count() != objectsIncludingUnpacked.Count() & !wrapNonBHoMObjects) { BH.Engine.Base.Compute.RecordWarning("Only non-null BHoMObjects are supported by the default Push. " + // = you can override if needed; "\nConsider specifying actionConfig['WrapNonBHoMObjects'] to true."); } - // -------------------------------- // - // OBJECT PREPARATION // - // -------------------------------- // - IEnumerable objectsToPush = new List(); // Wrap non-BHoM objects into a Custom BHoMObject to make them compatible with the CRUD. if (wrapNonBHoMObjects) - objectsToPush = WrapNonBHoMObjects(objects); + objectsToPush = WrapNonBHoMObjects(objectsIncludingUnpacked); else - objectsToPush = objects.OfType(); + objectsToPush = objectsIncludingUnpacked.OfType(); + + + // ----------------------------------------- // + // OBJECT PREPARATION - Cloning // + // ----------------------------------------- // // Clone the objects for immutability in the UI. CloneBeforePush should always be true, except for very specific cases. if (m_AdapterSettings.CloneBeforePush) objectsToPush = objectsToPush.Select(x => x.DeepClone()).ToList(); + //Run through any Preprocessing modules on the Adapter + foreach (IPushPreProcessModule preprocessModule in AdapterModules.OfType()) + { + objectsToPush = preprocessModule.PreprocessObjects(objectsToPush); + } + return objectsToPush; } } } + diff --git a/BHoM_Adapter/AdapterActions/_PushMethods/Support/UpdateTag.cs b/BHoM_Adapter/AdapterActions/_PushMethods/Support/UpdateTag.cs index 813471ad..a96bc862 100644 --- a/BHoM_Adapter/AdapterActions/_PushMethods/Support/UpdateTag.cs +++ b/BHoM_Adapter/AdapterActions/_PushMethods/Support/UpdateTag.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. @@ -44,3 +44,4 @@ protected virtual int UpdateTag(Type type, IEnumerable ids, object newTa + diff --git a/BHoM_Adapter/BHoMAdapter.cs b/BHoM_Adapter/BHoMAdapter.cs index 7854a3d3..7184c3fd 100644 --- a/BHoM_Adapter/BHoMAdapter.cs +++ b/BHoM_Adapter/BHoMAdapter.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. @@ -87,3 +87,4 @@ protected virtual void OnDataUpdated() + diff --git a/BHoM_Adapter/BHoM_Adapter.csproj b/BHoM_Adapter/BHoM_Adapter.csproj index 4b9d05a0..2d607c67 100644 --- a/BHoM_Adapter/BHoM_Adapter.csproj +++ b/BHoM_Adapter/BHoM_Adapter.csproj @@ -3,20 +3,25 @@ netstandard2.0 6.0.0.0 - https://github.com/BHoM/BHoM_Adapter + https://github.com/BHoM/BHoM_Adapter 5.0.0 BHoM Copyright © https://github.com/BHoM BH.Adapter - 6.0.0.0 + 6.1.0.0 + Debug;Release;Test ..\Build\ + + ..\Build\ + + - + @@ -69,6 +74,11 @@ false false + + C:\ProgramData\BHoM\Assemblies\Reflection_Engine.dll + false + false + diff --git a/BHoM_Adapter/CRUD/Cache.cs b/BHoM_Adapter/CRUD/Cache.cs new file mode 100644 index 00000000..e5e87977 --- /dev/null +++ b/BHoM_Adapter/CRUD/Cache.cs @@ -0,0 +1,372 @@ +/* + * 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 System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using BH.oM.Data.Requests; +using BH.oM.Adapter; +using BH.Engine.Adapter; +using System.Xml.Linq; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.ComponentModel; + +namespace BH.Adapter +{ + public abstract partial class BHoMAdapter + { + /***************************************************/ + /**** Basic methods ****/ + /***************************************************/ + + protected bool CreateAndCache(IEnumerable objects, ActionConfig actionConfig = null) where T : IBHoMObject + { + if (!ICreate(objects, actionConfig)) + return false; + + Type t = typeof(T); + Dictionary typeCache; + if (!m_cache.TryGetValue(t, out typeCache)) + typeCache = new Dictionary(); + + if (m_AdapterSettings.UseAdapterId) + { + foreach (IBHoMObject bhObj in objects) + { + typeCache[this.GetAdapterId(bhObj)] = bhObj; + } + } + else + { + // For adapters not using Ids, simply cache with integers counting up from 0. + + int n = typeCache.Count; + foreach (IBHoMObject bhObj in objects) + { + typeCache[n] = bhObj; + n++; + } + } + + m_cache[t] = typeCache; + + return true; + } + + /***************************************************/ + + [Description("Method for getting out a single object from the cache or the read from the model.")] + protected TObj GetCachedOrRead(TId id, ActionConfig actionConfig = null) where TObj : IBHoMObject + { + if (!m_AdapterSettings.UseAdapterId) + { + Engine.Base.Compute.RecordWarning("Unable to get single item from cache for adapters not using AdapterIds."); + return default(TObj); + } + + if (id == null) + return default(TObj); + + Type t = typeof(TObj); + Dictionary typeCache; + if (!m_cache.TryGetValue(t, out typeCache)) + typeCache = new Dictionary(); + + IBHoMObject cacheObj = null; + if (!typeCache.TryGetValue(id, out cacheObj)) + { + IEnumerable read = IRead(t, new List() { id }, actionConfig); + + foreach (IBHoMObject item in read) + { + object itemId = GetAdapterId(item); + if (itemId.Equals(id)) + cacheObj = item; + typeCache[itemId] = item; + } + m_cache[t] = typeCache; + } + + if (cacheObj is TObj) + return (TObj)cacheObj; + else + return default(TObj); + } + + /***************************************************/ + + [Description("Method for getting out a list of objects from the cache or the read from the model.")] + protected List GetCachedOrRead(IList ids = null, string tag = "", ActionConfig actionConfig = null) where T : IBHoMObject + { + // Call the method extracting from cache or reading from model as dictionary. + //Setting key type to object to allow for any type of key + List objects = GetCachedOrReadAsDictionary(ids, actionConfig).Values.ToList(); + + // Null guard + objects = objects ?? new List(); + + // Filter by tag if any + if (string.IsNullOrWhiteSpace(tag)) + return objects; + else + return objects.Where(x => x.Tags.Contains(tag)).ToList(); + } + + /***************************************************/ + + [Description("Method for getting out a Dictionary of id and object from the cache or the read from the model.")] + protected Dictionary GetCachedOrReadAsDictionary(IList ids = null, ActionConfig actionConfig = null) + { + + Type t = typeof(TObj); + if (ids == null || ids.Count == 0) + { + if (m_FullyCachedTypes.Contains(typeof(TObj))) + { + Dictionary typeCache; + if (m_cache.TryGetValue(t, out typeCache)) + return typeCache.ToDictionary(x => (TId)x.Key, x => (TObj)x.Value); + else + return new Dictionary(); + } + else + { + // Because we are in the case of no Ids specified, we want to be reading the entire model. + // There may be objects that may have been read, although objects of their type may not be all read yet. + // This means that, because there is no way of knowing which ids (objects) are missing to be read, + // we need to re-read the entire model and cache it fully, although some reads may be redundant. + IEnumerable readObjects = IRead(t, ids, actionConfig); + m_FullyCachedTypes.Add(t); + Dictionary typeCache; + if (m_AdapterSettings.UseAdapterId) + { + typeCache = readObjects.ToDictionary(x => this.GetAdapterId(x), x => x); + m_cache[t] = typeCache; + } + else + { + // For adapters not using Ids, simply cache with integers counting up from 0. + typeCache = new Dictionary(); + + int n = 0; + foreach (IBHoMObject bhObj in readObjects) + { + typeCache[n] = bhObj; + n++; + } + + m_cache[t] = typeCache; + } + + return typeCache.ToDictionary(x => (TId)x.Key, x => (TObj)x.Value); + } + } + else + { + //Filtered obejct collection to store the objects to be returned either from the model or from the cache + Dictionary filteredObjects = new Dictionary(); + Dictionary typeCache; + if (m_cache.TryGetValue(t, out typeCache)) + { + List idsNotInCache = new List(); + + //Loop through the ids and try to fetch from cache. + foreach (object id in ids) + { + if (typeCache.TryGetValue(id, out IBHoMObject obj)) + { + filteredObjects[id] = obj; + } + else + idsNotInCache.Add(id); + } + + if (idsNotInCache.Count > 0) + { + IEnumerable additionalObjects = IRead(t, idsNotInCache, actionConfig); + + if (m_AdapterSettings.UseAdapterId) + { + foreach (IBHoMObject bhObj in additionalObjects) + { + object id = this.GetAdapterId(bhObj); + typeCache[id] = bhObj; + filteredObjects[id] = bhObj; + } + } + else + { + int n = typeCache.Count; + foreach (IBHoMObject bhObj in additionalObjects) + { + typeCache[n] = bhObj; + filteredObjects[n] = bhObj; + n++; + } + } + m_cache[t] = typeCache; + } + + return filteredObjects.ToDictionary(x => (TId)x.Key, x => (TObj)x.Value); + } + else + { + typeCache = new Dictionary(); + IEnumerable readObjects = IRead(t, ids, actionConfig); + + if (m_AdapterSettings.UseAdapterId) + { + foreach (IBHoMObject bhObj in readObjects) + { + typeCache[this.GetAdapterId(bhObj)] = bhObj; + } + } + else + { + int n = typeCache.Count; + foreach (IBHoMObject bhObj in readObjects) + { + typeCache[n] = bhObj; + n++; + } + } + m_cache[t] = typeCache; + + return typeCache.ToDictionary(x => (TId)x.Key, x => (TObj)x.Value); + } + } + } + + /***************************************************/ + + protected virtual bool UpdateIncludingCache(IEnumerable objects, ActionConfig actionConfig = null) where T : IBHoMObject + { + if (!IUpdate(objects, actionConfig)) + return false; + + if (!m_AdapterSettings.UseAdapterId) + { + Engine.Base.Compute.RecordWarning("Unable to update cache for adapters not using AdapterIds."); + return true; + } + + Type t = typeof(T); + Dictionary typeCache; + if (!m_cache.TryGetValue(t, out typeCache)) + typeCache = new Dictionary(); + + foreach (IBHoMObject bhObj in objects) + { + typeCache[this.GetAdapterId(bhObj)] = bhObj; + } + + m_cache[t] = typeCache; + + return true; + } + + /***************************************************/ + + protected virtual int UpdateTagsIncludingCache(IEnumerable ids, IEnumerable> newTags, ActionConfig actionConfig = null) + { + Type t = typeof(T); + int updateCount = IUpdateTags(t, ids, newTags, actionConfig); + + if (updateCount == 0) + return 0; + + if (!m_AdapterSettings.UseAdapterId) + { + Engine.Base.Compute.RecordWarning("Unable to update cache for adapters not using AdapterIds."); + return updateCount; + } + + Dictionary typeCache; + if (!m_cache.TryGetValue(t, out typeCache)) + typeCache = new Dictionary(); + + List idsList = ids.ToList(); + List> tagsList = newTags.ToList(); + + if (idsList.Count != tagsList.Count) + return updateCount; //Not raising a warning here due to the assumption that the concrete adapter will already have done that + + for (int i = 0; i < idsList.Count; i++) + { + object id = idsList[i]; + IBHoMObject bhObj; + if (typeCache.TryGetValue(id, out bhObj)) + bhObj.Tags = tagsList[i]; + } + + m_cache[t] = typeCache; + + return updateCount; + } + + /***************************************************/ + + protected int DeleteIncludingCache(IEnumerable ids, ActionConfig actionConfig = null) where T : IBHoMObject + { + Type t = typeof(T); + int deleteCount = IDelete(t, ids, actionConfig); + if (deleteCount == 0) + return deleteCount; + + if (ids == null || !ids.Any()) + { + m_FullyCachedTypes.Remove(t); + m_cache.Remove(t); + } + else + { + Dictionary typeCache; + if (m_cache.TryGetValue(t, out typeCache)) + { + foreach (object id in ids) + { + typeCache.Remove(id); + } + } + m_cache[t] = typeCache; + } + + return deleteCount; + } + + /***************************************************/ + + protected void ClearCache() + { + m_cache = new Dictionary>(); + m_FullyCachedTypes = new HashSet(); + } + + /***************************************************/ + + private Dictionary> m_cache = new Dictionary>(); + private HashSet m_FullyCachedTypes = new HashSet(); + } +} \ No newline at end of file diff --git a/BHoM_Adapter/CRUD/ICreate.cs b/BHoM_Adapter/CRUD/ICreate.cs index 767937bf..5ead54a9 100644 --- a/BHoM_Adapter/CRUD/ICreate.cs +++ b/BHoM_Adapter/CRUD/ICreate.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. @@ -60,3 +60,4 @@ protected virtual bool ICreate(IEnumerable objects, ActionConfig actionCon } + diff --git a/BHoM_Adapter/CRUD/IDelete.cs b/BHoM_Adapter/CRUD/IDelete.cs index a2e3ff52..80ef9daa 100644 --- a/BHoM_Adapter/CRUD/IDelete.cs +++ b/BHoM_Adapter/CRUD/IDelete.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. @@ -103,3 +103,4 @@ protected virtual int Delete(Type type, string tag = "", ActionConfig actionConf + diff --git a/BHoM_Adapter/CRUD/IRead.cs b/BHoM_Adapter/CRUD/IRead.cs index ae8f8844..56f07bf3 100644 --- a/BHoM_Adapter/CRUD/IRead.cs +++ b/BHoM_Adapter/CRUD/IRead.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. @@ -113,3 +113,4 @@ protected virtual IEnumerable Read(Type type, string tag = "", Acti + diff --git a/BHoM_Adapter/CRUD/IUpdate.cs b/BHoM_Adapter/CRUD/IUpdate.cs index 66694815..ab42a046 100644 --- a/BHoM_Adapter/CRUD/IUpdate.cs +++ b/BHoM_Adapter/CRUD/IUpdate.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. @@ -67,3 +67,4 @@ protected virtual int IUpdateTags(Type type, IEnumerable ids, IEnumerabl + diff --git a/BHoM_Adapter/HelperMethods/AdapterId.cs b/BHoM_Adapter/HelperMethods/AdapterId.cs index 47a67cf3..214ffcfc 100644 --- a/BHoM_Adapter/HelperMethods/AdapterId.cs +++ b/BHoM_Adapter/HelperMethods/AdapterId.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. @@ -54,3 +54,4 @@ public T GetAdapterId(IBHoMObject bHoMObject) + diff --git a/BHoM_Adapter/HelperMethods/CopyBHoMObjectProperties.cs b/BHoM_Adapter/HelperMethods/CopyBHoMObjectProperties.cs index f600dcd0..a2f50132 100644 --- a/BHoM_Adapter/HelperMethods/CopyBHoMObjectProperties.cs +++ b/BHoM_Adapter/HelperMethods/CopyBHoMObjectProperties.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. @@ -36,7 +36,7 @@ namespace BH.Adapter public abstract partial class BHoMAdapter { [Description("Gets called during the Push. Takes properties specified from the source IBHoMObject and assigns them to the target IBHoMObject.")] - public void CopyBHoMObjectProperties(T target, T source) where T : class, IBHoMObject + public void CopyBHoMObjectProperties(T target, T source) where T : IBHoMObject { // Port tags from source to target foreach (string tag in source.Tags) @@ -55,3 +55,4 @@ public void CopyBHoMObjectProperties(T target, T source) where T : class, IBH + diff --git a/BHoM_Adapter/HelperMethods/DistinctWithCopiedProperties.cs b/BHoM_Adapter/HelperMethods/DistinctWithCopiedProperties.cs new file mode 100644 index 00000000..9e230b3b --- /dev/null +++ b/BHoM_Adapter/HelperMethods/DistinctWithCopiedProperties.cs @@ -0,0 +1,48 @@ +/* + * 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.Adapter; +using BH.Engine.Base; +using BH.oM.Adapter; +using BH.oM.Base; +using BH.oM.Data; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Adapter +{ + + public abstract partial class BHoMAdapter + { + [Description("Gets distinct objects based on implemented AdapterComparer of the particular type, with CopyPropertyModules available for the type run.")] + private List DistinctWithCopiedProperties(IEnumerable objectsToPush, ActionConfig actionConfig = null) where T : IBHoMObject + { + return GroupAndCopyProperties(objectsToPush, actionConfig).Select(x => x.Key).ToList(); + } + } +} + + + + diff --git a/BHoM_Adapter/HelperMethods/GroupAndCopyProperties.cs b/BHoM_Adapter/HelperMethods/GroupAndCopyProperties.cs new file mode 100644 index 00000000..2f07a91a --- /dev/null +++ b/BHoM_Adapter/HelperMethods/GroupAndCopyProperties.cs @@ -0,0 +1,64 @@ +/* + * 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.Adapter; +using BH.Engine.Base; +using BH.oM.Adapter; +using BH.oM.Base; +using BH.oM.Data; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Adapter +{ + public abstract partial class BHoMAdapter + { + [Description("Groups the objects by the coparer for the particular type, and then runs any CopyPropertiesModules available for the type.")] + private IEnumerable> GroupAndCopyProperties(IEnumerable objectsToPush, ActionConfig actionConfig = null) where T : IBHoMObject + { + IEnumerable> grouped = objectsToPush.GroupBy(x => x, Engine.Adapter.Query.GetComparerForType(this, actionConfig)); + + List> copyPropertiesModules = this.GetCopyPropertiesModules(); + + foreach (var group in grouped) + { + T keep = group.Key; + foreach (T item in group.Skip(1)) //Skip 1 as first instance is the key + { + CopyBHoMObjectProperties(keep, item); + foreach (var copyModule in copyPropertiesModules) + { + copyModule.CopyProperties(keep, item); + } + } + } + + return grouped; + } + } +} + + + + diff --git a/BHoM_Adapter/HelperMethods/WrapNonBHoMObjects.cs b/BHoM_Adapter/HelperMethods/WrapNonBHoMObjects.cs index c35ad9e0..6d48889a 100644 --- a/BHoM_Adapter/HelperMethods/WrapNonBHoMObjects.cs +++ b/BHoM_Adapter/HelperMethods/WrapNonBHoMObjects.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. @@ -49,3 +49,4 @@ public IEnumerable WrapNonBHoMObjects(IEnumerable objects) } + diff --git a/Structure_AdapterModules/CopyNodeProperties.cs b/Structure_AdapterModules/CopyNodeProperties.cs index f8fb36ce..28f6eb56 100644 --- a/Structure_AdapterModules/CopyNodeProperties.cs +++ b/Structure_AdapterModules/CopyNodeProperties.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. @@ -28,6 +28,8 @@ using System.ComponentModel; using BH.oM.Structure.Elements; using BH.oM.Structure.SectionProperties; +using BH.Engine.Structure; +using BH.Engine.Geometry; namespace BH.Adapter.Modules { @@ -38,15 +40,31 @@ public class CopyNodeProperties : ICopyPropertiesModule public void CopyProperties(Node target, Node source) { // If source is constrained and target is not, add source constraint to target - if (source.Support != null && target.Support == null) - target.Support = source.Support; + if (source.Support != null) + { + if (target.Support == null) + target.Support = source.Support; + else + { + string desc1 = target.Support.Description(); + string desc2 = source.Support.Description(); + if(desc1 != desc2) + Engine.Base.Compute.RecordNote($"Node in position ({target.Position.X},{target.Position.Y},{target.Position.Z}) contains conflicting supports. Support {desc1} will be used on the node."); + } + } // If source has a defined orientation and target does not, add local orientation from the source - if (source.Orientation != null && target.Orientation == null) - target.Orientation = source.Orientation; + if (source.Orientation != null) + { + if (target.Orientation == null) + target.Orientation = source.Orientation; + else if(!source.Orientation.IsEqual(target.Orientation)) + BH.Engine.Base.Compute.RecordNote($"Node in position ({target.Position.X}, {target.Position.Y}, {target.Position.Z}) contains conflicting orientaions. Orientation with Normal vector ({target.Orientation.Z.X}, {target.Orientation.Z.Y}, {target.Orientation.Z.Z}) will be used on the node."); + } } } } + diff --git a/Structure_AdapterModules/ErrorMessages/ErrorMessages.cs b/Structure_AdapterModules/ErrorMessages/ErrorMessages.cs index 83d3da09..0f434214 100644 --- a/Structure_AdapterModules/ErrorMessages/ErrorMessages.cs +++ b/Structure_AdapterModules/ErrorMessages/ErrorMessages.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. @@ -103,3 +103,4 @@ public static void LoadsWithoutObejctIdsAssignedError(IElementLoad load) w } + diff --git a/Structure_AdapterModules/GetCasesFromCombinations.cs b/Structure_AdapterModules/GetCasesFromCombinations.cs index 0e8f74eb..f19bd64d 100644 --- a/Structure_AdapterModules/GetCasesFromCombinations.cs +++ b/Structure_AdapterModules/GetCasesFromCombinations.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. @@ -42,3 +42,4 @@ public IEnumerable GetDependencies(IEnumerable object } } + diff --git a/Structure_AdapterModules/GetCombinationsFromCombinations.cs b/Structure_AdapterModules/GetCombinationsFromCombinations.cs index 231bfe57..b64885a6 100644 --- a/Structure_AdapterModules/GetCombinationsFromCombinations.cs +++ b/Structure_AdapterModules/GetCombinationsFromCombinations.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. @@ -42,3 +42,4 @@ public IEnumerable GetDependencies(IEnumerable } } + diff --git a/Structure_AdapterModules/GetGravityLoadElementsWithoutID.cs b/Structure_AdapterModules/GetGravityLoadElementsWithoutID.cs new file mode 100644 index 00000000..82790027 --- /dev/null +++ b/Structure_AdapterModules/GetGravityLoadElementsWithoutID.cs @@ -0,0 +1,83 @@ +/* + * 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.oM.Adapter; +using System; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.ComponentModel; +using BH.oM.Structure.Elements; +using System.Collections; +using BH.oM.Geometry; +using BH.oM.Structure.Constraints; +using BH.oM.Structure.Loads; + + +namespace BH.Adapter.Modules +{ + [Description("Get all elements of type T that do not contain an adapter ID of the expected type from the provided GravityLoads. Avoids the need to again read and check against elements already in the model.")] + public class GetGravityLoadElementsWithoutID : IGetDependencyModule where T : IBHoMObject + { + /***************************************************/ + /**** Interface method ****/ + /***************************************************/ + + public IEnumerable GetDependencies(IEnumerable objects) + { + + if (m_adapter?.AdapterIdFragmentType == null) + { + string adapterType = m_adapter == null ? "Adapter" : m_adapter.GetType().Name; + BH.Engine.Base.Compute.RecordWarning($"{adapterType} does not have a set {nameof(m_adapter.AdapterIdFragmentType)}. Unable to filter load objects by set ID. All objects on the load will be treated as a dependency."); + return objects.Select(x => x?.Objects?.Elements).Where(x => x != null).SelectMany(x => x).Where(x => x != null).OfType().ToList(); + } + else + { + List noIdLoadObjects = new List(); + foreach (GravityLoad load in objects) + { + if (load?.Objects?.Elements != null) + noIdLoadObjects.AddRange(load.Objects.Elements.OfType().Where(x => x != null && !x.Fragments.Contains(m_adapter.AdapterIdFragmentType))); + } + return noIdLoadObjects; + } + } + /***************************************************/ + /**** Constructors ****/ + /***************************************************/ + + public GetGravityLoadElementsWithoutID(IBHoMAdapter adapter) + { + m_adapter = adapter; + } + + /***************************************************/ + + private IBHoMAdapter m_adapter; + + /***************************************************/ + } +} + + diff --git a/Structure_AdapterModules/GetLoadElementsWithoutID.cs b/Structure_AdapterModules/GetLoadElementsWithoutID.cs new file mode 100644 index 00000000..5903c5cf --- /dev/null +++ b/Structure_AdapterModules/GetLoadElementsWithoutID.cs @@ -0,0 +1,84 @@ +/* + * 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.oM.Adapter; +using System; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.ComponentModel; +using BH.oM.Structure.Elements; +using System.Collections; +using BH.oM.Geometry; +using BH.oM.Structure.Constraints; +using BH.oM.Structure.Loads; + + +namespace BH.Adapter.Modules +{ + [Description("Get all elements that do not contain an adapter ID of the expected type from the provided IElementLoads. Avoids the need to again read and check against elements already in the model.")] + public class GetLoadElementsWithoutID : IGetDependencyModule, T> where T : IBHoMObject + { + /***************************************************/ + /**** Interface method ****/ + /***************************************************/ + + public IEnumerable GetDependencies(IEnumerable> objects) + { + if (m_adapter?.AdapterIdFragmentType == null) + { + string adapterType = m_adapter == null ? "Adapter" : m_adapter.GetType().Name; + BH.Engine.Base.Compute.RecordWarning($"{adapterType} does not have a set {nameof(m_adapter.AdapterIdFragmentType)}. Unable to filter load objects by set ID. All objects on the load will be treated as a dependency."); + return objects.Select(x => x?.Objects?.Elements).Where(x => x != null).SelectMany(x => x).Where(x => x != null).ToList(); + } + else + { + List noIdLoadObjects = new List(); + foreach (IElementLoad load in objects) + { + if (load?.Objects?.Elements != null) + noIdLoadObjects.AddRange(load.Objects.Elements.Where(x => x != null && !x.Fragments.Contains(m_adapter.AdapterIdFragmentType))); + } + return noIdLoadObjects; + } + + } + + /***************************************************/ + /**** Constructors ****/ + /***************************************************/ + + public GetLoadElementsWithoutID(IBHoMAdapter adapter) + { + m_adapter = adapter; + } + + /***************************************************/ + + private IBHoMAdapter m_adapter; + + /***************************************************/ + } +} + + diff --git a/Structure_AdapterModules/ModuleLoader.cs b/Structure_AdapterModules/ModuleLoader.cs index 943b38cd..f0a625f5 100644 --- a/Structure_AdapterModules/ModuleLoader.cs +++ b/Structure_AdapterModules/ModuleLoader.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. @@ -29,6 +29,7 @@ using BH.Adapter; using BH.oM.Adapter; using BH.oM.Base; +using BH.oM.Structure.Elements; namespace BH.Adapter.Modules.Structure { @@ -40,9 +41,17 @@ public static void LoadModules(this BHoMAdapter adapter) adapter.AdapterModules.Add(new CopyNodeProperties()); adapter.AdapterModules.Add(new GetCasesFromCombinations()); adapter.AdapterModules.Add(new GetCombinationsFromCombinations()); + adapter.AdapterModules.Add(new GetLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new GetLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new GetLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new GetGravityLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new GetGravityLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new GetGravityLoadElementsWithoutID(adapter)); + adapter.AdapterModules.Add(new ReplaceObjectsInLoadsModule()); } } } + diff --git a/Structure_AdapterModules/ReplaceObjectsInLoadsModule.cs b/Structure_AdapterModules/ReplaceObjectsInLoadsModule.cs new file mode 100644 index 00000000..f297d9fa --- /dev/null +++ b/Structure_AdapterModules/ReplaceObjectsInLoadsModule.cs @@ -0,0 +1,106 @@ +/* + * 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.Adapter.Module; +using System; +using System.Collections.Generic; +using System.Text; +using BH.oM.Structure.Loads; +using BH.oM.Base; +using System.Linq; +using System.Runtime.CompilerServices; +using System.ComponentModel; + +namespace BH.Adapter.Modules.Structure +{ + [Description("Module for replacing the objects in the loads with objects with the same BHoM_Guid being pushed at the same time.\n" + + "No action is taken if loads are pushed in isolation, without the elements pushed at the same time as individual instances.")] + public class ReplaceObjectsInLoadsModule : IPushPreProcessModule + { + public IEnumerable PreprocessObjects(IEnumerable objects) + { + List loads = new List(); + Dictionary> nonLoads = new Dictionary>(); + + //Split load obejcts from non-load objects + foreach (IBHoMObject obj in objects) + { + if (obj is ILoad load) + loads.Add(load); + else if (!nonLoads.ContainsKey(obj.BHoM_Guid)) + nonLoads[obj.BHoM_Guid] = new List { obj }; + else + nonLoads[obj.BHoM_Guid].Add(obj); + } + + //If no non-load obejcts are being pushed or the list does not contain any load, can simply return, as nothing can be replaced + if (nonLoads.Count == 0 || loads.Count == 0) + return objects; + + bool duplicatesFound = false; + foreach (ILoad load in loads) + { + //Loop through all loads, and try to update the objects + duplicatesFound |= ReplaceObjects(load as dynamic, nonLoads); + } + + if (duplicatesFound) + BH.Engine.Base.Compute.RecordWarning("Some objects pushed have duplicate BHoM_Guids. This means objects on loads not able to be updated."); + + //Returns the objects in order of first non-loads followed by loads + //This ensures that the objects are pushed before loads + //For many cases this will be handled by dependency types, but for cases where this is yet to be implemented, this solution helps fix the order + return nonLoads.Values.SelectMany(x => x).Concat(loads); + } + + + private bool ReplaceObjects(IElementLoad load, Dictionary> objects) where T : IBHoMObject + { + bool duplicatesFound = false; + //Run through all elements stored on the load + for (int i = 0; i < load.Objects.Elements.Count; i++) + { + //Try to find an item with the same guid in the non-load objects + if (objects.TryGetValue(load.Objects.Elements[i].BHoM_Guid, out List replacement)) + { + if (replacement.Count != 1) + { + duplicatesFound = true; + continue; + } + //Ensure the found object is of the correct type + if (replacement[0] is T tObject) + { + //replace with the other object + load.Objects.Elements[i] = tObject; + } + } + } + return duplicatesFound; + } + + private void ReplaceObjects(ILoad load, Dictionary objects) + { + //Do nothing for non-element loads + } + } +} diff --git a/Structure_AdapterModules/Structure_AdapterModules.csproj b/Structure_AdapterModules/Structure_AdapterModules.csproj index 844a42cb..18b427f6 100644 --- a/Structure_AdapterModules/Structure_AdapterModules.csproj +++ b/Structure_AdapterModules/Structure_AdapterModules.csproj @@ -3,12 +3,13 @@ netstandard2.0 6.0.0.0 - https://github.com/BHoM/BHoM_Adapter + https://github.com/BHoM/BHoM_Adapter 5.0.0 BHoM Copyright © https://github.com/BHoM BH.Adapter.Modules.Structure - 6.0.0.0 + 6.1.0.0 + Debug;Release;Test @@ -16,8 +17,13 @@ ..\Build\ + + Off + ..\Build\ + + - + @@ -72,11 +78,21 @@ false false + + C:\ProgramData\BHoM\Assemblies\Spatial_oM.dll + false + false + C:\ProgramData\BHoM\Assemblies\Structure_oM.dll false false + + C:\ProgramData\BHoM\Assemblies\Structure_Engine.dll + false + false +