Version 3.6.0.2, tag libreoffice-3.6.0.2
[LibreOffice.git] / l10ntools / source / help / HelpCompiler.cxx
blobfb92e5b7341a652fad2904e8208f0f10ed71cce7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include "HelpCompiler.hxx"
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <libxslt/xslt.h>
35 #include <libxslt/xsltInternals.h>
36 #include <libxslt/transform.h>
37 #include <libxslt/xsltutils.h>
38 #include <osl/thread.hxx>
40 static void impl_sleep( sal_uInt32 nSec )
42 TimeValue aTime;
43 aTime.Seconds = nSec;
44 aTime.Nanosec = 0;
46 osl::Thread::wait( aTime );
49 HelpCompiler::HelpCompiler(StreamTable &in_streamTable, const fs::path &in_inputFile,
50 const fs::path &in_src, const fs::path &in_resEmbStylesheet,
51 const std::string &in_module, const std::string &in_lang, bool in_bExtensionMode)
52 : streamTable(in_streamTable), inputFile(in_inputFile),
53 src(in_src), module(in_module), lang(in_lang), resEmbStylesheet(in_resEmbStylesheet),
54 bExtensionMode( in_bExtensionMode )
56 xmlKeepBlanksDefaultValue = 0;
59 xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
61 static const char *params[4 + 1];
62 static xsltStylesheetPtr cur = NULL;
64 xmlDocPtr res;
65 if( bExtensionMode )
67 res = xmlParseFile(filePath.native_file_string().c_str());
68 if( !res ){
69 impl_sleep( 3 );
70 res = xmlParseFile(filePath.native_file_string().c_str());
73 else
75 if (!cur)
77 static std::string fsroot('\'' + src.toUTF8() + '\'');
78 static std::string esclang('\'' + lang + '\'');
80 xmlSubstituteEntitiesDefault(1);
81 xmlLoadExtDtdDefaultValue = 1;
82 cur = xsltParseStylesheetFile((const xmlChar *)resEmbStylesheet.native_file_string().c_str());
84 int nbparams = 0;
85 params[nbparams++] = "Language";
86 params[nbparams++] = esclang.c_str();
87 params[nbparams++] = "fsroot";
88 params[nbparams++] = fsroot.c_str();
89 params[nbparams] = NULL;
91 xmlDocPtr doc = xmlParseFile(filePath.native_file_string().c_str());
92 if( !doc )
94 impl_sleep( 3 );
95 doc = xmlParseFile(filePath.native_file_string().c_str());
98 //???res = xmlParseFile(filePath.native_file_string().c_str());
100 res = xsltApplyStylesheet(cur, doc, params);
101 xmlFreeDoc(doc);
103 return res;
106 HashSet HelpCompiler::switchFind(xmlDocPtr doc)
108 HashSet hs;
109 xmlChar *xpath = (xmlChar*)"//switchinline";
111 xmlXPathContextPtr context = xmlXPathNewContext(doc);
112 xmlXPathObjectPtr result = xmlXPathEvalExpression(xpath, context);
113 xmlXPathFreeContext(context);
114 if (result)
116 xmlNodeSetPtr nodeset = result->nodesetval;
117 for (int i = 0; i < nodeset->nodeNr; i++)
119 xmlNodePtr el = nodeset->nodeTab[i];
120 xmlChar *select = xmlGetProp(el, (xmlChar*)"select");
121 if (select)
123 if (!strcmp((const char*)select, "appl"))
125 xmlNodePtr n1 = el->xmlChildrenNode;
126 while (n1)
128 if ((!xmlStrcmp(n1->name, (const xmlChar*)"caseinline")))
130 xmlChar *appl = xmlGetProp(n1, (xmlChar*)"select");
131 hs.push_back(std::string((const char*)appl));
132 xmlFree(appl);
134 else if ((!xmlStrcmp(n1->name, (const xmlChar*)"defaultinline")))
135 hs.push_back(std::string("DEFAULT"));
136 n1 = n1->next;
139 xmlFree(select);
142 xmlXPathFreeObject(result);
144 hs.push_back(std::string("DEFAULT"));
145 return hs;
148 // returns a node representing the whole stuff compiled for the current
149 // application.
150 xmlNodePtr HelpCompiler::clone(xmlNodePtr node, const std::string& appl)
152 xmlNodePtr parent = xmlCopyNode(node, 2);
153 xmlNodePtr n = node->xmlChildrenNode;
154 while (n != NULL)
156 bool isappl = false;
157 if ( (!strcmp((const char*)n->name, "switchinline")) ||
158 (!strcmp((const char*)n->name, "switch")) )
160 xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
161 if (select)
163 if (!strcmp((const char*)select, "appl"))
164 isappl = true;
165 xmlFree(select);
168 if (isappl)
170 xmlNodePtr caseNode = n->xmlChildrenNode;
171 if (appl == "DEFAULT")
173 while (caseNode)
175 if (!strcmp((const char*)caseNode->name, "defaultinline"))
177 xmlNodePtr cnl = caseNode->xmlChildrenNode;
178 while (cnl)
180 xmlAddChild(parent, clone(cnl, appl));
181 cnl = cnl->next;
183 break;
185 caseNode = caseNode->next;
188 else
190 while (caseNode)
192 isappl=false;
193 if (!strcmp((const char*)caseNode->name, "caseinline"))
195 xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
196 if (select)
198 if (!strcmp((const char*)select, appl.c_str()))
199 isappl = true;
200 xmlFree(select);
202 if (isappl)
204 xmlNodePtr cnl = caseNode->xmlChildrenNode;
205 while (cnl)
207 xmlAddChild(parent, clone(cnl, appl));
208 cnl = cnl->next;
210 break;
214 caseNode = caseNode->next;
219 else
220 xmlAddChild(parent, clone(n, appl));
222 n = n->next;
224 return parent;
227 class myparser
229 public:
230 std::string documentId;
231 std::string fileName;
232 std::string title;
233 HashSet *hidlist;
234 Hashtable *keywords;
235 Stringtable *helptexts;
236 private:
237 HashSet extendedHelpText;
238 public:
239 myparser(const std::string &indocumentId, const std::string &infileName,
240 const std::string &intitle) : documentId(indocumentId), fileName(infileName),
241 title(intitle)
243 hidlist = new HashSet;
244 keywords = new Hashtable;
245 helptexts = new Stringtable;
247 void traverse( xmlNodePtr parentNode );
248 private:
249 std::string dump(xmlNodePtr node);
252 std::string myparser::dump(xmlNodePtr node)
254 std::string app;
255 if (node->xmlChildrenNode)
257 xmlNodePtr list = node->xmlChildrenNode;
258 while (list)
260 app += dump(list);
261 list = list->next;
264 if (xmlNodeIsText(node))
266 xmlChar *pContent = xmlNodeGetContent(node);
267 app += std::string((const char*)pContent);
268 xmlFree(pContent);
270 return app;
273 void trim(std::string& str)
275 std::string::size_type pos = str.find_last_not_of(' ');
276 if(pos != std::string::npos)
278 str.erase(pos + 1);
279 pos = str.find_first_not_of(' ');
280 if(pos != std::string::npos)
281 str.erase(0, pos);
283 else
284 str.erase(str.begin(), str.end());
287 void myparser::traverse( xmlNodePtr parentNode )
289 // traverse all nodes that belong to the parent
290 xmlNodePtr test ;
291 for (test = parentNode->xmlChildrenNode; test; test = test->next)
293 if (fileName.empty() && !strcmp((const char*)test->name, "filename"))
295 xmlNodePtr node = test->xmlChildrenNode;
296 if (xmlNodeIsText(node))
298 xmlChar *pContent = xmlNodeGetContent(node);
299 fileName = std::string((const char*)pContent);
300 xmlFree(pContent);
303 else if (title.empty() && !strcmp((const char*)test->name, "title"))
305 title = dump(test);
306 if (title.empty())
307 title = "<notitle>";
309 else if (!strcmp((const char*)test->name, "bookmark"))
311 xmlChar *branchxml = xmlGetProp(test, (const xmlChar*)"branch");
312 xmlChar *idxml = xmlGetProp(test, (const xmlChar*)"id");
313 std::string branch((const char*)branchxml);
314 std::string anchor((const char*)idxml);
315 xmlFree (branchxml);
316 xmlFree (idxml);
318 std::string hid;
320 if (branch.find("hid") == 0)
322 size_t index = branch.find('/');
323 if (index != std::string::npos)
325 hid = branch.substr(1 + index);
326 // one shall serve as a documentId
327 if (documentId.empty())
328 documentId = hid;
329 extendedHelpText.push_back(hid);
330 std::string foo = anchor.empty() ? hid : hid + "#" + anchor;
331 HCDBG(std::cerr << "hid pushback" << foo << std::endl);
332 hidlist->push_back( anchor.empty() ? hid : hid + "#" + anchor);
334 else
335 continue;
337 else if (branch.compare("index") == 0)
339 LinkedList ll;
341 for (xmlNodePtr nd = test->xmlChildrenNode; nd; nd = nd->next)
343 if (strcmp((const char*)nd->name, "bookmark_value"))
344 continue;
346 std::string embedded;
347 xmlChar *embeddedxml = xmlGetProp(nd, (const xmlChar*)"embedded");
348 if (embeddedxml)
350 embedded = std::string((const char*)embeddedxml);
351 xmlFree (embeddedxml);
352 std::transform (embedded.begin(), embedded.end(),
353 embedded.begin(), tocharlower);
356 bool isEmbedded = !embedded.empty() && embedded.compare("true") == 0;
357 if (isEmbedded)
358 continue;
360 std::string keyword = dump(nd);
361 size_t keywordSem = keyword.find(';');
362 if (keywordSem != std::string::npos)
364 std::string tmppre =
365 keyword.substr(0,keywordSem);
366 trim(tmppre);
367 std::string tmppos =
368 keyword.substr(1+keywordSem);
369 trim(tmppos);
370 keyword = tmppre + ";" + tmppos;
372 ll.push_back(keyword);
374 if (!ll.empty())
375 (*keywords)[anchor] = ll;
377 else if (branch.compare("contents") == 0)
379 // currently not used
382 else if (!strcmp((const char*)test->name, "ahelp"))
384 std::string text = dump(test);
385 trim(text);
386 std::string name;
388 HashSet::const_iterator aEnd = extendedHelpText.end();
389 for (HashSet::const_iterator iter = extendedHelpText.begin(); iter != aEnd;
390 ++iter)
392 name = *iter;
393 (*helptexts)[name] = text;
395 extendedHelpText.clear();
398 // traverse children
399 traverse(test);
403 bool HelpCompiler::compile( void ) throw( HelpProcessingException )
405 // we now have the jaroutputstream, which will contain the document.
406 // now determine the document as a dom tree in variable docResolved
408 xmlDocPtr docResolvedOrg = getSourceDocument(inputFile);
410 // now add path to the document
411 // resolve the dom
412 if (!docResolvedOrg)
414 impl_sleep( 3 );
415 docResolvedOrg = getSourceDocument(inputFile);
416 if( !docResolvedOrg )
418 std::stringstream aStrStream;
419 aStrStream << "ERROR: file not existing: " << inputFile.native_file_string().c_str() << std::endl;
420 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
424 // now find all applications for which one has to compile
425 std::string documentId;
426 std::string fileName;
427 std::string title;
428 // returns all applications for which one has to compile
429 HashSet applications = switchFind(docResolvedOrg);
431 HashSet::const_iterator aEnd = applications.end();
432 for (HashSet::const_iterator aI = applications.begin(); aI != aEnd; ++aI)
434 std::string appl = *aI;
435 std::string modulename = appl;
436 if (modulename[0] == 'S')
438 modulename = modulename.substr(1);
439 std::transform(modulename.begin(), modulename.end(), modulename.begin(), tocharlower);
441 if (modulename != "DEFAULT" && modulename != module)
442 continue;
444 // returns a clone of the document with swich-cases resolved
445 xmlNodePtr docResolved = clone(xmlDocGetRootElement(docResolvedOrg), appl);
446 myparser aparser(documentId, fileName, title);
447 aparser.traverse(docResolved);
449 documentId = aparser.documentId;
450 fileName = aparser.fileName;
451 title = aparser.title;
453 HCDBG(std::cerr << documentId << " : " << fileName << " : " << title << std::endl);
455 xmlDocPtr docResolvedDoc = xmlCopyDoc(docResolvedOrg, false);
456 xmlDocSetRootElement(docResolvedDoc, docResolved);
458 if (modulename == "DEFAULT")
460 streamTable.dropdefault();
461 streamTable.default_doc = docResolvedDoc;
462 streamTable.default_hidlist = aparser.hidlist;
463 streamTable.default_helptexts = aparser.helptexts;
464 streamTable.default_keywords = aparser.keywords;
466 else
468 streamTable.dropappl();
469 streamTable.appl_doc = docResolvedDoc;
470 streamTable.appl_hidlist = aparser.hidlist;
471 streamTable.appl_helptexts = aparser.helptexts;
472 streamTable.appl_keywords = aparser.keywords;
474 } // end iteration over all applications
476 streamTable.document_id = documentId;
477 streamTable.document_path = fileName;
478 streamTable.document_title = title;
479 std::string actMod = module;
480 if ( !bExtensionMode && !fileName.empty())
482 if (fileName.find("/text/") == 0)
484 int len = strlen("/text/");
485 actMod = fileName.substr(len);
486 actMod = actMod.substr(0, actMod.find('/'));
489 streamTable.document_module = actMod;
491 xmlFreeDoc(docResolvedOrg);
492 return true;
495 namespace fs
497 rtl_TextEncoding getThreadTextEncoding( void )
499 static bool bNeedsInit = true;
500 static rtl_TextEncoding nThreadTextEncoding;
501 if( bNeedsInit )
503 bNeedsInit = false;
504 nThreadTextEncoding = osl_getThreadTextEncoding();
506 return nThreadTextEncoding;
509 void create_directory(const fs::path indexDirName)
511 HCDBG(
512 std::cerr << "creating " <<
513 rtl::OUStringToOString(indexDirName.data, RTL_TEXTENCODING_UTF8).getStr()
514 << std::endl
516 osl::Directory::createPath(indexDirName.data);
519 void copy(const fs::path &src, const fs::path &dest)
521 osl::File::copy(src.data, dest.data);
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */