diff options
author | rtk0c <[email protected]> | 2021-04-16 16:49:28 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2021-04-16 16:49:28 -0700 |
commit | 4e5730e1fcef150ce2f13f52a278890589ca96ad (patch) | |
tree | 0fe4002349047c7c770754e273d6a1d1ed666cbb /core/src/Model/WorkflowNodes.cpp | |
parent | 80d8ae5a6fef6c9a34e81e240539cb655dd99851 (diff) |
More work on workflows
- WorkflowStep -> WorkflowNode
- Added initial kinds of WorkflowNode's
Diffstat (limited to 'core/src/Model/WorkflowNodes.cpp')
-rw-r--r-- | core/src/Model/WorkflowNodes.cpp | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/core/src/Model/WorkflowNodes.cpp b/core/src/Model/WorkflowNodes.cpp new file mode 100644 index 0000000..8c315b6 --- /dev/null +++ b/core/src/Model/WorkflowNodes.cpp @@ -0,0 +1,309 @@ +#include "WorkflowNodes.hpp" + +#include "Utils/Macros.hpp" +#include "Utils/RTTI.hpp" +#include "Utils/Variant.hpp" + +#include <cassert> +#include <variant> + +WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) { + switch (type) { + case Addition: return KD_NumericAddition; + case Subtraction: return KD_NumericSubtraction; + case Multiplication: return KD_NumericMultiplication; + case Division: return KD_NumericDivision; + default: UNREACHABLE; + } +} + +NumericOperationNode::OperationType NumericOperationNode::NodeKindToOperationType(Kind kind) { + switch (kind) { + case KD_NumericAddition: return Addition; + case KD_NumericSubtraction: return Subtraction; + case KD_NumericMultiplication: return Multiplication; + case KD_NumericDivision: return Division; + default: UNREACHABLE; + } +} + +bool NumericOperationNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision; +} + +NumericOperationNode::NumericOperationNode(OperationType type) + : WorkflowNode(TransformType, OperationTypeToNodeKind(type)) + , mType{ type } { + mInputs.resize(2); + mInputs[0].MatchingType = BaseValue::KD_Numeric; + mInputs[1].MatchingType = BaseValue::KD_Numeric; + + mOutputs.resize(1); + mOutputs[0].MatchingType = BaseValue::KD_Numeric; +} + +void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) { + auto lhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[0])); + if (!lhsVal) return; + double lhs = lhsVal->GetValue(); + + auto rhsVal = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[1])); + if (!rhsVal) return; + double rhs = rhsVal->GetValue(); + + double res; + switch (mType) { + case Addition: res = lhs + rhs; break; + case Subtraction: res = lhs - rhs; break; + case Multiplication: res = lhs * rhs; break; + case Division: { + if (rhs == 0.0) { + // TODO localize + ctx.ReportError("Error: division by 0", *this); + return; + } + res = lhs / rhs; + } break; + + default: return; + } + + ctx.SetConnectionValue(mOutputs[0], res); +} + +bool NumericExpressionNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() == KD_NumericExpression; +} + +NumericExpressionNode::NumericExpressionNode() + : WorkflowNode(TransformType, KD_NumericExpression) { +} + +struct FormatTextNode::Argument { + ArgumentType ArgumentType; + int PinIdx; + + template <class TFunction> + static void ForArguments(std::vector<Element>::iterator begin, std::vector<Element>::iterator end, const TFunction& func) { + for (auto it = begin; it != end; ++it) { + auto& elm = *it; + if (auto arg = std::get_if<Argument>(&elm)) { + func(*arg); + } + } + } + + /// Find the pin index that the \c elmIdx -th element should have, based on the elements coming before it. + static int FindPinForElement(const std::vector<Element>& vec, int elmIdx) { + for (int i = elmIdx; i >= 0; --i) { + auto& elm = vec[i]; + if (auto arg = std::get_if<Argument>(&elm)) { + return arg->PinIdx + 1; + } + } + return 0; + } +}; + +BaseValue::Kind FormatTextNode::ArgumentTypeToValueKind(FormatTextNode::ArgumentType arg) { + switch (arg) { + case NumericArgument: return BaseValue::KD_Numeric; + case TextArgument: return BaseValue::KD_Text; + case DateTimeArgument: return BaseValue::KD_DateTime; + default: UNREACHABLE; + } +} + +bool FormatTextNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() == KD_TextFormatting; +} + +FormatTextNode::FormatTextNode() + : WorkflowNode(TransformType, KD_TextFormatting) { +} + +int FormatTextNode::GetElementCount() const { + return mElements.size(); +} + +const FormatTextNode::Element& FormatTextNode::GetElement(int idx) const { + return mElements[idx]; +} + +void FormatTextNode::SetElement(int idx, std::string text) { + assert(idx >= 0 && idx < mElements.size()); + + std::visit( + Overloaded{ + [&](const std::string& original) { mMinOutputChars -= original.size(); }, + [&](const Argument& original) { PreRemoveElement(idx); }, + }, + mElements[idx]); + + mMinOutputChars += text.size(); + mElements[idx] = std::move(text); +} + +void FormatTextNode::SetElement(int idx, ArgumentType argument) { + assert(idx >= 0 && idx < mElements.size()); + + std::visit( + Overloaded{ + [&](const std::string& original) { + mMinOutputChars -= original.size(); + + mElements[idx] = Argument{ + .ArgumentType = argument, + .PinIdx = Argument::FindPinForElement(mElements, idx), + }; + /* `original` is invalid from this point */ + }, + [&](const Argument& original) { + int pinIdx = original.PinIdx; + + // Create pin + auto& pin = mInputs[pinIdx]; + pin.MatchingType = ArgumentTypeToValueKind(argument); + + // Create element + mElements[idx] = Argument{ + .ArgumentType = argument, + .PinIdx = pinIdx, + }; + /* `original` is invalid from this point */ + }, + }, + mElements[idx]); +} + +void FormatTextNode::InsertElement(int idx, std::string text) { + assert(idx >= 0); + if (idx >= mElements.size()) AppendElement(std::move(text)); + + mMinOutputChars += text.size(); + mElements.insert(mElements.begin() + idx, std::move(text)); +} + +void FormatTextNode::InsertElement(int idx, ArgumentType argument) { + assert(idx >= 0); + if (idx >= mElements.size()) AppendElement(argument); + + int pinIdx = Argument::FindPinForElement(mElements, idx); + + // Create pin + auto& pin = InsertInputPin(pinIdx); + pin.MatchingType = ArgumentTypeToValueKind(argument); + + // Create element + mElements.insert( + mElements.begin() + idx, + Argument{ + .ArgumentType = argument, + .PinIdx = pinIdx, + }); +} + +void FormatTextNode::AppendElement(std::string text) { + mMinOutputChars += text.size(); + mElements.push_back(std::move(text)); +} + +void FormatTextNode::AppendElement(ArgumentType argument) { + int pinIdx = mInputs.size(); + // Create pin + mInputs.push_back(InputPin{}); + mInputs.back().MatchingType = ArgumentTypeToValueKind(argument); + // Creat eelement + mElements.push_back(Argument{ + .ArgumentType = argument, + .PinIdx = pinIdx, + }); +} + +void FormatTextNode::RemoveElement(int idx) { + assert(idx >= 0 && idx < mElements.size()); + + PreRemoveElement(idx); + if (auto arg = std::get_if<Argument>(&mElements[idx])) { + RemoveInputPin(arg->PinIdx); + } + mElements.erase(mElements.begin() + idx); +} + +void FormatTextNode::Evaluate(WorkflowEvaluationContext& ctx) { + std::string result; + result.reserve((size_t)(mMinOutputChars * 1.5f)); + + auto HandleText = [&](const std::string& str) { + result += str; + }; + auto HandleArgument = [&](const Argument& arg) { + switch (arg.ArgumentType) { + case NumericArgument: { + if (auto val = dyn_cast<NumericValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { + result += val->GetString(); + } else { + // TODO localize + ctx.ReportError("Non-numeric value connected to a numeric text format parameter.", *this); + } + } break; + case TextArgument: { + if (auto val = dyn_cast<TextValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { + result += val->GetValue(); + } else { + // TODO localize + ctx.ReportError("Non-text value connected to a textual text format parameter.", *this); + } + } break; + case DateTimeArgument: { + if (auto val = dyn_cast<DateTimeValue>(ctx.GetConnectionValue(mInputs[arg.PinIdx]))) { + result += val->GetString(); + } else { + // TODO localize + ctx.ReportError("Non-date/time value connected to a date/time text format parameter.", *this); + } + } break; + } + }; + + for (auto& elm : mElements) { + std::visit(Overloaded{ HandleText, HandleArgument }, elm); + } +} + +void FormatTextNode::PreRemoveElement(int idx) { + auto& elm = mElements[idx]; + if (auto arg = std::get_if<Argument>(&elm)) { + RemoveInputPin(arg->PinIdx); + Argument::ForArguments( + mElements.begin() + idx + 1, + mElements.end(), + [&](Argument& arg) { + arg.PinIdx--; + }); + } +} + +bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() == KD_DocumentTemplateExpansion; +} + +DocumentTemplateExpansionNode::DocumentTemplateExpansionNode() + : WorkflowNode(TransformType, KD_DocumentTemplateExpansion) { +} + +bool FormInputNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() == KD_FormInput; +} + +FormInputNode::FormInputNode() + : WorkflowNode(InputType, KD_FormInput) { +} + +bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) { + return node->GetKind() == KD_DatabaseRowsInput; +} + +DatabaseRowsInputNode::DatabaseRowsInputNode() + : WorkflowNode(InputType, KD_DatabaseRowsInput) { +} |