LibreOffice Module l10ntools (master) 1
treemerge.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#include <iostream>
11#include <cassert>
12#include <cstring>
13
14#include <libxml/tree.h>
15#include <libxml/parser.h>
16#include <libxml/xmlstring.h>
17
18#include <export.hxx>
19#include <helper.hxx>
20#include <common.hxx>
21#include <po.hxx>
22#include <treemerge.hxx>
23#include <utility>
24
25
26namespace
27{
28 // Extract strings from nodes on all level recursively
29 void lcl_ExtractLevel(
30 const xmlDocPtr pSource, const xmlNodePtr pRoot,
31 const xmlChar* pNodeName, PoOfstream& rPOStream )
32 {
33 if( !pRoot->children )
34 {
35 return;
36 }
37 for( xmlNodePtr pCurrent = pRoot->children->next;
38 pCurrent; pCurrent = pCurrent->next)
39 {
40 if (!xmlStrcmp(pCurrent->name, pNodeName))
41 {
42 xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
43 xmlChar* pText =
44 xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
45
47 "Treex", rPOStream, pSource->name, helper::xmlStrToOString( pNodeName ),
48 helper::xmlStrToOString( pID ), OString(), OString(), helper::xmlStrToOString( pText ));
49
50 xmlFree( pID );
51 xmlFree( pText );
52
53 lcl_ExtractLevel(
54 pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
55 rPOStream );
56 }
57 }
58 }
59
60 // Update id and content of the topic
61 xmlNodePtr lcl_UpdateTopic(
62 const xmlNodePtr pCurrent, std::string_view rXhpRoot )
63 {
64 xmlNodePtr pReturn = pCurrent;
65 xmlChar* pID = xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
66 const OString sID =
68 xmlFree( pID );
69
70 const sal_Int32 nFirstSlash = sID.indexOf('/');
71 // Update id attribute of topic
72 {
73 OString sNewID =
74 OString::Concat(sID.subView( 0, nFirstSlash + 1 )) +
75 rXhpRoot.substr( rXhpRoot.rfind('/') + 1 ) +
76 sID.subView( sID.indexOf( '/', nFirstSlash + 1 ) );
77 xmlSetProp(
78 pReturn, reinterpret_cast<const xmlChar*>("id"),
79 reinterpret_cast<const xmlChar*>(sNewID.getStr()));
80 }
81
82 const OString sXhpPath =
83 OString::Concat(rXhpRoot) +
84 sID.subView(sID.indexOf('/', nFirstSlash + 1));
85 xmlDocPtr pXhpFile = xmlParseFile( sXhpPath.getStr() );
86 // if xhpfile is missing than put this topic into comment
87 if ( !pXhpFile )
88 {
89 xmlNodePtr pTemp = pReturn;
90 xmlChar* sNewID =
91 xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
92 xmlChar* sComment =
93 xmlStrcat( xmlCharStrdup("removed "), sNewID );
94 pReturn = xmlNewComment( sComment );
95 xmlReplaceNode( pTemp, pReturn );
96 xmlFree( pTemp );
97 xmlFree( sNewID );
98 xmlFree( sComment );
99 }
100 // update topic's content on the basis of xhpfile's title
101 else
102 {
103 xmlNodePtr pXhpNode = xmlDocGetRootElement( pXhpFile );
104 for( pXhpNode = pXhpNode->children;
105 pXhpNode; pXhpNode = pXhpNode->children )
106 {
107 while( pXhpNode->type != XML_ELEMENT_NODE )
108 {
109 pXhpNode = pXhpNode->next;
110 }
111 if(!xmlStrcmp(pXhpNode->name, reinterpret_cast<const xmlChar *>("title")))
112 {
113 xmlChar* sTitle =
114 xmlNodeListGetString(pXhpFile, pXhpNode->children, 1);
115 OString sNewTitle =
116 helper::xmlStrToOString( sTitle ).
117 replaceAll("$[officename]","%PRODUCTNAME").
118 replaceAll("$[officeversion]","%PRODUCTVERSION");
119 xmlChar *xmlString = xmlEncodeSpecialChars(nullptr,
120 reinterpret_cast<const xmlChar*>( sNewTitle.getStr() ));
121 xmlNodeSetContent( pReturn, xmlString);
122 xmlFree( xmlString );
123 xmlFree( sTitle );
124 break;
125 }
126 }
127 if( !pXhpNode )
128 {
129 std::cerr
130 << "Treex error: Cannot find title in "
131 << sXhpPath << std::endl;
132 pReturn = nullptr;
133 }
134 xmlFreeDoc( pXhpFile );
135 xmlCleanupParser();
136 }
137 return pReturn;
138 }
139 // Localize title attribute of help_section and node tags
140 void lcl_MergeLevel(
141 xmlDocPtr io_pSource, const xmlNodePtr pRoot,
142 const xmlChar * pNodeName, MergeDataFile* pMergeDataFile,
143 const OString& rLang, const OString& rXhpRoot )
144 {
145 if( !pRoot->children )
146 {
147 return;
148 }
149 for( xmlNodePtr pCurrent = pRoot->children;
150 pCurrent; pCurrent = pCurrent->next)
151 {
152 if( !xmlStrcmp(pCurrent->name, pNodeName) )
153 {
154 if( rLang != "en-US" )
155 {
156 OString sNewText;
157 xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
158 ResData aResData(
160 static_cast<OString>(io_pSource->name) );
161 xmlFree( pID );
162 aResData.sResTyp = helper::xmlStrToOString( pNodeName );
163 if( pMergeDataFile )
164 {
165 MergeEntrys* pEntrys =
166 pMergeDataFile->GetMergeEntrys( &aResData );
167 if( pEntrys )
168 {
169 pEntrys->GetText( sNewText, rLang );
170 }
171 }
172 else if( rLang == "qtz" )
173 {
174 xmlChar* pText = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
175 const OString sOriginText = helper::xmlStrToOString(pText);
176 xmlFree( pText );
177 sNewText = MergeEntrys::GetQTZText(aResData, sOriginText);
178 }
179 if( !sNewText.isEmpty() )
180 {
181 xmlSetProp(
182 pCurrent, reinterpret_cast<const xmlChar*>("title"),
183 reinterpret_cast<const xmlChar*>(sNewText.getStr()));
184 }
185 }
186
187 lcl_MergeLevel(
188 io_pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
189 pMergeDataFile, rLang, rXhpRoot );
190 }
191 else if( !xmlStrcmp(pCurrent->name, reinterpret_cast<const xmlChar *>("topic")) )
192 {
193 pCurrent = lcl_UpdateTopic( pCurrent, rXhpRoot );
194 }
195 }
196 }
197}
198
200 const OString& rInputFile, OString _sLang )
201 : m_pSource( nullptr )
202 , m_sLang(std::move( _sLang ))
203 , m_bIsInitialized( false )
204{
205 m_pSource = xmlParseFile( rInputFile.getStr() );
206 if ( !m_pSource ) {
207 std::cerr
208 << "Treex error: Cannot open source file: "
209 << rInputFile << std::endl;
210 return;
211 }
212 if( !m_pSource->name )
213 {
214 m_pSource->name = static_cast<char *>(xmlMalloc(strlen(rInputFile.getStr())+1));
215 strcpy( m_pSource->name, rInputFile.getStr() );
216 }
217 m_bIsInitialized = true;
218}
219
221{
222 // be sure m_pSource is freed
224 xmlFreeDoc( m_pSource );
225}
226
227void TreeParser::Extract( const OString& rPOFile )
228{
229 assert( m_bIsInitialized );
230 PoOfstream aPOStream( rPOFile, PoOfstream::APP );
231 if( !aPOStream.isOpen() )
232 {
233 std::cerr
234 << "Treex error: Cannot open po file for extract: "
235 << rPOFile << std::endl;
236 return;
237 }
238
239 xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
240 lcl_ExtractLevel(
241 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
242 aPOStream );
243
244 xmlFreeDoc( m_pSource );
245 xmlCleanupParser();
246 aPOStream.close();
247 m_bIsInitialized = false;
248}
249
251 const OString &rMergeSrc, const OString &rDestinationFile,
252 const OString &rXhpRoot )
253{
254 assert( m_bIsInitialized );
255
256 const xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
257 std::unique_ptr<MergeDataFile> pMergeDataFile;
258 if( m_sLang != "qtz" && m_sLang != "en-US" )
259 {
260 pMergeDataFile.reset(new MergeDataFile(
261 rMergeSrc, static_cast<OString>( m_pSource->name ), false, false ));
262 const std::vector<OString> vLanguages = pMergeDataFile->GetLanguages();
263 if( !vLanguages.empty() && vLanguages[0] != m_sLang )
264 {
265 std::cerr
266 << ("Treex error: given language conflicts with language of"
267 " Mergedata file: ")
268 << m_sLang << " - "
269 << vLanguages[0] << std::endl;
270 return;
271 }
272 }
273 lcl_MergeLevel(
274 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
275 pMergeDataFile.get(), m_sLang, rXhpRoot );
276
277 pMergeDataFile.reset();
278 xmlSaveFile( rDestinationFile.getStr(), m_pSource );
279 xmlFreeDoc( m_pSource );
280 xmlCleanupParser();
281 m_bIsInitialized = false;
282}
283
284
285/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Purpose: holds information of data to merge, read from PO file.
Definition: export.hxx:114
MergeEntrys * GetMergeEntrys(ResData *pResData)
Definition: merge.cxx:274
std::vector< OString > GetLanguages() const
Definition: merge.cxx:242
Purpose: holds information of data to merge.
Definition: export.hxx:77
static OString GetQTZText(const ResData &rResData, std::string_view rOrigText)
Generate QTZ string with ResData For executable which works one language and without PO files.
Definition: merge.cxx:109
bool GetText(OString &rReturn, const OString &nLangIndex, bool bDel=false)
Definition: merge.cxx:87
Interface to write po entry to files as output streams.
Definition: po.hxx:101
bool isOpen() const
Definition: po.hxx:116
void close()
Definition: po.cxx:515
@ APP
Definition: po.hxx:109
Purpose: holds mandatory data to export a single res.
Definition: export.hxx:55
TreeParser(const OString &rInputFile, OString sLang)
Parse tree file.
Definition: treemerge.cxx:199
xmlDocPtr m_pSource
Definition: treemerge.hxx:26
bool m_bIsInitialized
Definition: treemerge.hxx:28
void Extract(const OString &rPOFile)
Export strings.
Definition: treemerge.cxx:227
void Merge(const OString &rMergeSrc, const OString &rDestinationFile, const OString &rXhpRoot)
Merge strings to tree file and update reference to help files(xhp)
Definition: treemerge.cxx:250
OString m_sLang
Definition: treemerge.hxx:27
void writePoEntry(const OString &rExecutable, PoOfstream &rPoStream, const OString &rSourceFile, std::string_view rResType, const OString &rGroupId, const OString &rLocalId, const OString &rHelpText, const OString &rText, const PoEntry::TYPE eType)
Write out a PoEntry with attention to exceptions.
Definition: common.cxx:110
OString xmlStrToOString(const xmlChar *pString)
Convert xmlChar* to OString.
Definition: helper.cxx:146