crashtesting: crash seen on exporting forum-it-5909.ods to xlsx
[LibreOffice.git] / l10ntools / source / treemerge.cxx
blob2920734eea4da0a2a6dacbf31eaeafce9d5eba2a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <iostream>
11 #include <fstream>
12 #include <cassert>
13 #include <cstring>
15 #include <libxml/tree.h>
16 #include <libxml/parser.h>
17 #include <libxml/xmlmemory.h>
18 #include <libxml/xmlstring.h>
20 #include <export.hxx>
21 #include <helper.hxx>
22 #include <common.hxx>
23 #include <po.hxx>
24 #include <treemerge.hxx>
27 namespace
29 // Extract strings from nodes on all level recursively
30 void lcl_ExtractLevel(
31 const xmlDocPtr pSource, const xmlNodePtr pRoot,
32 const xmlChar* pNodeName, PoOfstream& rPOStream )
34 if( !pRoot->children )
36 return;
38 for( xmlNodePtr pCurrent = pRoot->children->next;
39 pCurrent; pCurrent = pCurrent->next)
41 if (!xmlStrcmp(pCurrent->name, pNodeName))
43 xmlChar* pID = xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("id"));
44 xmlChar* pText =
45 xmlGetProp(pCurrent, reinterpret_cast<const xmlChar*>("title"));
47 common::writePoEntry(
48 "Treex", rPOStream, pSource->name, helper::xmlStrToOString( pNodeName ),
49 helper::xmlStrToOString( pID ), OString(), OString(), helper::xmlStrToOString( pText ));
51 xmlFree( pID );
52 xmlFree( pText );
54 lcl_ExtractLevel(
55 pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
56 rPOStream );
61 // Update id and content of the topic
62 xmlNodePtr lcl_UpdateTopic(
63 const xmlNodePtr pCurrent, const OString& rXhpRoot )
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 );
71 const sal_Int32 nFirstSlash = sID.indexOf('/');
72 // Update id attribute of topic
74 OString sNewID =
75 OString::Concat(sID.subView( 0, nFirstSlash + 1 )) +
76 rXhpRoot.subView( rXhpRoot.lastIndexOf('/') + 1 ) +
77 sID.subView( sID.indexOf( '/', nFirstSlash + 1 ) );
78 xmlSetProp(
79 pReturn, reinterpret_cast<const xmlChar*>("id"),
80 reinterpret_cast<const xmlChar*>(sNewID.getStr()));
83 const OString sXhpPath =
84 rXhpRoot +
85 sID.subView(sID.indexOf('/', nFirstSlash + 1));
86 xmlDocPtr pXhpFile = xmlParseFile( sXhpPath.getStr() );
87 // if xhpfile is missing than put this topic into comment
88 if ( !pXhpFile )
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 );
101 // update topic's content on the basis of xhpfile's title
102 else
104 xmlNodePtr pXhpNode = xmlDocGetRootElement( pXhpFile );
105 for( pXhpNode = pXhpNode->children;
106 pXhpNode; pXhpNode = pXhpNode->children )
108 while( pXhpNode->type != XML_ELEMENT_NODE )
110 pXhpNode = pXhpNode->next;
112 if(!xmlStrcmp(pXhpNode->name, reinterpret_cast<const xmlChar *>("title")))
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;
129 if( !pXhpNode )
131 std::cerr
132 << "Treex error: Cannot find title in "
133 << sXhpPath << std::endl;
134 pReturn = nullptr;
136 xmlFreeDoc( pXhpFile );
137 xmlCleanupParser();
139 return pReturn;
141 // Localize title attribute of help_section and node tags
142 void lcl_MergeLevel(
143 xmlDocPtr io_pSource, const xmlNodePtr pRoot,
144 const xmlChar * pNodeName, MergeDataFile* pMergeDataFile,
145 const OString& rLang, const OString& rXhpRoot )
147 if( !pRoot->children )
149 return;
151 for( xmlNodePtr pCurrent = pRoot->children;
152 pCurrent; pCurrent = pCurrent->next)
154 if( !xmlStrcmp(pCurrent->name, pNodeName) )
156 if( rLang != "en-US" )
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 )
167 MergeEntrys* pEntrys =
168 pMergeDataFile->GetMergeEntrys( &aResData );
169 if( pEntrys )
171 pEntrys->GetText( sNewText, rLang );
174 else if( rLang == "qtz" )
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);
181 if( !sNewText.isEmpty() )
183 xmlSetProp(
184 pCurrent, reinterpret_cast<const xmlChar*>("title"),
185 reinterpret_cast<const xmlChar*>(sNewText.getStr()));
189 lcl_MergeLevel(
190 io_pSource, pCurrent, reinterpret_cast<const xmlChar *>("node"),
191 pMergeDataFile, rLang, rXhpRoot );
193 else if( !xmlStrcmp(pCurrent->name, reinterpret_cast<const xmlChar *>("topic")) )
195 pCurrent = lcl_UpdateTopic( pCurrent, rXhpRoot );
201 TreeParser::TreeParser(
202 const OString& rInputFile, const OString& rLang )
203 : m_pSource( nullptr )
204 , m_sLang( rLang )
205 , m_bIsInitialized( false )
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;
214 if( !m_pSource->name )
216 m_pSource->name = static_cast<char *>(xmlMalloc(strlen(rInputFile.getStr())+1));
217 strcpy( m_pSource->name, rInputFile.getStr() );
219 m_bIsInitialized = true;
222 TreeParser::~TreeParser()
224 // be sure m_pSource is freed
225 if (m_bIsInitialized)
226 xmlFreeDoc( m_pSource );
229 void TreeParser::Extract( const OString& rPOFile )
231 assert( m_bIsInitialized );
232 PoOfstream aPOStream( rPOFile, PoOfstream::APP );
233 if( !aPOStream.isOpen() )
235 std::cerr
236 << "Treex error: Cannot open po file for extract: "
237 << rPOFile << std::endl;
238 return;
241 xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
242 lcl_ExtractLevel(
243 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
244 aPOStream );
246 xmlFreeDoc( m_pSource );
247 xmlCleanupParser();
248 aPOStream.close();
249 m_bIsInitialized = false;
252 void TreeParser::Merge(
253 const OString &rMergeSrc, const OString &rDestinationFile,
254 const OString &rXhpRoot )
256 assert( m_bIsInitialized );
258 const xmlNodePtr pRootNode = xmlDocGetRootElement( m_pSource );
259 std::unique_ptr<MergeDataFile> pMergeDataFile;
260 if( m_sLang != "qtz" && m_sLang != "en-US" )
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 )
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;
275 lcl_MergeLevel(
276 m_pSource, pRootNode, reinterpret_cast<const xmlChar *>("help_section"),
277 pMergeDataFile.get(), m_sLang, rXhpRoot );
279 pMergeDataFile.reset();
280 xmlSaveFile( rDestinationFile.getStr(), m_pSource );
281 xmlFreeDoc( m_pSource );
282 xmlCleanupParser();
283 m_bIsInitialized = false;
287 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */