blob: ecec06534c1b9bda69982f8ed1c801e785d19159 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "HelpCompiler.hxx"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <libxslt/xslt.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#ifdef __MINGW32__
#include <tools/prewin.h>
#include <tools/postwin.h>
#endif
#include <osl/thread.hxx>
static void impl_sleep( sal_uInt32 nSec )
{
TimeValue aTime;
aTime.Seconds = nSec;
aTime.Nanosec = 0;
osl::Thread::wait( aTime );
}
HelpCompiler::HelpCompiler(StreamTable &in_streamTable, const fs::path &in_inputFile,
const fs::path &in_src, const fs::path &in_resEmbStylesheet,
const std::string &in_module, const std::string &in_lang, bool in_bExtensionMode)
: streamTable(in_streamTable), inputFile(in_inputFile),
src(in_src), module(in_module), lang(in_lang), resEmbStylesheet(in_resEmbStylesheet),
bExtensionMode( in_bExtensionMode )
{
xmlKeepBlanksDefaultValue = 0;
}
xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
{
static const char *params[4 + 1];
static xsltStylesheetPtr cur = NULL;
xmlDocPtr res;
if( bExtensionMode )
{
res = xmlParseFile(filePath.native_file_string().c_str());
if( !res ){
impl_sleep( 3 );
res = xmlParseFile(filePath.native_file_string().c_str());
}
}
else
{
if (!cur)
{
static std::string fsroot('\'' + src.toUTF8() + '\'');
static std::string esclang('\'' + lang + '\'');
xmlSubstituteEntitiesDefault(1);
xmlLoadExtDtdDefaultValue = 1;
cur = xsltParseStylesheetFile((const xmlChar *)resEmbStylesheet.native_file_string().c_str());
int nbparams = 0;
params[nbparams++] = "Language";
params[nbparams++] = esclang.c_str();
params[nbparams++] = "fsroot";
params[nbparams++] = fsroot.c_str();
params[nbparams] = NULL;
}
xmlDocPtr doc = xmlParseFile(filePath.native_file_string().c_str());
if( !doc )
{
impl_sleep( 3 );
doc = xmlParseFile(filePath.native_file_string().c_str());
}
//???res = xmlParseFile(filePath.native_file_string().c_str());
res = xsltApplyStylesheet(cur, doc, params);
xmlFreeDoc(doc);
}
return res;
}
HashSet HelpCompiler::switchFind(xmlDocPtr doc)
{
HashSet hs;
xmlChar *xpath = (xmlChar*)"//switchinline";
xmlXPathContextPtr context = xmlXPathNewContext(doc);
xmlXPathObjectPtr result = xmlXPathEvalExpression(xpath, context);
xmlXPathFreeContext(context);
if (result)
{
xmlNodeSetPtr nodeset = result->nodesetval;
for (int i = 0; i < nodeset->nodeNr; i++)
{
xmlNodePtr el = nodeset->nodeTab[i];
xmlChar *select = xmlGetProp(el, (xmlChar*)"select");
if (select)
{
if (!strcmp((const char*)select, "appl"))
{
xmlNodePtr n1 = el->xmlChildrenNode;
while (n1)
{
if ((!xmlStrcmp(n1->name, (const xmlChar*)"caseinline")))
{
xmlChar *appl = xmlGetProp(n1, (xmlChar*)"select");
hs.push_back(std::string((const char*)appl));
xmlFree(appl);
}
else if ((!xmlStrcmp(n1->name, (const xmlChar*)"defaultinline")))
hs.push_back(std::string("DEFAULT"));
n1 = n1->next;
}
}
xmlFree(select);
}
}
xmlXPathFreeObject(result);
}
hs.push_back(std::string("DEFAULT"));
return hs;
}
// returns a node representing the whole stuff compiled for the current
// application.
xmlNodePtr HelpCompiler::clone(xmlNodePtr node, const std::string& appl)
{
xmlNodePtr parent = xmlCopyNode(node, 2);
xmlNodePtr n = node->xmlChildrenNode;
while (n != NULL)
{
bool isappl = false;
if ( (!strcmp((const char*)n->name, "switchinline")) ||
(!strcmp((const char*)n->name, "switch")) )
{
xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
if (select)
{
if (!strcmp((const char*)select, "appl"))
isappl = true;
xmlFree(select);
}
}
if (isappl)
{
xmlNodePtr caseNode = n->xmlChildrenNode;
if (appl == "DEFAULT")
{
while (caseNode)
{
if (!strcmp((const char*)caseNode->name, "defaultinline"))
{
xmlNodePtr cnl = caseNode->xmlChildrenNode;
while (cnl)
{
xmlAddChild(parent, clone(cnl, appl));
cnl = cnl->next;
}
break;
}
caseNode = caseNode->next;
}
}
else
{
while (caseNode)
{
isappl=false;
if (!strcmp((const char*)caseNode->name, "caseinline"))
{
xmlChar *select = xmlGetProp(n, (xmlChar*)"select");
if (select)
{
if (!strcmp((const char*)select, appl.c_str()))
isappl = true;
xmlFree(select);
}
if (isappl)
{
xmlNodePtr cnl = caseNode->xmlChildrenNode;
while (cnl)
{
xmlAddChild(parent, clone(cnl, appl));
cnl = cnl->next;
}
break;
}
}
caseNode = caseNode->next;
}
}
}
else
xmlAddChild(parent, clone(n, appl));
n = n->next;
}
return parent;
}
class myparser
{
public:
std::string documentId;
std::string fileName;
std::string title;
HashSet *hidlist;
Hashtable *keywords;
Stringtable *helptexts;
private:
HashSet extendedHelpText;
public:
myparser(const std::string &indocumentId, const std::string &infileName,
const std::string &intitle) : documentId(indocumentId), fileName(infileName),
title(intitle)
{
hidlist = new HashSet;
keywords = new Hashtable;
helptexts = new Stringtable;
}
void traverse( xmlNodePtr parentNode );
private:
std::string dump(xmlNodePtr node);
};
std::string myparser::dump(xmlNodePtr node)
{
std::string app;
if (node->xmlChildrenNode)
{
xmlNodePtr list = node->xmlChildrenNode;
while (list)
{
app += dump(list);
list = list->next;
}
}
if (xmlNodeIsText(node))
{
xmlChar *pContent = xmlNodeGetContent(node);
app += std::string((const char*)pContent);
xmlFree(pContent);
// std::cout << app << std::endl;
}
return app;
}
void trim(std::string& str)
{
std::string::size_type pos = str.find_last_not_of(' ');
if(pos != std::string::npos)
{
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != std::string::npos)
str.erase(0, pos);
}
else
str.erase(str.begin(), str.end());
}
void myparser::traverse( xmlNodePtr parentNode )
{
// traverse all nodes that belong to the parent
xmlNodePtr test ;
for (test = parentNode->xmlChildrenNode; test; test = test->next)
{
if (fileName.empty() && !strcmp((const char*)test->name, "filename"))
{
xmlNodePtr node = test->xmlChildrenNode;
if (xmlNodeIsText(node))
{
xmlChar *pContent = xmlNodeGetContent(node);
fileName = std::string((const char*)pContent);
xmlFree(pContent);
}
}
else if (title.empty() && !strcmp((const char*)test->name, "title"))
{
title = dump(test);
if (title.empty())
title = "<notitle>";
}
else if (!strcmp((const char*)test->name, "bookmark"))
{
xmlChar *branchxml = xmlGetProp(test, (const xmlChar*)"branch");
xmlChar *idxml = xmlGetProp(test, (const xmlChar*)"id");
std::string branch((const char*)branchxml);
std::string anchor((const char*)idxml);
xmlFree (branchxml);
xmlFree (idxml);
std::string hid;
if (branch.find("hid") == 0)
{
size_t index = branch.find('/');
if (index != std::string::npos)
{
hid = branch.substr(1 + index);
// one shall serve as a documentId
if (documentId.empty())
documentId = hid;
extendedHelpText.push_back(hid);
std::string foo = anchor.empty() ? hid : hid + "#" + anchor;
HCDBG(std::cerr << "hid pushback" << foo << std::endl);
hidlist->push_back( anchor.empty() ? hid : hid + "#" + anchor);
}
else
continue;
}
else if (branch.compare("index") == 0)
{
LinkedList ll;
for (xmlNodePtr nd = test->xmlChildrenNode; nd; nd = nd->next)
{
if (strcmp((const char*)nd->name, "bookmark_value"))
continue;
std::string embedded;
xmlChar *embeddedxml = xmlGetProp(nd, (const xmlChar*)"embedded");
if (embeddedxml)
{
embedded = std::string((const char*)embeddedxml);
xmlFree (embeddedxml);
std::transform (embedded.begin(), embedded.end(),
embedded.begin(), tolower);
}
bool isEmbedded = !embedded.empty() && embedded.compare("true") == 0;
if (isEmbedded)
continue;
std::string keyword = dump(nd);
size_t keywordSem = keyword.find(';');
if (keywordSem != std::string::npos)
{
std::string tmppre =
keyword.substr(0,keywordSem);
trim(tmppre);
std::string tmppos =
keyword.substr(1+keywordSem);
trim(tmppos);
keyword = tmppre + ";" + tmppos;
}
ll.push_back(keyword);
}
if (!ll.empty())
(*keywords)[anchor] = ll;
}
else if (branch.compare("contents") == 0)
{
// currently not used
}
}
else if (!strcmp((const char*)test->name, "ahelp"))
{
std::string text = dump(test);
trim(text);
std::string name;
HashSet::const_iterator aEnd = extendedHelpText.end();
for (HashSet::const_iterator iter = extendedHelpText.begin(); iter != aEnd;
++iter)
{
name = *iter;
(*helptexts)[name] = text;
}
extendedHelpText.clear();
}
// traverse children
traverse(test);
}
}
bool HelpCompiler::compile( void ) throw( HelpProcessingException )
{
// we now have the jaroutputstream, which will contain the document.
// now determine the document as a dom tree in variable docResolved
xmlDocPtr docResolvedOrg = getSourceDocument(inputFile);
// now add path to the document
// resolve the dom
if (!docResolvedOrg)
{
impl_sleep( 3 );
docResolvedOrg = getSourceDocument(inputFile);
if( !docResolvedOrg )
{
std::stringstream aStrStream;
aStrStream << "ERROR: file not existing: " << inputFile.native_file_string().c_str() << std::endl;
throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
}
}
// now find all applications for which one has to compile
std::string documentId;
std::string fileName;
std::string title;
// returns all applications for which one has to compile
HashSet applications = switchFind(docResolvedOrg);
HashSet::const_iterator aEnd = applications.end();
for (HashSet::const_iterator aI = applications.begin(); aI != aEnd; ++aI)
{
std::string appl = *aI;
std::string modulename = appl;
if (modulename[0] == 'S')
{
modulename = modulename.substr(1);
std::transform(modulename.begin(), modulename.end(), modulename.begin(), tolower);
}
if (modulename != "DEFAULT" && modulename != module)
continue;
// returns a clone of the document with swich-cases resolved
xmlNodePtr docResolved = clone(xmlDocGetRootElement(docResolvedOrg), appl);
myparser aparser(documentId, fileName, title);
aparser.traverse(docResolved);
documentId = aparser.documentId;
fileName = aparser.fileName;
title = aparser.title;
HCDBG(std::cerr << documentId << " : " << fileName << " : " << title << std::endl);
xmlDocPtr docResolvedDoc = xmlCopyDoc(docResolvedOrg, false);
xmlDocSetRootElement(docResolvedDoc, docResolved);
if (modulename == "DEFAULT")
{
streamTable.dropdefault();
streamTable.default_doc = docResolvedDoc;
streamTable.default_hidlist = aparser.hidlist;
streamTable.default_helptexts = aparser.helptexts;
streamTable.default_keywords = aparser.keywords;
}
else if (modulename == module)
{
streamTable.dropappl();
streamTable.appl_doc = docResolvedDoc;
streamTable.appl_hidlist = aparser.hidlist;
streamTable.appl_helptexts = aparser.helptexts;
streamTable.appl_keywords = aparser.keywords;
}
else
{
std::stringstream aStrStream;
aStrStream << "ERROR: Found unexpected module name \"" << modulename
<< "\" in file" << src.native_file_string().c_str() << std::endl;
throw HelpProcessingException( HELPPROCESSING_GENERAL_ERROR, aStrStream.str() );
}
} // end iteration over all applications
streamTable.document_id = documentId;
streamTable.document_path = fileName;
streamTable.document_title = title;
std::string actMod = module;
if ( !bExtensionMode && !fileName.empty())
{
if (fileName.find("/text/") == 0)
{
int len = strlen("/text/");
actMod = fileName.substr(len);
actMod = actMod.substr(0, actMod.find('/'));
}
}
streamTable.document_module = actMod;
xmlFreeDoc(docResolvedOrg);
return true;
}
namespace fs
{
rtl_TextEncoding getThreadTextEncoding( void )
{
static bool bNeedsInit = true;
static rtl_TextEncoding nThreadTextEncoding;
if( bNeedsInit )
{
bNeedsInit = false;
nThreadTextEncoding = osl_getThreadTextEncoding();
}
return nThreadTextEncoding;
}
void create_directory(const fs::path indexDirName)
{
HCDBG(
std::cerr << "creating " <<
rtl::OUStringToOString(indexDirName.data, RTL_TEXTENCODING_UTF8).getStr()
<< std::endl
);
osl::Directory::createPath(indexDirName.data);
}
void rename(const fs::path &src, const fs::path &dest)
{
osl::File::move(src.data, dest.data);
}
void copy(const fs::path &src, const fs::path &dest)
{
osl::File::copy(src.data, dest.data);
}
bool exists(const fs::path &in)
{
osl::File tmp(in.data);
return (tmp.open(osl_File_OpenFlag_Read) == osl::FileBase::E_None);
}
void remove(const fs::path &in)
{
osl::File::remove(in.data);
}
void removeRecursive(rtl::OUString const& _suDirURL)
{
{
osl::Directory aDir(_suDirURL);
aDir.open();
if (aDir.isOpen())
{
osl::DirectoryItem aItem;
osl::FileStatus aStatus(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes);
while (aDir.getNextItem(aItem) == ::osl::FileBase::E_None)
{
if (osl::FileBase::E_None == aItem.getFileStatus(aStatus) &&
aStatus.isValid(osl_FileStatus_Mask_FileName | osl_FileStatus_Mask_Attributes))
{
rtl::OUString suFilename = aStatus.getFileName();
rtl::OUString suFullFileURL;
suFullFileURL += _suDirURL;
suFullFileURL += rtl::OUString::createFromAscii("/");
suFullFileURL += suFilename;
if (aStatus.getFileType() == osl::FileStatus::Directory)
removeRecursive(suFullFileURL);
else
osl::File::remove(suFullFileURL);
}
}
aDir.close();
}
}
osl::Directory::remove(_suDirURL);
}
void remove_all(const fs::path &in)
{
removeRecursive(in.data);
}
}
/* vi:set tabstop=4 shiftwidth=4 expandtab: */