You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
987 lines
36 KiB
987 lines
36 KiB
/***************************************************************************
|
|
saparser.cpp - description
|
|
-------------------
|
|
begin : Wed Feb 11 2004
|
|
copyright : (C) 2004 Andras Mantia <amantia@kde.org>
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
//qt includes
|
|
#include <tqtimer.h>
|
|
|
|
//kde includes
|
|
#include <kdebug.h>
|
|
#include <ktexteditor/editinterface.h>
|
|
|
|
//own includes
|
|
#include "saparser.h"
|
|
#include "sagroupparser.h"
|
|
#include "document.h"
|
|
#include "dtds.h"
|
|
#include "node.h"
|
|
#include "parsercommon.h"
|
|
#include "qtag.h"
|
|
#include "quantacommon.h"
|
|
#include "resource.h"
|
|
|
|
//#define DEBUG_PARSER
|
|
|
|
SAParser::SAParser()
|
|
{
|
|
m_write = 0L;
|
|
m_baseNode = 0L;
|
|
m_currentNode = 0L;
|
|
m_quotesRx = TQRegExp("\"|'");
|
|
m_specialInsideXml = false;
|
|
m_parsingEnabled = true;
|
|
m_synchronous = true;
|
|
m_parseOneLineTimer = new TQTimer(this);
|
|
connect(m_parseOneLineTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotParseOneLine()));
|
|
m_parseInDetailTimer = new TQTimer(this);
|
|
connect(m_parseInDetailTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotParseNodeInDetail()));
|
|
}
|
|
|
|
SAParser::~SAParser()
|
|
{
|
|
}
|
|
|
|
void SAParser::init(Node *node, Document* write)
|
|
{
|
|
m_baseNode = node;
|
|
m_write = write;
|
|
m_dtd = write->defaultDTD();
|
|
}
|
|
|
|
|
|
bool SAParser::slotParseOneLine()
|
|
{
|
|
if ((!m_parsingEnabled || !baseNode) && !m_synchronous)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "slotParseOneLine - interrupted" << endl;
|
|
#endif
|
|
return false;
|
|
}
|
|
if (s_line <= s_endLine)
|
|
{
|
|
s_contextFound = false;
|
|
switch (s_currentContext.type)
|
|
{
|
|
case Group:
|
|
case Text: {
|
|
int areaEndPos = -1;
|
|
int quotedStringPos = -1;
|
|
int commentPos = -1;
|
|
int groupKeywordPos = -1;
|
|
if (s_searchContent || (s_parentNode && s_parentNode->tag->dtd()->family == Xml))
|
|
{
|
|
//search for different s_contexts
|
|
if (s_searchContent) //search for quoted strings, comments, groups only in non-comment special areas
|
|
{
|
|
quotedStringPos = s_textLine.find(m_quotesRx, s_col); //quoted strings
|
|
s_searchedString = s_textLine.left(quotedStringPos);
|
|
commentPos = s_searchedString.find(s_dtd->commentsStartRx, s_col); //comments
|
|
s_searchedString = s_textLine.left(commentPos);
|
|
if (s_fullParse)
|
|
groupKeywordPos = s_searchedString.find(s_dtd->structRx, s_col); //groups, like { }
|
|
} else
|
|
s_searchedString = s_textLine;
|
|
int specialAreaPos = -1;
|
|
if (s_searchForSpecialAreas) //special area inside special area
|
|
{
|
|
s_searchedString = s_textLine.left(groupKeywordPos);
|
|
specialAreaPos = s_searchedString.find(s_dtd->specialAreaStartRx, s_col);
|
|
}
|
|
if (s_searchForAreaEnd) //the end of the special area
|
|
{
|
|
s_searchedString = s_textLine.left(specialAreaPos);
|
|
areaEndPos = s_searchedString.find(s_areaEndString, s_col);
|
|
} else
|
|
if (s_searchForForcedAreaEnd) //the end of the special area if a forcing string was specified
|
|
{
|
|
s_searchedString = s_textLine.left(specialAreaPos);
|
|
areaEndPos = s_searchedString.find(s_forcedAreaRx, s_col);
|
|
if (areaEndPos != -1)
|
|
s_areaEndString = s_forcedAreaRx.cap();
|
|
}
|
|
//check which s_context was found first
|
|
if (quotedStringPos != -1) //is it a quoted string?
|
|
{
|
|
if ( (quotedStringPos < commentPos || commentPos == -1) &&
|
|
(quotedStringPos < groupKeywordPos || groupKeywordPos == -1) &&
|
|
(quotedStringPos < specialAreaPos || specialAreaPos == -1) &&
|
|
(quotedStringPos < areaEndPos || areaEndPos == -1) )
|
|
{
|
|
s_context.type = QuotedString;
|
|
s_context.area.bCol = quotedStringPos;
|
|
s_context.startString = s_textLine.mid(quotedStringPos, 1);
|
|
s_contextFound = true;
|
|
}
|
|
}
|
|
if (!s_contextFound && commentPos != -1) //is it a comment?
|
|
{
|
|
if ( (commentPos < groupKeywordPos || groupKeywordPos == -1) &&
|
|
(commentPos < specialAreaPos || specialAreaPos == -1) &&
|
|
(commentPos < areaEndPos || areaEndPos == -1) )
|
|
{
|
|
s_context.type = Comment;
|
|
s_context.area.bCol = commentPos;
|
|
s_context.startString = s_dtd->commentsStartRx.cap();
|
|
s_contextFound = true;
|
|
}
|
|
}
|
|
if (!s_contextFound && groupKeywordPos != -1) //is it a group structure?
|
|
{
|
|
if ( (groupKeywordPos < specialAreaPos || specialAreaPos == -1) &&
|
|
(groupKeywordPos < areaEndPos || areaEndPos == -1) )
|
|
{
|
|
TQString foundText = s_dtd->structRx.cap();
|
|
if (foundText == s_dtd->structBeginStr)
|
|
{
|
|
s_context.type = Group;
|
|
s_context.area.bCol = groupKeywordPos;
|
|
s_context.startString = foundText;
|
|
//create a text node until the struct. beginning
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = groupKeywordPos + foundText.length() - 1;
|
|
if (s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text ||
|
|
s_currentNode->tag->type == Tag::Empty) )
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
else
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_currentNode, s_line, s_currentContext.area.eCol + 1, s_currentContext.parentNode);
|
|
|
|
s_currentNode->tag->type = Tag::ScriptStructureBegin;
|
|
s_currentNode->tag->single = false;
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
s_currentContext.lastNode = s_currentNode;
|
|
|
|
s_contextStack.push(s_currentContext);
|
|
s_currentContext.parentNode = s_currentNode;
|
|
s_col = s_context.area.bCol + s_context.startString.length();
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = s_col;
|
|
s_currentContext.type = Group;
|
|
if (m_synchronous)
|
|
//slotParseOneLine();
|
|
return true;
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from parseArea (opening group struct)." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
}
|
|
return true;
|
|
} else //it's a closing group structure element (like "}")
|
|
{
|
|
if (s_currentContext.type != Group)
|
|
{
|
|
s_col = groupKeywordPos + foundText.length();
|
|
if (m_synchronous)
|
|
//slotParseOneLine();
|
|
return true;
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from parseArea (closing group struct)." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
}
|
|
return true;
|
|
}
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = groupKeywordPos - 1;
|
|
//kdDebug(24000) << TQString("Group Struct s_context: %1, %2, %3, %4").arg( s_currentContext.bLine).arg(s_currentContext.bCol).arg(s_currentContext.eLine).arg(s_currentContext.eCol) << endl;
|
|
|
|
if (s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text ||
|
|
s_currentNode->tag->type == Tag::Empty) )
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
else
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_currentNode, s_line, groupKeywordPos, s_currentContext.parentNode);
|
|
if (s_currentNode)
|
|
{
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
s_previousContext = s_contextStack.pop();
|
|
s_currentContext.parentNode = s_previousContext.parentNode;
|
|
s_currentContext.lastNode = s_previousContext.lastNode;
|
|
s_currentContext.type = s_previousContext.type;
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = groupKeywordPos + foundText.length();
|
|
s_currentContext.area.eLine = s_currentContext.area.eCol = -1;
|
|
s_currentContext.startString = s_previousContext.startString;
|
|
s_col = s_currentContext.area.bCol;
|
|
|
|
Tag *tag = new Tag();
|
|
tag->name = foundText;
|
|
tag->setStr(foundText);
|
|
tag->setWrite(m_write);
|
|
tag->setTagPosition(s_line, groupKeywordPos, s_line, s_col - 1);
|
|
tag->setDtd(s_dtd);
|
|
tag->type = Tag::ScriptStructureEnd;
|
|
tag->single = true;
|
|
Node *node = new Node(s_currentContext.parentNode);
|
|
nodeNum++;
|
|
node->tag = tag;
|
|
node->insideSpecial = true;
|
|
node->specialInsideXml = m_specialInsideXml;
|
|
if (s_currentContext.parentNode && !s_currentContext.parentNode->child)
|
|
{
|
|
s_currentContext.parentNode->child = node;
|
|
}
|
|
else if (s_currentContext.lastNode)
|
|
{
|
|
node->prev = s_currentContext.lastNode;
|
|
s_currentContext.lastNode->next = node;
|
|
}
|
|
s_currentNode = node;
|
|
|
|
if (m_synchronous)
|
|
return true;
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from parseArea (group structure)." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (!s_contextFound && specialAreaPos != -1) //is it a special area?
|
|
{
|
|
if (specialAreaPos < areaEndPos || areaEndPos == -1)
|
|
{
|
|
TQString foundText = s_dtd->specialAreaStartRx.cap();
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = specialAreaPos - 1;
|
|
if (s_fullParse)
|
|
{
|
|
if ( s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text ||
|
|
s_currentNode->tag->type == Tag::Empty) )
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
else
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_currentNode, s_line, specialAreaPos, s_currentContext.parentNode);
|
|
if (s_currentNode)
|
|
{
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
}
|
|
//create a toplevel node for the included special area
|
|
AreaStruct area(s_line, specialAreaPos, s_line, specialAreaPos + foundText.length() - 1);
|
|
Node *node = ParserCommon::createScriptTagNode(m_write, area, foundText, s_dtd, s_currentContext.parentNode, s_currentNode);
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Parsing a nested area." << endl;
|
|
#endif
|
|
AreaStruct area2(s_line, specialAreaPos, s_endLine, s_endCol);
|
|
SAParser *p = new SAParser();
|
|
p->init(m_baseNode, m_write);
|
|
s_currentNode = p->parseArea(area2, foundText, "", node, s_fullParse, true);
|
|
s_line = p->lastParsedLine();
|
|
s_col = p->lastParsedColumn();
|
|
delete p;
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = s_col + 1;
|
|
s_textLine = ParserCommon::getLine(m_write, s_line, s_endLine, s_endCol);
|
|
s_col++;
|
|
if (m_synchronous)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from slotParseOneLine (nested area)." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} else //when we look only for the area end string
|
|
if (s_searchForAreaEnd)
|
|
{
|
|
areaEndPos = s_textLine.find(s_areaEndString, s_col);
|
|
} else
|
|
if (s_searchForForcedAreaEnd)
|
|
{
|
|
areaEndPos = s_textLine.find(s_forcedAreaRx, s_col);
|
|
if (areaEndPos != -1)
|
|
s_areaEndString = s_forcedAreaRx.cap();
|
|
}
|
|
|
|
if (!s_contextFound && areaEndPos != -1) //did we find the end of the special area?
|
|
{
|
|
m_lastParsedLine = s_line;
|
|
m_lastParsedCol = areaEndPos + s_areaEndString.length() - 1;
|
|
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = areaEndPos - 1;
|
|
//Always create a node between the opening and closing special area nodes.
|
|
//This fixes the "commnet loss" bug when editing in VPL and autocompletion
|
|
//for simple special areas like <? a ?>
|
|
if (s_fullParse || !s_parentNode->child)
|
|
{
|
|
if ( s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text ||
|
|
s_currentNode->tag->type == Tag::Empty) )
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
else
|
|
{
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_currentNode, s_line, areaEndPos, s_parentNode);
|
|
}
|
|
if (s_currentNode)
|
|
{
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
}
|
|
//kdDebug(24000) << TQString("Special area %1 ends at %2, %3").arg(s_dtd->name).arg(s_line).arg(lastCol) << endl;
|
|
|
|
//create a closing node for the special area
|
|
Tag *tag = new Tag();
|
|
tag->setTagPosition(s_line, areaEndPos, s_line, m_lastParsedCol);
|
|
tag->parse(s_areaEndString, m_write);
|
|
tag->setDtd(s_dtd);
|
|
tag->type = Tag::XmlTagEnd;
|
|
tag->validXMLTag = false; //FIXME: this is more or less a workaround. We should introduce and handle Tag::ScriptTagEnd
|
|
tag->single = true;
|
|
//at this point s_parentNode = the opening node of the special area (eg. <?)
|
|
//and it should always exist
|
|
Node *node = new Node(s_parentNode->parent);
|
|
nodeNum++;
|
|
s_parentNode->next = node;
|
|
node->prev = s_parentNode;
|
|
node->tag = tag;
|
|
node->closesPrevious = true;
|
|
|
|
if (s_fullParse)
|
|
{
|
|
Node *g_node, *g_endNode;
|
|
g_node = s_parentNode->child;
|
|
/* g_endNode = s_currentNode;
|
|
if (g_node && g_node == g_endNode)
|
|
g_endNode = s_parentNode->next;*/
|
|
g_endNode = node;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseForScriptGroup from slotParseOneLine." << endl;
|
|
#endif
|
|
// slotParseForScriptGroup();
|
|
if (!m_synchronous)
|
|
{
|
|
bool parsingLastNode = true;
|
|
Node *n = g_endNode;
|
|
while (n)
|
|
{
|
|
n = n->nextSibling();
|
|
if (n && n->insideSpecial)
|
|
{
|
|
parsingLastNode = false;
|
|
break;
|
|
}
|
|
}
|
|
SAGroupParser *groupParser = new SAGroupParser(this, write(), g_node, g_endNode, m_synchronous, parsingLastNode, true);
|
|
connect(groupParser, TQT_SIGNAL(rebuildStructureTree(bool)), TQT_SIGNAL(rebuildStructureTree(bool)));
|
|
connect(groupParser, TQT_SIGNAL(cleanGroups()), TQT_SIGNAL(cleanGroups()));
|
|
connect(groupParser, TQT_SIGNAL(parsingDone(SAGroupParser*)), TQT_SLOT(slotGroupParsingDone(SAGroupParser*)));
|
|
groupParser->slotParseForScriptGroup();
|
|
m_groupParsers.append(groupParser);
|
|
}
|
|
}
|
|
|
|
m_lastParsedNode = node;
|
|
s_useReturnVars = true;
|
|
if (!m_synchronous)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling parsingDone from slotParseOneLine (area end found)." << endl;
|
|
#endif
|
|
m_lastParsedNode = parsingDone();
|
|
}
|
|
return false; //parsing finished
|
|
}
|
|
if (s_contextFound)
|
|
{
|
|
s_context.area.bLine = s_line;
|
|
s_context.area.eLine = s_context.area.eCol = -1;
|
|
s_context.parentNode = s_currentContext.parentNode;
|
|
s_currentContext.area.eLine = s_context.area.bLine;
|
|
s_currentContext.area.eCol = s_context.area.bCol - 1;
|
|
// s_currentContext.parentNode = s_parentNode;
|
|
s_contextStack.push(s_currentContext);
|
|
if (s_fullParse)
|
|
{
|
|
if (s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text || s_currentNode->tag->type == Tag::Empty) )
|
|
{
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
} else
|
|
if (s_currentContext.area.bLine < s_currentContext.area.eLine ||
|
|
(s_currentContext.area.bLine == s_currentContext.area.eLine &&
|
|
s_currentContext.area.bCol < s_currentContext.area.eCol))
|
|
{
|
|
//create a tag from the s_currentContext
|
|
Tag *tag = new Tag(s_currentContext.area, m_write, s_dtd);
|
|
TQString tagStr = tag->tagStr();
|
|
tag->cleanStr = tagStr;
|
|
QuantaCommon::removeCommentsAndQuotes(tag->cleanStr, s_dtd);
|
|
if (tagStr.simplifyWhiteSpace().isEmpty())
|
|
{
|
|
tag->type = Tag::Empty;
|
|
} else
|
|
{
|
|
tag->type = Tag::Text;
|
|
}
|
|
tag->single = true;
|
|
//create a node with the above tag
|
|
Node *node = new Node(s_currentContext.parentNode);
|
|
nodeNum++;
|
|
node->tag = tag;
|
|
node->insideSpecial = true;
|
|
node->specialInsideXml = m_specialInsideXml;
|
|
if (s_currentContext.parentNode && !s_currentContext.parentNode->child)
|
|
{
|
|
s_currentContext.parentNode->child = node;
|
|
}
|
|
else if (s_currentNode)
|
|
{
|
|
node->prev = s_currentNode;
|
|
s_currentNode->next = node;
|
|
}
|
|
s_currentNode = node;
|
|
}
|
|
}
|
|
//kdDebug(24000) << TQString("%1 s_context: %2, %3, %4, %5").arg(s_currentContext.type).arg( s_currentContext.bLine).arg(s_currentContext.bCol).arg(s_currentContext.eLine).arg(s_currentContext.eCol) << endl;
|
|
|
|
s_currentContext = s_context;
|
|
s_col = s_context.area.bCol + s_context.startString.length();
|
|
} else
|
|
{
|
|
s_line++;
|
|
s_col = 0;
|
|
s_textLine = ParserCommon::getLine(m_write, s_line, s_endLine, s_endCol);
|
|
}
|
|
break;
|
|
}
|
|
case QuotedString:
|
|
{
|
|
int pos = -1;
|
|
int p = s_col;
|
|
int l = s_textLine.length();
|
|
while (p < l)
|
|
{
|
|
p = s_textLine.find(s_currentContext.startString, p);
|
|
if (p != -1)
|
|
{
|
|
if (p >= 0)
|
|
{
|
|
int i = p - 1;
|
|
int slahNum = 0;
|
|
while (i > 0 && s_textLine[i] == '\\')
|
|
{
|
|
slahNum++;
|
|
i--;
|
|
}
|
|
if (p == 0 || (slahNum % 2 == 0))
|
|
{
|
|
pos = p;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
break;
|
|
p++;
|
|
}
|
|
if (pos != -1)
|
|
{
|
|
// if (pos != 0) pos++;
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = pos;
|
|
//kdDebug(24000) << TQString("Quoted String s_context: %1, %2, %3, %4").arg( s_currentContext.bLine).arg(s_currentContext.bCol).arg(s_currentContext.eLine).arg(s_currentContext.eCol) << endl;
|
|
if (s_fullParse)
|
|
{
|
|
if ( s_currentNode &&
|
|
(s_currentNode->tag->type == Tag::Text ||
|
|
s_currentNode->tag->type == Tag::Empty) )
|
|
ParserCommon::appendAreaToTextNode(m_write, s_currentContext.area, s_currentNode);
|
|
else
|
|
s_currentNode = ParserCommon::createTextNode(m_write, 0L, s_line, pos, s_currentContext.parentNode);
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
s_previousContext = s_contextStack.pop();
|
|
s_currentContext.parentNode = s_previousContext.parentNode;
|
|
s_currentContext.type = s_previousContext.type;
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = pos + 1;
|
|
s_currentContext.area.eLine = s_currentContext.area.eCol = -1;
|
|
s_currentContext.startString = s_previousContext.startString;
|
|
s_col = pos + 1;
|
|
} else
|
|
{
|
|
s_line++;
|
|
s_col = 0;
|
|
s_textLine = ParserCommon::getLine(m_write, s_line, s_endLine, s_endCol);
|
|
}
|
|
break;
|
|
}
|
|
case Comment:
|
|
{
|
|
int pos = s_textLine.find(s_dtd->comments[s_currentContext.startString], s_col);
|
|
if (pos == -1 && s_dtd->comments[s_currentContext.startString] == "\n")
|
|
{
|
|
int pos2 = s_textLine.find(s_areaEndString, s_col);
|
|
if (pos2 != -1)
|
|
{
|
|
pos = pos2 - 1;
|
|
} else
|
|
{
|
|
pos = s_textLine.length();
|
|
}
|
|
}
|
|
if (pos != -1)
|
|
{
|
|
s_currentContext.area.eLine = s_line;
|
|
s_currentContext.area.eCol = pos + s_dtd->comments[s_currentContext.startString].length() - 1;
|
|
s_currentContext.type = s_previousContext.type;
|
|
//kdDebug(24000) << TQString("Comment s_context: %1, %2, %3, %4").arg( s_currentContext.bLine).arg(s_currentContext.bCol).arg(s_currentContext.eLine).arg(s_currentContext.eCol) << endl;
|
|
|
|
if (s_fullParse)
|
|
{
|
|
//create a tag with the comment
|
|
Tag *tag = new Tag(s_currentContext.area, m_write, s_dtd);
|
|
tag->type = Tag::Comment;
|
|
tag->single = true;
|
|
//create a node with the above tag
|
|
Node *node = new Node(s_currentContext.parentNode);
|
|
nodeNum++;
|
|
node->tag = tag;
|
|
node->insideSpecial = true;
|
|
node->specialInsideXml = m_specialInsideXml;
|
|
if (s_currentNode && s_currentNode != node->parent)
|
|
{
|
|
s_currentNode->next = node;
|
|
node->prev = s_currentNode;
|
|
} else
|
|
if (node->parent && !node->parent->child)
|
|
node->parent->child = node;
|
|
s_currentNode = node;
|
|
}
|
|
s_previousContext = s_contextStack.pop();
|
|
s_currentContext.parentNode = s_previousContext.parentNode;
|
|
s_currentContext.type = s_previousContext.type;
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = s_currentContext.area.eCol + 1;
|
|
s_currentContext.area.eLine = s_currentContext.area.eCol = -1;
|
|
s_currentContext.startString = s_previousContext.startString;
|
|
s_col = s_currentContext.area.bCol;
|
|
} else
|
|
{
|
|
s_line++;
|
|
s_col = 0;
|
|
s_textLine = ParserCommon::getLine(m_write, s_line, s_endLine, s_endCol);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
s_line++;
|
|
s_col = 0;
|
|
s_textLine = ParserCommon::getLine(m_write, s_line, s_endLine, s_endCol);
|
|
}
|
|
}
|
|
if (m_synchronous)
|
|
{
|
|
//slotParseOneLine();
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from slotParseOneLine." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
}
|
|
} else
|
|
{
|
|
if (!m_synchronous)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling parsingDone from slotParseOneLine." << endl;
|
|
#endif
|
|
parsingDone();
|
|
}
|
|
return false; //parsing finished
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Node* SAParser::parseArea(const AreaStruct &specialArea,
|
|
const TQString &areaStartString,
|
|
const TQString &forcedAreaEndString,
|
|
Node *parentNode,
|
|
bool fullParse, bool synchronous)
|
|
{
|
|
m_synchronous = synchronous;
|
|
s_parentNode = parentNode;
|
|
s_fullParse = fullParse;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "parseArea full: " << s_fullParse << " synch: " << m_synchronous <<endl;
|
|
#endif
|
|
|
|
int s_startLine = specialArea.bLine;
|
|
int s_startCol = specialArea.bCol;
|
|
s_endLine = specialArea.eLine;
|
|
s_endCol = specialArea.eCol;
|
|
//kdDebug(24000) << TQString("Starting to parse at %1, %2 for %3").arg(s_startLine).arg(s_startCol).arg(areaStartString) << endl;
|
|
|
|
s_searchForAreaEnd = false;
|
|
s_searchForForcedAreaEnd = false;
|
|
s_dtd = 0L;
|
|
if (s_parentNode && !areaStartString.isEmpty())
|
|
{
|
|
const DTDStruct *parentDTD = m_dtd;
|
|
if (s_parentNode->parent)
|
|
parentDTD = s_parentNode->parent->tag->dtd();
|
|
s_dtd = DTDs::ref()->find(parentDTD->specialAreaNames[areaStartString]);
|
|
s_areaEndString = parentDTD->specialAreas[areaStartString];
|
|
s_searchForAreaEnd = true;
|
|
}
|
|
if (!forcedAreaEndString.isEmpty())
|
|
{
|
|
s_forcedAreaRx.setPattern(forcedAreaEndString);
|
|
s_forcedAreaRx.setCaseSensitive(m_dtd->caseSensitive);
|
|
s_searchForForcedAreaEnd = true;
|
|
s_searchForAreaEnd = false;
|
|
if (s_parentNode)
|
|
s_dtd = s_parentNode->tag->dtd();
|
|
}
|
|
s_searchContent = true;
|
|
if (s_parentNode && s_parentNode->tag->type == Tag::Comment)
|
|
s_searchContent = false;
|
|
if (!s_dtd)
|
|
{
|
|
if (s_parentNode)
|
|
s_dtd = s_parentNode->tag->dtd(); //fallback, usually when the special area is a comment
|
|
else
|
|
s_dtd = m_dtd; //fallback when there is no s_parentNode
|
|
}
|
|
m_write->addDTEP(s_dtd->name);
|
|
s_searchForSpecialAreas = (s_dtd->specialAreas.count() > 0);
|
|
if (s_parentNode && s_parentNode->tag->type == Tag::Comment)
|
|
s_searchForSpecialAreas = false;
|
|
s_col = s_startCol + areaStartString.length();
|
|
s_line = s_startLine;
|
|
s_textLine = m_write->text(s_startLine, 0, s_startLine, m_write->editIf->lineLength(s_startLine));
|
|
if (s_line == s_endLine)
|
|
{
|
|
if (s_endCol > 0)
|
|
s_textLine.truncate(s_endCol + 1);
|
|
else
|
|
s_textLine = "";
|
|
}
|
|
|
|
s_previousContext.type = Unknown;
|
|
s_currentContext.type = Text;
|
|
s_currentContext.area.bLine = s_line;
|
|
s_currentContext.area.bCol = s_col;
|
|
s_currentContext.area.eLine = s_currentContext.area.eCol = -1;
|
|
s_currentContext.parentNode = s_parentNode;
|
|
s_currentNode = s_parentNode;
|
|
m_lastParsedNode = 0L;
|
|
s_useReturnVars = false;
|
|
if (s_line <= s_endLine)
|
|
{
|
|
if (m_synchronous)
|
|
{
|
|
while (slotParseOneLine()); //actually this parses the whole area, as synchronous == true
|
|
if (s_useReturnVars) //this is true if the special area end was found
|
|
{
|
|
return m_lastParsedNode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseOneLine from parseArea." << endl;
|
|
#endif
|
|
m_parseOneLineTimer->start(0, true);
|
|
return 0L;
|
|
}
|
|
}
|
|
if (m_synchronous) //if the special area end was not found and we are in synchronous mode
|
|
{
|
|
s_next = 0L;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling parsingDone from parseArea." << endl;
|
|
#endif
|
|
s_currentNode = parsingDone();
|
|
return s_currentNode;
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
Node *SAParser::parsingDone()
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "parsingDone. Use return values:" << s_useReturnVars << endl;
|
|
#endif
|
|
if (s_useReturnVars)
|
|
{
|
|
if (s_fullParse)
|
|
{
|
|
Node *n = m_lastParsedNode;
|
|
if (m_useNext)
|
|
{
|
|
// kdDebug(24000) << "m_lastParsedNode: " << m_lastParsedNode << " " << m_lastParsedNode->tag->tagStr() << endl;
|
|
n->next = s_next;
|
|
if (s_next)
|
|
{
|
|
s_next->prev = n;
|
|
}
|
|
n->prev = s_parentNode;
|
|
}
|
|
m_currentNode = n->nextSibling();
|
|
if (m_currentNode)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseNodeInDetail from parsingDone (use return values)" << endl;
|
|
#endif
|
|
m_parseInDetailTimer->start(0, true);
|
|
return m_lastParsedNode;
|
|
}
|
|
else
|
|
{
|
|
m_parsingEnabled = true;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Emitting rebuildStructureTree from parsingDone (use return values). Enable parsing." << endl;
|
|
#endif
|
|
emit rebuildStructureTree(false);
|
|
#ifdef DEBUG_PARSER
|
|
// kdDebug(24000) << "Calling cleanGroups from SAParser::parsingDone" << endl;
|
|
#endif
|
|
emit cleanGroups();
|
|
}
|
|
}
|
|
m_currentNode = 0L;
|
|
return m_lastParsedNode;
|
|
}
|
|
if (!s_currentNode)
|
|
{
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_parentNode, s_endLine, s_endCol, s_parentNode);
|
|
if (s_currentNode)
|
|
{
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
}
|
|
else if (s_parentNode && !s_parentNode->next)
|
|
{
|
|
s_currentNode = ParserCommon::createTextNode(m_write, s_currentNode, s_endLine, s_endCol, s_parentNode);
|
|
s_currentNode->insideSpecial = true;
|
|
s_currentNode->specialInsideXml = m_specialInsideXml;
|
|
}
|
|
if (s_fullParse)
|
|
{
|
|
Node *n;
|
|
if (s_parentNode)
|
|
{
|
|
n = s_parentNode;//->child;
|
|
} else
|
|
{
|
|
n = s_currentNode;
|
|
while (n && n->prev)
|
|
n = n->prev;
|
|
s_currentNode = n;
|
|
}
|
|
Node *g_node = n;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseForScriptGroup from parsingDone. Synch:" << m_synchronous << endl;
|
|
#endif
|
|
//parse for groups only when doing aynchronous detailed parsing
|
|
if (!m_synchronous)
|
|
{
|
|
SAGroupParser *groupParser = new SAGroupParser(this, write(), g_node, 0L, m_synchronous, true /*last node*/, true);
|
|
connect(groupParser, TQT_SIGNAL(rebuildStructureTree(bool)), TQT_SIGNAL(rebuildStructureTree(bool)));
|
|
connect(groupParser, TQT_SIGNAL(cleanGroups()), TQT_SIGNAL(cleanGroups()));
|
|
connect(groupParser, TQT_SIGNAL(parsingDone(SAGroupParser*)), TQT_SLOT(slotGroupParsingDone(SAGroupParser*)));
|
|
groupParser->slotParseForScriptGroup();
|
|
m_groupParsers.append(groupParser);
|
|
}
|
|
}
|
|
|
|
m_lastParsedLine = s_endLine;
|
|
m_lastParsedCol = s_endCol + 1;
|
|
|
|
if (s_fullParse && m_currentNode)
|
|
{
|
|
if (m_useNext && s_currentNode)
|
|
{
|
|
// kdDebug(24000) << "s_currentNode: " << s_currentNode << endl;
|
|
Node *n = s_currentNode;
|
|
n->next = s_next;
|
|
if (s_next)
|
|
s_next->prev = n;
|
|
}
|
|
m_currentNode = m_currentNode->nextSibling();
|
|
if (m_currentNode)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseNodeInDetail from parsingDone." << endl;
|
|
#endif
|
|
m_parseInDetailTimer->start(0, true);
|
|
emit rebuildStructureTree(false);
|
|
}
|
|
else
|
|
{
|
|
m_parsingEnabled = true;
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Emitting detailedParsingDone from parsingDone. Enable parsing." << endl;
|
|
#endif
|
|
emit rebuildStructureTree(false);
|
|
}
|
|
}
|
|
m_currentNode = 0L;
|
|
return s_currentNode;
|
|
}
|
|
|
|
void SAParser::parseInDetail(bool synchronous)
|
|
{
|
|
// synchronous = true; //for testing. Uncomment to test the parser in synchronous mode
|
|
// return; //for testing. Uncomment to disable the detailed parser
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "parseInDetail. Enabled: " << m_parsingEnabled << endl;
|
|
#endif
|
|
if (!m_parsingEnabled)
|
|
{
|
|
m_currentNode = m_baseNode;
|
|
m_parsingEnabled = true;
|
|
m_synchronous = synchronous;
|
|
if (m_currentNode)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling slotParseNodeInDetail from parseInDetail." << endl;
|
|
#endif
|
|
slotParseNodeInDetail();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SAParser::slotParseNodeInDetail()
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "slotParseNodeInDetail. Enabled: " << m_parsingEnabled << " Synch: " << m_synchronous << endl; //this is really heavy debug information, enable only when really needed
|
|
#endif
|
|
if (m_currentNode && m_parsingEnabled && baseNode)
|
|
{
|
|
if (m_currentNode->insideSpecial &&
|
|
m_currentNode->tag->type != Tag::Comment &&
|
|
m_currentNode->tag->type != Tag::Text &&
|
|
m_currentNode->tag->type != Tag::Empty)
|
|
{
|
|
Node::deleteNode(m_currentNode->child);
|
|
m_currentNode->child = 0L;
|
|
AreaStruct area(m_currentNode->tag->area());
|
|
s_next = 0L;
|
|
m_useNext = false;
|
|
//FIXME: Find out why can the tag become 0L
|
|
if (m_currentNode->next && m_currentNode->next->tag)
|
|
{
|
|
AreaStruct area2(m_currentNode->next->tag->area());
|
|
area.eLine = area2.eLine;
|
|
area.eCol = area2.eCol + 1;
|
|
s_next = m_currentNode->next->next;
|
|
if (m_currentNode->next->closesPrevious)
|
|
{
|
|
m_currentNode->next->removeAll = false;
|
|
Node *secondNext = m_currentNode->next->next;
|
|
if (secondNext)
|
|
secondNext->prev = m_currentNode;
|
|
Node::deleteNode(m_currentNode->next);
|
|
m_currentNode->next = secondNext;
|
|
m_useNext = true;
|
|
}
|
|
} else
|
|
{
|
|
area.eLine = m_write->editIf->numLines() - 1;
|
|
area.eCol = m_write->editIf->lineLength(area.eLine);
|
|
}
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Calling parseArea from slotParseNodeInDetail." << endl;
|
|
#endif
|
|
TQString areaStartString = m_currentNode->tag->tagStr();
|
|
if (m_currentNode->tag->dtd()->specialAreaNames[areaStartString].isEmpty())
|
|
{
|
|
AreaStruct area2(m_currentNode->tag->area());
|
|
area.bLine = area2.eLine;
|
|
area.bCol = area2.eCol + 1;
|
|
parseArea(area, "", "</"+m_currentNode->tag->name+"\\s*>", m_currentNode, true, m_synchronous);
|
|
}
|
|
else
|
|
parseArea(area, m_currentNode->tag->tagStr(), "", m_currentNode, true, m_synchronous);
|
|
} else
|
|
{
|
|
// Node *node = m_currentNode;
|
|
m_currentNode = m_currentNode->nextSibling();
|
|
if (m_currentNode)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
// kdDebug(24001) << "Calling slotParseNodeInDetail from slotParseNodeInDetail." << endl; //this is really heavy debug information, enable only when really needed
|
|
#endif
|
|
m_parseInDetailTimer->start(0, true);
|
|
} else
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Emitting rebuildStructureTree from slotParseNodeInDetail." << endl;
|
|
#endif
|
|
emit rebuildStructureTree(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SAParser::setParsingEnabled(bool enabled)
|
|
{
|
|
#ifdef DEBUG_PARSER
|
|
kdDebug(24001) << "Parsing enabled: " << enabled << endl;
|
|
#endif
|
|
m_parsingEnabled = enabled;
|
|
if (!enabled)
|
|
{
|
|
m_parseOneLineTimer->stop();
|
|
m_parseInDetailTimer->stop();
|
|
for (TQValueList<SAGroupParser*>::Iterator it = m_groupParsers.begin(); it != m_groupParsers.end(); ++it)
|
|
{
|
|
(*it)->m_parseForGroupTimer->stop();
|
|
delete (*it);
|
|
}
|
|
m_groupParsers.clear();
|
|
}
|
|
}
|
|
|
|
void SAParser::slotGroupParsingDone(SAGroupParser *groupParser)
|
|
{
|
|
m_groupParsers.remove(groupParser);
|
|
delete groupParser;
|
|
}
|
|
|
|
|
|
#include "saparser.moc"
|