Skip to content

Commit

Permalink
Merge pull request #2752 from FirelyTeam/feature/element-tryparse-nul…
Browse files Browse the repository at this point in the history
…lability

element types tryparse nullability
  • Loading branch information
ewoutkramer authored Apr 9, 2024
2 parents 0258d45 + 69cd67e commit eb05df7
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 61 deletions.
2 changes: 1 addition & 1 deletion 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<ITypedElement> Children(string? name = null);
IEnumerable<TDerived> Children(string? name = null);

/// <summary>
/// Name of the node, e.g. "active", "value".
Expand Down
4 changes: 2 additions & 2 deletions src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ 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;
Expand All @@ -97,7 +97,7 @@ private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElem
/// <summary>
/// The instance type of the resource this element is part of.
/// </summary>
public string NearestResourceType => ParentResource == null ? Location : ParentResource.InstanceType;
public string? NearestResourceType => ParentResource == null ? Location : ParentResource.InstanceType;

/// <summary>
/// The %resource context, as defined by FHIRPath
Expand Down
10 changes: 5 additions & 5 deletions src/Hl7.Fhir.Base/ElementModel/Types/Any.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static object Parse(string value, Type primitiveType)
{
if (value is null) throw new ArgumentNullException(nameof(value));

return TryParse(value, primitiveType, out var result) ? result! :
return TryParse(value, primitiveType, out var result) ? result :
throw new FormatException($"Input string '{value}' was not in a correct format for type '{primitiveType}'.");
}

Expand All @@ -77,9 +77,9 @@ public static bool TryParse(string value, Type primitiveType, [NotNullWhen(true)
else if (primitiveType == typeof(Decimal))
return (Decimal.TryParse(value, out var p), p?.Value);
else if (primitiveType == typeof(Integer))
return (Integer.TryParse(value, out var p), p.Value);
return (Integer.TryParse(value, out var p), p?.Value);
else if (primitiveType == typeof(Long))
return (Long.TryParse(value, out var p), p.Value);
return (Long.TryParse(value, out var p), p?.Value);
else if (primitiveType == typeof(Date))
return (Date.TryParse(value, out var p), p);
else if (primitiveType == typeof(DateTime))
Expand All @@ -91,7 +91,7 @@ public static bool TryParse(string value, Type primitiveType, [NotNullWhen(true)
else if (primitiveType == typeof(Quantity))
return (Quantity.TryParse(value, out var p), p);
else if (primitiveType == typeof(String))
return (String.TryParse(value, out var p), p.Value);
return (String.TryParse(value, out var p), p?.Value);
else
return (false, null);
}
Expand Down Expand Up @@ -180,7 +180,7 @@ protected static Result<T> CannotCastTo<T>(Any from) =>
new Fail<T>(new InvalidCastException($"Cannot cast value '{from}' of type {from.GetType()} to an instance of type {typeof(T)}."));


protected static Result<T> propagateNull<T>(object obj, Func<T> a) => obj is null ?
protected static Result<T> propagateNull<T>(object? obj, Func<T> a) => obj is null ?
new Fail<T>(ArgNullException) : new Ok<T>(a());
}

Expand Down
4 changes: 2 additions & 2 deletions src/Hl7.Fhir.Base/ElementModel/Types/Boolean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public Boolean() : this(default) { }
public bool Value { get; }

public static Boolean Parse(string value) =>
TryParse(value, out var result) ? result! : throw new FormatException($"String '{value}' was not recognized as a valid boolean.");
TryParse(value, out var result) ? result : throw new FormatException($"String '{value}' was not recognized as a valid boolean.");

public static bool TryParse(string representation, [NotNullWhen(true)] out Boolean? value)
{
Expand Down Expand Up @@ -66,7 +66,7 @@ public static bool TryParse(string representation, [NotNullWhen(true)] out Boole
public static explicit operator Quantity(Boolean b) => ((ICqlConvertible)b).TryConvertToQuantity().ValueOrThrow();
public static explicit operator String(Boolean b) => ((ICqlConvertible)b).TryConvertToString().ValueOrThrow();

bool? ICqlEquatable.IsEqualTo(Any? other) => other is { } ? (bool?)Equals(other) : null;
bool? ICqlEquatable.IsEqualTo(Any? other) => other is { } ? Equals(other) : null;
bool ICqlEquatable.IsEquivalentTo(Any? other) => Equals(other);

Result<Boolean> ICqlConvertible.TryConvertToBoolean() => Ok(this);
Expand Down
16 changes: 8 additions & 8 deletions src/Hl7.Fhir.Base/ElementModel/Types/Date.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private Date(DateTimeOffset value, DateTimePrecision precision, bool hasOffset)
public static Date Parse(string representation) =>
TryParse(representation, out var result) ? result : throw new FormatException($"String '{representation}' was not recognized as a valid date.");

public static bool TryParse(string representation, out Date value) => tryParse(representation, out value);
public static bool TryParse(string representation, [NotNullWhen(true)] out Date? value) => tryParse(representation, out value);

public static Date FromDateTimeOffset(DateTimeOffset dto, DateTimePrecision prec = DateTimePrecision.Day,
bool includeOffset = false) => new(dto, prec, includeOffset);
Expand All @@ -43,11 +43,11 @@ public static Date FromDateTimeOffset(DateTimeOffset dto, DateTimePrecision prec
/// <summary>
/// The precision of the date available.
/// </summary>
public DateTimePrecision Precision { get; private set; }
public DateTimePrecision Precision { get; }

public int? Years => Precision >= DateTimePrecision.Year ? _value.Year : (int?)null;
public int? Months => Precision >= DateTimePrecision.Month ? _value.Month : (int?)null;
public int? Days => Precision >= DateTimePrecision.Day ? _value.Day : (int?)null;
public int? Years => Precision >= DateTimePrecision.Year ? _value.Year : null;
public int? Months => Precision >= DateTimePrecision.Month ? _value.Month : null;
public int? Days => Precision >= DateTimePrecision.Day ? _value.Day : null;

/// <summary>
/// The span of time ahead/behind UTC
Expand Down Expand Up @@ -106,14 +106,14 @@ public DateTimeOffset ToDateTimeOffset(int hours, int minutes, int seconds, int
/// Converts the date to a full DateTimeOffset instance.
/// </summary>
/// <returns></returns>
private static bool tryParse(string representation, [NotNullWhen(true)] out Date value)
private static bool tryParse(string representation, out Date? value)
{
if (representation is null) throw new ArgumentNullException(nameof(representation));

var matches = PARTIALDATEREGEX.Match(representation);
if (!matches.Success)
{
value = new Date(default, default, default);
value = null;
return false;
}

Expand Down Expand Up @@ -240,7 +240,7 @@ public Result<int> TryCompareTo(Any other)


public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => _originalParsedString is not null ? _originalParsedString : DateTime.ToStringWithPrecision(_value, Precision, HasOffset);
public override string ToString() => _originalParsedString ?? DateTime.ToStringWithPrecision(_value, Precision, HasOffset);

public static implicit operator DateTime(Date pd) => pd.ToDateTime();
public static explicit operator Date(DateTimeOffset dto) => FromDateTimeOffset(dto);
Expand Down
8 changes: 4 additions & 4 deletions src/Hl7.Fhir.Base/ElementModel/Types/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ private DateTime(DateTimeOffset value, DateTimePrecision precision, bool include
}

public static DateTime Parse(string representation) =>
TryParse(representation, out var result) ? result! : throw new FormatException($"String '{representation}' was not recognized as a valid datetime.");
TryParse(representation, out var result) ? result : throw new FormatException($"String '{representation}' was not recognized as a valid datetime.");

public static bool TryParse(string representation, [NotNullWhen(true)]out DateTime? value) => tryParse(representation, out value);
public static bool TryParse(string representation, [NotNullWhen(true)] out DateTime? value) => tryParse(representation, out value);

/// <summary>
/// Rounds the contents of a <see cref="DateTimeOffset"/> to the given precision, unused precision if filled out
Expand Down Expand Up @@ -105,14 +105,14 @@ public DateTimeOffset ToDateTimeOffset(TimeSpan defaultOffset) =>
new("^" + DATETIMEFORMAT + "$",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.ExplicitCapture);

private static bool tryParse(string representation, out DateTime value)
private static bool tryParse(string representation, out DateTime? value)
{
if (representation is null) throw new ArgumentNullException(nameof(representation));

var matches = DATETIMEREGEX.Match(representation);
if (!matches.Success)
{
value = new DateTime(default, default, default);
value = null;
return false;
}

Expand Down
12 changes: 6 additions & 6 deletions src/Hl7.Fhir.Base/ElementModel/Types/Integer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public Integer() : this(default) { }
public static Integer Parse(string value) =>
TryParse(value, out var result) ? result : throw new FormatException($"String '{value}' was not recognized as a valid integer.");

public static bool TryParse(string representation, out Integer value)
public static bool TryParse(string representation, [NotNullWhen(true)] out Integer? value)
{
if (representation == null) throw new ArgumentNullException(nameof(representation));

var (succ, val) = DoConvert(() => XmlConvert.ToInt32(representation));
value = new Integer(val);
value = succ ? new Integer(val) : null;
return succ;
}

Expand Down Expand Up @@ -72,11 +72,11 @@ public int CompareTo(object? obj)
public override string ToString() => XmlConvert.ToString(Value);

public static implicit operator int(Integer i) => i.Value;
public static implicit operator Long(Integer i) => new Long(i.Value);
public static implicit operator Decimal(Integer i) => new Decimal(i.Value);
public static implicit operator Quantity(Integer i) => new Quantity((decimal)i.Value, Quantity.UCUM_UNIT);
public static implicit operator Long(Integer i) => new (i.Value);
public static implicit operator Decimal(Integer i) => new (i.Value);
public static implicit operator Quantity(Integer i) => new (i.Value);

public static explicit operator Integer(int i) => new Integer(i);
public static explicit operator Integer(int i) => new (i);
public static explicit operator Boolean(Integer i) => ((ICqlConvertible)i).TryConvertToBoolean().ValueOrThrow();
public static explicit operator String(Integer i) => ((ICqlConvertible)i).TryConvertToString().ValueOrThrow();

Expand Down
12 changes: 6 additions & 6 deletions src/Hl7.Fhir.Base/ElementModel/Types/Long.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public Long() : this(default) { }
public static Long Parse(string value) =>
TryParse(value, out var result) ? result : throw new FormatException($"String '{value}' was not recognized as a valid long integer.");

public static bool TryParse(string representation, out Long value)
public static bool TryParse(string representation, [NotNullWhen(true)] out Long? value)
{
if (representation == null) throw new ArgumentNullException(nameof(representation));

(var succ, var val) = Any.DoConvert(() => XmlConvert.ToInt64(representation));
value = new Long(val);
var (succ, val) = Any.DoConvert(() => XmlConvert.ToInt64(representation));
value = succ ? new Long(val) : null;
return succ;
}

Expand Down Expand Up @@ -70,10 +70,10 @@ public int CompareTo(object? obj)
public override string ToString() => XmlConvert.ToString(Value);

public static implicit operator long(Long i) => i.Value;
public static implicit operator Decimal(Long i) => new Decimal(i.Value);
public static implicit operator Quantity(Long i) => new Quantity(i.Value, Quantity.UCUM_UNIT);
public static implicit operator Decimal(Long i) => new (i.Value);
public static implicit operator Quantity(Long i) => new (i.Value);

public static explicit operator Long(long i) => new Long(i);
public static explicit operator Long(long i) => new (i);
public static explicit operator Boolean(Long l) => ((ICqlConvertible)l).TryConvertToBoolean().ValueOrThrow();
public static explicit operator String(Long l) => ((ICqlConvertible)l).TryConvertToString().ValueOrThrow();
public static explicit operator Integer(Long l) => ((ICqlConvertible)l).TryConvertToInteger().ValueOrThrow();
Expand Down
22 changes: 11 additions & 11 deletions src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ public static Quantity ForCalendarDuration(decimal value, string calendarUnit)
public static Quantity Parse(string representation) =>
TryParse(representation, out var result) ? result : throw new FormatException($"String '{representation}' was not recognized as a valid quantity.");

public static bool TryParse(string representation, out Quantity quantity)
public static bool TryParse(string representation, [NotNullWhen(true)] out Quantity? quantity)
{
if (representation is null) throw new ArgumentNullException(nameof(representation));

quantity = new Quantity(default);
quantity = null;

var result = QUANTITYREGEX_FOR_PARSE.Match(representation);
if (!result.Success) return false;
Expand All @@ -98,23 +98,23 @@ public static bool TryParse(string representation, out Quantity quantity)

if (result.Groups["unit"].Success)
{
quantity = new Quantity(value!, result.Groups["unit"].Value);
quantity = new Quantity(value, result.Groups["unit"].Value);
return true;
}
else if (result.Groups["time"].Success)
{
if (TryParseTimeUnit(result.Groups["time"].Value, out var tv, out var isCalendarUnit))
{
quantity = isCalendarUnit ? ForCalendarDuration(value!, tv!)
: new Quantity(value!, tv!);
quantity = isCalendarUnit ? ForCalendarDuration(value, tv)
: new Quantity(value, tv);
return true;
}
else
return false;
}
else
{
quantity = new Quantity(value!, unit: UCUM_UNIT);
quantity = new Quantity(value, unit: UCUM_UNIT);
return true;
}
}
Expand Down Expand Up @@ -229,7 +229,7 @@ public int CompareTo(object? obj) => obj is Quantity q ?
/// NOTE: in the current normative specification, there is a difference between comparing incompatible duration units (result: {})
/// and performing the equals operator on incompatible units (result: false). This is going to be corrected
/// (see https://jira.hl7.org/browse/FHIR-28144), and this code already reflects this decision.</remarks>
public Result<int> TryCompareTo(Any other, QuantityComparison comparisonType)
public Result<int> TryCompareTo(Any? other, QuantityComparison comparisonType)
{
if (other is null) return 1; // as defined by the .NET framework guidelines
if (other is not Quantity otherQ) throw NotSameTypeComparison(this, other);
Expand All @@ -251,7 +251,7 @@ public Result<int> TryCompareTo(Any other, QuantityComparison comparisonType)
if (!otherQ.TryCanonicalize(out right)) right = otherQ;
}

return (left!.Unit == right!.Unit)
return (left.Unit == right.Unit)
? decimal.Compare(Math.Round(left.Value, 8), Math.Round(right.Value, 8)) // aligns with Decimal
: Fail<int>(Error.InvalidOperation($"UCUM quanties with unit '{left.Unit}' and '{right.Unit}' cannot be compared."));
}
Expand Down Expand Up @@ -323,7 +323,7 @@ private static (Quantity, Quantity) alignQuantityUnits(Quantity a, Quantity b)
if (!b.TryCanonicalize(out right)) right = b;
}

return (left!, right!);
return (left, right);
}

public static Quantity? operator +(Quantity a, Quantity b) =>
Expand All @@ -342,7 +342,7 @@ internal static Result<Quantity> Add(Quantity a, Quantity b)
{
var (left, right) = alignQuantityUnits(a, b);

return (left!.Unit == right!.Unit)
return (left.Unit == right.Unit)
? Ok<Quantity>(new(left.Value + right.Value, left.Unit))
: Fail<Quantity>(Error.InvalidOperation($"The add operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
}
Expand All @@ -351,7 +351,7 @@ internal static Result<Quantity> Substract(Quantity a, Quantity b)
{
var (left, right) = alignQuantityUnits(a, b);

return (left!.Unit == right!.Unit)
return (left.Unit == right.Unit)
? Ok<Quantity>(new(left.Value - right.Value, left.Unit))
: Fail<Quantity>(Error.InvalidOperation($"The substract operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
}
Expand Down
Loading

0 comments on commit eb05df7

Please sign in to comment.