TranslationGeneration.cpp 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "TranslationGeneration.h"
  9. #include <Source/Translation/TranslationBus.h>
  10. #include <AzCore/JSON/rapidjson.h>
  11. #include <AzCore/JSON/document.h>
  12. #include <AzCore/JSON/stringbuffer.h>
  13. #include <AzCore/JSON/prettywriter.h>
  14. #include <AzCore/IO/FileIO.h>
  15. #include <AzCore/IO/SystemFile.h>
  16. #include <AzCore/IO/Path/Path.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzCore/Serialization/SerializeContext.h>
  19. #include <AzCore/Serialization/EditContext.h>
  20. #include <AzCore/std/string/regex.h>
  21. #include <Libraries/Core/AzEventHandler.h>
  22. #include <Libraries/Libraries.h>
  23. #include <Libraries/Core/GetVariable.h>
  24. #include <Libraries/Core/SetVariable.h>
  25. #include <AzFramework/StringFunc/StringFunc.h>
  26. #include <AzQtComponents/Utilities/DesktopUtilities.h>
  27. #include <Editor/Translation/TranslationHelper.h>
  28. #include <Source/Translation/TranslationSerializer.h>
  29. #include "Data/DataRegistry.h"
  30. namespace ScriptCanvasEditorTools
  31. {
  32. namespace Helpers
  33. {
  34. //! Convenience function that writes a key/value string pair into a given JSON value
  35. void WriteString(rapidjson::Value& owner, const AZStd::string& key, const AZStd::string& value, rapidjson::Document& document);
  36. }
  37. TranslationGeneration::TranslationGeneration()
  38. {
  39. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  40. AZ::ComponentApplicationBus::BroadcastResult(m_behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
  41. }
  42. void TranslationGeneration::TranslateBehaviorClasses()
  43. {
  44. for (const auto& behaviorClassPair : m_behaviorContext->m_classes)
  45. {
  46. TranslateBehaviorClass(behaviorClassPair.second);
  47. }
  48. }
  49. void TranslationGeneration::TranslateEBus(const AZ::BehaviorEBus* behaviorEBus)
  50. {
  51. if (ShouldSkip(behaviorEBus))
  52. {
  53. return;
  54. }
  55. TranslationFormat translationRoot;
  56. // Get the handlers
  57. if (!TranslateEBusHandler(behaviorEBus, translationRoot))
  58. {
  59. if (behaviorEBus->m_events.empty())
  60. {
  61. return;
  62. }
  63. Entry entry;
  64. // Generate the translation file
  65. entry.m_key = behaviorEBus->m_name;
  66. entry.m_details.m_category = Helpers::GetStringAttribute(behaviorEBus, AZ::Script::Attributes::Category);;
  67. entry.m_details.m_tooltip = behaviorEBus->m_toolTip;
  68. entry.m_details.m_name = behaviorEBus->m_name;
  69. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::EBusSenderContext;
  70. AZStd::string prettyName = Helpers::GetStringAttribute(behaviorEBus, AZ::ScriptCanvasAttributes::PrettyName);
  71. if (!prettyName.empty())
  72. {
  73. entry.m_details.m_name = prettyName;
  74. }
  75. SplitCamelCase(entry.m_details.m_name);
  76. for (auto event : behaviorEBus->m_events)
  77. {
  78. const AZ::BehaviorEBusEventSender& ebusSender = event.second;
  79. AZ::BehaviorMethod* method = ebusSender.m_event;
  80. if (!method)
  81. {
  82. method = ebusSender.m_broadcast;
  83. }
  84. if (!method)
  85. {
  86. AZ_Warning("Script Canvas", false, "Failed to find method: %s", event.first.c_str());
  87. continue;
  88. }
  89. Method eventEntry;
  90. const char* eventName = event.first.c_str();
  91. eventEntry.m_key = eventName;
  92. prettyName = Helpers::GetStringAttribute(behaviorEBus, AZ::ScriptCanvasAttributes::PrettyName);
  93. eventEntry.m_details.m_name = prettyName.empty() ? eventName : prettyName;
  94. eventEntry.m_details.m_tooltip = Helpers::ReadStringAttribute(event.second.m_attributes, AZ::Script::Attributes::ToolTip);
  95. SplitCamelCase(eventEntry.m_details.m_name);
  96. eventEntry.m_entry.m_name = "In";
  97. eventEntry.m_entry.m_tooltip = AZStd::string::format("When signaled, this will invoke %s", eventEntry.m_details.m_name.c_str());
  98. eventEntry.m_exit.m_name = "Out";
  99. eventEntry.m_exit.m_tooltip = AZStd::string::format("Signaled after %s is invoked", eventEntry.m_details.m_name.c_str());
  100. size_t start = method->HasBusId() ? 1 : 0;
  101. for (size_t i = start; i < method->GetNumArguments(); ++i)
  102. {
  103. Argument argument;
  104. auto argumentType = method->GetArgument(i)->m_typeId;
  105. // Check the BC for metadata
  106. Helpers::GetTypeNameAndDescription(argumentType, argument.m_details.m_name, argument.m_details.m_tooltip);
  107. auto name = method->GetArgumentName(i);
  108. if (name && !name->empty())
  109. {
  110. argument.m_details.m_name = *name;
  111. }
  112. auto tooltip = method->GetArgumentToolTip(i);
  113. if (tooltip && !tooltip->empty())
  114. {
  115. argument.m_details.m_tooltip = *tooltip;
  116. }
  117. argument.m_typeId = argumentType.ToString<AZStd::string>();
  118. SplitCamelCase(argument.m_details.m_name);
  119. eventEntry.m_arguments.push_back(argument);
  120. }
  121. if (method->HasResult())
  122. {
  123. TranslateMethodResults(method->GetResult(), eventEntry);
  124. }
  125. entry.m_methods.push_back(eventEntry);
  126. }
  127. translationRoot.m_entries.push_back(entry);
  128. SaveJSONData(AZStd::string::format("EBus/Senders/%s", behaviorEBus->m_name.c_str()), translationRoot);
  129. }
  130. else
  131. {
  132. SaveJSONData(AZStd::string::format("EBus/Handlers/%s", behaviorEBus->m_name.c_str()), translationRoot);
  133. }
  134. }
  135. AZ::Entity* TranslationGeneration::GetAZEventNode(const AZ::BehaviorMethod& method)
  136. {
  137. // Make sure the method returns an AZ::Event by reference or pointer
  138. if (AZ::MethodReturnsAzEventByReferenceOrPointer(method))
  139. {
  140. // Read in AZ Event Description data to retrieve the event name and parameter names
  141. AZ::Attribute* azEventDescAttribute = AZ::FindAttribute(AZ::Script::Attributes::AzEventDescription, method.m_attributes);
  142. AZ::BehaviorAzEventDescription behaviorAzEventDesc;
  143. AZ::AttributeReader azEventDescAttributeReader(nullptr, azEventDescAttribute);
  144. azEventDescAttributeReader.Read<decltype(behaviorAzEventDesc)>(behaviorAzEventDesc);
  145. if (behaviorAzEventDesc.m_eventName.empty())
  146. {
  147. AZ_Error("NodeUtils", false, "Cannot create an AzEvent node with empty event name")
  148. }
  149. auto scriptCanvasEntity = aznew AZ::Entity{ AZStd::string::format("SC-EventNode(%s)", behaviorAzEventDesc.m_eventName.c_str()) };
  150. scriptCanvasEntity->Init();
  151. auto azEventHandler = scriptCanvasEntity->CreateComponent<ScriptCanvas::Nodes::Core::AzEventHandler>();
  152. azEventHandler->InitEventFromMethod(method);
  153. return scriptCanvasEntity;
  154. }
  155. return nullptr;
  156. }
  157. bool TranslationGeneration::TranslateBehaviorClass(const AZ::BehaviorClass* behaviorClass)
  158. {
  159. if (ShouldSkip(behaviorClass))
  160. {
  161. return false;
  162. }
  163. AZStd::string className = behaviorClass->m_name;
  164. AZStd::string prettyName = Helpers::GetStringAttribute(behaviorClass, AZ::ScriptCanvasAttributes::PrettyName);
  165. if (!prettyName.empty())
  166. {
  167. className = prettyName;
  168. }
  169. TranslationFormat translationRoot;
  170. Entry entry;
  171. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::BehaviorClassContext;
  172. entry.m_key = behaviorClass->m_name;
  173. EntryDetails& details = entry.m_details;
  174. details.m_name = className;
  175. details.m_category = Helpers::GetStringAttribute(behaviorClass, AZ::Script::Attributes::Category);
  176. details.m_tooltip = Helpers::GetStringAttribute(behaviorClass, AZ::Script::Attributes::ToolTip);
  177. SplitCamelCase(details.m_name);
  178. if (!behaviorClass->m_methods.empty())
  179. {
  180. for (const auto& methodPair : behaviorClass->m_methods)
  181. {
  182. const AZ::BehaviorMethod* behaviorMethod = methodPair.second;
  183. if (TranslateSingleAZEvent(behaviorMethod))
  184. {
  185. continue;
  186. }
  187. Method methodEntry;
  188. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(methodPair.first);
  189. methodEntry.m_key = cleanName;
  190. methodEntry.m_details.m_category = "";
  191. methodEntry.m_details.m_tooltip = "";
  192. methodEntry.m_details.m_name = methodPair.second->m_name;
  193. AZStd::string prefix = className + "::";
  194. AZ::StringFunc::Replace(methodEntry.m_details.m_name, prefix.c_str(), "");
  195. SplitCamelCase(methodEntry.m_details.m_name);
  196. methodEntry.m_entry.m_name = "In";
  197. methodEntry.m_entry.m_tooltip = AZStd::string::format("When signaled, this will invoke %s", methodEntry.m_details.m_name.c_str());
  198. methodEntry.m_exit.m_name = "Out";
  199. methodEntry.m_exit.m_tooltip = AZStd::string::format("Signaled after %s is invoked", methodEntry.m_details.m_name.c_str());
  200. if (!Helpers::MethodHasAttribute(behaviorMethod, AZ::ScriptCanvasAttributes::FloatingFunction))
  201. {
  202. methodEntry.m_details.m_category = details.m_category;
  203. }
  204. else if (Helpers::MethodHasAttribute(behaviorMethod, AZ::Script::Attributes::Category))
  205. {
  206. methodEntry.m_details.m_category = Helpers::ReadStringAttribute(behaviorMethod->m_attributes, AZ::Script::Attributes::Category);
  207. }
  208. // Arguments (Input Slots)
  209. TranslateMethodArguments(behaviorMethod, methodEntry);
  210. // Results (Output Slots)
  211. const AZ::BehaviorParameter* resultParameter = behaviorMethod->HasResult() ? behaviorMethod->GetResult() : nullptr;
  212. if (resultParameter)
  213. {
  214. TranslateMethodResults(resultParameter, methodEntry);
  215. }
  216. entry.m_methods.push_back(methodEntry);
  217. }
  218. }
  219. // Behavior Class properties
  220. if (!behaviorClass->m_properties.empty())
  221. {
  222. for (const auto& propertyEntry : behaviorClass->m_properties)
  223. {
  224. AZ::BehaviorProperty* behaviorProperty = propertyEntry.second;
  225. if (behaviorProperty)
  226. {
  227. TranslateBehaviorProperty(behaviorProperty, behaviorClass->m_name, ScriptCanvasEditor::TranslationHelper::AssetContext::BehaviorClassContext, &entry);
  228. }
  229. }
  230. }
  231. translationRoot.m_entries.push_back(entry);
  232. AZStd::string sanitizedFilename = GraphCanvas::TranslationKey::Sanitize(className);
  233. AZStd::string fileName = AZStd::string::format("Classes/%s", sanitizedFilename.c_str());
  234. SaveJSONData(fileName, translationRoot);
  235. return true;
  236. }
  237. //! Generate the translation data for a specific AZ::Event
  238. bool TranslationGeneration::TranslateSingleAZEvent(const AZ::BehaviorMethod* method)
  239. {
  240. AZ::Entity* node = GetAZEventNode(*method);
  241. if (!node)
  242. {
  243. return false;
  244. }
  245. TranslationFormat translationRoot;
  246. ScriptCanvas::Nodes::Core::AzEventHandler* nodeComponent = node->FindComponent<ScriptCanvas::Nodes::Core::AzEventHandler>();
  247. nodeComponent->Init();
  248. nodeComponent->Configure();
  249. const ScriptCanvas::Nodes::Core::AzEventEntry& azEventEntry{ nodeComponent->GetEventEntry() };
  250. Entry entry;
  251. entry.m_key = azEventEntry.m_eventName;
  252. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::AZEventContext;
  253. entry.m_details.m_name = azEventEntry.m_eventName;
  254. SplitCamelCase(entry.m_details.m_name);
  255. for (const ScriptCanvas::Slot& slot : nodeComponent->GetSlots())
  256. {
  257. Slot slotEntry;
  258. if (slot.IsVisible())
  259. {
  260. slotEntry.m_key = slot.GetName();
  261. if (slot.GetId() == azEventEntry.m_azEventInputSlotId)
  262. {
  263. slotEntry.m_details.m_name = azEventEntry.m_eventName;
  264. }
  265. else
  266. {
  267. slotEntry.m_details.m_name = slot.GetName();
  268. }
  269. entry.m_slots.push_back(slotEntry);
  270. }
  271. }
  272. translationRoot.m_entries.push_back(entry);
  273. // delete the node, don't need to keep it beyond this point
  274. delete node;
  275. AZStd::string filename = GraphCanvas::TranslationKey::Sanitize(entry.m_key);
  276. AZStd::string targetFile = AZStd::string::format("AZEvents/%s", filename.c_str());
  277. SaveJSONData(targetFile, translationRoot);
  278. translationRoot.m_entries.clear();
  279. return true;
  280. }
  281. void TranslationGeneration::TranslateAZEvents()
  282. {
  283. GraphCanvas::TranslationKey translationKey;
  284. AZStd::vector<AZ::BehaviorMethod*> methods;
  285. // Methods
  286. for (const auto& behaviorMethod : m_behaviorContext->m_methods)
  287. {
  288. const auto method = behaviorMethod.second;
  289. methods.push_back(method);
  290. }
  291. // Methods in classes
  292. for (auto behaviorClass : m_behaviorContext->m_classes)
  293. {
  294. for (auto behaviorMethod : behaviorClass.second->m_methods)
  295. {
  296. const auto method = behaviorMethod.second;
  297. methods.push_back(method);
  298. }
  299. }
  300. for (auto& method : methods)
  301. {
  302. TranslateSingleAZEvent(method);
  303. }
  304. }
  305. void TranslationGeneration::TranslateNodes()
  306. {
  307. GraphCanvas::TranslationKey translationKey;
  308. AZStd::vector<AZ::TypeId> nodes;
  309. auto getNodeClasses = [this, &nodes](const AZ::SerializeContext::ClassData*, const AZ::Uuid& type)
  310. {
  311. bool foundBaseClass = false;
  312. auto baseClassVisitorFn = [&nodes, &type, &foundBaseClass](const AZ::SerializeContext::ClassData* reflectedBase, const AZ::TypeId& /*rttiBase*/)
  313. {
  314. if (!reflectedBase)
  315. {
  316. foundBaseClass = false;
  317. return false; // stop iterating
  318. }
  319. foundBaseClass = (reflectedBase->m_typeId == azrtti_typeid<ScriptCanvas::Node>());
  320. if (foundBaseClass)
  321. {
  322. nodes.push_back(type);
  323. return false; // we have a base, stop iterating
  324. }
  325. return true; // keep iterating
  326. };
  327. AZ::EntityUtils::EnumerateBaseRecursive(m_serializeContext, baseClassVisitorFn, type);
  328. return true;
  329. };
  330. m_serializeContext->EnumerateAll(getNodeClasses);
  331. for (auto& node : nodes)
  332. {
  333. TranslateNode(node);
  334. }
  335. }
  336. void TranslationGeneration::TranslateNode(const AZ::TypeId& nodeTypeId)
  337. {
  338. TranslationFormat translationRoot;
  339. if (const AZ::SerializeContext::ClassData* classData = m_serializeContext->FindClassData(nodeTypeId))
  340. {
  341. Entry entry;
  342. entry.m_key = classData->m_typeId.ToString<AZStd::string>();
  343. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::CustomNodeContext;
  344. EntryDetails& details = entry.m_details;
  345. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(classData->m_name);
  346. if (classData->m_editData)
  347. {
  348. details.m_name = classData->m_editData->m_name;
  349. }
  350. else
  351. {
  352. details.m_name = cleanName;
  353. }
  354. SplitCamelCase(details.m_name);
  355. // Tooltip attribute takes priority over the edit data description
  356. AZStd::string tooltip = Helpers::GetStringAttribute(classData, AZ::Script::Attributes::ToolTip);
  357. if (!tooltip.empty())
  358. {
  359. details.m_tooltip = tooltip;
  360. }
  361. else
  362. {
  363. details.m_tooltip = classData->m_editData ? classData->m_editData->m_description : "";
  364. }
  365. details.m_category = Helpers::GetStringAttribute(classData, AZ::Script::Attributes::Category);
  366. if (details.m_subtitle.empty())
  367. {
  368. details.m_subtitle = details.m_category;
  369. }
  370. if (details.m_category.empty())
  371. {
  372. details.m_category = Helpers::GetStringAttribute(classData, AZ::Script::Attributes::Category);
  373. if (details.m_category.empty() && classData->m_editData)
  374. {
  375. details.m_category = Helpers::GetCategory(classData);
  376. if (details.m_category.empty())
  377. {
  378. auto elementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
  379. const AZStd::string categoryAttribute = Helpers::ReadStringAttribute(elementData->m_attributes, AZ::Script::Attributes::Category);
  380. if (!categoryAttribute.empty())
  381. {
  382. details.m_category = categoryAttribute;
  383. }
  384. }
  385. }
  386. }
  387. if (ScriptCanvas::Node* nodeComponent = reinterpret_cast<ScriptCanvas::Node*>(classData->m_factory->Create(classData->m_name)))
  388. {
  389. nodeComponent->Init();
  390. nodeComponent->Configure();
  391. int exeInputIndex = 0;
  392. int exeOutputIndex = 0;
  393. int dataInputIndex = 0;
  394. int dataOutputIndex = 0;
  395. const auto& allSlots = nodeComponent->GetAllSlots();
  396. for (const auto& slot : allSlots)
  397. {
  398. Slot slotEntry;
  399. if (slot->GetDescriptor().IsExecution())
  400. {
  401. if (slot->GetDescriptor().IsInput())
  402. {
  403. slotEntry.m_key = AZStd::string::format("Input_%s_%d", slot->GetName().c_str(), exeInputIndex);
  404. exeInputIndex++;
  405. slotEntry.m_details.m_name = slot->GetName();
  406. slotEntry.m_details.m_tooltip = slot->GetToolTip();
  407. }
  408. else if (slot->GetDescriptor().IsOutput())
  409. {
  410. slotEntry.m_key = AZStd::string::format("Output_%s_%d", slot->GetName().c_str(), exeOutputIndex);
  411. exeOutputIndex++;
  412. slotEntry.m_details.m_name = slot->GetName();
  413. slotEntry.m_details.m_tooltip = slot->GetToolTip();
  414. }
  415. entry.m_slots.push_back(slotEntry);
  416. }
  417. else
  418. {
  419. AZStd::string slotTypeKey = slot->GetDataType().IsValid() ? ScriptCanvas::Data::GetName(slot->GetDataType()) : "";
  420. if (slotTypeKey.empty())
  421. {
  422. if (!slot->GetDataType().GetAZType().IsNull())
  423. {
  424. slotTypeKey = slot->GetDataType().GetAZType().ToString<AZStd::string>();
  425. }
  426. }
  427. if (slotTypeKey.empty())
  428. {
  429. if (slot->GetDynamicDataType() == ScriptCanvas::DynamicDataType::Container)
  430. {
  431. slotTypeKey = "Container";
  432. }
  433. else if (slot->GetDynamicDataType() == ScriptCanvas::DynamicDataType::Value)
  434. {
  435. slotTypeKey = "Value";
  436. }
  437. else if (slot->GetDynamicDataType() == ScriptCanvas::DynamicDataType::Any)
  438. {
  439. slotTypeKey = "Any";
  440. }
  441. }
  442. Argument& argument = slotEntry.m_data;
  443. if (slot->GetDescriptor().IsInput())
  444. {
  445. slotEntry.m_key = AZStd::string::format("DataInput_%s_%d", slot->GetName().c_str(), dataInputIndex);
  446. dataInputIndex++;
  447. AZStd::string argumentKey = slotTypeKey;
  448. AZStd::string argumentName = slot->GetName();
  449. AZStd::string argumentDescription = slot->GetToolTip();
  450. argument.m_typeId = argumentKey;
  451. argument.m_details.m_name = argumentName;
  452. argument.m_details.m_tooltip = argumentDescription;
  453. }
  454. else if (slot->GetDescriptor().IsOutput())
  455. {
  456. slotEntry.m_key = AZStd::string::format("DataOutput_%s_%d", slot->GetName().c_str(), dataOutputIndex);
  457. dataOutputIndex++;
  458. AZStd::string resultKey = slotTypeKey;
  459. AZStd::string resultName = slot->GetName();
  460. AZStd::string resultDescription = slot->GetToolTip();
  461. argument.m_typeId = resultKey;
  462. argument.m_details.m_name = resultName;
  463. argument.m_details.m_tooltip = resultDescription;
  464. }
  465. entry.m_slots.push_back(slotEntry);
  466. }
  467. }
  468. delete nodeComponent;
  469. }
  470. translationRoot.m_entries.push_back(entry);
  471. if (details.m_category.empty())
  472. {
  473. details.m_category = "Uncategorized";
  474. }
  475. AZStd::string fileName = ScriptCanvasEditor::TranslationHelper::SanitizeCustomNodeFileName(details.m_name, classData->m_typeId);
  476. AZStd::string targetFile = AZStd::string::format("Nodes/%s", fileName.c_str());
  477. SaveJSONData(targetFile, translationRoot);
  478. translationRoot.m_entries.clear();
  479. }
  480. }
  481. void TranslationGeneration::TranslateOnDemandReflectedTypes(TranslationFormat& translationRoot)
  482. {
  483. AZStd::vector<AZ::Uuid> onDemandReflectedTypes;
  484. for (auto& typePair : m_behaviorContext->m_typeToClassMap)
  485. {
  486. if (m_behaviorContext->IsOnDemandTypeReflected(typePair.first))
  487. {
  488. onDemandReflectedTypes.push_back(typePair.first);
  489. }
  490. // Check for methods that come from node generics
  491. if (typePair.second->HasAttribute(AZ::ScriptCanvasAttributes::Internal::ImplementedAsNodeGeneric))
  492. {
  493. onDemandReflectedTypes.push_back(typePair.first);
  494. }
  495. }
  496. // Now that I know all the on demand reflected, I'll dump it out
  497. for (auto& onDemandReflectedType : onDemandReflectedTypes)
  498. {
  499. AZ::BehaviorClass* behaviorClass = m_behaviorContext->m_typeToClassMap[onDemandReflectedType];
  500. if (behaviorClass)
  501. {
  502. Entry entry;
  503. EntryDetails& details = entry.m_details;
  504. details.m_name = behaviorClass->m_name;
  505. SplitCamelCase(details.m_name);
  506. // Get the pretty name
  507. AZStd::string prettyName;
  508. if (AZ::Attribute* prettyNameAttribute = AZ::FindAttribute(AZ::ScriptCanvasAttributes::PrettyName, behaviorClass->m_attributes))
  509. {
  510. AZ::AttributeReader(nullptr, prettyNameAttribute).Read<AZStd::string>(prettyName, *m_behaviorContext);
  511. }
  512. entry.m_context = "OnDemandReflected";
  513. entry.m_key = behaviorClass->m_typeId.ToString<AZStd::string>().c_str();
  514. if (!prettyName.empty())
  515. {
  516. details.m_name = prettyName;
  517. }
  518. details.m_category = Helpers::GetStringAttribute(behaviorClass, AZ::Script::Attributes::Category);
  519. details.m_tooltip = Helpers::GetStringAttribute(behaviorClass, AZ::Script::Attributes::ToolTip);
  520. for (auto& methodPair : behaviorClass->m_methods)
  521. {
  522. AZ::BehaviorMethod* behaviorMethod = methodPair.second;
  523. if (behaviorMethod)
  524. {
  525. Method methodEntry;
  526. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(methodPair.first);
  527. methodEntry.m_key = cleanName;
  528. methodEntry.m_context = entry.m_key;
  529. methodEntry.m_details.m_tooltip = Helpers::GetStringAttribute(behaviorMethod, AZ::Script::Attributes::ToolTip);
  530. methodEntry.m_details.m_name = methodPair.second->m_name;
  531. SplitCamelCase(methodEntry.m_details.m_name);
  532. // Strip the className from the methodName
  533. AZStd::string qualifiedName = behaviorClass->m_name + "::";
  534. AzFramework::StringFunc::Replace(methodEntry.m_details.m_name, qualifiedName.c_str(), "");
  535. AZStd::string cleanMethodName = methodEntry.m_details.m_name;
  536. methodEntry.m_entry.m_name = "In";
  537. methodEntry.m_entry.m_tooltip = AZStd::string::format("When signaled, this will invoke %s", methodEntry.m_details.m_name.c_str());
  538. methodEntry.m_exit.m_name = "Out";
  539. methodEntry.m_exit.m_tooltip = AZStd::string::format("Signaled after %s is invoked", methodEntry.m_details.m_name.c_str());
  540. // Arguments (Input Slots)
  541. TranslateMethodArguments(behaviorMethod, methodEntry);
  542. const AZ::BehaviorParameter* resultParameter = behaviorMethod->HasResult() ? behaviorMethod->GetResult() : nullptr;
  543. if (resultParameter)
  544. {
  545. TranslateMethodResults(resultParameter, methodEntry);
  546. }
  547. entry.m_methods.push_back(methodEntry);
  548. }
  549. }
  550. translationRoot.m_entries.push_back(entry);
  551. }
  552. }
  553. SaveJSONData("Types/OnDemandReflectedTypes", translationRoot);
  554. }
  555. void TranslationGeneration::TranslateBehaviorGlobals()
  556. {
  557. for (const auto& [propertyName, behaviorProperty] : m_behaviorContext->m_properties)
  558. {
  559. TranslateBehaviorProperty(propertyName);
  560. }
  561. }
  562. void TranslationGeneration::TranslateBehaviorGlobalMethod(const AZStd::string& methodName)
  563. {
  564. const auto behaviorMethodEntry = m_behaviorContext->m_methods.find(methodName);
  565. if (behaviorMethodEntry == m_behaviorContext->m_methods.end())
  566. {
  567. return;
  568. }
  569. Entry entry;
  570. entry.m_key = methodName;
  571. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::BehaviorGlobalMethodContext;
  572. entry.m_details.m_name = methodName;
  573. SplitCamelCase(entry.m_details.m_name);
  574. AZ::BehaviorMethod* behaviorMethod = behaviorMethodEntry->second;
  575. if (behaviorMethod)
  576. {
  577. AZStd::string cleanName = behaviorMethod->m_name;
  578. Method method;
  579. method.m_key = cleanName;
  580. method.m_details.m_name = cleanName;
  581. method.m_details.m_tooltip = behaviorMethod->m_debugDescription ? behaviorMethod->m_debugDescription : "";
  582. SplitCamelCase(method.m_details.m_name);
  583. TranslateMethod(behaviorMethod, method);
  584. entry.m_methods.push_back(method);
  585. }
  586. TranslationFormat translationRoot;
  587. translationRoot.m_entries.push_back(entry);
  588. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(behaviorMethod->m_name);
  589. AZStd::string fileName = AZStd::string::format("GlobalMethods/%s", cleanName.c_str());
  590. SaveJSONData(fileName, translationRoot);
  591. }
  592. void TranslationGeneration::TranslateBehaviorProperty(const AZStd::string& propertyName)
  593. {
  594. const auto behaviorPropertyEntry = m_behaviorContext->m_properties.find(propertyName);
  595. if (behaviorPropertyEntry == m_behaviorContext->m_properties.end())
  596. {
  597. return;
  598. }
  599. const AZ::BehaviorProperty* behaviorProperty = behaviorPropertyEntry->second;
  600. Entry entry;
  601. TranslateBehaviorProperty(behaviorProperty, propertyName, ScriptCanvasEditor::TranslationHelper::AssetContext::BehaviorGlobalPropertyContext, &entry);
  602. TranslationFormat translationRoot;
  603. translationRoot.m_entries.push_back(entry);
  604. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(behaviorProperty->m_name);
  605. AZStd::string fileName = AZStd::string::format("Properties/%s", cleanName.c_str());
  606. SaveJSONData(fileName, translationRoot);
  607. }
  608. void TranslationGeneration::TranslateDataTypes()
  609. {
  610. TranslationFormat translationRoot;
  611. auto dataRegistry = ScriptCanvas::GetDataRegistry();
  612. for (auto& typePair : dataRegistry->m_creatableTypes)
  613. {
  614. if (ScriptCanvas::Data::IsContainerType(typePair.first))
  615. {
  616. continue;
  617. }
  618. const AZStd::string typeIDStr = typePair.first.GetAZType().ToString<AZStd::string>();
  619. AZStd::string typeName = ScriptCanvas::Data::GetName(typePair.first);
  620. Entry entry;
  621. entry.m_key = typeIDStr;
  622. entry.m_context = "BehaviorType";
  623. entry.m_details.m_name = typeName;
  624. translationRoot.m_entries.emplace_back(entry);
  625. }
  626. SaveJSONData("Types/BehaviorTypes", translationRoot);
  627. }
  628. void TranslationGeneration::TranslateMethod(AZ::BehaviorMethod* behaviorMethod, Method& methodEntry)
  629. {
  630. // Arguments (Input Slots)
  631. TranslateMethodArguments(behaviorMethod, methodEntry);
  632. // Results (Output Slots)
  633. const AZ::BehaviorParameter* resultParameter = behaviorMethod->HasResult() ? behaviorMethod->GetResult() : nullptr;
  634. if (resultParameter)
  635. {
  636. TranslateMethodResults(resultParameter, methodEntry);
  637. }
  638. }
  639. void TranslationGeneration::TranslateMethodArguments(const AZ::BehaviorMethod* behaviorMethod, Method& methodEntry)
  640. {
  641. if (behaviorMethod->GetNumArguments() > 0)
  642. {
  643. for (size_t argIndex = 0; argIndex < behaviorMethod->GetNumArguments(); ++argIndex)
  644. {
  645. const AZ::BehaviorParameter* parameter = behaviorMethod->GetArgument(argIndex);
  646. Argument argument;
  647. AZStd::string argumentKey = parameter->m_typeId.ToString<AZStd::string>();
  648. AZStd::string argumentName = parameter->m_name;
  649. AZStd::string argumentDescription;
  650. Helpers::GetTypeNameAndDescription(parameter->m_typeId, argumentName, argumentDescription);
  651. argument.m_typeId = argumentKey;
  652. const AZStd::string* argName = behaviorMethod->GetArgumentName(argIndex);
  653. argument.m_details.m_name = (argName && !argName->empty()) ? *argName : argumentName;
  654. const AZStd::string* argToolTip = behaviorMethod->GetArgumentToolTip(argIndex);
  655. argument.m_details.m_tooltip = (argToolTip && !argToolTip->empty()) ? *argToolTip : argumentDescription;
  656. SplitCamelCase(argument.m_details.m_name);
  657. methodEntry.m_arguments.push_back(argument);
  658. }
  659. }
  660. }
  661. void TranslationGeneration::TranslateMethodResults(const AZ::BehaviorParameter* resultParameter, Method& methodEntry)
  662. {
  663. if (resultParameter)
  664. {
  665. AZStd::vector<AZ::TypeId> unpackedTypes = Helpers::GetUnpackedTypes(resultParameter->m_typeId);
  666. size_t resultNum = unpackedTypes.size();
  667. for (size_t resultIndex = 0; resultIndex < resultNum; ++resultIndex)
  668. {
  669. Argument result;
  670. AZStd::string resultKey = unpackedTypes[resultIndex].ToString<AZStd::string>();
  671. AZStd::string resultName;
  672. AZStd::string resultDescription;
  673. Helpers::GetTypeNameAndDescription(unpackedTypes[resultIndex], resultName, resultDescription);
  674. result.m_typeId = resultKey;
  675. result.m_details.m_name = resultName;
  676. result.m_details.m_tooltip = resultDescription;
  677. SplitCamelCase(result.m_details.m_name);
  678. methodEntry.m_results.push_back(result);
  679. }
  680. }
  681. }
  682. void TranslationGeneration::TranslateBehaviorProperty(const AZ::BehaviorProperty* behaviorProperty, const AZStd::string& className, const AZStd::string& context, Entry* entry)
  683. {
  684. if (!behaviorProperty->m_getter && !behaviorProperty->m_setter)
  685. {
  686. return;
  687. }
  688. Entry localEntry;
  689. if (!entry)
  690. {
  691. entry = &localEntry;
  692. }
  693. else if (entry->m_key.empty())
  694. {
  695. entry->m_key = className;
  696. entry->m_context = context;
  697. entry->m_details.m_name = className;
  698. SplitCamelCase(entry->m_details.m_name);
  699. }
  700. if (behaviorProperty->m_getter)
  701. {
  702. AZStd::string cleanName = behaviorProperty->m_name;
  703. AZ::StringFunc::Replace(cleanName, "::Getter", "");
  704. Method method;
  705. AZStd::string methodName = "Get";
  706. methodName.append(cleanName);
  707. method.m_key = methodName;
  708. method.m_context = "Getter";
  709. method.m_details.m_name = methodName;
  710. method.m_details.m_tooltip = behaviorProperty->m_getter->m_debugDescription ? behaviorProperty->m_getter->m_debugDescription : "";
  711. SplitCamelCase(method.m_details.m_name);
  712. TranslateMethod(behaviorProperty->m_getter, method);
  713. // We know this is a getter, so there will only be one parameter, we will use the method name as a best
  714. // guess for the argument name
  715. SplitCamelCase(cleanName);
  716. method.m_results[0].m_details.m_name = cleanName;
  717. entry->m_methods.push_back(method);
  718. }
  719. if (behaviorProperty->m_setter)
  720. {
  721. AZStd::string cleanName = behaviorProperty->m_name;
  722. AZ::StringFunc::Replace(cleanName, "::Setter", "");
  723. Method method;
  724. AZStd::string methodName = "Set";
  725. methodName.append(cleanName);
  726. method.m_key = methodName;
  727. method.m_context = "Setter";
  728. method.m_details.m_name = methodName;
  729. method.m_details.m_tooltip = behaviorProperty->m_setter->m_debugDescription ? behaviorProperty->m_getter->m_debugDescription : "";
  730. SplitCamelCase(method.m_details.m_name);
  731. TranslateMethod(behaviorProperty->m_setter, method);
  732. // We know this is a setter, so there will only be one parameter, we will use the method name as a best
  733. // guess for the argument name
  734. SplitCamelCase(cleanName);
  735. method.m_arguments[1].m_details.m_name = cleanName;
  736. entry->m_methods.push_back(method);
  737. }
  738. }
  739. bool TranslationGeneration::TranslateEBusHandler(const AZ::BehaviorEBus* behaviorEbus, TranslationFormat& translationRoot)
  740. {
  741. // Must be a valid ebus handler
  742. if (!behaviorEbus || !behaviorEbus->m_createHandler || !behaviorEbus->m_destroyHandler)
  743. {
  744. return false;
  745. }
  746. // Create the handler in order to get information out of it
  747. AZ::BehaviorEBusHandler* handler(nullptr);
  748. if (behaviorEbus->m_createHandler->InvokeResult(handler))
  749. {
  750. Entry entry;
  751. // Generate the translation file
  752. entry.m_key = behaviorEbus->m_name;
  753. entry.m_context = ScriptCanvasEditor::TranslationHelper::AssetContext::EBusHandlerContext;
  754. entry.m_details.m_name = behaviorEbus->m_name;
  755. entry.m_details.m_tooltip = behaviorEbus->m_toolTip;
  756. entry.m_details.m_category = "EBus Handlers";
  757. SplitCamelCase(entry.m_details.m_name);
  758. for (const AZ::BehaviorEBusHandler::BusForwarderEvent& event : handler->GetEvents())
  759. {
  760. Method methodEntry;
  761. AZStd::string cleanName = GraphCanvas::TranslationKey::Sanitize(event.m_name);
  762. methodEntry.m_key = cleanName;
  763. methodEntry.m_details.m_category = "";
  764. methodEntry.m_details.m_tooltip = "";
  765. methodEntry.m_details.m_name = event.m_name;
  766. SplitCamelCase(methodEntry.m_details.m_name);
  767. // Arguments (Input Slots)
  768. if (!event.m_parameters.empty())
  769. {
  770. for (size_t argIndex = AZ::eBehaviorBusForwarderEventIndices::ParameterFirst; argIndex < event.m_parameters.size(); ++argIndex)
  771. {
  772. const AZ::BehaviorParameter& parameter = event.m_parameters[argIndex];
  773. Argument argument;
  774. AZStd::string argumentKey = parameter.m_typeId.ToString<AZStd::string>();
  775. AZStd::string argumentName = event.m_name;
  776. AZStd::string argumentDescription = "";
  777. if (!event.m_metadataParameters.empty() && event.m_metadataParameters.size() > argIndex)
  778. {
  779. argumentName = event.m_metadataParameters[argIndex].m_name;
  780. argumentDescription = event.m_metadataParameters[argIndex].m_toolTip;
  781. }
  782. if (argumentName.empty())
  783. {
  784. Helpers::GetTypeNameAndDescription(parameter.m_typeId, argumentName, argumentDescription);
  785. }
  786. if (!event.m_metadataParameters.empty() && event.m_metadataParameters.size() > argIndex)
  787. {
  788. auto name = event.m_metadataParameters[argIndex].m_name;
  789. auto tooltip = event.m_metadataParameters[argIndex].m_toolTip;
  790. if (!name.empty())
  791. {
  792. argumentName = name;
  793. }
  794. if (!tooltip.empty())
  795. {
  796. argumentDescription = tooltip;
  797. }
  798. }
  799. argument.m_typeId = argumentKey;
  800. argument.m_details.m_name = argumentName;
  801. argument.m_details.m_tooltip = argumentDescription;
  802. SplitCamelCase(argument.m_details.m_name);
  803. methodEntry.m_arguments.push_back(argument);
  804. }
  805. }
  806. auto resultIndex = AZ::eBehaviorBusForwarderEventIndices::Result;
  807. const AZ::BehaviorParameter* resultParameter = event.HasResult() ? &event.m_parameters[resultIndex] : nullptr;
  808. if (resultParameter)
  809. {
  810. Argument result;
  811. AZStd::string resultKey = resultParameter->m_typeId.ToString<AZStd::string>();
  812. AZStd::string resultName = event.m_name;
  813. AZStd::string resultDescription = "";
  814. if (!event.m_metadataParameters.empty() && event.m_metadataParameters.size() > resultIndex)
  815. {
  816. resultName = event.m_metadataParameters[resultIndex].m_name;
  817. resultDescription = event.m_metadataParameters[resultIndex].m_toolTip;
  818. }
  819. if (resultName.empty())
  820. {
  821. Helpers::GetTypeNameAndDescription(resultParameter->m_typeId, resultName, resultDescription);
  822. }
  823. result.m_typeId = resultKey;
  824. result.m_details.m_name = resultName;
  825. result.m_details.m_tooltip = resultDescription;
  826. SplitCamelCase(result.m_details.m_name);
  827. methodEntry.m_results.push_back(result);
  828. }
  829. entry.m_methods.push_back(methodEntry);
  830. }
  831. behaviorEbus->m_destroyHandler->Invoke(handler); // Destroys the Created EbusHandler
  832. translationRoot.m_entries.push_back(entry);
  833. }
  834. if (!translationRoot.m_entries.empty())
  835. {
  836. return true;
  837. }
  838. return false;
  839. }
  840. void TranslationGeneration::SaveJSONData(const AZStd::string& filename, TranslationFormat& translationRoot)
  841. {
  842. rapidjson::Document document;
  843. document.SetObject();
  844. rapidjson::Value entries(rapidjson::kArrayType);
  845. // Here I'll need to parse translationRoot myself and produce the JSON
  846. for (const auto& entrySource : translationRoot.m_entries)
  847. {
  848. rapidjson::Value entry(rapidjson::kObjectType);
  849. rapidjson::Value value(rapidjson::kStringType);
  850. value.SetString(entrySource.m_key.c_str(), document.GetAllocator());
  851. entry.AddMember(GraphCanvas::Schema::Field::key, value, document.GetAllocator());
  852. value.SetString(entrySource.m_context.c_str(), document.GetAllocator());
  853. entry.AddMember(GraphCanvas::Schema::Field::context, value, document.GetAllocator());
  854. value.SetString(entrySource.m_variant.c_str(), document.GetAllocator());
  855. entry.AddMember(GraphCanvas::Schema::Field::variant, value, document.GetAllocator());
  856. rapidjson::Value details(rapidjson::kObjectType);
  857. value.SetString(entrySource.m_details.m_name.c_str(), document.GetAllocator());
  858. details.AddMember("name", value, document.GetAllocator());
  859. Helpers::WriteString(details, "category", entrySource.m_details.m_category, document);
  860. Helpers::WriteString(details, "tooltip", entrySource.m_details.m_tooltip, document);
  861. Helpers::WriteString(details, "subtitle", entrySource.m_details.m_subtitle, document);
  862. entry.AddMember("details", details, document.GetAllocator());
  863. if (!entrySource.m_methods.empty())
  864. {
  865. rapidjson::Value methods(rapidjson::kArrayType);
  866. for (const auto& methodSource : entrySource.m_methods)
  867. {
  868. rapidjson::Value theMethod(rapidjson::kObjectType);
  869. value.SetString(methodSource.m_key.c_str(), document.GetAllocator());
  870. theMethod.AddMember(GraphCanvas::Schema::Field::key, value, document.GetAllocator());
  871. if (!methodSource.m_context.empty())
  872. {
  873. value.SetString(methodSource.m_context.c_str(), document.GetAllocator());
  874. if (value == "Getter" || value == "Setter")
  875. {
  876. theMethod.AddMember(GraphCanvas::Schema::Field::context, value, document.GetAllocator());
  877. }
  878. }
  879. if (!methodSource.m_entry.m_name.empty())
  880. {
  881. rapidjson::Value entrySlot(rapidjson::kObjectType);
  882. value.SetString(methodSource.m_entry.m_name.c_str(), document.GetAllocator());
  883. entrySlot.AddMember("name", value, document.GetAllocator());
  884. Helpers::WriteString(entrySlot, "tooltip", methodSource.m_entry.m_tooltip, document);
  885. theMethod.AddMember("entry", entrySlot, document.GetAllocator());
  886. }
  887. if (!methodSource.m_exit.m_name.empty())
  888. {
  889. rapidjson::Value exitSlot(rapidjson::kObjectType);
  890. value.SetString(methodSource.m_exit.m_name.c_str(), document.GetAllocator());
  891. exitSlot.AddMember("name", value, document.GetAllocator());
  892. Helpers::WriteString(exitSlot, "tooltip", methodSource.m_exit.m_tooltip, document);
  893. theMethod.AddMember("exit", exitSlot, document.GetAllocator());
  894. }
  895. rapidjson::Value methodDetails(rapidjson::kObjectType);
  896. value.SetString(methodSource.m_details.m_name.c_str(), document.GetAllocator());
  897. methodDetails.AddMember("name", value, document.GetAllocator());
  898. Helpers::WriteString(methodDetails, "category", methodSource.m_details.m_category, document);
  899. Helpers::WriteString(methodDetails, "tooltip", methodSource.m_details.m_tooltip, document);
  900. theMethod.AddMember("details", methodDetails, document.GetAllocator());
  901. if (!methodSource.m_arguments.empty())
  902. {
  903. rapidjson::Value methodArguments(rapidjson::kArrayType);
  904. [[maybe_unused]] size_t index = 0;
  905. for (const auto& argSource : methodSource.m_arguments)
  906. {
  907. rapidjson::Value argument(rapidjson::kObjectType);
  908. rapidjson::Value argumentDetails(rapidjson::kObjectType);
  909. value.SetString(argSource.m_typeId.c_str(), document.GetAllocator());
  910. argument.AddMember("typeid", value, document.GetAllocator());
  911. value.SetString(argSource.m_details.m_name.c_str(), document.GetAllocator());
  912. argumentDetails.AddMember("name", value, document.GetAllocator());
  913. Helpers::WriteString(argumentDetails, "category", argSource.m_details.m_category, document);
  914. Helpers::WriteString(argumentDetails, "tooltip", argSource.m_details.m_tooltip, document);
  915. argument.AddMember("details", argumentDetails, document.GetAllocator());
  916. methodArguments.PushBack(argument, document.GetAllocator());
  917. }
  918. theMethod.AddMember("params", methodArguments, document.GetAllocator());
  919. }
  920. if (!methodSource.m_results.empty())
  921. {
  922. rapidjson::Value methodArguments(rapidjson::kArrayType);
  923. for (const auto& argSource : methodSource.m_results)
  924. {
  925. rapidjson::Value argument(rapidjson::kObjectType);
  926. rapidjson::Value argumentDetails(rapidjson::kObjectType);
  927. value.SetString(argSource.m_typeId.c_str(), document.GetAllocator());
  928. argument.AddMember("typeid", value, document.GetAllocator());
  929. value.SetString(argSource.m_details.m_name.c_str(), document.GetAllocator());
  930. argumentDetails.AddMember("name", value, document.GetAllocator());
  931. Helpers::WriteString(argumentDetails, "category", argSource.m_details.m_category, document);
  932. Helpers::WriteString(argumentDetails, "tooltip", argSource.m_details.m_tooltip, document);
  933. argument.AddMember("details", argumentDetails, document.GetAllocator());
  934. methodArguments.PushBack(argument, document.GetAllocator());
  935. }
  936. theMethod.AddMember("results", methodArguments, document.GetAllocator());
  937. }
  938. methods.PushBack(theMethod, document.GetAllocator());
  939. }
  940. entry.AddMember("methods", methods, document.GetAllocator());
  941. }
  942. if (!entrySource.m_slots.empty())
  943. {
  944. rapidjson::Value slotsArray(rapidjson::kArrayType);
  945. for (const auto& slotSource : entrySource.m_slots)
  946. {
  947. rapidjson::Value theSlot(rapidjson::kObjectType);
  948. value.SetString(slotSource.m_key.c_str(), document.GetAllocator());
  949. theSlot.AddMember(GraphCanvas::Schema::Field::key, value, document.GetAllocator());
  950. rapidjson::Value sloDetails(rapidjson::kObjectType);
  951. if (!slotSource.m_details.m_name.empty())
  952. {
  953. Helpers::WriteString(sloDetails, "name", slotSource.m_details.m_name, document);
  954. Helpers::WriteString(sloDetails, "tooltip", slotSource.m_details.m_tooltip, document);
  955. theSlot.AddMember("details", sloDetails, document.GetAllocator());
  956. }
  957. if (!slotSource.m_data.m_details.m_name.empty())
  958. {
  959. rapidjson::Value slotDataDetails(rapidjson::kObjectType);
  960. Helpers::WriteString(slotDataDetails, "name", slotSource.m_data.m_details.m_name, document);
  961. theSlot.AddMember("details", slotDataDetails, document.GetAllocator());
  962. }
  963. slotsArray.PushBack(theSlot, document.GetAllocator());
  964. }
  965. entry.AddMember("slots", slotsArray, document.GetAllocator());
  966. }
  967. entries.PushBack(entry, document.GetAllocator());
  968. }
  969. document.AddMember("entries", entries, document.GetAllocator());
  970. AZ::IO::Path gemPath = ScriptCanvasEditor::TranslationHelper::GetTranslationDefaultFolderPath();
  971. gemPath = gemPath / filename;
  972. gemPath.ReplaceExtension(".names");
  973. AZStd::string folderPath;
  974. AZ::StringFunc::Path::GetFolderPath(gemPath.c_str(), folderPath);
  975. if (!AZ::IO::FileIOBase::GetInstance()->Exists(folderPath.c_str()))
  976. {
  977. if (AZ::IO::FileIOBase::GetInstance()->CreatePath(folderPath.c_str()) != AZ::IO::ResultCode::Success)
  978. {
  979. AZ_Error("Translation", false, "Failed to create output folder");
  980. return;
  981. }
  982. }
  983. char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 };
  984. AZ::IO::FileIOBase::GetInstance()->ResolvePath(gemPath.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN);
  985. AZStd::string endPath = resolvedBuffer;
  986. AZ::StringFunc::Path::Normalize(endPath);
  987. AZ::IO::SystemFile outputFile;
  988. if (!outputFile.Open(endPath.c_str(),
  989. AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE |
  990. AZ::IO::SystemFile::OpenMode::SF_OPEN_CREATE_PATH |
  991. AZ::IO::SystemFile::OpenMode::SF_OPEN_WRITE_ONLY))
  992. {
  993. AZ_Error("Translation", false, "Failed to open file for writing: %s", filename.c_str());
  994. return;
  995. }
  996. rapidjson::StringBuffer scratchBuffer;
  997. rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(scratchBuffer);
  998. document.Accept(writer);
  999. outputFile.Write(scratchBuffer.GetString(), scratchBuffer.GetSize());
  1000. outputFile.Close();
  1001. scratchBuffer.Clear();
  1002. // AzQtComponents::ShowFileOnDesktop(endPath.c_str());
  1003. }
  1004. void TranslationGeneration::SplitCamelCase(AZStd::string& text)
  1005. {
  1006. AZStd::regex splitRegex(R"(/[a-z]+|[0-9]+|(?:[A-Z][a-z]+)|(?:[A-Z]+(?=(?:[A-Z][a-z])|[^AZa-z]|[$\d\n]))/g)");
  1007. text = AZStd::regex_replace(text, splitRegex, " $&");
  1008. text = AZ::StringFunc::LStrip(text);
  1009. AZ::StringFunc::Replace(text, " ", " ");
  1010. }
  1011. namespace Helpers
  1012. {
  1013. AZStd::string ReadStringAttribute(const AZ::AttributeArray& attributes, const AZ::Crc32& attribute)
  1014. {
  1015. AZStd::string attributeValue = "";
  1016. if (auto attributeItem = azrtti_cast<AZ::AttributeData<AZStd::string>*>(AZ::FindAttribute(attribute, attributes)))
  1017. {
  1018. attributeValue = attributeItem->Get(nullptr);
  1019. return attributeValue;
  1020. }
  1021. if (auto attributeItem = azrtti_cast<AZ::AttributeData<const char*>*>(AZ::FindAttribute(attribute, attributes)))
  1022. {
  1023. attributeValue = attributeItem->Get(nullptr);
  1024. return attributeValue;
  1025. }
  1026. return {};
  1027. }
  1028. bool MethodHasAttribute(const AZ::BehaviorMethod* method, AZ::Crc32 attribute)
  1029. {
  1030. return AZ::FindAttribute(attribute, method->m_attributes) != nullptr; // warning C4800: 'AZ::Attribute *': forcing value to bool 'true' or 'false' (performance warning)
  1031. }
  1032. void GetTypeNameAndDescription(AZ::TypeId typeId, AZStd::string& outName, AZStd::string& outDescription)
  1033. {
  1034. AZ::SerializeContext* serializeContext{};
  1035. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  1036. AZ_Assert(serializeContext, "Serialize Context is required");
  1037. if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId))
  1038. {
  1039. if (classData->m_editData)
  1040. {
  1041. outName = classData->m_editData->m_name ? classData->m_editData->m_name : classData->m_name;
  1042. outDescription = classData->m_editData->m_description ? classData->m_editData->m_description : "";
  1043. }
  1044. else
  1045. {
  1046. outName = classData->m_name;
  1047. }
  1048. }
  1049. }
  1050. AZStd::string GetCategory(const AZ::SerializeContext::ClassData* classData)
  1051. {
  1052. AZStd::string categoryPath;
  1053. if (classData->m_editData)
  1054. {
  1055. auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
  1056. if (editorElementData)
  1057. {
  1058. if (auto categoryAttribute = editorElementData->FindAttribute(AZ::Edit::Attributes::Category))
  1059. {
  1060. if (auto categoryAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
  1061. {
  1062. categoryPath = categoryAttributeData->Get(nullptr);
  1063. }
  1064. }
  1065. }
  1066. }
  1067. return categoryPath;
  1068. }
  1069. AZStd::vector<AZ::TypeId> GetUnpackedTypes(const AZ::TypeId& typeID)
  1070. {
  1071. using namespace AZ::ScriptCanvasAttributes;
  1072. if (const AZ::BehaviorClass* bcClass = AZ::BehaviorContextHelper::GetClass(typeID))
  1073. {
  1074. if (AZ::Attribute* attribute = AZ::FindAttribute(ReturnValueTypesFunction, bcClass->m_attributes))
  1075. {
  1076. GetUnpackedReturnValueTypesHolder holder;
  1077. AZ::AttributeReader attributeReader(nullptr, attribute);
  1078. attributeReader.Read<GetUnpackedReturnValueTypesHolder>(holder);
  1079. return AZStd::invoke(holder.m_function);
  1080. }
  1081. }
  1082. return { typeID };
  1083. }
  1084. void WriteString(rapidjson::Value& owner, const AZStd::string& key, const AZStd::string& value, rapidjson::Document& document)
  1085. {
  1086. if (key.empty() || value.empty())
  1087. {
  1088. return;
  1089. }
  1090. rapidjson::Value item(rapidjson::kStringType);
  1091. item.SetString(value.c_str(), document.GetAllocator());
  1092. rapidjson::Value keyVal(rapidjson::kStringType);
  1093. keyVal.SetString(key.c_str(), document.GetAllocator());
  1094. owner.AddMember(keyVal, item, document.GetAllocator());
  1095. }
  1096. }
  1097. }