-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDynamicExtensions.cs
146 lines (140 loc) · 6.77 KB
/
DynamicExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using IS4.SFI.Services;
using IS4.SFI.Vocabulary;
using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading.Tasks;
namespace IS4.SFI
{
/// <summary>
/// Stores extension methods for operations on a <see cref="ILinkedNode"/> or <see cref="IEntityAnalyzers"/>.
/// </summary>
public static class DynamicExtensions
{
/// <summary>
/// Dynamically calls <see cref="IEntityAnalyzers.Analyze{T}(T, AnalysisContext)"/>
/// based on the runtime type of <paramref name="entity"/> constrained to type <typeparamref name="TConstraint"/>.
/// </summary>
/// <param name="analyzers">The instance of <see cref="IEntityAnalyzers"/> to use.</param>
/// <param name="entity">The entity to analyze.</param>
/// <param name="context">The context to be passed to <see cref="IEntityAnalyzers.Analyze{T}(T, AnalysisContext)"/>.</param>
/// <typeparam name="TConstraint">The constraining type to affect the selected runtime type.</typeparam>
/// <returns>The result from the method, or the default value of <see cref="AnalysisResult"/> on failure.</returns>
[DynamicDependency(nameof(Constrained<object>.Analyze), typeof(Constrained<>))]
public static ValueTask<AnalysisResult> TryAnalyze<TConstraint>(this IEntityAnalyzers analyzers, TConstraint entity, AnalysisContext context) where TConstraint : class
{
if(entity == null) return default;
try{
return Constrained<TConstraint>.Analyze(analyzers, (dynamic)entity, context);
}catch(RuntimeBinderException)
{
return default;
}
}
/// <summary>
/// Helper class to constrain the <see cref="Analyze{T}(IEntityAnalyzers, T, AnalysisContext)"/> method
/// to an argument compatible with <typeparamref name="TConstraint"/>.
/// </summary>
/// <typeparam name="TConstraint">The constraining type.</typeparam>
static class Constrained<TConstraint>
{
/// <inheritdoc cref="IEntityAnalyzers.Analyze{T}(T, AnalysisContext)"/>
/// <param name="analyzers">The instance of <see cref="IEntityAnalyzers"/> to use.</param>
/// <param name="entity"><inheritdoc cref="IEntityAnalyzers.Analyze{T}(T, AnalysisContext)" path="/param[@name='entity']"/></param>
/// <param name="context"><inheritdoc cref="IEntityAnalyzers.Analyze{T}(T, AnalysisContext)" path="/param[@name='context']"/></param>
public static ValueTask<AnalysisResult> Analyze<T>(IEntityAnalyzers analyzers, T entity, AnalysisContext context) where T : class, TConstraint
{
return analyzers.Analyze(entity, context);
}
}
/// <inheritdoc cref="TrySet{TProp}(ILinkedNode, IPropertyUriFormatter{TProp}, TProp, ValueType)"/>
public static bool TrySet(this ILinkedNode node, PropertyUri property, object? value)
{
switch(value)
{
case string str:
node.Set(property, str);
return true;
case Uri uri:
node.Set(property, uri);
return true;
case DBNull:
case Missing:
node.Set(property, Individuals.Nil);
return true;
case ValueType val:
return TrySet(node, property, val);
default:
return false;
}
}
/// <summary>
/// Dynamically calls <see cref="ILinkedNode.Set{TValue}(PropertyUri, TValue)"/>
/// based on the runtime type of <paramref name="value"/>.
/// </summary>
/// <param name="node">The instance of <see cref="ILinkedNode"/> to use.</param>
/// <param name="property">The property to assign.</param>
/// <param name="value">The value of the property.</param>
/// <returns>Whether the call was successful or not.</returns>
[DynamicDependency(nameof(ILinkedNode.Set), typeof(ILinkedNode))]
public static bool TrySet(this ILinkedNode node, PropertyUri property, ValueType value)
{
try{
node.Set(property, (dynamic)value);
return true;
}catch(RuntimeBinderException)
{
return false;
}catch(ArgumentException)
{
return false;
}
}
/// <inheritdoc cref="TrySet{TProp}(ILinkedNode, IPropertyUriFormatter{TProp}, TProp, ValueType)"/>
public static bool TrySet<TProp>(this ILinkedNode node, IPropertyUriFormatter<TProp> propertyFormatter, TProp propertyValue, object? value)
{
switch(value)
{
case string str:
node.Set(propertyFormatter, propertyValue, str);
return true;
case Uri uri:
node.Set(propertyFormatter, propertyValue, uri);
return true;
case DBNull:
case Missing:
node.Set(propertyFormatter, propertyValue, Individuals.Nil);
return true;
case ValueType val:
return TrySet(node, propertyFormatter, propertyValue, val);
default:
return false;
}
}
/// <summary>
/// Dynamically calls <see cref="ILinkedNode.Set{TProp, TValue}(IPropertyUriFormatter{TProp}, TProp, TValue)"/>
/// based on the runtime type of <paramref name="value"/>.
/// </summary>
/// <typeparam name="TProp">The type of <paramref name="propertyValue"/>.</typeparam>
/// <param name="node">The instance of <see cref="ILinkedNode"/> to use.</param>
/// <param name="propertyFormatter">The formatter to provide the property based on <paramref name="propertyValue"/>.</param>
/// <param name="propertyValue">The value to pass to the <paramref name="propertyFormatter"/>.</param>
/// <param name="value">The value of the property.</param>
/// <returns>Whether the call was successful or not.</returns>
[DynamicDependency(nameof(ILinkedNode.Set), typeof(ILinkedNode))]
public static bool TrySet<TProp>(this ILinkedNode node, IPropertyUriFormatter<TProp> propertyFormatter, TProp propertyValue, ValueType value)
{
try{
node.Set(propertyFormatter, propertyValue, (dynamic)value);
return true;
}catch(RuntimeBinderException)
{
return false;
}catch(ArgumentException)
{
return false;
}
}
}
}