/*************************************************************************** tag.cpp - description ------------------- begin : Sun Sep 1 2002 copyright : (C) 2002, 2003 by Andras Mantia ***************************************************************************/ /*************************************************************************** * * * 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 #include #include #include #include #include #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::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 *attrDict) { TQTag *qTag = QuantaCommon::tagFromDTD(m_dtd, name); TQDictIterator 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::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 "> 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; }