-
Notifications
You must be signed in to change notification settings - Fork 28
/
PropertyMappingBuilder.cs
180 lines (155 loc) · 6.51 KB
/
PropertyMappingBuilder.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using Bonsai.Dag;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Serialization;
namespace Bonsai.Expressions
{
/// <summary>
/// Represents an expression builder that assigns values of an observable sequence
/// to properties of a workflow element.
/// </summary>
[DefaultProperty(nameof(PropertyMappings))]
[WorkflowElementCategory(ElementCategory.Property)]
[XmlType("PropertyMapping", Namespace = Constants.XmlNamespace)]
[Description("Assigns values of an observable sequence to properties of a workflow element.")]
[TypeDescriptionProvider(typeof(PropertyMappingTypeDescriptionProvider))]
public class PropertyMappingBuilder : SingleArgumentExpressionBuilder, INamedElement, IArgumentBuilder
{
/// <summary>
/// Gets a collection of property mappings that specify how input values are assigned
/// to properties of the workflow element.
/// </summary>
[XmlArrayItem("Property")]
[Description("Specifies how input values are assigned to properties of the workflow element.")]
[Editor("Bonsai.Design.MappingCollectionEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
public PropertyMappingCollection PropertyMappings { get; } = new PropertyMappingCollection();
string INamedElement.Name
{
get
{
if (PropertyMappings.Count > 0)
{
return string.Join(
ExpressionHelper.ArgumentSeparator,
PropertyMappings.Select(mapping => mapping.Name));
}
return GetElementDisplayName(GetType());
}
}
/// <summary>
/// Generates an <see cref="Expression"/> node from a collection of input arguments.
/// The result can be chained with other builders in a workflow.
/// </summary>
/// <param name="arguments">
/// A collection of <see cref="Expression"/> nodes that represents the input arguments.
/// </param>
/// <returns>An <see cref="Expression"/> tree node.</returns>
public override Expression Build(IEnumerable<Expression> arguments)
{
return arguments.First();
}
bool IArgumentBuilder.BuildArgument(Expression source, Edge<ExpressionBuilder, ExpressionBuilderArgument> successor, out Expression argument)
{
return BuildArgument(source, successor, out argument);
}
internal virtual bool BuildArgument(Expression source, Edge<ExpressionBuilder, ExpressionBuilderArgument> successor, out Expression argument)
{
argument = source;
var workflowElement = GetWorkflowElement(successor.Target.Value);
var instance = Expression.Constant(workflowElement);
foreach (var mapping in PropertyMappings)
{
argument = BuildPropertyMapping(argument, instance, mapping.Name, mapping.Selector);
}
return false;
}
class PropertyMappingTypeDescriptionProvider : TypeDescriptionProvider
{
static readonly TypeDescriptionProvider parentProvider = TypeDescriptor.GetProvider(typeof(PropertyMappingBuilder));
public PropertyMappingTypeDescriptionProvider()
: base(parentProvider)
{
}
public override ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance)
{
var builder = (PropertyMappingBuilder)instance;
if (builder != null) return new PropertyMappingCollectionTypeDescriptor(builder.PropertyMappings);
else return base.GetExtendedTypeDescriptor(instance);
}
}
internal class PropertyMappingCollectionTypeDescriptor : CustomTypeDescriptor
{
readonly PropertyMappingCollection instance;
public PropertyMappingCollectionTypeDescriptor(PropertyMappingCollection collection)
{
instance = collection;
}
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
if (instance == null) return base.GetProperties(attributes);
return new PropertyDescriptorCollection(
(from mapping in instance
where !string.IsNullOrEmpty(mapping.Name)
select new MappingPropertyDescriptor(mapping))
.ToArray());
}
}
class MappingPropertyDescriptor : PropertyDescriptor
{
readonly PropertyMapping mapping;
static readonly Attribute[] DescriptorAttributes = new Attribute[]
{
new ExternalizableAttribute(false),
new EditorAttribute("Bonsai.Design.MultiMemberSelectorEditor, Bonsai.Design", DesignTypes.UITypeEditor)
};
public MappingPropertyDescriptor(PropertyMapping mapping)
: base(mapping.Name, DescriptorAttributes)
{
this.mapping = mapping;
}
public override string Category
{
get { return "Properties"; }
}
public override bool CanResetValue(object component)
{
return ShouldSerializeValue(component);
}
public override Type ComponentType
{
get { return typeof(PropertyMappingCollection); }
}
public override object GetValue(object component)
{
return mapping.Selector;
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override void ResetValue(object component)
{
mapping.Selector = null;
}
public override void SetValue(object component, object value)
{
mapping.Selector = (string)value;
}
public override bool ShouldSerializeValue(object component)
{
return !string.IsNullOrEmpty(mapping.Selector);
}
}
}
}