root/src/Tag.cpp

////////////////////////////////////////////////////////////////////////
// Copyright (c) Nehmulos 2011-2014
// This file is part of N0 Strain Serialization Library.
//
// N0Strain-Serialization-Library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// N0Strain-Serialization-Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with N0Strain-Serialization-Library.  If not, see https://gnu.org/licenses/lgpl-3.0
////////////////////////////////////////////////////////////////////////




#include "Tag.h"

namespace nw {

bool Tag::useAttribute = true;

Tag::Tag() {
        children.clear();
        parent = NULL;
        name = "Unnamed";
        value.clear();
        level = 0;
        isComment = false;
        isValueArray = false;
        canBeAttribute = useAttribute;
}

Tag::Tag(String tagName) : name(tagName)
{
        children.clear();
        parent = NULL;
        value.clear();
        level = 0;
        isComment = false;
        isValueArray = false;
        canBeAttribute = useAttribute;
}


Tag::~Tag() {
        for (unsigned int i = 0; i < children.size(); ++i)
        {
                delete children.at(i);
        }
}

Tag* Tag::addNewChild(String tagName)
{
        Tag* newChild = new Tag(tagName);
        newChild->setParent(this);
        newChild->setLevel(this->level+1);
        children.push_back(newChild);
        return newChild;
}
//void Tag::addNewChild(String tagName, String tagValue)
//{
//      Tag* newChild = new Tag(tagName);
//      newChild->setValue(tagValue);
//      newChild->setParent(this);
//      newChild->setLevel(this->level+1);
//      children.push_back(newChild);
//}
//template <class T>
//void Tag::addNewChild(String tagName, T tagValue)
//{
//      Tag* newChild = new Tag(tagName);
//      newChild->setValue(tagValue);
//      newChild->setParent(this);
//      newChild->setLevel(this->level+1);
//      children.push_back(newChild);
//}

void Tag::addChild(Tag* child)
{
        this->children.push_back(child);
        child->setParent(this);
}

void Tag::addComment(String text)
{
        this->addNewChild("_Comment", text);
        this->getLastChild()->setIsComment(true);
}

void Tag::removeChild(Tag* child)
{
        for (std::vector<Tag*>::iterator it = children.begin(); it < children.end(); ++it)
        {
                if(*it == child)
                {
                        this->children.erase(it);
                        return;
                }
        }
}


Tag* Tag::getChildWithName(const String childrenTagName)
{
        unsigned int numberOfChildren = children.size();
        for (unsigned int i = 0; i < numberOfChildren; ++i)
        {
                if(children.at(i)->getName() == childrenTagName)
                {
                        return children.at(i);
                }
        }
        return NULL;
}
Tag* Tag::getLastChild()
{
        return children.empty() ? NULL : children.back();
}
String Tag::getName()
{
        return this->name;
}
String Tag::getValue()
{
        return this->value;
}
Tag* Tag::getParent()
{
        return this->parent;
}
void Tag::setParent(Tag* parent)
{
        this->parent = parent;
}
void Tag::setName(String value)
{
        this->name = value;
}
void Tag::setValue(String value)
{
        this->value = value;
}
//template <class T>
//void Tag::setValue(T value)
//{
//      stringstream ss;
//      String stringValue;
//      ss << value;
//      ss >> stringValue;
//      setValue(stringValue);
//}

void Tag::setLevel(int level)
{
        this->level = level;
}
std::vector<Tag*>& Tag::getChildren()
{
        return this->children;
}

void Tag::writeXmlHeader(std::ostream& file)
{
        file << "<?xml version=\"1.0\"?>\n";
}

void Tag::writeAsXmlWithAttributes(std::ostream& file)
{
        std::stringstream tabs;
        for (int i = 0; i < this->level; ++i)
        {
                tabs << '\t';
        }

        if(this->isComment)
        {
                file << tabs.str() << "<!-- " << this->value << " -->\n";
                return;
        }

        //Escape Value
        bool escapeWithCdata;
        if(!this->value.empty())
        {
                //Contains delimiters?
                if(this->value.find("<") != String::npos || this->value.find(">") != String::npos)
                        escapeWithCdata = true;
                else
                        escapeWithCdata = false;
        }
        else
        {
                escapeWithCdata = false;
        }

        unsigned int numberOfChildren = children.size();
        std::vector<unsigned int> indeciesOfParentTags;
        file << tabs.str() << '<' << name;

        //write ValueArray and return
        if(this->isValueArray)
        {
                file << ">\n";
                for (unsigned int i = 0; i < numberOfChildren; ++i)
                {
                        file << tabs.str() <<  "\t<value>" << children.at(i)->getValue() << "</value>\n";
                }
                file << tabs.str() <<  "</" << name << ">\n";

                return;
        }

        //write Attributes
        for (unsigned int i = 0; i < numberOfChildren; ++i)
        {
                if(children.at(i)->getCanBeAttribute() && !children.at(i)->hasChildren() && !children.at(i)->getIsComent())
                {
                        file << ' ';
                        children.at(i)->writeAsXmlAttribute(file);
                }
                else
                {
                        indeciesOfParentTags.push_back(i);
                }
        }
        //Write contents
        unsigned int numberOfParentTags = indeciesOfParentTags.size();
        if(numberOfParentTags > 0)
        {
                file << ">\n";
                for (unsigned int i = 0; i < numberOfParentTags; ++i)
                {
                        children.at(indeciesOfParentTags.at(i))->writeAsXmlWithAttributes(file);
                }
                file << tabs.str() << "</" << name << ">\n";
        }
        else
        {
                if(this->value.empty())
                {
                        file << "/>\n";
                }
                else if(escapeWithCdata)
                {
                        file << "><![CDATA[" << value << "</" << name << "]]>\n";
                }
                else
                {
                        file << '>' << value << "</" << name << ">\n";
                }

        }
}

bool Tag::hasChildren()
{
        return !this->children.empty();
}

bool Tag::hasGrandChildren()
{
        for (unsigned int i = 0; i < this->children.size(); ++i)
        {
                if(!children.at(i)->getChildren().empty())
                        return true;
        }
        return false;
}

bool Tag::getIsComent() const
{
    return isComment;
}

bool Tag::getCanBeAttribute() const
{
    return this->canBeAttribute;
}

void Tag::setIsComment(bool isComment)
{
    this->isComment = isComment;
}

void Tag::setCanBeAttribute(bool canBe)
{
    this->canBeAttribute = canBe;
}

void Tag::setIsValueArray(bool isValueArray)
{
        this->isValueArray = isValueArray;
}

void Tag::setCanBeAttributeRecursive(bool canBe) {
        this->setCanBeAttribute(canBe);
        for (Tag* tag : children) {
                tag->setCanBeAttributeRecursive(canBe);
        }
}

void Tag::writeAsXmlAttribute(std::ostream &file)
{
        file << name << "=\"" << value << '"';
}

bool Tag::getBooleanValue()
{
        if(value.size() != 0)
        {
                if(value == "true" || value == "True" || value == "1")
                        return true;
        }
        return false;
}

short  Tag::getShortValue()
{
        std::stringstream ss(value);
        short ret;
        ss >> ret;
        return ret;
}

int Tag::getIntegerValue()
{
        std::stringstream ss(value);
        int ret;
        ss >> ret;
        return ret;
}

long long Tag::getLongLongValue()
{
        std::stringstream ss(value);
        long long ret;
        ss >> ret;
        return ret;
}

long double Tag::getLongDoubleValue()
{
        std::stringstream ss(value);
        long double ret;
        ss >> ret;
        return ret;
}

double Tag::getDoubleValue()
{
        std::stringstream ss(value);
        double ret;
        ss >> ret;
        return ret;
}
float Tag::getFloatValue()
{
        std::stringstream ss(value);
        float ret;
        ss >> ret;
        return ret;
}
long Tag::getLongValue()
{
        std::stringstream ss(value);
        float ret;
        ss >> ret;
        return ret;
}
void* Tag::getPointerValue()
{
        std::stringstream ss(value);
        void *ret;
        ss >> ret;
        return ret;
}
//using integers to store chars to prevent them form being control signs in the file
char Tag::getCharacterValue()
{
        std::stringstream ss(value);
        int ret;
        ss >> ret;
        return (char)ret;
}

unsigned char  Tag::getUnsignedCharacterValue()
{
        std::stringstream ss(value);
        int ret;
        ss >> ret;
        return (char)ret;
}
signed char    Tag::getSignedCharacterValue()
{
        std::stringstream ss(value);
        int ret;
        ss >> ret;
        return (signed char)ret;
}

unsigned short Tag::getUnsignedShortValue()
{
        std::stringstream ss(value);
        unsigned short ret;
        ss >> ret;
        return ret;
}
unsigned int   Tag::getUnsignedIntegerValue()
{
        std::stringstream ss(value);
        unsigned int ret;
        ss >> ret;
        return ret;
}
unsigned long  Tag::getUnsignedLongValue()
{
        std::stringstream ss(value);
        unsigned long ret;
        ss >> ret;
        return ret;
}

//template<class T> T Tag::getValueAs()
//{
//      stringstream ss(value);
//      T ret;
//      ss >> ret;
//      return ret;
//}

void *Tag::getBase64Value()
{
        String decodedValue = utils::fromBase64(value);
        char* blob = new char[decodedValue.size()];
        decodedValue.copy(blob, decodedValue.size(), 0);
        return blob;
}

void *Tag::getHexValue()
{
        String decodedValue = utils::fromHex(value);
        char* blob = new char[decodedValue.size()];
        decodedValue.copy(blob, decodedValue.size(), 0);
        return blob;
}

//TODO it's inefficient to wrap these Strings in tags again, but easy for the readers to parse
std::stack<Tag*> Tag::getCsvTags()
{
        std::stack<Tag*> tags;
        std::stringstream ss;
        bool startedReading = false;
        char c;
        char lastChar = 'x';
        for(unsigned int i=0; i < value.size(); ++i)
        {
                c = value.at(i);
                // TODO make quotes modular
                if(c == '\'' && lastChar != '\\')
                {
                        if(startedReading)
                        {
                                startedReading = false;
                                tags.push(new Tag(ss.str()));
                                ss.str("");
                        }
                        else
                        {
                                startedReading = true;
                        }
                }
                else if(startedReading)
                {
                        ss << c;
                }
                lastChar = c;
        }
        return tags;
}

bool Tag::operator==(Tag& that)
        {
                if
                  (
                        that.name       == this->name   &&
                        that.value      == this->value  &&
                        that.level      == this->level  &&
                        that.parent     == this->parent
                  )
                {
                        return true;
                }
                return false;
        }

}  // namespace nw