1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
)
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* guitmp
= getenv("GUI");
52 gui
= (strcmp(guitmp
, "UNX") ? gui
: "UNIX");
53 gui
= (strcmp(guitmp
, "MAC") ? gui
: "MAC");
54 gui
= (strcmp(guitmp
, "WNT") ? gui
: "WIN");
58 void HelpCompiler::tagBasicCodeExamples( xmlDocPtr doc
)
62 BasicCodeTagger
bct( doc
);
65 catch ( BasicCodeTagger::TaggerException
&ex
)
67 if ( ex
!= BasicCodeTagger::EMPTY_DOCUMENT
)
72 xmlDocPtr
HelpCompiler::compactXhpForJar( xmlDocPtr doc
)
74 static xsltStylesheetPtr compact
= NULL
;
75 static const char *params
[2 + 1];
81 compact
= xsltParseStylesheetFile((const xmlChar
*)resCompactStylesheet
.native_file_string().c_str());
84 compacted
= xsltApplyStylesheet(compact
, doc
, params
);
88 void HelpCompiler::saveXhpForJar( xmlDocPtr doc
, const fs::path
&filePath
)
90 //save processed xhp document in ziptmp<module>_<lang>/text directory
92 std::string pathSep
= "\\";
94 std::string pathSep
= "/";
96 const std::string
& sourceXhpPath
= filePath
.native_file_string();
97 std::string zipdirPath
= zipdir
.native_file_string();
98 const std::string
srcdirPath( src
.native_file_string() );
99 // srcdirPath contains trailing /, but we want the file path with / at the beginning
100 std::string jarXhpPath
= sourceXhpPath
.substr( srcdirPath
.length() - 1 );
101 std::string xhpFileName
= jarXhpPath
.substr( jarXhpPath
.rfind( pathSep
) + 1 );
102 jarXhpPath
= jarXhpPath
.substr( 0, jarXhpPath
.rfind( pathSep
) );
103 if ( !jarXhpPath
.compare( 1, 11, "text" + pathSep
+ "sbasic" ) )
105 tagBasicCodeExamples( doc
);
107 if ( !jarXhpPath
.compare( 1, 11, "text" + pathSep
+ "shared" ) )
109 const size_t pos
= zipdirPath
.find( "ziptmp" );
110 if ( pos
!= std::string::npos
)
111 zipdirPath
.replace( pos
+ 6, module
.length(), "shared" );
113 xmlDocPtr compacted
= compactXhpForJar( doc
);
114 fs::create_directory( fs::path( zipdirPath
+ jarXhpPath
, fs::native
) );
115 if ( -1 == xmlSaveFormatFileEnc( (zipdirPath
+ jarXhpPath
+ pathSep
+ xhpFileName
).c_str(), compacted
, "utf-8", 0 ) )
116 std::cerr
<< "Error saving file to " << (zipdirPath
+ jarXhpPath
+ pathSep
+ xhpFileName
).c_str() << std::endl
;
117 xmlFreeDoc(compacted
);
121 xmlDocPtr
HelpCompiler::getSourceDocument(const fs::path
&filePath
)
123 static xsltStylesheetPtr cur
= NULL
;
128 res
= xmlParseFile(filePath
.native_file_string().c_str());
131 res
= xmlParseFile(filePath
.native_file_string().c_str());
136 static const char *params
[2 + 1];
139 static std::string
fsroot('\'' + src
.toUTF8() + '\'');
141 xmlSubstituteEntitiesDefault(1);
142 xmlLoadExtDtdDefaultValue
= 1;
143 cur
= xsltParseStylesheetFile((const xmlChar
*)resEmbStylesheet
.native_file_string().c_str());
146 params
[nbparams
++] = "fsroot";
147 params
[nbparams
++] = fsroot
.c_str();
148 params
[nbparams
] = NULL
;
150 xmlDocPtr doc
= xmlParseFile(filePath
.native_file_string().c_str());
154 doc
= xmlParseFile(filePath
.native_file_string().c_str());
157 saveXhpForJar( doc
, filePath
);
159 res
= xsltApplyStylesheet(cur
, doc
, params
);
165 // returns a node representing the whole stuff compiled for the current
167 xmlNodePtr
HelpCompiler::clone(xmlNodePtr node
, const std::string
& appl
)
169 xmlNodePtr root
= xmlCopyNode(node
, 2);
170 if (node
->xmlChildrenNode
)
172 xmlNodePtr list
= node
->xmlChildrenNode
;
175 if (strcmp((const char*)list
->name
, "switchinline") == 0 || strcmp((const char*)list
->name
, "switch") == 0)
178 if (strcmp((const char*)xmlGetProp(list
, (xmlChar
*)"select"), "sys"))
182 if (strcmp((const char*)xmlGetProp(list
, (xmlChar
*)"select"), "appl"))
186 if (tmp
.compare("") != 0)
189 xmlNodePtr caseList
=list
->xmlChildrenNode
;
192 xmlChar
*select
= xmlGetProp(caseList
, (xmlChar
*)"select");
195 if (!strcmp((const char*)select
, tmp
.c_str()) && !isCase
)
198 xmlNodePtr clp
= caseList
->xmlChildrenNode
;
201 xmlAddChild(root
, clone(clp
, appl
));
209 if ((strcmp((const char*)caseList
->name
, "defaultinline") != 0) && (strcmp((const char*)caseList
->name
, "default") != 0))
211 xmlAddChild(root
, clone(caseList
, appl
));
217 xmlNodePtr clp
= caseList
->xmlChildrenNode
;
220 xmlAddChild(root
, clone(clp
, appl
));
226 caseList
= caseList
->next
;
232 xmlAddChild(root
, clone(list
, appl
));
243 std::string documentId
;
244 std::string fileName
;
248 Stringtable
*helptexts
;
250 HashSet extendedHelpText
;
252 myparser(const std::string
&indocumentId
, const std::string
&infileName
,
253 const std::string
&intitle
) : documentId(indocumentId
), fileName(infileName
),
256 hidlist
= new HashSet
;
257 keywords
= new Hashtable
;
258 helptexts
= new Stringtable
;
260 void traverse( xmlNodePtr parentNode
);
263 std::string
dump(xmlNodePtr node
);
266 std::string
myparser::dump(xmlNodePtr node
)
269 if (node
->xmlChildrenNode
)
271 xmlNodePtr list
= node
->xmlChildrenNode
;
278 if (xmlNodeIsText(node
))
280 xmlChar
*pContent
= xmlNodeGetContent(node
);
281 app
+= std::string((const char*)pContent
);
287 void trim(std::string
& str
)
289 std::string::size_type pos
= str
.find_last_not_of(' ');
290 if(pos
!= std::string::npos
)
293 pos
= str
.find_first_not_of(' ');
294 if(pos
!= std::string::npos
)
298 str
.erase(str
.begin(), str
.end());
301 void myparser::traverse( xmlNodePtr parentNode
)
303 // traverse all nodes that belong to the parent
305 for (test
= parentNode
->xmlChildrenNode
; test
; test
= test
->next
)
307 if (fileName
.empty() && !strcmp((const char*)test
->name
, "filename"))
309 xmlNodePtr node
= test
->xmlChildrenNode
;
310 if (xmlNodeIsText(node
))
312 xmlChar
*pContent
= xmlNodeGetContent(node
);
313 fileName
= std::string((const char*)pContent
);
317 else if (title
.empty() && !strcmp((const char*)test
->name
, "title"))
323 else if (!strcmp((const char*)test
->name
, "bookmark"))
325 xmlChar
*branchxml
= xmlGetProp(test
, (const xmlChar
*)"branch");
326 xmlChar
*idxml
= xmlGetProp(test
, (const xmlChar
*)"id");
327 std::string
branch((const char*)branchxml
);
328 std::string
anchor((const char*)idxml
);
334 if (branch
.find("hid") == 0)
336 size_t index
= branch
.find('/');
337 if (index
!= std::string::npos
)
339 hid
= branch
.substr(1 + index
);
340 // one shall serve as a documentId
341 if (documentId
.empty())
343 extendedHelpText
.push_back(hid
);
344 HCDBG(std::cerr
<< "hid pushback" << (anchor
.empty() ? hid
: hid
+ "#" + anchor
) << std::endl
);
345 hidlist
->push_back( anchor
.empty() ? hid
: hid
+ "#" + anchor
);
350 else if (branch
.compare("index") == 0)
354 for (xmlNodePtr nd
= test
->xmlChildrenNode
; nd
; nd
= nd
->next
)
356 if (strcmp((const char*)nd
->name
, "bookmark_value"))
359 std::string embedded
;
360 xmlChar
*embeddedxml
= xmlGetProp(nd
, (const xmlChar
*)"embedded");
363 embedded
= std::string((const char*)embeddedxml
);
364 xmlFree (embeddedxml
);
365 std::transform (embedded
.begin(), embedded
.end(),
366 embedded
.begin(), tocharlower
);
369 bool isEmbedded
= !embedded
.empty() && embedded
.compare("true") == 0;
373 std::string keyword
= dump(nd
);
374 size_t keywordSem
= keyword
.find(';');
375 if (keywordSem
!= std::string::npos
)
378 keyword
.substr(0,keywordSem
);
381 keyword
.substr(1+keywordSem
);
383 keyword
= tmppre
+ ";" + tmppos
;
385 ll
.push_back(keyword
);
388 (*keywords
)[anchor
] = ll
;
390 else if (branch
.compare("contents") == 0)
392 // currently not used
395 else if (!strcmp((const char*)test
->name
, "ahelp"))
397 std::string text
= dump(test
);
401 HashSet::const_iterator aEnd
= extendedHelpText
.end();
402 for (HashSet::const_iterator iter
= extendedHelpText
.begin(); iter
!= aEnd
; ++iter
)
405 (*helptexts
)[name
] = text
;
407 extendedHelpText
.clear();
414 bool HelpCompiler::compile( void ) throw( HelpProcessingException
)
416 // we now have the jaroutputstream, which will contain the document.
417 // now determine the document as a dom tree in variable docResolved
419 xmlDocPtr docResolvedOrg
= getSourceDocument(inputFile
);
421 // now add path to the document
427 docResolvedOrg
= getSourceDocument(inputFile
);
428 if( !docResolvedOrg
)
430 std::stringstream aStrStream
;
431 aStrStream
<< "ERROR: file not existing: " << inputFile
.native_file_string().c_str() << std::endl
;
432 throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR
, aStrStream
.str() );
436 std::string documentId
;
437 std::string fileName
;
439 // returns a clone of the document with switch-cases resolved
440 std::string appl
= module
.substr(1);
441 for (unsigned int i
= 0; i
< appl
.length(); ++i
)
443 appl
[i
]=toupper(appl
[i
]);
445 xmlNodePtr docResolved
= clone(xmlDocGetRootElement(docResolvedOrg
), appl
);
446 myparser
aparser(documentId
, fileName
, title
);
447 aparser
.traverse(docResolved
);
448 documentId
= aparser
.documentId
;
449 fileName
= aparser
.fileName
;
450 title
= aparser
.title
;
452 HCDBG(std::cerr
<< documentId
<< " : " << fileName
<< " : " << title
<< std::endl
);
454 xmlDocPtr docResolvedDoc
= xmlCopyDoc(docResolvedOrg
, false);
455 xmlDocSetRootElement(docResolvedDoc
, docResolved
);
457 streamTable
.dropappl();
458 streamTable
.appl_doc
= docResolvedDoc
;
459 streamTable
.appl_hidlist
= aparser
.hidlist
;
460 streamTable
.appl_helptexts
= aparser
.helptexts
;
461 streamTable
.appl_keywords
= aparser
.keywords
;
463 streamTable
.document_id
= documentId
;
464 streamTable
.document_path
= fileName
;
465 streamTable
.document_title
= title
;
466 std::string actMod
= module
;
468 if ( !bExtensionMode
&& !fileName
.empty())
470 if (fileName
.find("/text/") == 0)
472 int len
= strlen("/text/");
473 actMod
= fileName
.substr(len
);
474 actMod
= actMod
.substr(0, actMod
.find('/'));
477 streamTable
.document_module
= actMod
;
478 xmlFreeDoc(docResolvedOrg
);
484 rtl_TextEncoding
getThreadTextEncoding( void )
486 static bool bNeedsInit
= true;
487 static rtl_TextEncoding nThreadTextEncoding
;
491 nThreadTextEncoding
= osl_getThreadTextEncoding();
493 return nThreadTextEncoding
;
496 void create_directory(const fs::path indexDirName
)
499 std::cerr
<< "creating " <<
500 OUStringToOString(indexDirName
.data
, RTL_TEXTENCODING_UTF8
).getStr()
503 osl::Directory::createPath(indexDirName
.data
);
506 void copy(const fs::path
&src
, const fs::path
&dest
)
508 osl::File::copy(src
.data
, dest
.data
);
512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */