diff options
Diffstat (limited to 'app/source/Cplt/Model/Workflow/Nodes')
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.cpp | 18 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.hpp | 13 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/NumericNodes.cpp | 94 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/NumericNodes.hpp | 44 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/TextNodes.cpp | 231 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/TextNodes.hpp | 53 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.cpp | 32 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.hpp | 23 | ||||
-rw-r--r-- | app/source/Cplt/Model/Workflow/Nodes/fwd.hpp | 15 |
9 files changed, 523 insertions, 0 deletions
diff --git a/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.cpp b/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.cpp new file mode 100644 index 0000000..df4a8bb --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.cpp @@ -0,0 +1,18 @@ +#include "DocumentNodes.hpp" + +#include <Cplt/Model/Workflow/Evaluation.hpp> +#include <Cplt/Model/Workflow/Values/Basic.hpp> + +bool DocumentTemplateExpansionNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() == KD_DocumentTemplateExpansion; +} + +DocumentTemplateExpansionNode::DocumentTemplateExpansionNode() + : WorkflowNode(KD_DocumentTemplateExpansion, false) +{ +} + +void DocumentTemplateExpansionNode::Evaluate(WorkflowEvaluationContext& ctx) +{ +} diff --git a/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.hpp b/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.hpp new file mode 100644 index 0000000..a266b2c --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/DocumentNodes.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <Cplt/Model/Workflow/Workflow.hpp> + +class DocumentTemplateExpansionNode : public WorkflowNode +{ +public: + static bool IsInstance(const WorkflowNode* node); + DocumentTemplateExpansionNode(); + + // TODO + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; +}; diff --git a/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.cpp b/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.cpp new file mode 100644 index 0000000..f8b29bb --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.cpp @@ -0,0 +1,94 @@ +#include "NumericNodes.hpp" + +#include <Cplt/Model/Workflow/Evaluation.hpp> +#include <Cplt/Model/Workflow/Values/Basic.hpp> +#include <Cplt/Utils/I18n.hpp> +#include <Cplt/Utils/Macros.hpp> +#include <Cplt/Utils/RTTI.hpp> + +#include <cassert> +#include <utility> + +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: return InvalidKind; + } +} + +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: return InvalidType; + } +} + +bool NumericOperationNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() >= KD_NumericAddition && node->GetKind() <= KD_NumericDivision; +} + +NumericOperationNode::NumericOperationNode(OperationType type) + : WorkflowNode(OperationTypeToNodeKind(type), false) + , 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) { + ctx.ReportError(I18N_TEXT("Error: division by 0", L10N_WORKFLOW_RTERROR_DIV_BY_0), *this); + return; + } + res = lhs / rhs; + } break; + + default: return; + } + + auto value = std::make_unique<NumericValue>(); + value->SetValue(res); + ctx.SetConnectionValue(mOutputs[0], std::move(value)); +} + +bool NumericExpressionNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() == KD_NumericExpression; +} + +NumericExpressionNode::NumericExpressionNode() + : WorkflowNode(KD_NumericExpression, false) +{ +} + +void NumericExpressionNode::Evaluate(WorkflowEvaluationContext& ctx) +{ +} diff --git a/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.hpp b/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.hpp new file mode 100644 index 0000000..3c89708 --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/NumericNodes.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <Cplt/Model/Workflow/Workflow.hpp> + +#include <cstddef> +#include <memory> +#include <variant> +#include <vector> + +class NumericOperationNode : public WorkflowNode +{ +public: + enum OperationType + { + Addition, + Subtraction, + Multiplication, + Division, + + InvalidType, + TypeCount = InvalidType, + }; + +private: + OperationType mType; + +public: + static Kind OperationTypeToNodeKind(OperationType type); + static OperationType NodeKindToOperationType(Kind kind); + static bool IsInstance(const WorkflowNode* node); + NumericOperationNode(OperationType type); + + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; +}; + +class NumericExpressionNode : public WorkflowNode +{ +public: + static bool IsInstance(const WorkflowNode* node); + NumericExpressionNode(); + + // TODO + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; +};
\ No newline at end of file diff --git a/app/source/Cplt/Model/Workflow/Nodes/TextNodes.cpp b/app/source/Cplt/Model/Workflow/Nodes/TextNodes.cpp new file mode 100644 index 0000000..9b31f7a --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/TextNodes.cpp @@ -0,0 +1,231 @@ +#include "TextNodes.hpp" + +#include <Cplt/Model/Workflow/Evaluation.hpp> +#include <Cplt/Model/Workflow/Values/Basic.hpp> +#include <Cplt/Utils/Macros.hpp> +#include <Cplt/Utils/RTTI.hpp> +#include <Cplt/Utils/Variant.hpp> + +#include <cassert> +#include <utility> +#include <variant> +#include <vector> + +class TextFormatterNode::Impl +{ +public: + 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 TextFormatterNode::ArgumentTypeToValueKind(TextFormatterNode::ArgumentType arg) +{ + switch (arg) { + case NumericArgument: return BaseValue::KD_Numeric; + case TextArgument: return BaseValue::KD_Text; + case DateTimeArgument: return BaseValue::KD_DateTime; + } +} + +bool TextFormatterNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() == KD_TextFormatting; +} + +TextFormatterNode::TextFormatterNode() + : WorkflowNode(KD_TextFormatting, false) +{ +} + +int TextFormatterNode::GetElementCount() const +{ + return mElements.size(); +} + +const TextFormatterNode::Element& TextFormatterNode::GetElement(int idx) const +{ + return mElements[idx]; +} + +void TextFormatterNode::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 TextFormatterNode::SetElement(int idx, ArgumentType argument) +{ + assert(idx >= 0 && idx < mElements.size()); + + std::visit( + Overloaded{ + [&](const std::string& original) { + mMinOutputChars -= original.size(); + + mElements[idx] = Argument{ + .Type = argument, + .PinIdx = Impl::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{ + .Type = argument, + .PinIdx = pinIdx, + }; + /* `original` is invalid from this point */ + }, + }, + mElements[idx]); +} + +void TextFormatterNode::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 TextFormatterNode::InsertElement(int idx, ArgumentType argument) +{ + assert(idx >= 0); + if (idx >= mElements.size()) AppendElement(argument); + + int pinIdx = Impl::FindPinForElement(mElements, idx); + + // Create pin + auto& pin = InsertInputPin(pinIdx); + pin.MatchingType = ArgumentTypeToValueKind(argument); + + // Create element + mElements.insert( + mElements.begin() + idx, + Argument{ + .Type = argument, + .PinIdx = pinIdx, + }); +} + +void TextFormatterNode::AppendElement(std::string text) +{ + mMinOutputChars += text.size(); + mElements.push_back(std::move(text)); +} + +void TextFormatterNode::AppendElement(ArgumentType argument) +{ + int pinIdx = mInputs.size(); + // Create pin + mInputs.push_back(InputPin{}); + mInputs.back().MatchingType = ArgumentTypeToValueKind(argument); + // Creat eelement + mElements.push_back(Argument{ + .Type = argument, + .PinIdx = pinIdx, + }); +} + +void TextFormatterNode::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 TextFormatterNode::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.Type) { + 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 TextFormatterNode::PreRemoveElement(int idx) +{ + auto& elm = mElements[idx]; + if (auto arg = std::get_if<Argument>(&elm)) { + RemoveInputPin(arg->PinIdx); + Impl::ForArguments( + mElements.begin() + idx + 1, + mElements.end(), + [&](Argument& arg) { + arg.PinIdx--; + }); + } +} diff --git a/app/source/Cplt/Model/Workflow/Nodes/TextNodes.hpp b/app/source/Cplt/Model/Workflow/Nodes/TextNodes.hpp new file mode 100644 index 0000000..4689931 --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/TextNodes.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <Cplt/Model/Workflow/Workflow.hpp> + +#include <cstddef> +#include <memory> +#include <variant> +#include <vector> + +class TextFormatterNode : public WorkflowNode +{ +public: + enum ArgumentType + { + NumericArgument, + TextArgument, + DateTimeArgument, + }; + +private: + class Impl; + + struct Argument + { + ArgumentType Type; + int PinIdx; + }; + using Element = std::variant<std::string, Argument>; + + std::vector<Element> mElements; + int mMinOutputChars; + +public: + static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg); + static bool IsInstance(const WorkflowNode* node); + TextFormatterNode(); + + int GetElementCount() const; + const Element& GetElement(int idx) const; + + void SetElement(int idx, std::string text); + void SetElement(int idx, ArgumentType argument); + void InsertElement(int idx, std::string text); + void InsertElement(int idx, ArgumentType argument); + void AppendElement(std::string text); + void AppendElement(ArgumentType argument); + void RemoveElement(int idx); + + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; + +private: + void PreRemoveElement(int idx); +}; diff --git a/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.cpp b/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.cpp new file mode 100644 index 0000000..93d458c --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.cpp @@ -0,0 +1,32 @@ +#include "UserInputNodes.hpp" + +#include <Cplt/Model/Workflow/Evaluation.hpp> +#include <Cplt/Model/Workflow/Values/Basic.hpp> + +bool FormInputNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() == KD_FormInput; +} + +FormInputNode::FormInputNode() + : WorkflowNode(KD_FormInput, false) +{ +} + +void FormInputNode::Evaluate(WorkflowEvaluationContext& ctx) +{ +} + +bool DatabaseRowsInputNode::IsInstance(const WorkflowNode* node) +{ + return node->GetKind() == KD_DatabaseRowsInput; +} + +DatabaseRowsInputNode::DatabaseRowsInputNode() + : WorkflowNode(KD_DatabaseRowsInput, false) +{ +} + +void DatabaseRowsInputNode::Evaluate(WorkflowEvaluationContext& ctx) +{ +} diff --git a/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.hpp b/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.hpp new file mode 100644 index 0000000..f0b923c --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/UserInputNodes.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <Cplt/Model/Workflow/Workflow.hpp> + +class FormInputNode : public WorkflowNode +{ +public: + static bool IsInstance(const WorkflowNode* node); + FormInputNode(); + + // TODO + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; +}; + +class DatabaseRowsInputNode : public WorkflowNode +{ +public: + static bool IsInstance(const WorkflowNode* node); + DatabaseRowsInputNode(); + + // TODO + virtual void Evaluate(WorkflowEvaluationContext& ctx) override; +}; diff --git a/app/source/Cplt/Model/Workflow/Nodes/fwd.hpp b/app/source/Cplt/Model/Workflow/Nodes/fwd.hpp new file mode 100644 index 0000000..4153825 --- /dev/null +++ b/app/source/Cplt/Model/Workflow/Nodes/fwd.hpp @@ -0,0 +1,15 @@ +#pragma once + +// DocumentNodes.hpp +class DocumentTemplateExpansionNode; + +// InputNodes.hpp +class FormInputNode; +class DatabaseRowsInputNode; + +// NumericNodes.hpp +class NumericOperationNode; +class NumericExpressionNode; + +// TextNodes.hpp +class TextFormatterNode; |