fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / helpcompiler / source / HelpCompiler.cxx
bloba45b3567b44817c5a5f2da4f2387491016edd842
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <HelpCompiler.hxx>
22 #include <BasCodeTagger.hxx>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <libxslt/xslt.h>
27 #include <libxslt/xsltInternals.h>
28 #include <libxslt/transform.h>
29 #include <libxslt/xsltutils.h>
30 #include <osl/thread.hxx>
32 static void impl_sleep( sal_uInt32 nSec )
34 TimeValue aTime;
35 aTime.Seconds = nSec;
36 aTime.Nanosec = 0;
38 osl::Thread::wait( aTime );
40 HelpCompiler::HelpCompiler(StreamTable &in_streamTable, const fs::path &in_inputFile,
41 const fs::path &in_src, const fs::path &in_zipdir, const fs::path &in_resCompactStylesheet,
42 const fs::path &in_resEmbStylesheet, const std::string &in_module, const std::string &in_lang,
43 bool in_bExtensionMode)
44 : streamTable(in_streamTable), inputFile(in_inputFile),
45 src(in_src), zipdir(in_zipdir), module(in_module), lang(in_lang), resCompactStylesheet(in_resCompactStylesheet),
46 resEmbStylesheet(in_resEmbStylesheet), bExtensionMode( in_bExtensionMode )
48 xmlKeepBlanksDefaultValue = 0;
49 char* os = getenv("OS");
50 if (os)
52 gui = (strcmp(os, "WNT") ? "UNIX" : "WIN");
53 gui = (strcmp(os, "MACOSX") ? gui : "MAC");
57 void HelpCompiler::tagBasicCodeExamples( xmlDocPtr doc )
59 try
61 BasicCodeTagger bct( doc );
62 bct.tagBasicCodes();
64 catch ( BasicCodeTagger::TaggerException &ex )
66 if ( ex != BasicCodeTagger::EMPTY_DOCUMENT )
67 throw;
71 xmlDocPtr HelpCompiler::compactXhpForJar( xmlDocPtr doc )
73 static xsltStylesheetPtr compact = NULL;
74 static const char *params[2 + 1];
75 params[0] = NULL;
76 xmlDocPtr compacted;
78 if (!compact)
80 compact = xsltParseStylesheetFile(reinterpret_cast<const xmlChar *>(resCompactStylesheet.native_file_string().c_str()));
83 compacted = xsltApplyStylesheet(compact, doc, params);
84 return compacted;
87 void HelpCompiler::saveXhpForJar( xmlDocPtr doc, const fs::path &filePath )
89 //save processed xhp document in ziptmp<module>_<lang>/text directory
90 #ifdef WNT
91 std::string pathSep = "\\";
92 #else
93 std::string pathSep = "/";
94 #endif
95 const std::string& sourceXhpPath = filePath.native_file_string();
96 std::string zipdirPath = zipdir.native_file_string();
97 const std::string srcdirPath( src.native_file_string() );
98 // srcdirPath contains trailing /, but we want the file path with / at the beginning
99 std::string jarXhpPath = sourceXhpPath.substr( srcdirPath.length() - 1 );
100 std::string xhpFileName = jarXhpPath.substr( jarXhpPath.rfind( pathSep ) + 1 );
101 jarXhpPath = jarXhpPath.substr( 0, jarXhpPath.rfind( pathSep ) );
102 if ( !jarXhpPath.compare( 1, 11, "text" + pathSep + "sbasic" ) )
104 tagBasicCodeExamples( doc );
106 if ( !jarXhpPath.compare( 1, 11, "text" + pathSep + "shared" ) )
108 const size_t pos = zipdirPath.find( "ziptmp" );
109 if ( pos != std::string::npos )
110 zipdirPath.replace( pos + 6, module.length(), "shared" );
112 xmlDocPtr compacted = compactXhpForJar( doc );
113 fs::create_directory( fs::path( zipdirPath + jarXhpPath, fs::native ) );
114 if ( -1 == xmlSaveFormatFileEnc( (zipdirPath + jarXhpPath + pathSep + xhpFileName).c_str(), compacted, "utf-8", 0 ) )
115 std::cerr << "Error saving file to " << (zipdirPath + jarXhpPath + pathSep + xhpFileName).c_str() << std::endl;
116 xmlFreeDoc(compacted);
120 xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
122 static xsltStylesheetPtr cur = NULL;
124 xmlDocPtr res;
125 if( bExtensionMode )
127 res = xmlParseFile(filePath.native_file_string().c_str());
128 if( !res ){
129 impl_sleep( 3 );
130 res = xmlParseFile(filePath.native_file_string().c_str());
133 else
135 static const char *params[2 + 1];
136 if (!cur)
138 static std::string fsroot('\'' + src.toUTF8() + '\'');
140 xmlSubstituteEntitiesDefault(1);
141 xmlLoadExtDtdDefaultValue = 1;
142 cur = xsltParseStylesheetFile(reinterpret_cast<const xmlChar *>(resEmbStylesheet.native_file_string().c_str()));
144 int nbparams = 0;
145 params[nbparams++] = "fsroot";
146 params[nbparams++] = fsroot.c_str();
147 params[nbparams] = NULL;
149 xmlDocPtr doc = xmlParseFile(filePath.native_file_string().c_str());
150 if( !doc )
152 impl_sleep( 3 );
153 doc = xmlParseFile(filePath.native_file_string().c_str());
156 saveXhpForJar( doc, filePath );
158 res = xsltApplyStylesheet(cur, doc, params);
159 xmlFreeDoc(doc);
161 return res;
164 // returns a node representing the whole stuff compiled for the current
165 // application.
166 xmlNodePtr HelpCompiler::clone(xmlNodePtr node, const std::string& appl)
168 xmlNodePtr root = xmlCopyNode(node, 2);
169 if (node->xmlChildrenNode)
171 xmlNodePtr list = node->xmlChildrenNode;
172 while (list)
174 if (strcmp(reinterpret_cast<const char*>(list->name), "switchinline") == 0 || strcmp(reinterpret_cast<const char*>(list->name), "switch") == 0)
176 std::string tmp="";
177 xmlChar * prop = xmlGetProp(list, reinterpret_cast<xmlChar const *>("select"));
178 if (prop != 0)
180 if (strcmp(reinterpret_cast<char *>(prop), "sys") == 0)
182 tmp = gui;
184 else if (strcmp(reinterpret_cast<char *>(prop), "appl") == 0)
186 tmp = appl;
188 xmlFree(prop);
190 if (tmp.compare("") != 0)
192 bool isCase=false;
193 xmlNodePtr caseList=list->xmlChildrenNode;
194 while (caseList)
196 xmlChar *select = xmlGetProp(caseList, reinterpret_cast<xmlChar const *>("select"));
197 if (select)
199 if (!strcmp(reinterpret_cast<char*>(select), tmp.c_str()) && !isCase)
201 isCase=true;
202 xmlNodePtr clp = caseList->xmlChildrenNode;
203 while (clp)
205 xmlAddChild(root, clone(clp, appl));
206 clp = clp->next;
209 xmlFree(select);
211 else
213 if ((strcmp(reinterpret_cast<const char*>(caseList->name), "defaultinline") != 0) && (strcmp(reinterpret_cast<const char*>(caseList->name), "default") != 0))
215 xmlAddChild(root, clone(caseList, appl));
217 else
219 if (!isCase)
221 xmlNodePtr clp = caseList->xmlChildrenNode;
222 while (clp)
224 xmlAddChild(root, clone(clp, appl));
225 clp = clp->next;
230 caseList = caseList->next;
234 else
236 xmlAddChild(root, clone(list, appl));
238 list = list->next;
241 return root;
244 class myparser
246 public:
247 std::string documentId;
248 std::string fileName;
249 std::string title;
250 std::unique_ptr<HashSet> hidlist;
251 std::unique_ptr<Hashtable> keywords;
252 std::unique_ptr<Stringtable> helptexts;
253 private:
254 HashSet extendedHelpText;
255 public:
256 myparser(const std::string &indocumentId, const std::string &infileName,
257 const std::string &intitle) : documentId(indocumentId), fileName(infileName),
258 title(intitle)
260 hidlist.reset(new HashSet);
261 keywords.reset(new Hashtable);
262 helptexts.reset(new Stringtable);
264 void traverse( xmlNodePtr parentNode );
265 private:
266 std::string module;
267 std::string dump(xmlNodePtr node);
270 std::string myparser::dump(xmlNodePtr node)
272 std::string app;
273 if (node->xmlChildrenNode)
275 xmlNodePtr list = node->xmlChildrenNode;
276 while (list)
278 app += dump(list);
279 list = list->next;
282 if (xmlNodeIsText(node))
284 xmlChar *pContent = xmlNodeGetContent(node);
285 app += std::string(reinterpret_cast<char*>(pContent));
286 xmlFree(pContent);
288 return app;
291 void trim(std::string& str)
293 std::string::size_type pos = str.find_last_not_of(' ');
294 if(pos != std::string::npos)
296 str.erase(pos + 1);
297 pos = str.find_first_not_of(' ');
298 if(pos != std::string::npos)
299 str.erase(0, pos);
301 else
302 str.erase(str.begin(), str.end());
305 void myparser::traverse( xmlNodePtr parentNode )
307 // traverse all nodes that belong to the parent
308 xmlNodePtr test ;
309 for (test = parentNode->xmlChildrenNode; test; test = test->next)
311 if (fileName.empty() && !strcmp(reinterpret_cast<const char*>(test->name), "filename"))
313 xmlNodePtr node = test->xmlChildrenNode;
314 if (xmlNodeIsText(node))
316 xmlChar *pContent = xmlNodeGetContent(node);
317 fileName = std::string(reinterpret_cast<char*>(pContent));
318 xmlFree(pContent);
321 else if (title.empty() && !strcmp(reinterpret_cast<const char*>(test->name), "title"))
323 title = dump(test);
324 if (title.empty())
325 title = "<notitle>";
327 else if (!strcmp(reinterpret_cast<const char*>(test->name), "bookmark"))
329 xmlChar *branchxml = xmlGetProp(test, reinterpret_cast<const xmlChar*>("branch"));
330 xmlChar *idxml = xmlGetProp(test, reinterpret_cast<const xmlChar*>("id"));
331 std::string branch(reinterpret_cast<char*>(branchxml));
332 std::string anchor(reinterpret_cast<char*>(idxml));
333 xmlFree (branchxml);
334 xmlFree (idxml);
336 std::string hid;
338 if (branch.find("hid") == 0)
340 size_t index = branch.find('/');
341 if (index != std::string::npos)
343 hid = branch.substr(1 + index);
344 // one shall serve as a documentId
345 if (documentId.empty())
346 documentId = hid;
347 extendedHelpText.push_back(hid);
348 HCDBG(std::cerr << "hid pushback" << (anchor.empty() ? hid : hid + "#" + anchor) << std::endl);
349 hidlist->push_back( anchor.empty() ? hid : hid + "#" + anchor);
351 else
352 continue;
354 else if (branch.compare("index") == 0)
356 LinkedList ll;
358 for (xmlNodePtr nd = test->xmlChildrenNode; nd; nd = nd->next)
360 if (strcmp(reinterpret_cast<const char*>(nd->name), "bookmark_value"))
361 continue;
363 std::string embedded;
364 xmlChar *embeddedxml = xmlGetProp(nd, reinterpret_cast<const xmlChar*>("embedded"));
365 if (embeddedxml)
367 embedded = std::string(reinterpret_cast<char*>(embeddedxml));
368 xmlFree (embeddedxml);
369 std::transform (embedded.begin(), embedded.end(),
370 embedded.begin(), tocharlower);
373 bool isEmbedded = !embedded.empty() && embedded.compare("true") == 0;
374 if (isEmbedded)
375 continue;
377 std::string keyword = dump(nd);
378 size_t keywordSem = keyword.find(';');
379 if (keywordSem != std::string::npos)
381 std::string tmppre =
382 keyword.substr(0,keywordSem);
383 trim(tmppre);
384 std::string tmppos =
385 keyword.substr(1+keywordSem);
386 trim(tmppos);
387 keyword = tmppre + ";" + tmppos;
389 ll.push_back(keyword);
391 if (!ll.empty())
392 (*keywords)[anchor] = ll;
394 else if (branch.compare("contents") == 0)
396 // currently not used
399 else if (!strcmp(reinterpret_cast<const char*>(test->name), "ahelp"))
401 //tool-tip
402 std::string text = dump(test);
403 std::replace(text.begin(), text.end(), '\n', ' ');
404 trim(text);
406 //tool-tip target
407 std::string hidstr("."); //. == previous seen hid bookmarks
408 xmlChar *hid = xmlGetProp(test, reinterpret_cast<const xmlChar*>("hid"));
409 if (hid)
411 hidstr = std::string(reinterpret_cast<char*>(hid));
412 xmlFree (hid);
415 if (hidstr != "." && !hidstr.empty()) //simple case of explicitly named target
417 assert(!hidstr.empty());
418 (*helptexts)[hidstr] = text;
420 else //apply to list of "current" hids determined by recent bookmarks that have hid in their branch
422 //TODO: make these asserts and flush out all our broken help ids
423 SAL_WARN_IF(hidstr.empty(), "helpcompiler", "hid='' for text:" << text);
424 SAL_WARN_IF(!hidstr.empty() && extendedHelpText.empty(), "helpcompiler", "hid='.' with no hid bookmark branches for text:" << text);
425 HashSet::const_iterator aEnd = extendedHelpText.end();
426 for (HashSet::const_iterator iter = extendedHelpText.begin(); iter != aEnd; ++iter)
428 std::string name = *iter;
429 (*helptexts)[name] = text;
432 extendedHelpText.clear();
434 // traverse children
435 traverse(test);
439 bool HelpCompiler::compile()
440 throw (HelpProcessingException, BasicCodeTagger::TaggerException)
442 // we now have the jaroutputstream, which will contain the document.
443 // now determine the document as a dom tree in variable docResolved
445 xmlDocPtr docResolvedOrg = getSourceDocument(inputFile);
447 // now add path to the document
448 // resolve the dom
450 if (!docResolvedOrg)
452 impl_sleep( 3 );
453 docResolvedOrg = getSourceDocument(inputFile);
454 if( !docResolvedOrg )
456 std::stringstream aStrStream;
457 aStrStream << "ERROR: file not existing: " << inputFile.native_file_string().c_str() << std::endl;
458 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
462 std::string documentId;
463 std::string fileName;
464 std::string title;
465 // returns a clone of the document with switch-cases resolved
466 std::string appl = module.substr(1);
467 for (unsigned int i = 0; i < appl.length(); ++i)
469 appl[i]=toupper(appl[i]);
471 xmlNodePtr docResolved = clone(xmlDocGetRootElement(docResolvedOrg), appl);
472 myparser aparser(documentId, fileName, title);
473 aparser.traverse(docResolved);
474 documentId = aparser.documentId;
475 fileName = aparser.fileName;
476 title = aparser.title;
478 HCDBG(std::cerr << documentId << " : " << fileName << " : " << title << std::endl);
480 xmlDocPtr docResolvedDoc = xmlCopyDoc(docResolvedOrg, false);
481 xmlDocSetRootElement(docResolvedDoc, docResolved);
483 streamTable.dropappl();
484 streamTable.appl_doc = docResolvedDoc;
485 streamTable.appl_hidlist = aparser.hidlist.release();
486 streamTable.appl_helptexts = aparser.helptexts.release();
487 streamTable.appl_keywords = aparser.keywords.release();
489 streamTable.document_id = documentId;
490 streamTable.document_path = fileName;
491 streamTable.document_title = title;
492 std::string actMod = module;
494 if ( !bExtensionMode && !fileName.empty())
496 if (fileName.find("/text/") == 0)
498 int len = strlen("/text/");
499 actMod = fileName.substr(len);
500 actMod = actMod.substr(0, actMod.find('/'));
503 streamTable.document_module = actMod;
504 xmlFreeDoc(docResolvedOrg);
505 return true;
508 namespace fs
510 rtl_TextEncoding getThreadTextEncoding()
512 static bool bNeedsInit = true;
513 static rtl_TextEncoding nThreadTextEncoding;
514 if( bNeedsInit )
516 bNeedsInit = false;
517 nThreadTextEncoding = osl_getThreadTextEncoding();
519 return nThreadTextEncoding;
522 void create_directory(const fs::path& indexDirName)
524 HCDBG(
525 std::cerr << "creating " <<
526 OUStringToOString(indexDirName.data, RTL_TEXTENCODING_UTF8).getStr()
527 << std::endl
529 osl::Directory::createPath(indexDirName.data);
532 void copy(const fs::path &src, const fs::path &dest)
534 osl::File::copy(src.data, dest.data);
538 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */