Skip to content

Commit

Permalink
Merge pull request #2741 from FirelyTeam/feature/try-nullability
Browse files Browse the repository at this point in the history
Nullability and Try-style function annotations
  • Loading branch information
ewoutkramer authored Apr 4, 2024
2 parents 2ad3c09 + d4cbbeb commit 0258d45
Show file tree
Hide file tree
Showing 65 changed files with 614 additions and 333 deletions.
18 changes: 10 additions & 8 deletions src/Hl7.Fhir.Base/ElementModel/DomNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
* available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
*/

#nullable enable

using Hl7.Fhir.Utility;
using System;
using System.Collections.Generic;
Expand All @@ -16,28 +18,28 @@ namespace Hl7.Fhir.ElementModel
{
public class DomNode<T> : IAnnotatable where T : DomNode<T>
{
public string Name { get; set; }
public string Name { get; set; } = null!;

private List<T> _childList = null;
private List<T>? _childList;

protected List<T> ChildList
{
get => LazyInitializer.EnsureInitialized(ref _childList, () => new());
get => LazyInitializer.EnsureInitialized(ref _childList, () => [])!;
set => _childList = value;
}

internal IEnumerable<T> ChildrenInternal(string name = null) =>
internal IEnumerable<T> ChildrenInternal(string? name = null) =>
name == null ? ChildList : ChildList.Where(c => c.Name.MatchesPrefix(name));

public T Parent { get; protected set; }
public T? Parent { get; protected set; }

public DomNodeList<T> this[string name] => new DomNodeList<T>(ChildrenInternal(name));
public DomNodeList<T> this[string name] => new (ChildrenInternal(name));

public T this[int index] => ChildList[index];

#region << Annotations >>
private AnnotationList _annotations = null;
protected AnnotationList AnnotationsInternal => LazyInitializer.EnsureInitialized(ref _annotations, () => new());
private AnnotationList? _annotations;
protected AnnotationList AnnotationsInternal => LazyInitializer.EnsureInitialized(ref _annotations, () => [])!;

protected bool HasAnnotations => _annotations is not null && !_annotations.IsEmpty;

Expand Down
48 changes: 25 additions & 23 deletions src/Hl7.Fhir.Base/ElementModel/ElementNode.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@

/*
/*
* Copyright (c) 2018, Firely (info@fire.ly) 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 Hl7.Fhir.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using P = Hl7.Fhir.ElementModel.Types;

namespace Hl7.Fhir.ElementModel
{
public class ElementNode : DomNode<ElementNode>, ITypedElement, IAnnotated, IAnnotatable, IShortPathGenerator
public class ElementNode : DomNode<ElementNode>, ITypedElement, IAnnotated, IShortPathGenerator
{
/// <summary>
/// Creates an implementation of ITypedElement that represents a primitive value
Expand All @@ -43,12 +45,12 @@ public static ITypedElement ForPrimitive(object value)
/// <param name="value"></param>
/// <param name="primitiveValue"></param>
/// <returns></returns>
public static bool TryConvertToElementValue(object value, out object primitiveValue)
public static bool TryConvertToElementValue(object? value, [NotNullWhen(true)] out object? primitiveValue)
{
primitiveValue = conv();
return primitiveValue is not null;

object conv()
object? conv()
{
// NOTE: Keep Any.TryConvertToSystemValue, TypeSpecifier.TryGetNativeType and TypeSpecifier.ForNativeType in sync
switch (value)
Expand Down Expand Up @@ -94,8 +96,8 @@ public static IEnumerable<ITypedElement> CreateList(params object[] values) =>
values switch
{
null => EmptyList,
[var one] => [toTT(one)],
_ => values.Select(toTT).ToList()
[var one] => [toTe(one)!],
_ => values.Select(toTe).ToList()!
};

/// <summary>
Expand All @@ -107,10 +109,10 @@ public static IEnumerable<ITypedElement> CreateList(params object[] values) =>
public static IEnumerable<ITypedElement> CreateList(IEnumerable<object> values) => values switch
{
null => EmptyList,
_ => values.Select(toTT).ToList()
_ => values.Select(toTe).ToList()!
};

private static ITypedElement toTT(object value) => value switch
private static ITypedElement? toTe(object? value) => value switch
{
null => null,
ITypedElement element => element,
Expand All @@ -119,26 +121,26 @@ public static IEnumerable<ITypedElement> CreateList(params object[] values) =>


public static readonly IEnumerable<ITypedElement> EmptyList = [];
public IEnumerable<ITypedElement> Children(string name = null) => ChildrenInternal(name);
public IEnumerable<ITypedElement> Children(string? name = null) => ChildrenInternal(name);

internal ElementNode(string name, object value, string instanceType, IElementDefinitionSummary definition)
private ElementNode(string name, object? value, string? instanceType, IElementDefinitionSummary? definition)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
InstanceType = instanceType;
Value = value;
Definition = definition;
}

private IReadOnlyCollection<IElementDefinitionSummary> _childDefinitions = null;
private IReadOnlyCollection<IElementDefinitionSummary> _childDefinitions = null!;

private IReadOnlyCollection<IElementDefinitionSummary> getChildDefinitions(IStructureDefinitionSummaryProvider provider)
{
LazyInitializer.EnsureInitialized(ref _childDefinitions, () => this.ChildDefinitions(provider));

return _childDefinitions!;
return _childDefinitions;
}

public ElementNode Add(IStructureDefinitionSummaryProvider provider, ElementNode child, string name = null)
public ElementNode Add(IStructureDefinitionSummaryProvider provider, ElementNode child, string? name = null)
{
if (provider == null) throw new ArgumentNullException(nameof(provider));
if (child == null) throw new ArgumentNullException(nameof(child));
Expand All @@ -147,7 +149,7 @@ public ElementNode Add(IStructureDefinitionSummaryProvider provider, ElementNode
return child;
}

public ElementNode Add(IStructureDefinitionSummaryProvider provider, string name, object value = null, string instanceType = null)
public ElementNode Add(IStructureDefinitionSummaryProvider provider, string name, object? value = null, string? instanceType = null)
{
if (provider == null) throw new ArgumentNullException(nameof(provider));
if (name == null) throw new ArgumentNullException(nameof(name));
Expand Down Expand Up @@ -182,7 +184,7 @@ public void Replace(IStructureDefinitionSummaryProvider provider, ElementNode ol
/// <summary>
/// Will update the child to reflect it being a child of this element, but will not yet add the child at any position within this element
/// </summary>
private void importChild(IStructureDefinitionSummaryProvider provider, ElementNode child, string name, int? position = null)
private void importChild(IStructureDefinitionSummaryProvider provider, ElementNode child, string? name, int? position = null)
{
child.Name = name ?? child.Name;
if (child.Name == null) throw Error.Argument($"The ElementNode given should have its Name property set or the '{nameof(name)}' parameter should be given.");
Expand All @@ -195,7 +197,7 @@ private void importChild(IStructureDefinitionSummaryProvider provider, ElementNo
// we think it should be - this way you can safely first create a node representing
// an independently created root for a resource of datatype, and then add it to the tree.
var childDefs = getChildDefinitions(provider ?? throw Error.ArgumentNull(nameof(provider)));
var childDef = childDefs.Where(cd => cd.ElementName == child.Name).SingleOrDefault();
var childDef = childDefs.SingleOrDefault(cd => cd.ElementName == child.Name);

child.Definition = childDef ?? child.Definition; // if we don't know about the definition, stick with the old one (if any)

Expand Down Expand Up @@ -230,7 +232,7 @@ private void importChild(IStructureDefinitionSummaryProvider provider, ElementNo

}

public static ElementNode Root(IStructureDefinitionSummaryProvider provider, string type, string name = null, object value = null)
public static ElementNode Root(IStructureDefinitionSummaryProvider provider, string type, string? name = null, object? value = null)
{
if (provider == null) throw Error.ArgumentNull(nameof(provider));
if (type == null) throw Error.ArgumentNull(nameof(type));
Expand All @@ -241,13 +243,13 @@ public static ElementNode Root(IStructureDefinitionSummaryProvider provider, str
return new ElementNode(name ?? type, value, type, definition);
}

public static ElementNode FromElement(ITypedElement node, bool recursive = true, IEnumerable<Type> annotationsToCopy = null)
public static ElementNode FromElement(ITypedElement node, bool recursive = true, IEnumerable<Type>? annotationsToCopy = null)
{
if (node == null) throw new ArgumentNullException(nameof(node));
return buildNode(node, recursive, annotationsToCopy, null);
}

private static ElementNode buildNode(ITypedElement node, bool recursive, IEnumerable<Type> annotationsToCopy, ElementNode parent)
private static ElementNode buildNode(ITypedElement node, bool recursive, IEnumerable<Type>? annotationsToCopy, ElementNode? parent)
{
var me = new ElementNode(node.Name, node.Value, node.InstanceType, node.Definition)
{
Expand Down Expand Up @@ -288,11 +290,11 @@ public ElementNode ShallowCopy()
return copy;
}

public IElementDefinitionSummary Definition { get; private set; }
public IElementDefinitionSummary? Definition { get; private set; }

public string InstanceType { get; private set; }
public string? InstanceType { get; private set; }

public object Value { get; set; }
public object? Value { get; set; }

public IEnumerable<object> Annotations(Type type)
{
Expand Down
6 changes: 3 additions & 3 deletions src/Hl7.Fhir.Base/ElementModel/IBaseElementNavigator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface IBaseElementNavigator<TDerived> where TDerived : IBaseElementNa
/// </summary>
/// <param name="name">Return only the children with the given name.</param>
/// <returns></returns>
IEnumerable<TDerived> Children(string? name = null);
IEnumerable<ITypedElement> Children(string? name = null);

/// <summary>
/// Name of the node, e.g. "active", "value".
Expand All @@ -36,7 +36,7 @@ public interface IBaseElementNavigator<TDerived> where TDerived : IBaseElementNa
/// <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; }
string? InstanceType { get; }

/// <summary>
/// The value of the node (if it represents a primitive FHIR value)
Expand All @@ -63,7 +63,7 @@ public interface IBaseElementNavigator<TDerived> where TDerived : IBaseElementNa
/// base64Binary string (uuencoded)
/// xhtml string
/// </remarks>
object Value { get; }
object? Value { get; }
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/Hl7.Fhir.Base/ElementModel/ITypedElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

using Hl7.Fhir.Specification;

#nullable enable


namespace Hl7.Fhir.ElementModel
{
/// <summary>
Expand All @@ -28,10 +31,10 @@ public interface ITypedElement : IBaseElementNavigator<ITypedElement>
/// An indication of the location of this node within the data represented by the <c>ITypedElement</c>.
/// </summary>
/// <remarks>The format of the location is the dotted name of the property, including indices to make
/// sure repeated occurences of an element can be distinguished. It needs to be sufficiently precise to aid
/// sure repeated occurrences of an element can be distinguished. It needs to be sufficiently precise to aid
/// the user in locating issues in the data.</remarks>
string Location { get; }

IElementDefinitionSummary Definition { get; }
IElementDefinitionSummary? Definition { get; }
}
}
4 changes: 1 addition & 3 deletions src/Hl7.Fhir.Base/ElementModel/PrimitiveElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public PrimitiveElement(object value, string? name = null, bool useFullTypeName
InstanceType = useFullTypeName ? systemType.FullName : systemType.Name;
Name = name ?? "@primitivevalue@";
}





public string Name { get; private set; }

Expand Down
6 changes: 3 additions & 3 deletions src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElem
public string Name => Current.Name;

/// <inheritdoc/>
public string InstanceType => Current.InstanceType;
public string InstanceType => Current.InstanceType!;

/// <inheritdoc/>
public object Value => Current.Value;
public object? Value => Current.Value;

/// <inheritdoc/>
public string Location => Current.Location;
Expand Down Expand Up @@ -123,7 +123,7 @@ public ITypedElement ResourceContext
}

/// <inheritdoc />
public IElementDefinitionSummary Definition => Current.Definition;
public IElementDefinitionSummary? Definition => Current.Definition;

/// <summary>
/// Get the list of container parents in a list, nearest parent first.
Expand Down
8 changes: 4 additions & 4 deletions src/Hl7.Fhir.Base/ElementModel/TypedElementOnSourceNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public TypedElementOnSourceNode(ISourceNode source, string? type, IStructureDefi
(InstanceType, Definition) = buildRootPosition(type);
}

private (string instanceType, IElementDefinitionSummary? definition) buildRootPosition(string? type)
private (string? instanceType, IElementDefinitionSummary? definition) buildRootPosition(string? type)
{
var rootType = type ?? _source.GetResourceTypeIndicator();
if (rootType == null)
Expand All @@ -48,7 +48,7 @@ public TypedElementOnSourceNode(ISourceNode source, string? type, IStructureDefi
throw Error.Format(nameof(type), $"Cannot determine the type of the root element at '{_source.Location}', " +
$"please supply a type argument.");
else
return ("Base", null);
return (null, null);
}

var elementType = Provider.Provide(rootType);
Expand Down Expand Up @@ -97,7 +97,7 @@ private void raiseTypeError(string message, object source, bool warning = false,
ExceptionHandler.NotifyOrThrow(source, notification);
}

public string InstanceType { get; private set; }
public string? InstanceType { get; private set; }

private readonly ISourceNode _source;

Expand Down Expand Up @@ -489,7 +489,7 @@ private IEnumerable<ITypedElement> runAdditionalRules(IEnumerable<ITypedElement>
public string ShortPath { get; private set; }

public override string ToString() =>
$"{(($"[{InstanceType}] "))}{_source}";
$"{(InstanceType != null ? ($"[{InstanceType}] ") : "")}{_source}";

public IEnumerable<object> Annotations(Type type)
{
Expand Down
Loading

0 comments on commit 0258d45

Please sign in to comment.