Skip to content

Commit

Permalink
Merge pull request #2918 from FirelyTeam/6.0/add-support-for-overflow
Browse files Browse the repository at this point in the history
#2901 Add support for overflow to POCOs
  • Loading branch information
ewoutkramer authored Oct 18, 2024
2 parents 208de2f + fa4eb6f commit 283ed30
Show file tree
Hide file tree
Showing 530 changed files with 56,478 additions and 662 deletions.
28 changes: 28 additions & 0 deletions src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,32 @@
<Left>lib/netstandard2.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Model.Parameters.get_Item(System.String)</Target>
<Left>lib/net8.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Utility.ReflectionHelper.IsTypedCollection(System.Type)</Target>
<Left>lib/net8.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Model.Parameters.get_Item(System.String)</Target>
<Left>lib/netstandard2.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/netstandard2.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Utility.ReflectionHelper.IsTypedCollection(System.Type)</Target>
<Left>lib/netstandard2.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/netstandard2.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
2 changes: 1 addition & 1 deletion src/Hl7.Fhir.Base/Introspection/PropertyMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public static bool TryCreate(PropertyInfo prop, [NotNullWhen(true)] out Property
// FHIR repeating elements. If we would allow this, we'd also have stuff like `string` and binary
// data as repeating element, and would need to exclude these exceptions on a case by case basis.
// This is pretty ugly, so we prefer to not support arrays - you should use lists instead.
bool isCollection = ReflectionHelper.IsTypedCollection(prop.PropertyType) && !prop.PropertyType.IsArray;
bool isCollection = ReflectionHelper.IsTypedList(prop.PropertyType) && !prop.PropertyType.IsArray;

var cardinalityAttr = ClassMapping.GetAttribute<CardinalityAttribute>(prop, release);

Expand Down
91 changes: 91 additions & 0 deletions src/Hl7.Fhir.Base/Model/Base.Dictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Hl7.Fhir.Model;

public abstract partial class Base: IReadOnlyDictionary<string,object>, IDictionary<string, object>
{
private object get(string key) =>
this.TryGetValue(key, out var value)
? value
: throw new KeyNotFoundException($"Element '{key}' is not a known FHIR element or has no value.");

public IReadOnlyDictionary<string, object> AsReadOnlyDictionary() => this;
public IDictionary<string, object> AsDictionary() => this;

#region IReadOnlyDictionary

IEnumerable<string> IReadOnlyDictionary<string, object>.Keys => GetElementPairs().Select(kvp => kvp.Key);
IEnumerable<object> IReadOnlyDictionary<string, object>.Values => GetElementPairs().Select(kvp => kvp.Value);
int IReadOnlyCollection<KeyValuePair<string, object>>.Count => GetElementPairs().Count();
object IReadOnlyDictionary<string, object>.this[string key] => get(key);

bool IReadOnlyDictionary<string, object>.TryGetValue(string key, out object value) =>
TryGetValue(key, out value!);

bool IReadOnlyDictionary<string, object>.ContainsKey(string key) => TryGetValue(key, out _);

IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() =>
GetElementPairs().GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetElementPairs().GetEnumerator();

#endregion

#region IDictionary

void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) =>
AsDictionary().Add(item.Key, item.Value);

void ICollection<KeyValuePair<string, object>>.Clear()
{
// Slow....
foreach (var kvp in this)
SetValue(kvp.Key, null);
}

bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) =>
TryGetValue(item.Key, out _); // we don't care about the item, we cannot have the same key twice.

void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
foreach (var kvp in this)
array[arrayIndex++] = kvp;
}

bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) =>
AsDictionary().Remove(item.Key);

int ICollection<KeyValuePair<string, object>>.Count => AsReadOnlyDictionary().Count;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
ICollection<object> IDictionary<string, object>.Values => AsReadOnlyDictionary().Values.ToList();
ICollection<string> IDictionary<string, object>.Keys => AsReadOnlyDictionary().Keys.ToList();
bool IDictionary<string, object>.TryGetValue(string key, out object value) => TryGetValue(key, out value!);

object IDictionary<string, object>.this[string key]
{
get => this.AsReadOnlyDictionary()[key];
set => SetValue(key, value);
}

void IDictionary<string, object>.Add(string key, object value)
{
if (TryGetValue(key, out _))
throw new ArgumentException($"An element with the key '{key}' already exists in the dictionary.");

SetValue(key, value);
}

bool IDictionary<string, object>.ContainsKey(string key) => TryGetValue(key, out _);

bool IDictionary<string, object>.Remove(string key)
{
var existed = TryGetValue(key, out _);
SetValue(key, null);
return existed;
}

#endregion
}
144 changes: 94 additions & 50 deletions src/Hl7.Fhir.Base/Model/Base.cs
Original file line number Diff line number Diff line change
@@ -1,90 +1,134 @@
/*
Copyright (c) 2011-2012, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

using Hl7.Fhir.Introspection;
#nullable enable

using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using DataType = System.ComponentModel.DataAnnotations.DataType;

namespace Hl7.Fhir.Model;

namespace Hl7.Fhir.Model
public abstract partial class Base : IDeepCopyable, IDeepComparable,
IAnnotated, IAnnotatable, IValidatableObject, INotifyPropertyChanged
{
public abstract partial class Base : IDeepCopyable, IDeepComparable,
IAnnotated, IAnnotatable, IValidatableObject, INotifyPropertyChanged, IReadOnlyDictionary<string, object>
{
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) => Enumerable.Empty<ValidationResult>();
/// <summary>
/// FHIR Type Name
/// </summary>
public virtual string TypeName => GetType().Name;


private Dictionary<string, object>? _overflow = null;

/// <summary>
/// A dictionary containing all elements that are not explicitly defined in the class.
/// </summary>
protected Dictionary<string, object> Overflow =>
LazyInitializer.EnsureInitialized(ref _overflow, () => new Dictionary<string, object>())!;

public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) => [];

#region << Annotations >>
[NonSerialized]
private AnnotationList _annotations = null;
#region << Annotations >>

private AnnotationList annotations => LazyInitializer.EnsureInitialized(ref _annotations, () => new());
[NonSerialized] private AnnotationList? _annotations = null;

public IEnumerable<object> Annotations(Type type) => annotations.OfType(type);
private AnnotationList annotations => LazyInitializer.EnsureInitialized(ref _annotations, () => [])!;

public void AddAnnotation(object annotation) => annotations.AddAnnotation(annotation);
public IEnumerable<object> Annotations(Type type) => annotations.OfType(type);

public void RemoveAnnotations(Type type) => annotations.RemoveAnnotations(type);
#endregion
public void AddAnnotation(object annotation) => annotations.AddAnnotation(annotation);

#region INotifyPropertyChanged
public void RemoveAnnotations(Type type) => annotations.RemoveAnnotations(type);

public event PropertyChangedEventHandler PropertyChanged;
#endregion

protected virtual void OnPropertyChanged(String property) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
#region INotifyPropertyChanged

#endregion
public event PropertyChangedEventHandler? PropertyChanged;

public IReadOnlyDictionary<string, object> AsReadOnlyDictionary() => this;
protected virtual void OnPropertyChanged(string property) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

#region IReadOnlyDictionary
IEnumerable<string> IReadOnlyDictionary<string, object>.Keys => GetElementPairs().Select(kvp => kvp.Key);
#endregion

IEnumerable<object> IReadOnlyDictionary<string, object>.Values => GetElementPairs().Select(kvp => kvp.Value);
protected virtual Base SetValue(string key, object? value)
{
if (value is null)
Overflow.Remove(key);
else
Overflow[key] = value;

return this;
}

int IReadOnlyCollection<KeyValuePair<string, object>>.Count => GetElementPairs().Count();
protected virtual bool TryGetValue(string key, [NotNullWhen(true)] out object? value) =>
Overflow.TryGetValue(key, out value);

object IReadOnlyDictionary<string, object>.this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException();
protected virtual IEnumerable<KeyValuePair<string, object>> GetElementPairs() => Overflow;

bool IReadOnlyDictionary<string, object>.ContainsKey(string key) => TryGetValue(key, out _);
// TODO bring Children + NamedChildren over as well.
}

IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() => GetElementPairs().GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetElementPairs().GetEnumerator();
/// <summary>
/// A dynamic data type that can hold any element.
/// </summary>
public class DynamicDataType : DataType
{
public void Add(string arg1, object arg2) => this.SetValue(arg1, arg2);

bool IReadOnlyDictionary<string, object>.TryGetValue(string key, out object value) => TryGetValue(key, out value);
#endregion
public object this[string key]
{
get => this.AsReadOnlyDictionary()[key];
set => SetValue(key, value);
}
}


/// <summary>
/// A dynamic resource that can hold any element.
/// </summary>
public class DynamicResource : Resource
{
public void Add(string arg1, object arg2) => this.SetValue(arg1, arg2);

public object this[string key]
{
get => this.AsReadOnlyDictionary()[key];
set => SetValue(key, value);
}
}
51 changes: 50 additions & 1 deletion src/Hl7.Fhir.Base/Model/Generated/Attachment.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// <auto-generated/>
// Contents of: hl7.fhir.r5.expansions#5.0.0, hl7.fhir.r5.core#5.0.0
// Contents of: hl7.fhir.r5.expansions@5.0.0, hl7.fhir.r5.core@5.0.0

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -638,6 +638,55 @@ protected override bool TryGetValue(string key, out object value)

}

protected override Base SetValue(string key, object value)
{
switch (key)
{
case "contentType":
ContentTypeElement = (Hl7.Fhir.Model.Code)value;
return this;
case "language":
LanguageElement = (Hl7.Fhir.Model.Code)value;
return this;
case "data":
DataElement = (Hl7.Fhir.Model.Base64Binary)value;
return this;
case "url":
UrlElement = (Hl7.Fhir.Model.FhirUrl)value;
return this;
case "size":
SizeElement = (Hl7.Fhir.Model.Integer64)value;
return this;
case "hash":
HashElement = (Hl7.Fhir.Model.Base64Binary)value;
return this;
case "title":
TitleElement = (Hl7.Fhir.Model.FhirString)value;
return this;
case "creation":
CreationElement = (Hl7.Fhir.Model.FhirDateTime)value;
return this;
case "height":
HeightElement = (Hl7.Fhir.Model.PositiveInt)value;
return this;
case "width":
WidthElement = (Hl7.Fhir.Model.PositiveInt)value;
return this;
case "frames":
FramesElement = (Hl7.Fhir.Model.PositiveInt)value;
return this;
case "duration":
DurationElement = (Hl7.Fhir.Model.FhirDecimal)value;
return this;
case "pages":
PagesElement = (Hl7.Fhir.Model.PositiveInt)value;
return this;
default:
return base.SetValue(key, value);
}

}

protected override IEnumerable<KeyValuePair<string, object>> GetElementPairs()
{
foreach (var kvp in base.GetElementPairs()) yield return kvp;
Expand Down
Loading

0 comments on commit 283ed30

Please sign in to comment.