Files
o3de/Gems/ScriptCanvasDeveloper/Code/Editor/Source/XMLDoc.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

412 lines
14 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "XMLDoc.h"
#include <AzCore/XML/rapidxml_print.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace ScriptCanvasDeveloperEditor
{
namespace Internal
{
xml_node<> *FindDocumentTypeNode(const xml_document<> & doc, const AZStd::string & nodeName)
{
// this will only work if the xml document was parsed with the flag "parse_doctype_node"
for(xml_node<> *docTypeNode=doc.first_node(); docTypeNode!=nullptr; docTypeNode=docTypeNode->next_sibling())
{
if (docTypeNode->type() == node_type::node_doctype)
{
if ( nodeName == docTypeNode->value() )
{
return docTypeNode;
}
}
}
return nullptr;
}
bool GetAttribute(xml_node<> *tsNode, AZStd::string_view attribName, float & value)
{
if( tsNode != nullptr)
{
for (xml_attribute<> *attrib = tsNode->first_attribute(); attrib != nullptr; attrib = attrib->next_attribute())
{
if ( attribName == attrib->name() )
{
value = AzFramework::StringFunc::ToFloat( attrib->value() );
return true;
}
}
}
return false;
}
bool GetAttribute(xml_node<> *tsNode, AZStd::string_view attribName, AZStd::string & value)
{
if( tsNode != nullptr)
{
for (xml_attribute<> *attrib = tsNode->first_attribute(); attrib != nullptr; attrib = attrib->next_attribute())
{
if (attribName == attrib->name())
{
value = attrib->value();
return true;
}
}
}
return false;
}
xml_node<> *FindContextNode(const xml_document<> & doc, const AZStd::string & contextName)
{
xml_node<> *tsNode = doc.first_node("TS");
if (tsNode != nullptr)
{
for(xml_node<> *contextNode=tsNode->first_node("context"); contextNode!=nullptr; contextNode=contextNode->next_sibling("context") )
{
xml_node<> *contextNameNode = contextNode->first_node("name");
if (contextNameNode != nullptr)
{
if( contextName == contextNameNode->value() )
{
return contextNode;
}
}
}
}
return nullptr;
}
}
XMLDocPtr XMLDoc::Alloc(const AZStd::string& contextName)
{
XMLDocPtr xmlDoc( AZStd::make_shared<XMLDoc>() );
xmlDoc->CreateTSDoc(contextName);
return xmlDoc;
}
XMLDocPtr XMLDoc::LoadFromDisk(const AZStd::string& fileName)
{
XMLDocPtr xmlDoc(AZStd::make_shared<XMLDoc>());
if ( !xmlDoc->LoadTSDoc(fileName) )
{
xmlDoc = nullptr;
}
return xmlDoc;
}
XMLDoc::XMLDoc()
: m_tsNode(nullptr)
, m_context(nullptr)
, m_readBuffer(0)
{
}
void XMLDoc::CreateTSDoc(const AZStd::string& contextName)
{
xml_node<>* decl = m_doc.allocate_node(node_declaration);
decl->append_attribute(m_doc.allocate_attribute("version", "1.0"));
decl->append_attribute(m_doc.allocate_attribute("encoding", "utf-8"));
m_doc.append_node(decl);
xml_node<>* commentNode = m_doc.allocate_node(node_comment, "", AllocString(AZStd::string::format("Generated for %s", contextName.c_str())));
m_doc.append_node(commentNode);
xml_node<>* docType = m_doc.allocate_node(node_doctype, "", "TS");
m_doc.append_node(docType);
m_tsNode = m_doc.allocate_node(node_element, "TS");
m_doc.append_node(m_tsNode);
m_tsNode->append_attribute(m_doc.allocate_attribute("version", "2.1"));
m_tsNode->append_attribute(m_doc.allocate_attribute("language", "en_US"));
}
bool XMLDoc::LoadTSDoc(const AZStd::string& fileName)
{
bool success = false;
char tsFilePath[AZ::IO::MaxPathLength] = { "" };
if (AZ::IO::FileIOBase::GetInstance()->ResolvePath(fileName.c_str(), tsFilePath, AZ::IO::MaxPathLength))
{
AZ::IO::FileIOStream xmlFile;
if( AZ::IO::FileIOBase::GetInstance()->Exists(tsFilePath) )
{
if ( xmlFile.Open(tsFilePath, AZ::IO::OpenMode::ModeRead|AZ::IO::OpenMode::ModeText) )
{
AZ::IO::SizeType bytesToRead = xmlFile.GetLength();
if( bytesToRead > 0 )
{
m_readBuffer.resize(bytesToRead, '\0');
if( xmlFile.Read(bytesToRead, m_readBuffer.data() ) == bytesToRead )
{
m_doc.parse<parse_doctype_node|parse_declaration_node|parse_comment_nodes>( m_readBuffer.data() );
success = IsValidTSDoc();
if (success)
{
AZ_TracePrintf("ScriptCanvas", "Loaded \"%s\"", tsFilePath);
}
}
else
{
AZ_Error("ScriptCanvas", false, "XMLDoc::LoadTSDoc-Error reading Qt .ts file! filename=\"%s\".", tsFilePath);
}
}
else
{
AZ_Error("ScriptCanvas", false, "XMLDoc::LoadTSDoc-Zero byte Qt .ts file! filename=\"%s\".", tsFilePath);
}
}
else
{
AZ_Error("ScriptCanvas", false, "XMLDoc::LoadTSDoc-Can't open file Qt .ts file! filename=\"%s\".", tsFilePath);
}
}
}
else
{
AZ_Error("ScriptCanvas", false, "XMLDoc::LoadTSDoc-Invalid filename specified! filename=\"%s\".", tsFilePath);
}
return success;
}
bool XMLDoc::WriteToDisk(const AZStd::string & fileName)
{
bool success = false;
char tsFilePath[AZ::IO::MaxPathLength] = { "" };
if (AZ::IO::FileIOBase::GetInstance()->ResolvePath(fileName.c_str(), tsFilePath, AZ::IO::MaxPathLength))
{
AZStd::string writeFolder(tsFilePath);
AzFramework::StringFunc::Path::StripFullName(writeFolder);
if (!AZ::IO::FileIOBase::GetInstance()->IsDirectory(writeFolder.c_str()))
{
AZ::IO::FileIOBase::GetInstance()->CreatePath(writeFolder.c_str());
}
AZ::IO::FileIOStream xmlFile;
if (xmlFile.Open(tsFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeText))
{
AZStd::string xmlData(ToString());
AZ::IO::SizeType bytesWritten = xmlFile.Write(xmlData.size(), xmlData.data());
if (bytesWritten != xmlData.size())
{
AZ_Error("ScriptCanvas", false, "Write error writing out %s, bytes actually written: %llu expected bytes to write: %llu!", tsFilePath, bytesWritten, xmlData.size());
}
else
{
AZ_TracePrintf("ScriptCanvas", "Successfully wrote out ScriptCanvas localization file \"%s\".", tsFilePath);
success = true;
}
xmlFile.Close();
}
else
{
AZ_Error("ScriptCanvas", false, "Could not open file \"%s\"!", tsFilePath);
}
}
else
{
AZ_Error("ScriptCanvas", false, "Invalid filename specified XMLDoc::WriteToDisk! filename=\"%s\".", tsFilePath);
}
return success;
}
bool XMLDoc::StartContext(const AZStd::string& contextName)
{
bool isNew = false;
if( !contextName.empty() )
{
// check to see if we have a context of this name already, if so find that node and use it, otherwise
// create a new one.
xml_node<> *existingContextNode = Internal::FindContextNode(m_doc, contextName);
if (existingContextNode == nullptr)
{
m_context = m_doc.allocate_node(node_element, "context");
m_tsNode->append_node(m_context);
xml_node<>* contextNameNode = m_doc.allocate_node(node_element, "name", AllocString(contextName));
m_context->append_node(contextNameNode);
isNew = true;
}
else
{
m_context = existingContextNode;
}
}
else
{
m_context = nullptr;
}
return isNew;
}
void XMLDoc::AddToContext(const AZStd::string& id, const AZStd::string& translation/*=""*/, const AZStd::string& comment /*=""*/, const AZStd::string& source/*=""*/)
{
if (m_context == nullptr)
{
return;
}
xml_node<>* messageNode = m_doc.allocate_node(node_element, "message");
messageNode->append_attribute(m_doc.allocate_attribute("id", AllocString(id)));
xml_node<>* sourceNode = m_doc.allocate_node(node_element, "source", AllocString(source.empty() ? id.c_str() : source.c_str()));
messageNode->append_node(sourceNode);
xml_node<>* translationNode = m_doc.allocate_node(node_element, "translation", AllocString(translation));
messageNode->append_node(translationNode);
xml_node<>* commentNode = m_doc.allocate_node(node_element, "comment", AllocString(comment));
messageNode->append_node(commentNode);
m_context->append_node(messageNode);
}
bool XMLDoc::MethodFamilyExists(const AZStd::string& baseId) const
{
if (m_tsNode != nullptr)
{
AZStd::string nameID(baseId + "_NAME");
for (xml_node<> *contextNode=m_tsNode->first_node("context"); contextNode!=nullptr; contextNode=contextNode->next_sibling("context"))
{
for (xml_node<> *messageNode = contextNode->first_node("message"); messageNode != nullptr; messageNode = messageNode->next_sibling("message"))
{
AZStd::string id;
if ( Internal::GetAttribute(messageNode, "id", id) )
{
if (id == nameID)
{
return true;
}
}
}
}
}
return false;
}
AZStd::string XMLDoc::ToString() const
{
AZStd::string buffer;
print( AZStd::back_inserter(buffer), m_doc, 0);
return buffer;
}
const char *XMLDoc::AllocString(const AZStd::string& str)
{
return m_doc.allocate_string( str.c_str(), str.size() + 1 );
}
bool XMLDoc::IsValidTSDoc()
{
bool isTSDoc = false;
//
// Basic format of a .ts file, we are checking to make sure the document type is ts, and the version is 2.1 or greater
// and there is a non-null language attribute. and at least 1 context section
//
//<?xml version="1.0" encoding="utf-8"?>
//<!DOCTYPE TS>
//<TS version="2.1" language="en_US">
// <context>
// ...
// </context>
//</TS>
//
// this node should be the doc type
xml_node<> *docTypeNode = Internal::FindDocumentTypeNode(m_doc, "TS");
if (docTypeNode !=nullptr)
{
xml_node<> *tsNode = m_doc.first_node("TS");
if( tsNode != nullptr)
{
float attribVersion = 0.0f;
if( Internal::GetAttribute(tsNode, "version", attribVersion) && (attribVersion >= 2.1f) )
{
AZStd::string attribLanguage;
if( Internal::GetAttribute(tsNode, "language", attribLanguage) && !attribLanguage.empty() )
{
xml_node<> *contextNode = tsNode->first_node("context");
if(contextNode != nullptr)
{
m_tsNode = tsNode;
AZ_TracePrintf("ScriptCanvas", "TS: Version=%2.1f, Language=\"%s\"", attribVersion, attribLanguage.c_str());
isTSDoc = true;
}
else
{
AZ_Warning("ScriptCanvas", false, "TS document contains no \"context\" nodes!");
}
}
else
{
AZ_Warning("ScriptCanvas", false, "TS document has a bad or missing \"language\" attribute!");
}
}
else
{
AZ_Warning("ScriptCanvas", false, "TS document has a bad or missing \"version\" attribute!");
}
}
else
{
AZ_Error("ScriptCanvas", false, "TS document contains no \"TS\" node!");
}
}
else
{
AZ_Error("ScriptCanvas", false, "XML doc is not a valid TS document!");
}
return isTSDoc;
}
}