-
Notifications
You must be signed in to change notification settings - Fork 28
/
WorkflowExpressionBuilder.cs
156 lines (143 loc) · 5.88 KB
/
WorkflowExpressionBuilder.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Reactive.Linq;
namespace Bonsai.Expressions
{
/// <summary>
/// Provides a base class for expression builders that generate their output by means
/// of an encapsulated workflow.
/// </summary>
[DefaultProperty(nameof(Name))]
[WorkflowElementCategory(ElementCategory.Combinator)]
[XmlType("Workflow", Namespace = Constants.XmlNamespace)]
[TypeDescriptionProvider(typeof(WorkflowTypeDescriptionProvider))]
public abstract class WorkflowExpressionBuilder : ExpressionBuilder, IWorkflowExpressionBuilder, INamedElement, IPropertyMappingBuilder, IRequireBuildContext
{
IBuildContext buildContext;
readonly ExpressionBuilderGraph workflow;
/// <summary>
/// Initializes a new instance of the <see cref="WorkflowExpressionBuilder"/> class.
/// </summary>
protected WorkflowExpressionBuilder()
: this(new ExpressionBuilderGraph())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WorkflowExpressionBuilder"/> class
/// with the specified expression builder workflow.
/// </summary>
/// <param name="workflow">
/// The expression builder workflow instance that will be used by this builder
/// to generate the output expression tree.
/// </param>
protected WorkflowExpressionBuilder(ExpressionBuilderGraph workflow)
{
this.workflow = workflow ?? throw new ArgumentNullException(nameof(workflow));
}
/// <summary>
/// Gets or sets the name of the encapsulated workflow.
/// </summary>
[Category("Design")]
[Externalizable(false)]
[Description("The name of the encapsulated workflow.")]
public string Name { get; set; }
/// <summary>
/// Gets or sets a description for the encapsulated workflow.
/// </summary>
[Category("Design")]
[Externalizable(false)]
[Description("A description for the encapsulated workflow.")]
[Editor(DesignTypes.MultilineStringEditor, DesignTypes.UITypeEditor)]
public string Description { get; set; }
string INamedElement.Name
{
get { return Name; }
}
/// <summary>
/// Gets the expression builder workflow that will be used to generate the
/// output expression tree.
/// </summary>
[XmlIgnore]
[Browsable(false)]
public ExpressionBuilderGraph Workflow
{
get { return workflow; }
}
/// <summary>
/// Gets the XML serializable representation of the encapsulated workflow.
/// </summary>
[Browsable(false)]
[XmlElement("Workflow")]
public ExpressionBuilderGraphDescriptor WorkflowDescriptor
{
get { return workflow.ToDescriptor(); }
set
{
workflow.Clear();
workflow.AddDescriptor(value);
}
}
/// <summary>
/// Gets the collection of property mappings assigned to this expression builder.
/// Property mapping subscriptions are processed before evaluating other output generation
/// expressions. In the case of an encapsulated workflow, mappings to nested workflow
/// properties are also allowed.
/// </summary>
[Obsolete]
[Browsable(false)]
[XmlArrayItem("PropertyMapping")]
public PropertyMappingCollection PropertyMappings { get; } = new PropertyMappingCollection();
/// <summary>
/// Gets the range of input arguments that this expression builder accepts.
/// </summary>
public override Range<int> ArgumentRange
{
get
{
var parameterCount = workflow.GetNestedParameters().Count();
return Range.Create(0, parameterCount);
}
}
IBuildContext IRequireBuildContext.BuildContext
{
get { return buildContext; }
set { buildContext = value; }
}
internal IBuildContext BuildContext
{
get { return buildContext; }
}
/// <summary>
/// Builds the output of the encapsulated workflow for the specified source and applies
/// a selector taking into account any available workflow mappings.
/// </summary>
/// <param name="arguments">
/// A collection of <see cref="Expression"/> nodes that represents the input arguments.
/// </param>
/// <param name="source">
/// The expression tree that will be used as input to the encapsulated workflow.
/// </param>
/// <param name="selector">
/// A selector that will be applied to the output of the encapsulated workflow to determine
/// the final output of the expression builder.
/// </param>
/// <returns>
/// An <see cref="Expression"/> tree that is the result of applying the encapsulated
/// workflow to the specified input <paramref name="source"/>. Property mappings are also
/// resolved in the correct sequence.
/// </returns>
protected Expression BuildWorkflow(IEnumerable<Expression> arguments, Expression source, Func<Expression, Expression> selector)
{
// Assign sources if available
var nestedContext = new BuildContext(buildContext);
var inputArguments = source != null ? Enumerable.Repeat(source, 1).Concat(arguments.Skip(1)) : arguments;
var expression = workflow.BuildNested(inputArguments, nestedContext);
if (nestedContext.BuildResult != null) return nestedContext.BuildResult;
return selector(expression);
}
}
}