diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs index 27159ce2f9..846841aea8 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadBasicConnectorBinding.cs @@ -88,7 +88,7 @@ public void HighlightModel(string modelCardId) if (model is ReceiverModelCard receiverModelCard) { - var dbObjects = doc.GetObjects((receiverModelCard.ReceiveResult?.BakedObjectIds).NotNull()); + var dbObjects = doc.GetObjects((receiverModelCard.ReceiveResult?.GetSuccessfulResultIds()).NotNull()); objectIds = dbObjects.Select(tuple => tuple.Root.Id).ToArray(); } diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs index 181e152051..e9b69c6976 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Bindings/AutocadReceiveBinding.cs @@ -8,12 +8,13 @@ using Speckle.Connectors.Utils.Operations; using Speckle.Core.Logging; using ICancelable = System.Reactive.Disposables.ICancelable; +using NotNullExtensions = Speckle.Converters.Common.NotNullExtensions; namespace Speckle.Connectors.Autocad.Bindings; public sealed class AutocadReceiveBinding : IReceiveBinding, ICancelable { - public string Name { get; } = "receiveBinding"; + public string Name => "receiveBinding"; public IBridge Parent { get; } private readonly DocumentModelStore _store; @@ -53,19 +54,19 @@ public async Task Receive(string modelCardId) } // Receive host objects - IEnumerable receivedObjectIds = await unitOfWork.Service + IReadOnlyList conversionResults = await unitOfWork.Service .Execute( - modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils - modelCard.ProjectId.NotNull(), - modelCard.ProjectName.NotNull(), - modelCard.ModelName.NotNull(), - modelCard.SelectedVersionId.NotNull(), + NotNullExtensions.NotNull(modelCard.AccountId), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + NotNullExtensions.NotNull(modelCard.ProjectId), + NotNullExtensions.NotNull(modelCard.ProjectName), + NotNullExtensions.NotNull(modelCard.ModelName), + NotNullExtensions.NotNull(modelCard.SelectedVersionId), cts.Token, onOperationProgressed: (status, progress) => OnSendOperationProgress(modelCardId, status, progress) ) .ConfigureAwait(false); - Commands.SetModelReceiveResult(modelCardId, receivedObjectIds.ToList()); + Commands.SetModelReceiveResult(modelCardId, conversionResults); } catch (Exception e) when (!e.IsFatal()) // All exceptions should be handled here if possible, otherwise we enter "crashing the host app" territory. { diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs index 1ddc70d912..d98c98b3db 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/HostApp/Extensions/EntityExtensions.cs @@ -10,7 +10,7 @@ public static class EntityExtensions /// Entity to add into database. /// Layer to append object. /// Throws when there is no top transaction in the document. - public static ObjectId Append(this Entity entity, string? layer = null) + public static ObjectId AppendToDb(this Entity entity, string? layer = null) { // POC: Will be addressed to move it into AutocadContext! var db = entity.Database ?? Application.DocumentManager.MdiActiveDocument.Database; diff --git a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs index 9b6b081c5e..563f7cdd57 100644 --- a/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs +++ b/DUI3-DX/Connectors/Autocad/Speckle.Connectors.AutocadShared/Operations/Receive/AutocadHostObjectBuilder.cs @@ -1,7 +1,7 @@ -using System.Diagnostics; using Autodesk.AutoCAD.DatabaseServices; using Speckle.Connectors.Autocad.HostApp; using Speckle.Connectors.Autocad.HostApp.Extensions; +using Speckle.Connectors.Utils; using Speckle.Core.Models; using Speckle.Connectors.Utils.Builders; using Speckle.Converters.Common; @@ -12,22 +12,22 @@ namespace Speckle.Connectors.Autocad.Operations.Receive; public class AutocadHostObjectBuilder : IHostObjectBuilder { - private readonly IRootToHostConverter _converter; private readonly AutocadLayerManager _autocadLayerManager; + private readonly IRootToHostConverter _converter; private readonly GraphTraversal _traversalFunction; public AutocadHostObjectBuilder( IRootToHostConverter converter, - AutocadLayerManager autocadLayerManager, - GraphTraversal traversalFunction + GraphTraversal traversalFunction, + AutocadLayerManager autocadLayerManager ) { _converter = converter; - _autocadLayerManager = autocadLayerManager; _traversalFunction = traversalFunction; + _autocadLayerManager = autocadLayerManager; } - public IEnumerable Build( + public IReadOnlyList Build( Base rootObject, string projectName, string modelName, @@ -40,64 +40,67 @@ CancellationToken cancellationToken // Layer filter for received commit with project and model name _autocadLayerManager.CreateLayerFilter(projectName, modelName); - var traversalGraph = _traversalFunction.Traverse(rootObject).ToArray(); + //TODO: make the layerManager handle \/ ? string baseLayerPrefix = $"SPK-{projectName}-{modelName}-"; - HashSet uniqueLayerNames = new(); - List handleValues = new(); - int count = 0; - // POC: Will be addressed to move it into AutocadContext! - using (TransactionContext.StartTransaction(Application.DocumentManager.MdiActiveDocument)) + List results = new(); + foreach (var tc in _traversalFunction.TraverseWithProgress(rootObject, onOperationProgressed, cancellationToken)) { - foreach (TraversalContext tc in traversalGraph) + try + { + var convertedObjects = ConvertObject(tc, baseLayerPrefix, uniqueLayerNames); + + results.AddRange( + convertedObjects.Select(e => new ReceiveConversionResult(tc.Current, e, e.Handle.Value.ToString())) + ); + } + catch (Exception ex) when (!ex.IsFatal()) { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - string layerFullName = GetLayerPath(tc, baseLayerPrefix); - - if (uniqueLayerNames.Add(layerFullName)) - { - _autocadLayerManager.CreateLayerOrPurge(layerFullName); - } - - //POC: this transaction used to be called in the converter, We've moved it here to unify converter implementation - //POC: Is this transaction 100% needed? we are already inside a transaction? - object converted; - using (var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction()) - { - converted = _converter.Convert(tc.Current); - tr.Commit(); - } - - List flattened = Utilities.FlattenToHostConversionResult(converted); - - foreach (Entity conversionResult in flattened.Cast()) - { - if (conversionResult == null) - { - // POC: This needed to be double checked why we check null and continue - continue; - } - - conversionResult.Append(layerFullName); - - handleValues.Add(conversionResult.Handle.Value.ToString()); - } - - onOperationProgressed?.Invoke("Converting", (double)++count / traversalGraph.Length); - } - catch (Exception e) when (!e.IsFatal()) // DO NOT CATCH SPECIFIC STUFF, conversion errors should be recoverable - { - // POC: report, etc. - Debug.WriteLine("conversion error happened."); - } + results.Add(new(tc.Current, ex)); } } - return handleValues; + + return results; + } + + private IEnumerable ConvertObject(TraversalContext tc, string baseLayerPrefix, ISet uniqueLayerNames) + { + using TransactionContext transactionContext = TransactionContext.StartTransaction( + Application.DocumentManager.MdiActiveDocument + ); + + string layerFullName = GetLayerPath(tc, baseLayerPrefix); + + if (uniqueLayerNames.Add(layerFullName)) + { + _autocadLayerManager.CreateLayerOrPurge(layerFullName); + } + + //POC: this transaction used to be called in the converter, We've moved it here to unify converter implementation + //POC: Is this transaction 100% needed? we are already inside a transaction? + object converted; + using (var tr = Application.DocumentManager.CurrentDocument.Database.TransactionManager.StartTransaction()) + { + converted = _converter.Convert(tc.Current); + tr.Commit(); + } + + IEnumerable flattened = Utilities.FlattenToHostConversionResult(converted).Cast(); + + foreach (Entity? conversionResult in flattened) + { + if (conversionResult == null) + { + // POC: This needed to be double checked why we check null and continue + continue; + } + + conversionResult.AppendToDb(layerFullName); + + yield return conversionResult; + } } private string GetLayerPath(TraversalContext context, string baseLayerPrefix) diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs index d28a3a22dc..fd8eb4d15f 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoBasicConnectorBinding.cs @@ -64,7 +64,7 @@ public void HighlightModel(string modelCardId) if (myModel is ReceiverModelCard receiver && receiver.ReceiveResult != null) { - objectIds = receiver.ReceiveResult.BakedObjectIds.NotNull(); + objectIds = receiver.ReceiveResult.GetSuccessfulResultIds(); } if (objectIds.Count == 0) diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs index 62071b8b54..8ce12addd8 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Bindings/RhinoReceiveBinding.cs @@ -7,6 +7,7 @@ using Speckle.Connectors.Utils.Cancellation; using Speckle.Connectors.Utils.Operations; using Speckle.Core.Logging; +using NotNullExtensions = Speckle.Connectors.Utils.NotNullExtensions; namespace Speckle.Connectors.Rhino7.Bindings; @@ -51,20 +52,19 @@ public async Task Receive(string modelCardId) } // Receive host objects - IEnumerable receivedObjectIds = await unitOfWork.Service + IReadOnlyList conversionResults = await unitOfWork.Service .Execute( - modelCard.AccountId.NotNull(), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils - modelCard.ProjectId.NotNull(), - modelCard.ProjectName.NotNull(), - modelCard.ModelName.NotNull(), - modelCard.SelectedVersionId.NotNull(), + NotNullExtensions.NotNull(modelCard.AccountId), // POC: I hear -you are saying why we're passing them separately. Not sure pass the DUI3-> Connectors.DUI project dependency to the SDK-> Connector.Utils + NotNullExtensions.NotNull(modelCard.ProjectId), + NotNullExtensions.NotNull(modelCard.ProjectName), + NotNullExtensions.NotNull(modelCard.ModelName), + NotNullExtensions.NotNull(modelCard.SelectedVersionId), cts.Token, (status, progress) => OnSendOperationProgress(modelCardId, status, progress) ) .ConfigureAwait(false); - // POC: Here we can't set receive result if ReceiveOperation throws an error. - Commands.SetModelReceiveResult(modelCardId, receivedObjectIds.ToList()); + Commands.SetModelReceiveResult(modelCardId, conversionResults); } catch (Exception e) when (!e.IsFatal()) // All exceptions should be handled here if possible, otherwise we enter "crashing the host app" territory. { diff --git a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs index f0a2f5822e..9ff0b554ea 100644 --- a/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs +++ b/DUI3-DX/Connectors/Rhino/Speckle.Connectors.Rhino7/Operations/Receive/RhinoHostObjectBuilder.cs @@ -1,6 +1,7 @@ using Rhino; using Rhino.DocObjects; using Rhino.Geometry; +using Speckle.Connectors.Utils; using Speckle.Connectors.Utils.Builders; using Speckle.Converters.Common; using Speckle.Core.Logging; @@ -26,7 +27,7 @@ GraphTraversal traverseFunction _traverseFunction = traverseFunction; } - public IEnumerable Build( + public IReadOnlyList Build( Base rootObject, string projectName, string modelName, @@ -38,24 +39,20 @@ CancellationToken cancellationToken var baseLayerName = $"Project {projectName}: Model {modelName}"; var objectsToConvert = _traverseFunction - .Traverse(rootObject) - .Where(obj => obj.Current is not Collection) - .Select(ctx => (GetLayerPath(ctx), ctx.Current)) - .ToArray(); + .TraverseWithProgress(rootObject, onOperationProgressed, cancellationToken) + .Where(obj => obj.Current is not Collection); - var convertedIds = BakeObjects(objectsToConvert, baseLayerName, onOperationProgressed, cancellationToken); + var conversionResults = BakeObjects(objectsToConvert, baseLayerName); _contextStack.Current.Document.Views.Redraw(); - return convertedIds; + return conversionResults; } // POC: Potentially refactor out into an IObjectBaker. - private List BakeObjects( - IReadOnlyCollection<(string[], Base)> objects, - string baseLayerName, - Action? onOperationProgressed, - CancellationToken cancellationToken + private IReadOnlyList BakeObjects( + IEnumerable objectsGraph, + string baseLayerName ) { RhinoDoc doc = _contextStack.Current.Document; @@ -85,48 +82,36 @@ CancellationToken cancellationToken rootLayerIndex = doc.Layers.Add(new Layer { Name = baseLayerName }); cache.Add(baseLayerName, rootLayerIndex); - var newObjectIds = new List(); - var count = 0; - - // POC: We delay throwing conversion exceptions until the end of the conversion loop, then throw all within an aggregate exception if something happened. - var conversionExceptions = new List(); - using var noDraw = new DisableRedrawScope(doc.Views); - foreach ((string[] path, Base baseObj) in objects) + var conversionResults = new List(); + + foreach (TraversalContext tc in objectsGraph) { try { - cancellationToken.ThrowIfCancellationRequested(); + var path = GetLayerPath(tc); var fullLayerName = string.Join(Layer.PathSeparator, path); var layerIndex = cache.TryGetValue(fullLayerName, out int value) ? value : GetAndCreateLayerFromPath(path, baseLayerName, cache); - onOperationProgressed?.Invoke("Converting & creating objects", (double)++count / objects.Count); - - var result = _converter.Convert(baseObj); + var result = _converter.Convert(tc.Current); - var conversionIds = HandleConversionResult(result, baseObj, layerIndex); - newObjectIds.AddRange(conversionIds); - } - catch (OperationCanceledException) - { - throw; + var conversionIds = HandleConversionResult(result, tc.Current, layerIndex); + foreach (var r in conversionIds) + { + conversionResults.Add(new(tc.Current, result, r)); + } } - catch (Exception e) when (!e.IsFatal()) + catch (Exception ex) when (!ex.IsFatal()) { - conversionExceptions.Add(e); + conversionResults.Add(new(tc.Current, ex)); } } - if (conversionExceptions.Count != 0) - { - throw new AggregateException("Conversion failed for some objects.", conversionExceptions); - } - - return newObjectIds; + return conversionResults; } private IReadOnlyList HandleConversionResult(object conversionResult, Base originalObject, int layerIndex) diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs index c64cdac40f..c960da2a78 100644 --- a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Bindings/ReceiveBindingUICommands.cs @@ -1,5 +1,6 @@ using Speckle.Connectors.DUI.Bridge; using Speckle.Connectors.DUI.Models.Card; +using Speckle.Connectors.Utils; namespace Speckle.Connectors.DUI.Bindings; @@ -11,14 +12,10 @@ public class ReceiveBindingUICommands : BasicConnectorBindingCommands public ReceiveBindingUICommands(IBridge bridge) : base(bridge) { } - public void SetModelReceiveResult(string modelCardId, List bakedObjectIds) + public void SetModelReceiveResult(string modelCardId, IReadOnlyList conversionResults) { ReceiverModelCardResult res = - new() - { - ModelCardId = modelCardId, - ReceiveResult = new ReceiveResult() { BakedObjectIds = bakedObjectIds } - }; + new() { ModelCardId = modelCardId, ReceiveResult = new ReceiveResult(conversionResults, true) }; Bridge.Send(SET_MODEL_RECEIVE_RESULT_UI_COMMAND_NAME, res); } } diff --git a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs index 81431dafb2..332445b90d 100644 --- a/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs +++ b/DUI3-DX/DUI3/Speckle.Connectors.DUI/Models/Card/ReceiveResult.cs @@ -1,7 +1,11 @@ +using Speckle.Connectors.Utils; + namespace Speckle.Connectors.DUI.Models.Card; -public class ReceiveResult +public record ReceiveResult(IReadOnlyList Results, bool Display) { - public List? BakedObjectIds { get; set; } - public bool Display { get; set; } + public List GetSuccessfulResultIds() + { + return Results.Where(x => x.IsSuccessful).Select(x => x.ResultId!).ToList(); + } } diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs index aedb5887a6..c402219f7a 100644 --- a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/IHostObjectBuilder.cs @@ -16,7 +16,7 @@ public interface IHostObjectBuilder /// List of application ids. // POC: Where we will return these ids will matter later when we target to also cache received application ids. /// Project and model name are needed for now to construct host app objects into related layers or filters. /// POC: we might consider later to have HostObjectBuilderContext? that might hold all possible data we will need. - IEnumerable Build( + IReadOnlyList Build( Base rootObject, string projectName, string modelName, diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs new file mode 100644 index 0000000000..56df5701f7 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Builders/TraversalExtensions.cs @@ -0,0 +1,26 @@ +using Speckle.Core.Models; +using Speckle.Core.Models.GraphTraversal; + +namespace Speckle.Connectors.Utils.Builders; + +public static class TraversalExtensions +{ + public static IEnumerable TraverseWithProgress( + this GraphTraversal traversalFunction, + Base rootObject, + Action? onOperationProgressed, + CancellationToken cancellationToken = default + ) + { + var traversalGraph = traversalFunction.Traverse(rootObject).ToArray(); + int count = 0; + foreach (var tc in traversalGraph) + { + cancellationToken.ThrowIfCancellationRequested(); + + yield return tc; + + onOperationProgressed?.Invoke("Converting", (double)++count / traversalGraph.Length); + } + } +} diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs index dcf56c1a53..81a0b9ef8b 100644 --- a/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/Operations/ReceiveOperation.cs @@ -17,7 +17,7 @@ public ReceiveOperation(IHostObjectBuilder hostObjectBuilder, ISyncToThread sync _syncToThread = syncToThread; } - public async Task> Execute( + public async Task> Execute( string accountId, // POC: all these string arguments exists in ModelCard but not sure to pass this dependency here, TBD! string projectId, string projectName, diff --git a/DUI3-DX/Sdk/Speckle.Connectors.Utils/ReceiveConversionResult.cs b/DUI3-DX/Sdk/Speckle.Connectors.Utils/ReceiveConversionResult.cs new file mode 100644 index 0000000000..e5f80701a9 --- /dev/null +++ b/DUI3-DX/Sdk/Speckle.Connectors.Utils/ReceiveConversionResult.cs @@ -0,0 +1,42 @@ +using Speckle.Core.Models; +using Speckle.Newtonsoft.Json; + +namespace Speckle.Connectors.Utils; + +public sealed class ReceiveConversionResult +{ + public string? ResultId { get; } + + [JsonIgnore] + public object? Result { get; } + public Exception? Error { get; } + + [JsonIgnore] + public Base Target { get; } + + public string TargetId => Target.id; + public string? TargetAppId => Target.applicationId; + + //[MemberNotNullWhen(true, nameof(Result))] + //[MemberNotNullWhen(true, nameof(ResultId))] + //[MemberNotNullWhen(false, nameof(Error))] + public bool IsSuccessful => Result is not null; + + public ReceiveConversionResult(Base target, object result, string resultId) + { + Target = target; + Result = result; + ResultId = resultId; + } + + public ReceiveConversionResult(Base target, Exception error) + { + Target = target; + Error = error; + } + + public override string ToString() => + IsSuccessful + ? $"Successfully converted {Target} to {Result}" + : $"Failed to convert {Target}: {Error!.GetType()}: {Error!.Message}"; +} diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs index d20f915ba9..9d972cdd99 100644 --- a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithFallback.cs @@ -14,12 +14,10 @@ namespace Speckle.Converters.Common.DependencyInjection.ToHost; /// public sealed class ConverterWithFallback : IRootToHostConverter { - private readonly IConverterResolver _toHost; private readonly ConverterWithoutFallback _baseConverter; public ConverterWithFallback(IConverterResolver toHost) { - _toHost = toHost; _baseConverter = new ConverterWithoutFallback(toHost); } @@ -31,7 +29,7 @@ public ConverterWithFallback(IConverterResolver toHost /// Fallbacks to display value if a direct conversion is not possible. /// /// The conversion is done in the following order of preference: - /// 1. Direct conversion using the method. + /// 1. Direct conversion using the . /// 2. Fallback to display value using the method, if a direct conversion is not possible. /// /// If the direct conversion is not available and there is no displayValue, a is thrown. @@ -39,12 +37,12 @@ public ConverterWithFallback(IConverterResolver toHost /// Thrown when no conversion is found for . public object Convert(Base target) { - var typeName = target.GetType().Name; + Type type = target.GetType(); // Direct conversion if a converter is found - if (_baseConverter.TryConvert(target, out object? result)) + if (_baseConverter.TryGetConverter(type, out IToHostTopLevelConverter? result)) { - return result; + return result.Convert(target); } // Fallback to display value if it exists. @@ -54,24 +52,13 @@ public object Convert(Base target) return FallbackToDisplayValue(displayValue); } - // Throw instead of null-return! - throw new NotSupportedException($"No conversion found for {typeName}"); + throw new NotSupportedException($"No conversion found for {type}"); } private object FallbackToDisplayValue(IReadOnlyList displayValue) { - // Create a temp Displayable object that handles the displayValue. var tempDisplayableObject = new DisplayableObject(displayValue); - var displayableObjectConverter = _toHost.GetConversionForType(typeof(DisplayableObject)); - - // It is not guaranteed that a fallback converter has been registered in all connectors - if (displayableObjectConverter == null) - { - throw new InvalidOperationException("No converter for fallback displayable objects was found."); - } - - // Run the conversion, which will (or could?) return an `IEnumerable`. We don't care at this point, connector will. - return displayableObjectConverter.Convert(tempDisplayableObject); + return _baseConverter.Convert(tempDisplayableObject); } } diff --git a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs index c6b24fff40..c2ec1bd9e3 100644 --- a/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs +++ b/DUI3-DX/Sdk/Speckle.Converters.Common.DependencyInjection/ToHost/ConverterWithoutFallback.cs @@ -21,20 +21,22 @@ public ConverterWithoutFallback(IConverterResolver con public object Convert(Base target) { - if (TryConvert(target, out object? result)) + if (!TryGetConverter(target.GetType(), out IToHostTopLevelConverter? converter)) { - return result; + throw new NotSupportedException($"No conversion found for {target.GetType()}"); } - throw new NotSupportedException($"No conversion found for {target.GetType()}"); + + object result = converter.Convert(target); + return result; } - internal bool TryConvert(Base target, [NotNullWhen(true)] out object? result) + internal bool TryGetConverter(Type target, [NotNullWhen(true)] out IToHostTopLevelConverter? result) { // Direct conversion if a converter is found - var objectConverter = _toHost.GetConversionForType(target.GetType()); + var objectConverter = _toHost.GetConversionForType(target); if (objectConverter != null) { - result = objectConverter.Convert(target); + result = objectConverter; return true; }