Files
libreoffice/helpcompiler/source/BasCodeTagger.cxx
Caolán McNamara 62fb1958f6 make building help not crash
a) subStr is not ours to delete, belongs to the OString
b) and anyway subStr points to the buffer of the out-of-scope temporary OString

Change-Id: I1689c6626c52ae9d94e0cfa5de4235be3c1f3262
2013-02-13 11:52:57 +00:00

233 lines
6.7 KiB
C++

#include <helpcompiler/BasCodeTagger.hxx>
LibXmlTreeWalker::LibXmlTreeWalker( xmlDocPtr doc )
{
if ( doc == NULL )
throw BasicCodeTagger::NULL_DOCUMENT;
m_pCurrentNode = xmlDocGetRootElement( doc );
if ( m_pCurrentNode == NULL )
throw BasicCodeTagger::EMPTY_DOCUMENT;
else if ( m_pCurrentNode->xmlChildrenNode != NULL )
m_Queue.push_back( m_pCurrentNode->xmlChildrenNode );
nextNode();
}
void LibXmlTreeWalker::nextNode()
{
//next node
if ( m_pCurrentNode->next == NULL )
{
m_pCurrentNode = m_Queue.front();
m_Queue.pop_front();
}
else
m_pCurrentNode = m_pCurrentNode->next;
//queue chiledren if they exist
if ( m_pCurrentNode->xmlChildrenNode != NULL )
m_Queue.push_back( m_pCurrentNode->xmlChildrenNode );
}
void LibXmlTreeWalker::ignoreCurrNodesChildren()
{
if ( m_pCurrentNode->xmlChildrenNode != NULL )
m_Queue.pop_back();
}
bool LibXmlTreeWalker::end()
{
return m_pCurrentNode->next == NULL && m_Queue.empty();
}
xmlNodePtr LibXmlTreeWalker::currentNode()
{
return m_pCurrentNode;
}
//======================================================
BasicCodeTagger::BasicCodeTagger( xmlDocPtr rootDoc )
{
if ( rootDoc == NULL )
throw NULL_DOCUMENT;
m_pDocument = rootDoc;
m_pXmlTreeWalker = NULL;
m_Highlighter.initialize( HIGHLIGHT_BASIC );
m_bTaggingCompleted = false;
}
BasicCodeTagger::~BasicCodeTagger()
{
if ( m_pXmlTreeWalker != NULL )
delete m_pXmlTreeWalker;
}
//!Gathers all the <bascode> tag nodes from xml tree.
/*!
* Assumes m_pDocument is valid. Handles m_pXmlTreeWalker and m_BasicCodeContainerTags members.
*/
void BasicCodeTagger::getBasicCodeContainerNodes()
{
xmlNodePtr currentNode;
m_BasicCodeContainerTags.clear();
if ( m_pXmlTreeWalker != NULL )
delete m_pXmlTreeWalker;
m_pXmlTreeWalker = new LibXmlTreeWalker( m_pDocument );
currentNode = m_pXmlTreeWalker->currentNode();
if ( !( xmlStrcmp( currentNode->name, (const xmlChar*) "bascode" ) ) )
{ //Found <bascode>
m_BasicCodeContainerTags.push_back( currentNode ); //it goes to the end of the list
}
while ( !m_pXmlTreeWalker->end() )
{
m_pXmlTreeWalker->nextNode();
if ( !( xmlStrcmp( m_pXmlTreeWalker->currentNode()->name, (const xmlChar*) "bascode" ) ) )
{ //Found <bascode>
m_BasicCodeContainerTags.push_back( m_pXmlTreeWalker->currentNode() ); //it goes to the end of the list
m_pXmlTreeWalker->ignoreCurrNodesChildren();
}
}
}
//! Extracts Basic Codes containted in <bascode> tags.
/*!
* For each <bascode> this method iterates trough it's <paragraph> tags and "inserts" <item> tags according
* to the Basic code syntax found in that paragraph.
*/
void BasicCodeTagger::tagBasCodeParagraphs()
{
//helper variables
xmlNodePtr currBascodeNode;
xmlNodePtr currParagraph;
while ( !m_BasicCodeContainerTags.empty() )
{
currBascodeNode = m_BasicCodeContainerTags.front();
currParagraph = currBascodeNode->xmlChildrenNode; //first <paragraph>
while ( currParagraph != NULL )
{
tagParagraph( currParagraph );
currParagraph=currParagraph->next;
}
m_BasicCodeContainerTags.pop_front(); //next element
}
}
//! Used by tagBasCodeParagraphs(). It does the work on the current paragraph containing Basic code.
void BasicCodeTagger::tagParagraph( xmlNodePtr paragraph )
{
//1. get paragraph text
xmlChar* codeSnippet;
codeSnippet = xmlNodeListGetString( m_pDocument, paragraph->xmlChildrenNode, 1 );
if ( codeSnippet == NULL )
{
return; //no text, nothing more to do here
}
//2. delete every child from paragraph (except attributes)
xmlNodePtr curNode = paragraph->xmlChildrenNode;
xmlNodePtr sibling;
while ( curNode != NULL )
{
sibling = curNode->next;
xmlUnlinkNode( curNode );
xmlFreeNode( curNode );
curNode = sibling;
}
//3. create new paragraph content
OUString strLine( reinterpret_cast<const sal_Char*>(codeSnippet),
strlen(reinterpret_cast<const char*>(codeSnippet)),
RTL_TEXTENCODING_UTF8 );
m_Highlighter.notifyChange ( 0, 0, &strLine, 1 );
HighlightPortions portions;
m_Highlighter.getHighlightPortions( 0, strLine, portions );
for ( size_t i=0; i<portions.size(); i++ )
{
HighlightPortion& r = portions[i];
OString sToken(OUStringToOString(strLine.copy(r.nBegin, r.nEnd-r.nBegin), RTL_TEXTENCODING_UTF8));
xmlNodePtr text = xmlNewText((const xmlChar*)sToken.getStr());
if ( r.tokenType != TT_WHITESPACE )
{
xmlChar* typeStr = getTypeString( r.tokenType );
curNode = xmlNewTextChild( paragraph, 0, (xmlChar*)"item", 0 );
xmlNewProp( curNode, (xmlChar*)"type", typeStr );
xmlAddChild( curNode, text );
xmlFree( typeStr );
}
else
xmlAddChild( paragraph, text );
}
xmlFree( codeSnippet );
}
//! Manages tagging process.
/*!
* This is the "main" function of BasicCodeTagger.
*/
void BasicCodeTagger::tagBasicCodes()
{
if ( m_bTaggingCompleted )
return;
//gather <bascode> nodes
try
{
getBasicCodeContainerNodes();
}
catch (TaggerException &ex)
{
std::cout << "BasCodeTagger error occured. Error code:" << ex << std::endl;
}
//tag basic code paragraphs in <bascode> tag
tagBasCodeParagraphs();
m_bTaggingCompleted = true;
}
//! Converts SyntaxHighlighter's TokenTypes enum to a type string for <item type=... >
xmlChar* BasicCodeTagger::getTypeString( TokenTypes tokenType )
{
const char* str;
switch ( tokenType )
{
case TT_UNKNOWN :
str = "unknown";
break;
case TT_IDENTIFIER :
str = "identifier";
break;
case TT_WHITESPACE :
str = "whitespace";
break;
case TT_NUMBER :
str = "number";
break;
case TT_STRING :
str = "string";
break;
case TT_EOL :
str = "eol";
break;
case TT_COMMENT :
str = "comment";
break;
case TT_ERROR :
str = "error";
break;
case TT_OPERATOR :
str = "operator";
break;
case TT_KEYWORDS :
str = "keyword";
break;
case TT_PARAMETER :
str = "parameter";
break;
default :
str = "unknown";
break;
}
return xmlCharStrdup( str );
}