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 <fstream>
12 #include <cassert>
13 #include <cstring>
14
15 #include <libxml/tree.h>
16 #include <libxml/parser.h>
17 #include <libxml/xmlmemory.h>
18 #include <libxml/xmlstring.h>
19
20 #include <export.hxx>
21 #include <helper.hxx>
22 #include <common.hxx>
23 #include <po.hxx>
24 #include <treemerge.hxx>
25
26
27 namespace
28 {
29 // Extract strings from nodes on all level recursively
lcl_ExtractLevel(const xmlDocPtr pSource,const xmlNodePtr pRoot,const xmlChar * pNodeName,PoOfstream & rPOStream)30 void lcl_ExtractLevel(
31 const xmlDocPtr pSource, const xmlNodePtr pRoot,
32 const xmlChar* pNodeName, PoOfstream& rPOStream )
33 {
34 if( !pRoot->children )
35 {
36 return;
37 }
38 for( xmlNodePtr pCurrent = pRoot->children->next;
39 pCurrent; pCurrent = pCurrent->next)
40 {
41 if (!xmlStrcmp(pCurrent->name, pNodeName))
42 {
43 xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
44 xmlChar* pText =
45 xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
46
47 common::writePoEntry(
48 "Treex", rPOStream, pSource->name, helper::xmlStrToOString( pNodeName ),
49 helper::xmlStrToOString( pID ), OString(), OString(), helper::xmlStrToOString( pText ));
50
51 xmlFree( pID );
52 xmlFree( pText );
53
54 lcl_ExtractLevel(
55 pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
56 rPOStream );
57 }
58 }
59 }
60
61 // Update id and content of the topic
lcl_UpdateTopic(const xmlNodePtr pCurrent,const OString & rXhpRoot)62 xmlNodePtr lcl_UpdateTopic(
63 const xmlNodePtr pCurrent, const OString& rXhpRoot )
64 {
65 xmlNodePtr pReturn = pCurrent;
66 xmlChar* pID = xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
67 const OString sID =
68 helper::xmlStrToOString( pID );
69 xmlFree( pID );
70
71 const sal_Int32 nFirstSlash = sID.indexOf('/');
72 // Update id attribute of topic
73 {
74 OString sNewID =
75 sID.copy( 0, nFirstSlash + 1 ) +
76 rXhpRoot.copy( rXhpRoot.lastIndexOf('/') + 1 ) +
77 sID.copy( sID.indexOf( '/', nFirstSlash + 1 ) );
78 xmlSetProp(
79 pReturn, reinterpret_cast<const xmlChar*>("id"),
80 reinterpret_cast<const xmlChar*>(sNewID.getStr()));
81 }
82
83 const OString sXhpPath =
84 rXhpRoot +
85 sID.copy(sID.indexOf('/', nFirstSlash + 1));
86 xmlDocPtr pXhpFile = xmlParseFile( sXhpPath.getStr() );
87 // if xhpfile is missing than put this topic into comment
88 if ( !pXhpFile )
89 {
90 xmlNodePtr pTemp = pReturn;
91 xmlChar* sNewID =
92 xmlGetProp(pReturn, reinterpret_cast<const xmlChar*>("id"));
93 xmlChar* sComment =
94 xmlStrcat( xmlCharStrdup("removed "), sNewID );
95 pReturn = xmlNewComment( sComment );
96 xmlReplaceNode( pTemp, pReturn );
97 xmlFree( pTemp );
98 xmlFree( sNewID );
99 xmlFree( sComment );
100 }
101 // update topic's content on the basis of xhpfile's title
102 else
103 {
104 xmlNodePtr pXhpNode = xmlDocGetRootElement( pXhpFile );
105 for( pXhpNode = pXhpNode->children;
106 pXhpNode; pXhpNode = pXhpNode->children )
107 {
108 while( pXhpNode->type != XML_ELEMENT_NODE )
109 {
110 pXhpNode = pXhpNode->next;
111 }
112 if(!xmlStrcmp(pXhpNode->name, reinterpret_cast<const xmlChar *>("title")))
113 {
114 xmlChar* sTitle =
115 xmlNodeListGetString(pXhpFile, pXhpNode->children, 1);
116 OString sNewTitle =
117 helper::xmlStrToOString( sTitle ).
118 replaceAll("$[officename]","%PRODUCTNAME").
119 replaceAll("$[officeversion]","%PRODUCTVERSION");
120 xmlNodeSetContent(
121 pReturn,
122 xmlEncodeSpecialChars( nullptr,
123 reinterpret_cast<const xmlChar*>(
124 sNewTitle.getStr() )));
125 xmlFree( sTitle );
126 break;
127 }
128 }
129 if( !pXhpNode )
130 {
131 std::cerr
132 << "Treex error: Cannot find title in "
133 << sXhpPath << std::endl;
134 return nullptr;
135 }
136 xmlFree( pXhpFile );
137 xmlCleanupParser();
138 }
139 return pReturn;
140 }
141 // Localize title attribute of help_section and node tags
lcl_MergeLevel(xmlDocPtr io_pSource,const xmlNodePtr pRoot,const xmlChar * pNodeName,MergeDataFile * pMergeDataFile,const OString & rLang,const OString & rXhpRoot)142 void lcl_MergeLevel(
143 xmlDocPtr io_pSource, const xmlNodePtr pRoot,
144 const xmlChar * pNodeName, MergeDataFile* pMergeDataFile,
145 const OString& rLang, const OString& rXhpRoot )
146 {
147 if( !pRoot->children )
148 {
149 return;
150 }
151 for( xmlNodePtr pCurrent = pRoot->children;
152 pCurrent; pCurrent = pCurrent->next)
153 {
154 if( !xmlStrcmp(pCurrent->name, pNodeName) )
155 {
156 if( rLang != "en-US" )
157 {
158 OString sNewText;
159 xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
160 ResData aResData(
161 helper::xmlStrToOString( pID ),
162 static_cast<OString>(io_pSource->name) );
163 xmlFree( pID );
164 aResData.sResTyp = helper::xmlStrToOString( pNodeName );
165 if( pMergeDataFile )
166 {
167 MergeEntrys* pEntrys =
168 pMergeDataFile->GetMergeEntrys( &aResData );
169 if( pEntrys )
170 {
171 pEntrys->GetText( sNewText, rLang );
172 }
173 }
174 else if( rLang == "qtz" )
175 {
176 xmlChar* pText = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
177 const OString sOriginText = helper::xmlStrToOString(pText);
178 xmlFree( pText );
179 sNewText = MergeEntrys::GetQTZText(aResData, sOriginText);
180 }
181 if( !sNewText.isEmpty() )
182 {
183 xmlSetProp(
184 pCurrent, reinterpret_cast<const xmlChar*>("title"),
185 reinterpret_cast<const xmlChar*>(sNewText.getStr()));
186 }
187 }
188
189 lcl_MergeLevel(
190 io_pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
191 pMergeDataFile, rLang, rXhpRoot );
192 }
193 else if( !xmlStrcmp(pCurrent->name, reinterpret_cast<const xmlChar *>("topic")) )
194 {
195 pCurrent = lcl_UpdateTopic( pCurrent, rXhpRoot );
196 }
197 }
198 }
199 }
200
TreeParser(const OString & rInputFile,const OString & rLang)201 TreeParser::TreeParser(
202 const OString& rInputFile, const OString& rLang )
203 : m_pSource( nullptr )
204 , m_sLang( rLang )
205 , m_bIsInitialized( false )
206 {
207 m_pSource = xmlParseFile( rInputFile.getStr() );
208 if ( !m_pSource ) {
209 std::cerr
210 << "Treex error: Cannot open source file: "
211 << rInputFile << std::endl;
212 return;
213 }
214 if( !m_pSource->name )
215 {
216 m_pSource->name = static_cast<char *>(xmlMalloc(strlen(rInputFile.getStr())+1));
217 strcpy( m_pSource->name, rInputFile.getStr() );
218 }
219 m_bIsInitialized = true;
220 }
221
~TreeParser()222 TreeParser::~TreeParser()
223 {
224 // be sure m_pSource is freed
225 if (m_bIsInitialized)
226 xmlFreeDoc( m_pSource );
227 }
228
Extract(const OString & rPOFile)229 void TreeParser::Extract( const OString& rPOFile )
230 {
231 assert( m_bIsInitialized );
232 PoOfstream aPOStream( rPOFile, PoOfstream::APP );
233 if( !aPOStream.isOpen() )
234 {
235 std::cerr
236 << "Treex error: Cannot open po file for extract: "
237 << rPOFile << std::endl;
238 return;
239 }
240
241 xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
242 lcl_ExtractLevel(
243 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
244 aPOStream );
245
246 xmlFreeDoc( m_pSource );
247 xmlCleanupParser();
248 aPOStream.close();
249 m_bIsInitialized = false;
250 }
251
Merge(const OString & rMergeSrc,const OString & rDestinationFile,const OString & rXhpRoot)252 void TreeParser::Merge(
253 const OString &rMergeSrc, const OString &rDestinationFile,
254 const OString &rXhpRoot )
255 {
256 assert( m_bIsInitialized );
257
258 const xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
259 std::unique_ptr<MergeDataFile> pMergeDataFile;
260 if( m_sLang != "qtz" && m_sLang != "en-US" )
261 {
262 pMergeDataFile.reset(new MergeDataFile(
263 rMergeSrc, static_cast<OString>( m_pSource->name ), false, false ));
264 const std::vector<OString> vLanguages = pMergeDataFile->GetLanguages();
265 if( !vLanguages.empty() && vLanguages[0] != m_sLang )
266 {
267 std::cerr
268 << ("Treex error: given language conflicts with language of"
269 " Mergedata file: ")
270 << m_sLang << " - "
271 << vLanguages[0] << std::endl;
272 return;
273 }
274 }
275 lcl_MergeLevel(
276 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
277 pMergeDataFile.get(), m_sLang, rXhpRoot );
278
279 pMergeDataFile.reset();
280 xmlSaveFile( rDestinationFile.getStr(), m_pSource );
281 xmlFreeDoc( m_pSource );
282 xmlCleanupParser();
283 m_bIsInitialized = false;
284 }
285
286
287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
288