/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "HelpCompiler.hxx" #include #include #include #include #include #include #include #include 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 = ""; } 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(RTL_CONSTASCII_USTRINGPARAM("/")); 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); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */