Skip to content

Commit

Permalink
Implemented IDictionary.
Browse files Browse the repository at this point in the history
  • Loading branch information
ewoutkramer committed Oct 16, 2024
1 parent faa19a7 commit 2d1a887
Show file tree
Hide file tree
Showing 5 changed files with 873 additions and 105 deletions.
14 changes: 14 additions & 0 deletions src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,18 @@
<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.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>
</Suppressions>
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
}
179 changes: 74 additions & 105 deletions src/Hl7.Fhir.Base/Model/Base.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
/*
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.
*/

#nullable enable

using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
using System;
using System.Collections;
Expand All @@ -38,116 +39,84 @@ POSSIBILITY OF SUCH DAMAGE.
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using DataType = System.ComponentModel.DataAnnotations.DataType;

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


private Dictionary<string, object>? _overflow = null;
namespace Hl7.Fhir.Model;

/// <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;

private AnnotationList annotations => LazyInitializer.EnsureInitialized(ref _annotations, () => [])!;

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

public void AddAnnotation(object annotation) => annotations.AddAnnotation(annotation);

public void RemoveAnnotations(Type type) => annotations.RemoveAnnotations(type);
#endregion
public abstract partial class Base : IDeepCopyable, IDeepComparable,
IAnnotated, IAnnotatable, IValidatableObject, INotifyPropertyChanged
{
/// <summary>
/// FHIR Type Name
/// </summary>
public virtual string TypeName => GetType().Name;

#region INotifyPropertyChanged

public event PropertyChangedEventHandler? PropertyChanged;
private Dictionary<string, object>? _overflow = null;

protected virtual void OnPropertyChanged(string property) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
/// <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>())!;

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

public IReadOnlyDictionary<string, object> AsReadOnlyDictionary() => this;
#region << Annotations >>

#region IReadOnlyDictionary
IEnumerable<string> IReadOnlyDictionary<string, object>.Keys => GetElementPairs().Select(kvp => kvp.Key);
[NonSerialized] private AnnotationList? _annotations = null;

IEnumerable<object> IReadOnlyDictionary<string, object>.Values => GetElementPairs().Select(kvp => kvp.Value);
private AnnotationList annotations => LazyInitializer.EnsureInitialized(ref _annotations, () => [])!;

int IReadOnlyCollection<KeyValuePair<string, object>>.Count => GetElementPairs().Count();
public IEnumerable<object> Annotations(Type type) => annotations.OfType(type);

// object IReadOnlyDictionary<string, object>.this[string key] => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException();
public void AddAnnotation(object annotation) => annotations.AddAnnotation(annotation);

bool IReadOnlyDictionary<string, object>.ContainsKey(string key) => TryGetValue(key, out _);
public void RemoveAnnotations(Type type) => annotations.RemoveAnnotations(type);

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

IEnumerator IEnumerable.GetEnumerator() => GetElementPairs().GetEnumerator();
#region INotifyPropertyChanged

bool IReadOnlyDictionary<string, object>.TryGetValue(string key, out object value) => TryGetValue(key, out value!);
#endregion
public event PropertyChangedEventHandler? PropertyChanged;

protected virtual void OnPropertyChanged(string property) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

protected virtual Base SetValue(string key, object? value)
{
if (value is null)
Overflow.Remove(key);
else
Overflow[key] = value;
#endregion

return this;
}
protected virtual Base SetValue(string key, object? value)
{
if (value is null)
Overflow.Remove(key);
else
Overflow[key] = value;

protected virtual bool TryGetValue(string key, [NotNullWhen(true)] out object? value) => Overflow.TryGetValue(key, out value);
return this;
}

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

// TODO bring Children + NamedChildren over as well.
protected virtual IEnumerable<KeyValuePair<string, object>> GetElementPairs() => Overflow;

// TODO: This should maybe be hidden behind IReadOnlyDictionary or the new IScopedNode.
public object this[string key]
{
get => TryGetValue(key, out var value) ? value : throw new KeyNotFoundException();
set => SetValue(key, value);
}
}
// TODO bring Children + NamedChildren over as well.
}

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

}
/// <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);
}


/// <summary>
/// A dynamic resource that can hold any element.
/// </summary>
public class DynamicResource : Resource
{
public void Add(string arg1, object arg2)
{
this[arg1] = arg2;
}
}
/// <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);
}
Loading

0 comments on commit 2d1a887

Please sign in to comment.