Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade SnapshotSource to support async resolvers #2632

Closed
wants to merge 12 commits into from
Closed
15 changes: 9 additions & 6 deletions src/Benchmarks/EnumUtilityBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Hl7.Fhir.Utility;
using System;

#nullable enable

namespace Firely.Sdk.Benchmarks
{
[MemoryDiagnoser]
Expand All @@ -16,7 +18,7 @@ public string EnumToString()
=> SearchParamType.String.ToString();

[Benchmark]
public string EnumGetName()
public string? EnumGetName()
=> Enum.GetName(StringSearchParam);

[Benchmark]
Expand All @@ -37,26 +39,27 @@ public SearchParamType EnumParseIgnoreCase()

[Benchmark]
public SearchParamType EnumUtilityParseLiteral()
=> EnumUtility.ParseLiteral<SearchParamType>("string").Value;
=> EnumUtility.ParseLiteral<SearchParamType>("string")!.Value;

[Benchmark]
public Enum EnumUtilityParseLiteralNonGeneric()
public Enum? EnumUtilityParseLiteralNonGeneric()
=> EnumUtility.ParseLiteral("string", typeof(SearchParamType));

[Benchmark]
public SearchParamType EnumUtilityParseLiteralIgnoreCase()
=> EnumUtility.ParseLiteral<SearchParamType>("string", true).Value;
=> EnumUtility.ParseLiteral<SearchParamType>("string", true)!.Value;

[Benchmark]
public Enum EnumUtilityParseLiteralIgnoreCaseNonGeneric()
public Enum? EnumUtilityParseLiteralIgnoreCaseNonGeneric()
=> EnumUtility.ParseLiteral("string", typeof(SearchParamType), true);

[Benchmark]
public string? EnumUtilityGetSystem()
=> EnumUtility.GetSystem(StringSearchParam);

[Benchmark]
public string EnumUtilityGetSystemNonGeneric()
public string? EnumUtilityGetSystemNonGeneric()
=> EnumUtility.GetSystem(StringSearchParamEnum);
}
}
#nullable restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2023, Firely ([email protected]) and contributors
* See the file CONTRIBUTORS for details.
*
* This file is licensed under the BSD 3-Clause license
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

#nullable enable

using Hl7.Fhir.Specification;
using System.Collections.Generic;
using System.Linq;

namespace Hl7.Fhir.ElementModel
{
/// <summary>
/// An adapter from <see cref="IScopedNode"/> to <see cref="ITypedElement"/>.
/// </summary>
/// <remarks>Be careful, this adapter does not implement the <see cref="ITypedElement.Location"/> and
/// <see cref="ITypedElement.Definition"/> property.
/// </remarks>
internal class ScopedNodeToTypedElementAdapter : ITypedElement
{
private readonly IScopedNode _adaptee;

public ScopedNodeToTypedElementAdapter(IScopedNode adaptee)
{
_adaptee = adaptee;
}

public string Location => throw new System.NotImplementedException();

public IElementDefinitionSummary Definition => throw new System.NotImplementedException();

public string Name => _adaptee.Name;

public string InstanceType => _adaptee.InstanceType;

public object Value => _adaptee.Value;

public IEnumerable<ITypedElement> Children(string? name = null) =>
_adaptee.Children(name).Select(n => new ScopedNodeToTypedElementAdapter(n));
}
}

#nullable restore
9 changes: 9 additions & 0 deletions src/Hl7.Fhir.Base/ElementModel/ElementNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,14 @@ public static IReadOnlyCollection<IElementDefinitionSummary> ChildDefinitions(th

public static ScopedNode ToScopedNode(this ITypedElement node) =>
node as ScopedNode ?? new ScopedNode(node);

/// <summary>
/// Convert a <see cref="ITypedElement"/> to a <see cref="IScopedNode"/>.
/// </summary>
/// <param name="node">An <see cref="ITypedElement"/></param>
/// <returns></returns>
public static IScopedNode AsScopedNode(this ITypedElement node) => ToScopedNode(node);
}
}

#nullable restore
67 changes: 67 additions & 0 deletions src/Hl7.Fhir.Base/ElementModel/IBaseElementNavigator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2023, Firely ([email protected]) and contributors
* See the file CONTRIBUTORS for details.
*
* This file is licensed under the BSD 3-Clause license
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

using System.Collections.Generic;

#nullable enable

namespace Hl7.Fhir.ElementModel
{
/// <summary>
/// The base interface for <see cref="ITypedElement"/> and <see cref="IScopedNode"/>."/>
/// </summary>
/// <typeparam name="TDerived"></typeparam>
public interface IBaseElementNavigator<TDerived> where TDerived : IBaseElementNavigator<TDerived>
{
/// <summary>
/// Enumerate the child nodes present in the source representation (if any)
/// </summary>
/// <param name="name">Return only the children with the given name.</param>
/// <returns></returns>
IEnumerable<TDerived> Children(string? name = null);

/// <summary>
/// Name of the node, e.g. "active", "value".
/// </summary>
string Name { get; }

/// <summary>
/// Type of the node. If a FHIR type, this is just a simple string, otherwise a StructureDefinition url for a type defined as a logical model.
/// </summary>
string InstanceType { get; }

/// <summary>
/// The value of the node (if it represents a primitive FHIR value)
/// </summary>
/// <remarks>
/// FHIR primitives are mapped to underlying C# types as follows:
///
/// instant Hl7.Fhir.ElementModel.Types.DateTime
/// time Hl7.Fhir.ElementModel.Types.Time
/// date Hl7.Fhir.ElementModel.Types.Date
/// dateTime Hl7.Fhir.ElementModel.Types.DateTime
/// decimal decimal
/// boolean bool
/// integer int
/// unsignedInt int
/// positiveInt int
/// long/integer64 long (name will be finalized in R5)
/// string string
/// code string
/// id string
/// uri, oid, uuid,
/// canonical, url string
/// markdown string
/// base64Binary string (uuencoded)
/// xhtml string
/// </remarks>
object Value { get; }
}
}

#nullable restore
29 changes: 29 additions & 0 deletions src/Hl7.Fhir.Base/ElementModel/IScopedNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2023, Firely ([email protected]) and contributors
* See the file CONTRIBUTORS for details.
*
* This file is licensed under the BSD 3-Clause license
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

#nullable enable

namespace Hl7.Fhir.ElementModel
{
/// <summary>
/// An element within a tree of typed FHIR data with also a parent element.
/// </summary>
/// <remarks>
/// This interface represents FHIR data as a tree of elements, including type information either present in
/// the instance or derived from fully aware of the FHIR definitions and types
/// </remarks>
public interface IScopedNode : IBaseElementNavigator<IScopedNode>
{
/// <summary>
/// The parent node of this node, or null if this is the root node.
/// </summary>
IScopedNode? Parent { get; }
}
}

#nullable restore
42 changes: 42 additions & 0 deletions src/Hl7.Fhir.Base/ElementModel/IScopedNodeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023, Firely ([email protected]) and contributors
* See the file CONTRIBUTORS for details.
*
* This file is licensed under the BSD 3-Clause license
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

#nullable enable


using System.Collections.Generic;
using System.Linq;

namespace Hl7.Fhir.ElementModel
{
public static class IScopedNodeExtensions
{
/// <summary>
/// Converts a <see cref="IScopedNode"/> to a <see cref="ITypedElement"/>.
/// </summary>
/// <param name="node">An <see cref="IScopedNode"/> node</param>
/// <returns>An implementation of <see cref="ITypedElement"/></returns>
/// <remarks>Be careful when using this method, the returned <see cref="ITypedElement"/> does not implement
/// the methods <see cref="ITypedElement.Location"/> and <see cref="ITypedElement.Definition"/>.
/// </remarks>
public static ITypedElement AsTypedElement(this IScopedNode node) =>
node is ITypedElement ite ? ite : new ScopedNodeToTypedElementAdapter(node);

/// <summary>
/// Returns the parent resource of this node, or null if this node is not part of a resource.
/// </summary>
/// <param name="nodes"></param>
/// <param name="name"></param>
/// <returns></returns>
public static IEnumerable<IScopedNode> Children(this IEnumerable<IScopedNode> nodes, string? name = null) =>
nodes.SelectMany(n => n.Children(name));

}
}

#nullable restore
45 changes: 1 addition & 44 deletions src/Hl7.Fhir.Base/ElementModel/ITypedElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

using Hl7.Fhir.Specification;
using System.Collections.Generic;

namespace Hl7.Fhir.ElementModel
{
Expand All @@ -19,51 +18,9 @@ namespace Hl7.Fhir.ElementModel
/// the instance or derived from fully aware of the FHIR definitions and types
/// </remarks>

public interface ITypedElement
public interface ITypedElement : IBaseElementNavigator<ITypedElement>
{
/// <summary>
/// Enumerate the child nodes present in the source representation (if any)
/// </summary>
/// <param name="name">Return only the children with the given name.</param>
/// <returns></returns>
IEnumerable<ITypedElement> Children(string name = null);

/// <summary>
/// Name of the node, e.g. "active", "value".
/// </summary>
string Name { get; }

/// <summary>
/// Type of the node. If a FHIR type, this is just a simple string, otherwise a StructureDefinition url for a type defined as a logical model.
/// </summary>
string InstanceType { get; }

/// <summary>
/// The value of the node (if it represents a primitive FHIR value)
/// </summary>
/// <remarks>
/// FHIR primitives are mapped to underlying C# types as follows:
///
/// instant Hl7.Fhir.ElementModel.Types.DateTime
/// time Hl7.Fhir.ElementModel.Types.Time
/// date Hl7.Fhir.ElementModel.Types.Date
/// dateTime Hl7.Fhir.ElementModel.Types.DateTime
/// decimal decimal
/// boolean bool
/// integer int
/// unsignedInt int
/// positiveInt int
/// long/integer64 long (name will be finalized in R5)
/// string string
/// code string
/// id string
/// uri, oid, uuid,
/// canonical, url string
/// markdown string
/// base64Binary string (uuencoded)
/// xhtml string
/// </remarks>
object Value { get; }

/// <summary>
/// An indication of the location of this node within the data represented by the <c>ITypedElement</c>.
Expand Down
19 changes: 15 additions & 4 deletions src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace Hl7.Fhir.ElementModel
{
public class ScopedNode : ITypedElement, IAnnotated, IExceptionSource
public class ScopedNode : ITypedElement, IScopedNode, IAnnotated, IExceptionSource
{
private class Cache
{
Expand Down Expand Up @@ -45,6 +45,7 @@ public ScopedNode(ITypedElement wrapped, string? instanceUri = null)
private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElement wrapped, string? fullUrl)
{
Current = wrapped;
Parent = parentNode;
ExceptionHandler = parentNode.ExceptionHandler;
ParentResource = parentNode.AtResource ? parentNode : parentResource;

Expand Down Expand Up @@ -217,8 +218,7 @@ public string? InstanceUri
{
// Scan up until the first parent that knowns the instance uri (at the last the
// root, if it has been supplied).
if (_cache.InstanceUri is null)
_cache.InstanceUri = ParentResources().SkipWhile(p => p.InstanceUri is null).FirstOrDefault()?.InstanceUri;
_cache.InstanceUri ??= ParentResources().SkipWhile(p => p.InstanceUri is null).FirstOrDefault()?.InstanceUri;

return _cache.InstanceUri;
}
Expand All @@ -229,11 +229,22 @@ private set
}
}

/// <inheritdoc />
public IScopedNode? Parent { get; private set; }

/// <inheritdoc />
public IEnumerable<object> Annotations(Type type) => type == typeof(ScopedNode) ? (new[] { this }) : Current.Annotations(type);

private IEnumerable<ScopedNode> childrenInternal(string? name = null) =>
Current.Children(name).Select(c => new ScopedNode(this, ParentResource, c, _fullUrl));

/// <inheritdoc />
public IEnumerable<ITypedElement> Children(string? name = null) =>
Current.Children(name).Select(c => new ScopedNode(this, ParentResource, c, _fullUrl));
childrenInternal(name);

IEnumerable<IScopedNode> IBaseElementNavigator<IScopedNode>.Children(string? name) =>
childrenInternal(name);
}
}

#nullable restore
2 changes: 1 addition & 1 deletion src/Hl7.Fhir.Base/ElementModel/ScopedNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public static string MakeAbsolute(this ScopedNode node, string reference) =>
string? url = element switch
{
{ Value: string s } => s,
{ InstanceType: FhirTypeConstants.REFERENCE } => element.ParseResourceReference()?.Reference,
{ InstanceType: FhirTypeConstants.REFERENCE } => element.ParseResourceReference<ITypedElement>()?.Reference,
_ => null
};

Expand Down
Loading
Loading