summaryrefslogtreecommitdiffstats
path: root/l10ntools/source/treemerge.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--l10ntools/source/treemerge.cxx285
1 files changed, 285 insertions, 0 deletions
diff --git a/l10ntools/source/treemerge.cxx b/l10ntools/source/treemerge.cxx
new file mode 100644
index 000000000..8e577f3da
--- /dev/null
+++ b/l10ntools/source/treemerge.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <iostream>
+#include <cassert>
+#include <cstring>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlstring.h>
+
+#include <export.hxx>
+#include <helper.hxx>
+#include <common.hxx>
+#include <po.hxx>
+#include <treemerge.hxx>
+
+
+namespace
+{
+ // Extract strings from nodes on all level recursively
+ void lcl_ExtractLevel(
+ const xmlDocPtr pSource, const xmlNodePtr pRoot,
+ const xmlChar* pNodeName, PoOfstream& rPOStream )
+ {
+ if( !pRoot->children )
+ {
+ return;
+ }
+ for( xmlNodePtr pCurrent = pRoot->children->next;
+ pCurrent; pCurrent = pCurrent->next)
+ {
+ if (!xmlStrcmp(pCurrent->name, pNodeName))
+ {
+ xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
+ xmlChar* pText =
+ xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
+
+ common::writePoEntry(
+ "Treex", rPOStream, pSource->name, helper::xmlStrToOString( pNodeName ),
+ helper::xmlStrToOString( pID ), OString(), OString(), helper::xmlStrToOString( pText ));
+
+ xmlFree( pID );
+ xmlFree( pText );
+
+ lcl_ExtractLevel(
+ pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
+ rPOStream );
+ }
+ }
+ }
+
+ // Update id and content of the topic
+ xmlNodePtr lcl_UpdateTopic(
+ const xmlNodePtr pCurrent, std::string_view rXhpRoot )
+ {
+ xmlNodePtr pReturn = pCurrent;
+ xmlChar* pID = xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
+ const OString sID =
+ helper::xmlStrToOString( pID );
+ xmlFree( pID );
+
+ const sal_Int32 nFirstSlash = sID.indexOf('/');
+ // Update id attribute of topic
+ {
+ OString sNewID =
+ OString::Concat(sID.subView( 0, nFirstSlash + 1 )) +
+ rXhpRoot.substr( rXhpRoot.rfind('/') + 1 ) +
+ sID.subView( sID.indexOf( '/', nFirstSlash + 1 ) );
+ xmlSetProp(
+ pReturn, reinterpret_cast<const xmlChar*>("id"),
+ reinterpret_cast<const xmlChar*>(sNewID.getStr()));
+ }
+
+ const OString sXhpPath =
+ OString::Concat(rXhpRoot) +
+ sID.subView(sID.indexOf('/', nFirstSlash + 1));
+ xmlDocPtr pXhpFile = xmlParseFile( sXhpPath.getStr() );
+ // if xhpfile is missing than put this topic into comment
+ if ( !pXhpFile )
+ {
+ xmlNodePtr pTemp = pReturn;
+ xmlChar* sNewID =
+ xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
+ xmlChar* sComment =
+ xmlStrcat( xmlCharStrdup("removed "), sNewID );
+ pReturn = xmlNewComment( sComment );
+ xmlReplaceNode( pTemp, pReturn );
+ xmlFree( pTemp );
+ xmlFree( sNewID );
+ xmlFree( sComment );
+ }
+ // update topic's content on the basis of xhpfile's title
+ else
+ {
+ xmlNodePtr pXhpNode = xmlDocGetRootElement( pXhpFile );
+ for( pXhpNode = pXhpNode->children;
+ pXhpNode; pXhpNode = pXhpNode->children )
+ {
+ while( pXhpNode->type != XML_ELEMENT_NODE )
+ {
+ pXhpNode = pXhpNode->next;
+ }
+ if(!xmlStrcmp(pXhpNode->name, reinterpret_cast<const xmlChar *>("title")))
+ {
+ xmlChar* sTitle =
+ xmlNodeListGetString(pXhpFile, pXhpNode->children, 1);
+ OString sNewTitle =
+ helper::xmlStrToOString( sTitle ).
+ replaceAll("$[officename]","%PRODUCTNAME").
+ replaceAll("$[officeversion]","%PRODUCTVERSION");
+ xmlNodeSetContent(
+ pReturn,
+ xmlEncodeSpecialChars( nullptr,
+ reinterpret_cast<const xmlChar*>(
+ sNewTitle.getStr() )));
+ xmlFree( sTitle );
+ break;
+ }
+ }
+ if( !pXhpNode )
+ {
+ std::cerr
+ << "Treex error: Cannot find title in "
+ << sXhpPath << std::endl;
+ pReturn = nullptr;
+ }
+ xmlFreeDoc( pXhpFile );
+ xmlCleanupParser();
+ }
+ return pReturn;
+ }
+ // Localize title attribute of help_section and node tags
+ void lcl_MergeLevel(
+ xmlDocPtr io_pSource, const xmlNodePtr pRoot,
+ const xmlChar * pNodeName, MergeDataFile* pMergeDataFile,
+ const OString& rLang, const OString& rXhpRoot )
+ {
+ if( !pRoot->children )
+ {
+ return;
+ }
+ for( xmlNodePtr pCurrent = pRoot->children;
+ pCurrent; pCurrent = pCurrent->next)
+ {
+ if( !xmlStrcmp(pCurrent->name, pNodeName) )
+ {
+ if( rLang != "en-US" )
+ {
+ OString sNewText;
+ xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
+ ResData aResData(
+ helper::xmlStrToOString( pID ),
+ static_cast<OString>(io_pSource->name) );
+ xmlFree( pID );
+ aResData.sResTyp = helper::xmlStrToOString( pNodeName );
+ if( pMergeDataFile )
+ {
+ MergeEntrys* pEntrys =
+ pMergeDataFile->GetMergeEntrys( &aResData );
+ if( pEntrys )
+ {
+ pEntrys->GetText( sNewText, rLang );
+ }
+ }
+ else if( rLang == "qtz" )
+ {
+ xmlChar* pText = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
+ const OString sOriginText = helper::xmlStrToOString(pText);
+ xmlFree( pText );
+ sNewText = MergeEntrys::GetQTZText(aResData, sOriginText);
+ }
+ if( !sNewText.isEmpty() )
+ {
+ xmlSetProp(
+ pCurrent, reinterpret_cast<const xmlChar*>("title"),
+ reinterpret_cast<const xmlChar*>(sNewText.getStr()));
+ }
+ }
+
+ lcl_MergeLevel(
+ io_pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
+ pMergeDataFile, rLang, rXhpRoot );
+ }
+ else if( !xmlStrcmp(pCurrent->name, reinterpret_cast<const xmlChar *>("topic")) )
+ {
+ pCurrent = lcl_UpdateTopic( pCurrent, rXhpRoot );
+ }
+ }
+ }
+}
+
+TreeParser::TreeParser(
+ const OString& rInputFile, const OString& rLang )
+ : m_pSource( nullptr )
+ , m_sLang( rLang )
+ , m_bIsInitialized( false )
+{
+ m_pSource = xmlParseFile( rInputFile.getStr() );
+ if ( !m_pSource ) {
+ std::cerr
+ << "Treex error: Cannot open source file: "
+ << rInputFile << std::endl;
+ return;
+ }
+ if( !m_pSource->name )
+ {
+ m_pSource->name = static_cast<char *>(xmlMalloc(strlen(rInputFile.getStr())+1));
+ strcpy( m_pSource->name, rInputFile.getStr() );
+ }
+ m_bIsInitialized = true;
+}
+
+TreeParser::~TreeParser()
+{
+ // be sure m_pSource is freed
+ if (m_bIsInitialized)
+ xmlFreeDoc( m_pSource );
+}
+
+void TreeParser::Extract( const OString& rPOFile )
+{
+ assert( m_bIsInitialized );
+ PoOfstream aPOStream( rPOFile, PoOfstream::APP );
+ if( !aPOStream.isOpen() )
+ {
+ std::cerr
+ << "Treex error: Cannot open po file for extract: "
+ << rPOFile << std::endl;
+ return;
+ }
+
+ xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
+ lcl_ExtractLevel(
+ m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
+ aPOStream );
+
+ xmlFreeDoc( m_pSource );
+ xmlCleanupParser();
+ aPOStream.close();
+ m_bIsInitialized = false;
+}
+
+void TreeParser::Merge(
+ const OString &rMergeSrc, const OString &rDestinationFile,
+ const OString &rXhpRoot )
+{
+ assert( m_bIsInitialized );
+
+ const xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
+ std::unique_ptr<MergeDataFile> pMergeDataFile;
+ if( m_sLang != "qtz" && m_sLang != "en-US" )
+ {
+ pMergeDataFile.reset(new MergeDataFile(
+ rMergeSrc, static_cast<OString>( m_pSource->name ), false, false ));
+ const std::vector<OString> vLanguages = pMergeDataFile->GetLanguages();
+ if( !vLanguages.empty() && vLanguages[0] != m_sLang )
+ {
+ std::cerr
+ << ("Treex error: given language conflicts with language of"
+ " Mergedata file: ")
+ << m_sLang << " - "
+ << vLanguages[0] << std::endl;
+ return;
+ }
+ }
+ lcl_MergeLevel(
+ m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
+ pMergeDataFile.get(), m_sLang, rXhpRoot );
+
+ pMergeDataFile.reset();
+ xmlSaveFile( rDestinationFile.getStr(), m_pSource );
+ xmlFreeDoc( m_pSource );
+ xmlCleanupParser();
+ m_bIsInitialized = false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */