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.
tdewebdev/quanta/parsers/tag.cpp

673 lines
18 KiB

/***************************************************************************
tag.cpp - description
-------------------
begin : Sun Sep 1 2002
copyright : (C) 2002, 2003 by 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; version 2 of the License. *
* *
***************************************************************************/
#include <ctype.h>
#include <tqdict.h>
#include <tqstring.h>
#include <tqcstring.h>
#include <tqdom.h>
#include <kdebug.h>
#include "tag.h"
#include "document.h"
#include "quantacommon.h"
#include "resource.h"
#include "parser.h"
#include "node.h"
void TagAttr::save(TQDomElement& element) const
{
element.setAttribute("name", name); // TQString
element.setAttribute("value", value); // TQString
element.setAttribute("nameLine", nameLine); // int
element.setAttribute("nameCol", nameCol); // int
element.setAttribute("valueLine", valueLine); // int
element.setAttribute("valueCol", valueCol); // int
element.setAttribute("quoted", quoted); // bool
element.setAttribute("special", special); // bool
}
bool TagAttr::load(TQDomElement const& element)
{
name = element.attribute("name");
value = element.attribute("value");
nameLine = TQString(element.attribute("nameLine")).toInt();
nameCol = TQString(element.attribute("nameCol")).toInt();
valueLine = TQString(element.attribute("valueLine")).toInt();
valueCol = TQString(element.attribute("valueCol")).toInt();
quoted = TQString(element.attribute("quoted")).toInt();
special = TQString(element.attribute("special")).toInt();
return true;
}
Tag::Tag()
{
init();
}
Tag::Tag(const AreaStruct &area, Document *write, const DTDStruct *dtd, bool doParse)
{
init();
TQString s = write->text(area);
m_area = area;
m_dtd = dtd;
if (doParse)
{
parse(s, write);
} else
{
m_write = write;
m_tagStr = s;
cleanStr = s;
}
}
Tag::Tag( const Tag &t)
{
name = t.name;
nameSpace = t.nameSpace;
m_dtd = t.m_dtd;
single = t.single;
closingMissing = t.closingMissing;
m_area = t.m_area;
m_tagStr = t.m_tagStr;
cleanStr = t.cleanStr;
m_write = t.m_write;
type = t.type;
structBeginStr = t.structBeginStr;
m_nameLine = t.m_nameLine;
m_nameCol = t.m_nameCol;
attrs = t.attrs;
validXMLTag = t.validXMLTag;
m_cleanStrBuilt = t.m_cleanStrBuilt;
m_indentationDone = t.m_indentationDone;
m_notInTree = t.m_notInTree;
}
Tag::~Tag()
{
attrs.clear();
}
void Tag::init()
{
name = "";
m_dtd = 0L;
m_write = 0L;
type = Unknown;
single = false;
closingMissing = false;
structBeginStr = "";
cleanStr = "";
m_nameLine = -1;
m_nameCol = -1;
validXMLTag = true;
m_cleanStrBuilt = true;
m_indentationDone = true;
m_notInTree = false;
}
void Tag::save(TQDomElement& element) const
{
element.setAttribute("name", name); // TQString
element.setAttribute("nameSpace", nameSpace); // TQString
element.setAttribute("cleanStr", cleanStr); // TQString
element.setAttribute("type", type); // int
element.setAttribute("single", single); // bool
element.setAttribute("closingMissing", closingMissing); // bool
element.setAttribute("structBeginStr", structBeginStr); // TQString
element.setAttribute("validXMLTag", validXMLTag); // bool
element.setAttribute("cleanStrBuilt", m_cleanStrBuilt); // bool
element.setAttribute("indentationDone", m_indentationDone); // bool
element.setAttribute("notInTree", m_notInTree); // bool
element.setAttribute("nameLine", m_nameLine); // int
element.setAttribute("nameCol", m_nameCol); // int
TQValueList<TagAttr>::const_iterator it;
for (it = attrs.begin(); it != attrs.end(); ++it)
{
TQDomElement child_element = element.ownerDocument().createElement("tagAttr");
element.appendChild(child_element);
(*it).save(child_element);
}
element.setAttribute("tagStr", m_tagStr); // TQString
}
bool Tag::load(TQDomElement const& element)
{
name = element.attribute("name"); // TQString
nameSpace = element.attribute("nameSpace"); // TQString
cleanStr = element.attribute("cleanStr"); // TQString
type = TQString(element.attribute("type")).toInt(); // int
single = TQString(element.attribute("single")).toInt(); // bool
closingMissing = TQString(element.attribute("closingMissing")).toInt(); // bool
structBeginStr = element.attribute("structBeginStr"); // TQString
validXMLTag = TQString(element.attribute("validXMLTag")).toInt(); // bool
m_cleanStrBuilt = TQString(element.attribute("cleanStrBuilt")).toInt(); // bool
m_indentationDone = TQString(element.attribute("indentationDone")).toInt(); // bool
m_notInTree = TQString(element.attribute("notInTree")).toInt(); // bool
m_nameLine = TQString(element.attribute("nameLine")).toInt(); // int
m_nameCol = TQString(element.attribute("nameCol")).toInt(); // int
TQDomNodeList list = element.childNodes();
for (unsigned int i = 0; i != list.count(); ++i)
{
if (list.item(i).isElement())
{
TQDomElement e = list.item(i).toElement();
if (e.tagName() == "tagAttr")
{
TagAttr tag_attr;
tag_attr.load(e);
addAttribute(tag_attr);
}
}
}
m_tagStr = element.attribute("tagStr"); // TQString
return true;
}
void Tag::parse(const TQString &p_tagStr, Document *p_write)
{
attrs.clear();
m_tagStr = p_tagStr;
uint strLength = m_tagStr.length();
cleanStr = m_tagStr;
m_write = p_write;
if (!m_tagStr.startsWith("<"))
{
type = Text;
return;
}
m_nameLine = m_area.bLine;
m_nameCol = m_area.bCol + 1;
uint pos = 1;
while (pos < strLength &&
!m_tagStr[pos].isSpace() && m_tagStr[pos] != '>' && m_tagStr[pos] != '<' && m_tagStr[pos] != '\n')
{
pos++;
}
name = m_tagStr.mid(1, pos - 1);
int nameSpacePos = name.find(':');
if (nameSpacePos != -1)
{
nameSpace = name.left(nameSpacePos);
name = name.mid(++nameSpacePos);
m_nameCol += nameSpacePos;
}
TQString attrStr;
TagAttr attr;
attr.special = false; //by default non of the attributes are special
while (pos < strLength && m_tagStr[pos].isSpace())
pos++;
int sPos = pos;
int valueStartPos = 0;
while (pos < strLength)
{
//find the attribute name
while (pos < strLength &&
!m_tagStr[pos].isSpace() && m_tagStr[pos] != '=')
{
pos++;
}
attr.name = m_tagStr.mid(sPos, pos - sPos);
if (attr.name.endsWith(">") && pos == strLength)
{
attr.name = attr.name.left(attr.name.length() - 1).lower();
if (!attr.name.stripWhiteSpace().isEmpty())
{
attr.nameLine = m_tagStr.left(sPos).contains('\n') + m_area.bLine;
if (attr.nameLine == m_area.bLine)
attr.nameCol = sPos + m_area.bCol;
else
attr.nameCol = m_tagStr.left(sPos).section('\n',-1).length();
attr.value = (m_dtd != 0) ? m_dtd->booleanTrue : TQString("checked");
attr.valueCol = attr.nameCol;
attr.valueLine = attr.nameLine;
attr.quoted = false;
attrs.append(attr);
}
break;
}
if (m_dtd && !m_dtd->caseSensitive)
attr.name = attr.name.lower();
attr.nameLine = m_tagStr.left(sPos).contains('\n') + m_area.bLine;
if (attr.nameLine == m_area.bLine)
attr.nameCol = sPos + m_area.bCol;
else
attr.nameCol = m_tagStr.left(sPos).section('\n',-1).length();
while (pos < m_tagStr.length() && m_tagStr[pos].isSpace())
pos++;
//if the attribute is just listed and there is no value specified,
//treate it as a "true" boolean
if (m_tagStr[pos] != '=' || pos == strLength)
{
attr.value = (m_dtd != 0) ? m_dtd->booleanTrue : TQString("checked");
attr.valueCol = attr.nameCol;
attr.valueLine = attr.nameLine;
attr.quoted = false;
pos--;
} else
{
pos++;
while (pos < strLength && m_tagStr[pos].isSpace())
pos++;
if (m_tagStr[pos] == '\'' || m_tagStr[pos] == '"')
{
attr.quoted = true;
valueStartPos = pos + 1;
TQChar quotation = m_tagStr[pos];
pos += 1;
while (pos < strLength &&
(m_tagStr[pos] != quotation ||
(m_tagStr[pos] == quotation &&
(m_tagStr[pos-1] == '\\' || isInsideScript(m_tagStr.mid(valueStartPos, pos - valueStartPos)) ) )))
{
pos++;
}
attr.value = m_tagStr.mid(valueStartPos, pos - valueStartPos);
} else
{
attr.quoted = false;
valueStartPos = pos;
while (pos < strLength && !m_tagStr[pos].isSpace())
pos++;
if (pos == strLength)
pos--;
attr.value = m_tagStr.mid(valueStartPos, pos - valueStartPos);
}
attr.valueLine = m_tagStr.left(valueStartPos).contains('\n') + m_area.bLine;
if (attr.valueLine == m_area.bLine)
attr.valueCol = valueStartPos + m_area.bCol;
else
attr.valueCol = m_tagStr.left(valueStartPos).section('\n',-1).length();
}
attrs.append(attr);
//go to the first non-space char. This is where the next attribute name starts
pos++;
while (pos < strLength && m_tagStr[pos].isSpace())
pos++;
sPos = pos++;
}
//add the tag to the document usertag list if it's not present in the dtd
if (m_tagStr.startsWith("<") && m_tagStr.endsWith(">") && m_dtd)
{
//TQString tagName = (m_parsingDTD->caseSensitive) ? name : name.upper();
TQString tagName = name.lower();
//add the new xml tags to the userTagList
if ( !QuantaCommon::isKnownTag(m_dtd->name, tagName) &&
name[0] != '/' )
{
TQTag *newTag = m_write->userTagList.find(tagName);
bool insertNew = !newTag;
if (insertNew)
{
newTag = new TQTag();
newTag->setName(name);
newTag->parentDTD = m_dtd;
}
for (int i = 0; i >attrCount(); i++)
{
Attribute *attr = new Attribute;
attr->name = attribute(i);
attr->values.append(attributeValue(i));
newTag->addAttribute(attr);
delete attr;
}
if (insertNew)
{
m_write->userTagList.replace(tagName, newTag);
}
}
}
}
TQString Tag::attribute(int index)
{
TQString attr="";
if ( index != -1 && index < (int)attrs.count() )
{
attr = attrs[index].name;
}
return attr;
}
TQString Tag::attributeValue(int index)
{
TQString val = "";
if ( index != -1 && index < (int)attrs.count() )
{
val = attrs[index].value;
}
return val;
}
TQString Tag::attributeValue(const TQString &attr, bool ignoreCase)
{
TQString val = "";
for (uint i = 0 ; i < attrs.count(); i++)
{
if ( attr == attrs[i].name ||
((!m_dtd->caseSensitive || ignoreCase) && attrs[i].name.lower() == attr.lower()))
{
val = attrs[i].value;
break;
}
}
return val;
}
/** Check if this tag has the attr attribute defined */
bool Tag::hasAttribute(const TQString &attr, bool ignoreCase)
{
for (uint i = 0; i < attrs.count(); i++)
{
if ( attrs[i].name == attr ||
((!m_dtd->caseSensitive || ignoreCase) && attrs[i].name.lower() == attr.lower()))
return true;
}
return false;
}
void Tag::setAttributePosition(int index, int bLineName, int bColName, int bLineValue, int bColValue)
{
TagAttr attr;
attr = attrs[index];
attr.nameLine = bLineName;
attr.nameCol = bColName;
attr.valueLine = bLineValue;
attr.valueCol = bColValue;
attrs.remove(attrs.at(index));
//attrs.append(attr);
attrs.insert(attrs.at(index) ,attr);
}
/** Set the coordinates of tag inside the document */
void Tag::setTagPosition(int bLine, int bCol, int eLine, int eCol)
{
m_area.bLine = bLine;
m_area.bCol = bCol;
m_area.eLine = eLine;
m_area.eCol = eCol;
}
/** Set the coordinates of tag inside the document, but using an AreaStruct as argument */
void Tag::setTagPosition(const AreaStruct &area)
{
m_area = area;
}
/** Return the index of attr. */
int Tag::attributeIndex(const TQString &attr)
{
int index = -1;
uint i = 0;
do{
if (attrs[i].name == attr ||
(!m_dtd->caseSensitive && attrs[i].name == attr.lower()))
index = i;
i++;
} while (index == -1 && i < attrs.count());
return index;
}
/** Returns the index of attribute at (line,col). */
int Tag::attributeIndexAtPos(int line, int col)
{
int index = -1;
uint i = 0;
do
{
if (attrs[i].nameLine == line)
{
if (attrs[i].nameCol <= col &&
(int) (attrs[i].nameCol + attrs[i].name.length()) >=col)
{
index = i;
}
}
i++;
} while (i < attrs.count() && index == -1);
return index;
}
/** Returns the index of attributevalue at (line,col). */
int Tag::valueIndexAtPos(int line, int col)
{
int index = -1;
uint i = 0;
do
{
if (attrs[i].valueLine == line &&
(attrs[i].valueLine != attrs[i].nameLine || attrs[i].valueCol != attrs[i].nameCol))
{
if (attrs[i].valueCol <= col &&
(int) (attrs[i].valueCol + attrs[i].value.length()) >=col)
{
index = i;
}
}
i++;
} while (i < attrs.count() && index == -1);
return index;
}
void Tag::namePos(int &line, int &col)
{
line = m_nameLine;
col = m_nameCol;
}
void Tag::setStr(const TQString &p_tagStr)
{
m_tagStr = p_tagStr; cleanStr = m_tagStr;
}
int Tag::size()
{
int l = sizeof(name) + 8*sizeof(int) + 2*sizeof(bool);
l += sizeof(cleanStr) + sizeof(m_tagStr);
l += sizeof(structBeginStr) + sizeof(attrs);
l += sizeof(m_dtd) + sizeof(m_write);
return l;
}
void Tag::attributeNamePos(int index, int &line, int &col)
{
line = -1;
col = -1;
if ( index != -1 && index < (int)attrs.count() )
{
line = attrs[index].nameLine;
col = attrs[index].nameCol;
}
}
void Tag::attributeValuePos(int index, int &line, int &col)
{
line = -1;
col = -1;
if ( index != -1 && index < (int)attrs.count() )
{
line = attrs[index].valueLine;
col = attrs[index].valueCol;
}
}
bool Tag::editAttribute(const TQString& attrName, const TQString& attrValue)
{
TagAttr attr;
for (uint i = 0 ; i < attrs.count(); i++)
{
if ( attrName == attrs[i].name ||
(!m_dtd->caseSensitive && attrs[i].name.lower() == attrName.lower()))
{
if(attr.value == attrValue)
return false;
attr = attrs[i];
attr.value = attrValue;
attrs.remove(attrs.at(i));
attrs.append(attr);
return true;
}
}
//attrName not found, creating the attr, if attrValue not empty
if(!attrValue.isEmpty())
{
attr.name = attrName;
attr.value = attrValue;
attr.quoted = true;
attrs.append(attr);
return true;
}
return false;
}
void Tag::deleteAttribute(const TQString& attrName)
{
for (uint i = 0 ; i < attrs.count(); i++)
{
if ( attrName == attrs[i].name ||
(!m_dtd->caseSensitive && attrs[i].name.lower() == attrName.lower()))
{
attrs.remove(attrs.at(i));
}
}
}
void Tag::modifyAttributes(TQDict<TQString> *attrDict)
{
TQTag *qTag = QuantaCommon::tagFromDTD(m_dtd, name);
TQDictIterator<TQString> it(*attrDict);
TQString attribute;
TQString value;
while ( it.current() )
{
attribute = it.currentKey();
value = *(it.current());
if (qTag && qTag->parentDTD->singleTagStyle == "xml" && attribute=="/")
{
++it;
continue;
}
editAttribute(attribute, value);
++it;
}
for (uint i = 0 ; i < attrs.count(); i++)
{
if ( !attrDict->find(attrs[i].name) )
{
attrs.remove(attrs.at(i));
}
}
}
TQString Tag::toString()
{
TQTag *qTag = QuantaCommon::tagFromDTD(m_dtd, name);
TQValueList<TagAttr>::Iterator it;
TagAttr attr;
TQString attrString;
TQString tagString;
for (it = attrs.begin(); it != attrs.end(); ++it)
{
attr = *it;
attrString = " ";
if (attr.value.isEmpty() || attr.name == "/")
{
attrString.append(attr.name);
} else
{
attrString.append(attr.name + "=");
if (!attr.value.startsWith("\\\"") && !attr.value.startsWith("\\\'"))
attrString.append(qConfig.attrValueQuotation);
attrString.append(attr.value);
if (!attr.value.endsWith("\\\"") && !attr.value.endsWith("\\\'"))
attrString.append(qConfig.attrValueQuotation);
}
tagString.prepend(attrString);
}
attrString = "<";
if (!nameSpace.isEmpty())
attrString += nameSpace + ":";
attrString.append(QuantaCommon::tagCase(name));
tagString.prepend(attrString);
if (attrs.isEmpty() && qTag && qTag->parentDTD->singleTagStyle == "xml" &&
(qTag->isSingle() ||
(!qConfig.closeOptionalTags && qTag->isOptional()) || single)
)
{
tagString.append(" /");
}
tagString.append(">");
return tagString;
}
bool Tag::isClosingTag()
{
return (name[0] == '/' || nameSpace[0] == '/');
}
void Tag::setAttributeSpecial(int index, bool special)
{
if ( index != -1 && index < (int)attrs.count() )
{
attrs[index].special = special;
}
}
void Tag::setDtd(const DTDStruct *dtd)
{
m_dtd = dtd;
}
bool Tag::isInsideScript(const TQString &str)
{
if (!m_dtd)
return false; //happens when the DTD is not known yet, e.g in Document::findDTDName
//This detects if the last char from str is inside a script area or not, to
//treat cases like <a href="<? echo "foo" ?>"> correctly
//TODO: speed up if you can...
if (str.find(m_dtd->specialAreaStartRx) != -1)
{
TQString foundString = m_dtd->specialAreaStartRx.cap();
if (str.find(m_dtd->specialAreas[foundString]) == -1)
{
return true;
}
}
return false;
}