From 34a875db053c219536b9b2de54f89e67186b842c Mon Sep 17 00:00:00 2001
From: Bensong Liu <bensl@microsoft.com>
Date: Tue, 20 Oct 2020 17:37:44 +0800
Subject: [PATCH] allow explicit argument and ignore argument

---
 activity.hpp            | 69 +++++++++++++++++++++++++++++++----------
 uuid.hpp => utility.hpp | 12 ++++++-
 xaml-template.hpp       | 21 +++++++++----
 xaml.gen.example.cc     |  6 +++-
 4 files changed, 83 insertions(+), 25 deletions(-)
 rename uuid.hpp => utility.hpp (70%)

diff --git a/activity.hpp b/activity.hpp
index db46a1a..2bcfcae 100644
--- a/activity.hpp
+++ b/activity.hpp
@@ -9,7 +9,7 @@
 #include <queue>
 #include <algorithm>
 
-#include "uuid.hpp"
+#include "utility.hpp"
 #include "xaml-template.hpp"
 #include <rlib/string.hpp>
 #include <rlib/require/cxx14>
@@ -24,39 +24,74 @@ namespace CIS {
     class Activity {
     public:
         friend Flow;
+        // All `Name` should not contain QuotationMark(")
         Activity(string displayName, string className, string entityName = "")
-            : displayName(displayName), className(className), entityName(entityName), taskId(GenUUID()) {}
+            : displayName(Utility::HtmlEscapeString(displayName)), className(className), entityName(entityName), taskId(Utility::GenUUID()) {}
         
         Flow operator>>(const Flow &seqNext) const;
         Flow operator|(const Flow &seqNext) const;
         void addInputSetting(string k, string v) {
             inputSettings[k] = v;
         }
-        void addRawActivityArgument(string xamlTypeString, string csharpValueCode) {
-            throw std::invalid_argument("Not implemented yet.");
+        void explicitSetRawArgument(string argTypeInXaml, string argValueInCSharp) {
+            explicitArgType = argTypeInXaml;
+            explicitArgValue = argValueInCSharp;
         }
     private:
         string displayName, className, entityName;
         string taskId;
         std::unordered_map<string, string> inputSettings;
+
+        string explicitArgType, explicitArgValue;
+
+        auto inputSettingsToCodelines() const {
+            // Convert InputSettings Dictionary to C# code. 
+            std::list<string> inputSettingStrings;
+            std::transform(this->inputSettings.begin(), this->inputSettings.end(), std::back_inserter(inputSettingStrings), [](auto &&kv) {
+                return "            {\"{}\", \"{}\"}"_rs.format(kv.first, kv.second);
+            });
+            auto inputSettingsString = ",\n"_rs.join(inputSettingStrings);
+            return rlib::string(templates::ACTIVITY_DICT_TEMPLATE_UNESCAPED).replace_once("__TEMPLATE_ARG_DictLines", inputSettingsString);
+        }
+        auto generateXaml() const {
+            rlib::string xamlCode = templates::ACTIVITY_XAML_TEMPLATE;
+
+            string argType, argValue;
+            if(explicitArgType.empty() && explicitArgValue.empty()) {
+                // no explicit argument specified. 
+                if(inputSettings.empty()) {
+                    // Also no inputSettings. 
+                    xamlCode = templates::ACTIVITY_XAML_TEMPLATE_WITHOUT_INPUTSETTINGS;
+                }
+                else {
+                    // Generate inputSettings.
+                    argType = templates::ACTIVITY_DICT_TYPENAME;
+                    argValue = inputSettingsToCodelines();
+                }
+            }
+            else {
+                // Use explicit argument.
+                argType = explicitArgType;
+                argValue = explicitArgValue;
+            }
+
+            xamlCode.replace("__TEMPLATE_ARG_TypeName", argType);
+            xamlCode.replace_once("__TEMPLATE_ARG_TypeValue", argValue);
+            xamlCode.replace_once("__TEMPLATE_ARG_ClassName", this->className);
+            xamlCode.replace_once("__TEMPLATE_ARG_DisplayName", this->displayName);
+            xamlCode.replace_once("__TEMPLATE_ARG_TaskId", this->taskId);
+
+            auto entityXaml = this->entityName == "" ? "" : rlib::string(templates::ENTITY_DEF_TEMPLATE).replace("__TEMPLATE_ARG_EntityName", this->entityName);
+            xamlCode.replace_once("__TEMPLATE_ARG_EntityDefPlaceholder", entityXaml);
+
+            return xamlCode;
+        }
     };
 
     class Flow {
     public:
         Flow(const Activity &activity) {
-            xamlCode = templates::ACTIVITY_XAML_TEMPLATE;
-            xamlCode.replace_once("__TEMPLATE_ARG_ClassName", activity.className);
-            xamlCode.replace_once("__TEMPLATE_ARG_DisplayName", activity.displayName);
-            xamlCode.replace_once("__TEMPLATE_ARG_TaskId", activity.taskId);
-            auto entityXaml = activity.entityName == "" ? "" : rlib::string(templates::ENTITY_DEF_TEMPLATE).replace("__TEMPLATE_ARG_EntityName", activity.entityName);
-            xamlCode.replace_once("__TEMPLATE_ARG_EntityDefPlaceholder", entityXaml);
-
-            std::list<string> inputSettingStrings;
-            std::transform(activity.inputSettings.begin(), activity.inputSettings.end(), std::back_inserter(inputSettingStrings), [](auto &&kv) {
-                return "{\"{}\", \"{}\"}"_rs.format(kv.first, kv.second);
-            });
-            auto inputSettingsString = ",\n            "_rs.join(inputSettingStrings);
-            xamlCode.replace_once("__TEMPLATE_ARG_DictLines", inputSettingsString);
+            xamlCode = activity.generateXaml();
         }
         Flow(rlib::string xamlCode) : xamlCode(xamlCode) {}
         Flow(const Flow &another) : queued(another.queued), xamlCode(another.xamlCode), prevOperationIsSequential(another.prevOperationIsSequential) {}
diff --git a/uuid.hpp b/utility.hpp
similarity index 70%
rename from uuid.hpp
rename to utility.hpp
index 35167b0..da1f935 100644
--- a/uuid.hpp
+++ b/utility.hpp
@@ -3,8 +3,12 @@
 
 #include <random>
 #include <string>
+#include <rlib/string.hpp>
 
-inline static std::string GenUUID() {
+namespace Utility {
+
+
+inline static auto GenUUID() {
     static std::random_device dev;
     static std::mt19937 rng(dev());
 
@@ -22,4 +26,10 @@ inline static std::string GenUUID() {
     return res;
 }
 
+inline static auto HtmlEscapeString(rlib::string s) {
+    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
+}
+
+}
+
 #endif
\ No newline at end of file
diff --git a/xaml-template.hpp b/xaml-template.hpp
index 26215f6..58a26eb 100644
--- a/xaml-template.hpp
+++ b/xaml-template.hpp
@@ -12,17 +12,26 @@ namespace templates {
 constexpr auto ACTIVITY_XAML_TEMPLATE = 
 R"XAMLTL(    <mwcwa:ControlledActivity ClassName="__TEMPLATE_ARG_ClassName" DisplayName="__TEMPLATE_ARG_DisplayName" TaskId="__TEMPLATE_ARG_TaskId" __TEMPLATE_ARG_EntityDefPlaceholder>
       <mwcwa:ControlledActivity.InputSettings>
-        <InArgument x:TypeArguments="scg:Dictionary(x:String, x:String)">
-          <mca:CSharpValue x:TypeArguments="scg:Dictionary(x:String, x:String)" xml:space="preserve">
-          new Dictionary&lt;string, string&gt;()
-          {
-            __TEMPLATE_ARG_DictLines
-          }
+        <InArgument x:TypeArguments="__TEMPLATE_ARG_TypeName">
+          <mca:CSharpValue x:TypeArguments="__TEMPLATE_ARG_TypeName" xml:space="preserve">
+__TEMPLATE_ARG_TypeValue
           </mca:CSharpValue>
         </InArgument>
       </mwcwa:ControlledActivity.InputSettings>
     </mwcwa:ControlledActivity>
 )XAMLTL";
+constexpr auto ACTIVITY_XAML_TEMPLATE_WITHOUT_INPUTSETTINGS = 
+R"XAMLTL(    <mwcwa:ControlledActivity ClassName="__TEMPLATE_ARG_ClassName" DisplayName="__TEMPLATE_ARG_DisplayName" TaskId="__TEMPLATE_ARG_TaskId" InputSettings="{x:Null}" __TEMPLATE_ARG_EntityDefPlaceholder>
+    </mwcwa:ControlledActivity>
+)XAMLTL";
+
+constexpr auto ACTIVITY_DICT_TYPENAME = "scg:Dictionary(x:String, x:String)";
+constexpr auto ACTIVITY_DICT_TEMPLATE_UNESCAPED = 
+R"XAMLTL(          new Dictionary<string, string>()
+          {
+__TEMPLATE_ARG_DictLines
+          }
+)XAMLTL";
 
 constexpr auto ENTITY_DEF_TEMPLATE = R"(coordination:DependencyBinder.EntityName="__TEMPLATE_ARG_EntityName")";
 
diff --git a/xaml.gen.example.cc b/xaml.gen.example.cc
index 91a467b..b0f8618 100644
--- a/xaml.gen.example.cc
+++ b/xaml.gen.example.cc
@@ -31,10 +31,14 @@ auto complexExample() {
     DEFINE_ACTIVITY(IntegrationTesting, "")
     DEFINE_ACTIVITY(TSConfigAndInterop, "PreRteg.InitiateBareMetalComplete")
 
+    // All Names of activity should not contain quotation mark (")
+    Activity OneMoreMagicActivity("MyName Contains Symbols: {(<&>)}", "FleetAGC.Workflow.Magic");
+    OneMoreMagicActivity.explicitSetRawArgument("x:Boolean", "(0b_1100_1000 | 0b_1000_0001 == 201)");
+
     auto block1 = SCS >> (SearchAnalytics | (SearchFarms >> (ClassisSearchUX | ModernSearch)));
     auto block3 = Loki >> Yggdrasil >> OfficeGraph;
     auto block4 = IC3Tooling >> (MonitoringSetup | (MicroServices >> DevelopmentValidation >> IntegrationTesting));
-    auto completeFlow = block1 | TSConfigAndInterop | block3 | block4;
+    auto completeFlow = (block1 | TSConfigAndInterop | block3 | block4) >> OneMoreMagicActivity;
 
     auto myMetadata = Metadata("FleetAGC.Workflows.BuildTeams").setXtraAssemblies({"FleetAGC.Workflows"});
     println(to_file("BuildTeams.xaml"), completeFlow.generateXaml(myMetadata));
-- 
GitLab