summaryrefslogtreecommitdiffstats
path: root/quanta/parsers/tag.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'quanta/parsers/tag.cpp')
-rw-r--r--quanta/parsers/tag.cpp672
1 files changed, 672 insertions, 0 deletions
diff --git a/quanta/parsers/tag.cpp b/quanta/parsers/tag.cpp
new file mode 100644
index 00000000..8a1921db
--- /dev/null
+++ b/quanta/parsers/tag.cpp
@@ -0,0 +1,672 @@
+/***************************************************************************
+ 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 <qdict.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include <qdom.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(QDomElement& element) const
+{
+ element.setAttribute("name", name); // QString
+ element.setAttribute("value", value); // QString
+ 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(QDomElement const& element)
+{
+ name = element.attribute("name");
+ value = element.attribute("value");
+ nameLine = QString(element.attribute("nameLine")).toInt();
+ nameCol = QString(element.attribute("nameCol")).toInt();
+ valueLine = QString(element.attribute("valueLine")).toInt();
+ valueCol = QString(element.attribute("valueCol")).toInt();
+ quoted = QString(element.attribute("quoted")).toInt();
+ special = QString(element.attribute("special")).toInt();
+
+ return true;
+}
+
+
+Tag::Tag()
+{
+ init();
+}
+
+Tag::Tag(const AreaStruct &area, Document *write, const DTDStruct *dtd, bool doParse)
+{
+ init();
+ QString 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(QDomElement& element) const
+{
+ element.setAttribute("name", name); // QString
+ element.setAttribute("nameSpace", nameSpace); // QString
+ element.setAttribute("cleanStr", cleanStr); // QString
+ element.setAttribute("type", type); // int
+ element.setAttribute("single", single); // bool
+ element.setAttribute("closingMissing", closingMissing); // bool
+ element.setAttribute("structBeginStr", structBeginStr); // QString
+ 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
+
+ QValueList<TagAttr>::const_iterator it;
+ for (it = attrs.begin(); it != attrs.end(); ++it)
+ {
+ QDomElement child_element = element.ownerDocument().createElement("tagAttr");
+ element.appendChild(child_element);
+ (*it).save(child_element);
+ }
+
+ element.setAttribute("tagStr", m_tagStr); // QString
+}
+
+bool Tag::load(QDomElement const& element)
+{
+ name = element.attribute("name"); // QString
+ nameSpace = element.attribute("nameSpace"); // QString
+ cleanStr = element.attribute("cleanStr"); // QString
+ type = QString(element.attribute("type")).toInt(); // int
+ single = QString(element.attribute("single")).toInt(); // bool
+ closingMissing = QString(element.attribute("closingMissing")).toInt(); // bool
+ structBeginStr = element.attribute("structBeginStr"); // QString
+ validXMLTag = QString(element.attribute("validXMLTag")).toInt(); // bool
+ m_cleanStrBuilt = QString(element.attribute("cleanStrBuilt")).toInt(); // bool
+ m_indentationDone = QString(element.attribute("indentationDone")).toInt(); // bool
+ m_notInTree = QString(element.attribute("notInTree")).toInt(); // bool
+ m_nameLine = QString(element.attribute("nameLine")).toInt(); // int
+ m_nameCol = QString(element.attribute("nameCol")).toInt(); // int
+
+ QDomNodeList list = element.childNodes();
+ for (unsigned int i = 0; i != list.count(); ++i)
+ {
+ if (list.item(i).isElement())
+ {
+ QDomElement e = list.item(i).toElement();
+ if (e.tagName() == "tagAttr")
+ {
+ TagAttr tag_attr;
+ tag_attr.load(e);
+ addAttribute(tag_attr);
+ }
+ }
+ }
+
+ m_tagStr = element.attribute("tagStr"); // QString
+
+ return true;
+}
+
+void Tag::parse(const QString &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;
+ }
+ QString 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 : QString("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 : QString("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;
+ QChar 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)
+ {
+ //QString tagName = (m_parsingDTD->caseSensitive) ? name : name.upper();
+ QString tagName = name.lower();
+ //add the new xml tags to the userTagList
+ if ( !QuantaCommon::isKnownTag(m_dtd->name, tagName) &&
+ name[0] != '/' )
+ {
+ QTag *newTag = m_write->userTagList.find(tagName);
+ bool insertNew = !newTag;
+ if (insertNew)
+ {
+ newTag = new QTag();
+ 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);
+ }
+ }
+ }
+}
+
+
+QString Tag::attribute(int index)
+{
+ QString attr="";
+ if ( index != -1 && index < (int)attrs.count() )
+ {
+ attr = attrs[index].name;
+ }
+ return attr;
+}
+
+QString Tag::attributeValue(int index)
+{
+ QString val = "";
+ if ( index != -1 && index < (int)attrs.count() )
+ {
+ val = attrs[index].value;
+ }
+ return val;
+}
+
+QString Tag::attributeValue(const QString &attr, bool ignoreCase)
+{
+ QString 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 QString &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 QString &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 QString &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 QString& attrName, const QString& 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 QString& 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(QDict<QString> *attrDict)
+{
+ QTag *qTag = QuantaCommon::tagFromDTD(m_dtd, name);
+ QDictIterator<QString> it(*attrDict);
+ QString attribute;
+ QString 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));
+ }
+ }
+}
+
+QString Tag::toString()
+{
+ QTag *qTag = QuantaCommon::tagFromDTD(m_dtd, name);
+ QValueList<TagAttr>::Iterator it;
+ TagAttr attr;
+ QString attrString;
+ QString 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 QString &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)
+ {
+ QString foundString = m_dtd->specialAreaStartRx.cap();
+ if (str.find(m_dtd->specialAreas[foundString]) == -1)
+ {
+ return true;
+ }
+ }
+ return false;
+}