<commit_before><commit_msg>INTEGRATION: CWS helplinker01 (1.1.2); FILE ADDED 2007/05/21 13:13:15 cmc 1.1.2.2: #i70155# nitpicky macox warnings 2007/05/19 12:16:07 cmc 1.1.2.1: #ii70155# add a native HelpLinker<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: HelpCompiler.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: ihi $ $Date: 2007-06-05 10:24:30 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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 for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/


#include "HelpCompiler.hxx"
#include <limits.h>
#include <stdlib.h>
#include <libxslt/xslt.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>

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)
    : streamTable(in_streamTable), inputFile(in_inputFile),
    src(in_src), module(in_module), lang(in_lang), resEmbStylesheet(in_resEmbStylesheet)
{
    xmlKeepBlanksDefaultValue = 0;
}

xmlDocPtr HelpCompiler::getSourceDocument(const fs::path &filePath)
{
    static const char *params[4 + 1];
    static xsltStylesheetPtr cur = NULL;
    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());
    xmlDocPtr 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()
{
    // 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)
    {
        std::cerr << "ERROR: file not existing: " << src.native_file_string().c_str() << std::endl;
        exit(1);
    }

    // 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.default_doc = docResolvedDoc;
            streamTable.default_hidlist = aparser.hidlist;
            streamTable.default_helptexts = aparser.helptexts;
            streamTable.default_keywords = aparser.keywords;
        }
        else if (modulename == module)
        {
            streamTable.appl_doc = docResolvedDoc;
            streamTable.appl_hidlist = aparser.hidlist;
            streamTable.appl_helptexts = aparser.helptexts;
            streamTable.appl_keywords = aparser.keywords;
        }
        else
        {
            std::cerr << "unexpected case situation" << std::endl;
            exit(-1);
        }

    } // end iteration over all applications

    streamTable.document_id = documentId;
    streamTable.document_path = fileName;
    streamTable.document_title = title;
    std::string actMod = module;
    if (!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
{
    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);
    }

    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: */
{'fraction_non_alphanumeric': 0.12395133062947941, 'fraction_numerical': 0.007389299405522529, 'mean_word_length': 1.6540843409023887, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 107}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5389425', 'n_tokens_mistral': 5168, 'n_tokens_neox': 4924, 'n_words': 1079}