/*
http://code.google.com/p/ticpp/
Copyright (c) 2006 Ryan Pusztai, Ryan Mulder

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define TIXML_USE_TICPP
#ifdef TIXML_USE_TICPP

#include "ticpp.h"
#include "ticpprc.h"
#include "tinyxml.h"
#include <sstream>

using namespace ticpp;

// In the following Visitor functions, casting away const should be safe, as the
// object can only be referred to by a const &
bool Visitor::VisitEnter(const TiXmlDocument& doc) {
  return VisitEnter(Document(const_cast< TiXmlDocument* >(&doc)));
}

bool Visitor::VisitExit(const TiXmlDocument& doc) {
  return VisitEnter(Document(const_cast< TiXmlDocument* >(&doc)));
}

bool Visitor::VisitEnter(const TiXmlElement&   element,
                         const TiXmlAttribute* firstAttribute) {
  if (0 != firstAttribute) {
    Attribute attribute(const_cast< TiXmlAttribute* >(firstAttribute));
    return VisitEnter(Element(const_cast< TiXmlElement* >(&element)), &attribute);
  } else {
    return VisitEnter(Element(const_cast< TiXmlElement* >(&element)), 0);
  }
}

bool Visitor::VisitExit(const TiXmlElement& element) {
  return VisitExit(Element(const_cast< TiXmlElement* >(&element)));
}

bool Visitor::Visit(const TiXmlDeclaration& declaration) {
  return Visit(Declaration(const_cast< TiXmlDeclaration* >(&declaration)));
}

bool Visitor::Visit(const TiXmlStylesheetReference& stylesheet) {
  return Visit(
    StylesheetReference(const_cast< TiXmlStylesheetReference* >(&stylesheet)));
}

bool Visitor::Visit(const TiXmlText& text) {
  return Visit(Text(const_cast< TiXmlText* >(&text)));
}

bool Visitor::Visit(const TiXmlComment& comment) {
  return Visit(Comment(const_cast< TiXmlComment* >(&comment)));
}

bool Visitor::Visit(const TiXmlUnknown& x) { return TiXmlVisitor::Visit(x); }

Attribute::Attribute() {
  SetTiXmlPointer(new TiXmlAttribute());
  m_impRC->InitRef();
}

Attribute::Attribute(TiXmlAttribute* attribute) {
  SetTiXmlPointer(attribute);
  m_impRC->IncRef();
}

Attribute::Attribute(const std::string& name, const std::string& value) {
  SetTiXmlPointer(new TiXmlAttribute(name, value));
  m_impRC->InitRef();
}

void Attribute::operator=(const Attribute& copy) {
  // Dropping the reference to the old object
  this->m_impRC->DecRef();

  // Pointing to the new Object
  SetTiXmlPointer(copy.m_tiXmlPointer);

  // The internal tixml pointer changed in the above line
  this->m_impRC->IncRef();
}

Attribute::Attribute(const Attribute& copy)
    : Base() {
  // Dropping the reference to the old object
  this->m_impRC->DecRef();

  // Pointing to the new Object
  SetTiXmlPointer(copy.m_tiXmlPointer);

  // The internal tixml pointer changed in the above line
  this->m_impRC->IncRef();
}

Attribute::~Attribute() { m_impRC->DecRef(); }

std::string Attribute::Value() const {
  ValidatePointer();
  return m_tiXmlPointer->ValueStr();
}

std::string Attribute::Name() const {
  ValidatePointer();
  return m_tiXmlPointer->Name();
}

Attribute* Attribute::Next(bool throwIfNoAttribute) const {
  ValidatePointer();
  TiXmlAttribute* attribute = m_tiXmlPointer->Next();

  if (0 == attribute) {
    if (throwIfNoAttribute) {
      TICPPTHROW("No more attributes found")
    } else {
      return 0;
    }
  }

  Attribute* temp = new Attribute(attribute);
  attribute->m_spawnedWrappers.push_back(temp);

  return temp;
}

Attribute* Attribute::Previous(bool throwIfNoAttribute) const {
  ValidatePointer();
  TiXmlAttribute* attribute = m_tiXmlPointer->Previous();

  if (0 == attribute) {
    if (throwIfNoAttribute) {
      TICPPTHROW("No more attributes found")
    } else {
      return 0;
    }
  }

  Attribute* temp = new Attribute(attribute);
  attribute->m_spawnedWrappers.push_back(temp);

  return temp;
}

void Attribute::IterateNext(const std::string&, Attribute** next) const {
  *next = Next(false);
}

void Attribute::IteratePrevious(const std::string&, Attribute** previous) const {
  *previous = Previous(false);
}

void Attribute::Print(FILE* file, int depth) const {
  ValidatePointer();
  m_tiXmlPointer->Print(file, depth);
}

void Attribute::SetTiXmlPointer(TiXmlAttribute* newPointer) {
  m_tiXmlPointer = newPointer;
  SetImpRC(newPointer);
}

//*****************************************************************************

Node* Node::NodeFactory(TiXmlNode* tiXmlNode,
                        bool       throwIfNull,
                        bool       rememberSpawnedWrapper) const {
  if (0 == tiXmlNode) {
    if (throwIfNull) {
      TICPPTHROW("tiXmlNode is nullptr")
    } else {
      return 0;
    }
  }

  Node* temp;

  switch (tiXmlNode->Type()) {
    case TiXmlNode::DOCUMENT:
      temp = new Document(tiXmlNode->ToDocument());
      break;

    case TiXmlNode::ELEMENT:
      temp = new Element(tiXmlNode->ToElement());
      break;

    case TiXmlNode::COMMENT:
      temp = new Comment(tiXmlNode->ToComment());
      break;

    case TiXmlNode::TEXT:
      temp = new Text(tiXmlNode->ToText());
      break;

    case TiXmlNode::DECLARATION:
      temp = new Declaration(tiXmlNode->ToDeclaration());
      break;

    case TiXmlNode::STYLESHEETREFERENCE:
      temp = new StylesheetReference(tiXmlNode->ToStylesheetReference());
      break;

    default:
      TICPPTHROW("Type is unsupported")
  }

  if (rememberSpawnedWrapper) {
    tiXmlNode->m_spawnedWrappers.push_back(temp);
  }

  return temp;
}

std::string Node::Value() const { return GetTiXmlPointer()->ValueStr(); }

void Node::Clear() { GetTiXmlPointer()->Clear(); }

Node* Node::Parent(bool throwIfNoParent) const {
  TiXmlNode* parent = GetTiXmlPointer()->Parent();

  if ((0 == parent) && throwIfNoParent) {
    TICPPTHROW("No parent exists");
  }

  return NodeFactory(parent, false);
}

Node* Node::FirstChild(bool throwIfNoChildren) const {
  return FirstChild("", throwIfNoChildren);
}

Node* Node::FirstChild(const std::string& value, bool throwIfNoChildren) const {
  return FirstChild(value.c_str(), throwIfNoChildren);
}

Node* Node::FirstChild(const char* value, bool throwIfNoChildren) const {
  TiXmlNode* childNode;

  if (0 == strlen(value)) {
    childNode = GetTiXmlPointer()->FirstChild();
  } else {
    childNode = GetTiXmlPointer()->FirstChild(value);
  }

  if ((0 == childNode) && throwIfNoChildren) {
    TICPPTHROW("Child with the value of \"" << value << "\" not found");
  }

  return NodeFactory(childNode, false);
}

Node* Node::LastChild(bool throwIfNoChildren) const {
  return LastChild("", throwIfNoChildren);
}

Node* Node::LastChild(const std::string& value, bool throwIfNoChildren) const {
  return LastChild(value.c_str(), throwIfNoChildren);
}

Node* Node::LastChild(const char* value, bool throwIfNoChildren) const {
  TiXmlNode* childNode;

  if (0 == strlen(value)) {
    childNode = GetTiXmlPointer()->LastChild();
  } else {
    childNode = GetTiXmlPointer()->LastChild(value);
  }

  if ((0 == childNode) && throwIfNoChildren) {
    TICPPTHROW("Child with the value of \"" << value << "\" not found");
  }

  return NodeFactory(childNode, false);
}

Node* Node::IterateChildren(Node* previous) const {
  TiXmlNode* pointer;

  if (0 == previous) {
    pointer = GetTiXmlPointer()->IterateChildren(0);
  } else {
    pointer = GetTiXmlPointer()->IterateChildren(previous->GetTiXmlPointer());
  }

  return NodeFactory(pointer, false);
}

Node* Node::IterateChildren(const std::string& value, Node* previous) const {
  TiXmlNode* pointer;

  if (0 == previous) {
    pointer = GetTiXmlPointer()->IterateChildren(value, 0);
  } else {
    pointer =
      GetTiXmlPointer()->IterateChildren(value, previous->GetTiXmlPointer());
  }

  return NodeFactory(pointer, false);
}

Node* Node::InsertEndChild(Node& addThis) {
  if (addThis.Type() == TiXmlNode::DOCUMENT) {
    TICPPTHROW("Node is a Document and can't be inserted");
  }

  TiXmlNode* pointer =
    GetTiXmlPointer()->InsertEndChild(*addThis.GetTiXmlPointer());

  if (0 == pointer) {
    TICPPTHROW("Node can't be inserted");
  }

  return NodeFactory(pointer);
}

Node* Node::LinkEndChild(Node* childNode) {
  if (childNode->Type() == TiXmlNode::DOCUMENT) {
    TICPPTHROW("Node is a Document and can't be linked");
  }

  // Increment reference count when adding to the tree
  childNode->m_impRC->IncRef();

  if (0 == GetTiXmlPointer()->LinkEndChild(childNode->GetTiXmlPointer())) {
    TICPPTHROW("Node can't be linked");
  }

  return childNode;
}

Node* Node::InsertBeforeChild(Node* beforeThis, Node& addThis) {
  if (addThis.Type() == TiXmlNode::DOCUMENT) {
    TICPPTHROW("Node is a Document and can't be inserted");
  }

  // Increment reference count when adding to the tree
  addThis.m_impRC->IncRef();

  TiXmlNode* pointer = GetTiXmlPointer()->InsertBeforeChild(
    beforeThis->GetTiXmlPointer(), *addThis.GetTiXmlPointer());

  if (0 == pointer) {
    TICPPTHROW("Node can't be inserted");
  }

  return NodeFactory(pointer);
}

Node* Node::InsertAfterChild(Node* afterThis, Node& addThis) {
  if (addThis.Type() == TiXmlNode::DOCUMENT) {
    TICPPTHROW("Node is a Document and can't be inserted");
  }

  // Increment reference count when adding to the tree
  addThis.m_impRC->IncRef();

  TiXmlNode* pointer = GetTiXmlPointer()->InsertAfterChild(
    afterThis->GetTiXmlPointer(), *addThis.GetTiXmlPointer());

  if (0 == pointer) {
    TICPPTHROW("Node can't be inserted");
  }

  return NodeFactory(pointer);
}

Node* Node::ReplaceChild(Node* replaceThis, Node& withThis) {
  if (withThis.Type() == TiXmlNode::DOCUMENT) {
    TICPPTHROW("Node is a Document and can't be inserted");
  }

  // Increment reference count when adding to the tree
  withThis.m_impRC->IncRef();

  TiXmlNode* pointer = GetTiXmlPointer()->ReplaceChild(
    replaceThis->GetTiXmlPointer(), *withThis.GetTiXmlPointer());

  if (0 == pointer) {
    TICPPTHROW("Node can't be inserted");
  }

  return NodeFactory(pointer);
}

void Node::RemoveChild(Node* removeThis) {
  if (!GetTiXmlPointer()->RemoveChild(removeThis->GetTiXmlPointer())) {
    TICPPTHROW("Node to remove (" << removeThis->Value()
                                  << ") is not a child of this Node ("
                                  << Value()
                                  << ")")
  }
}

Node* Node::PreviousSibling(bool throwIfNoSiblings) const {
  return PreviousSibling("", throwIfNoSiblings);
}

Node* Node::PreviousSibling(const std::string& value,
                            bool               throwIfNoSiblings) const {
  return PreviousSibling(value.c_str(), throwIfNoSiblings);
}

Node* Node::PreviousSibling(const char* value, bool throwIfNoSiblings) const {
  TiXmlNode* sibling;

  if (0 == strlen(value)) {
    sibling = GetTiXmlPointer()->PreviousSibling();
  } else {
    sibling = GetTiXmlPointer()->PreviousSibling(value);
  }

  if ((0 == sibling) && throwIfNoSiblings) {
    TICPPTHROW("No Siblings found with value, '" << value
                                                 << "', Prior to this Node ("
                                                 << Value()
                                                 << ")")
  }

  return NodeFactory(sibling, false);
}

Node* Node::NextSibling(bool throwIfNoSiblings) const {
  return NextSibling("", throwIfNoSiblings);
}

Node* Node::NextSibling(const std::string& value, bool throwIfNoSiblings) const {
  return NextSibling(value.c_str(), throwIfNoSiblings);
}

Node* Node::NextSibling(const char* value, bool throwIfNoSiblings) const {
  TiXmlNode* sibling;

  if (0 == strlen(value)) {
    sibling = GetTiXmlPointer()->NextSibling();
  } else {
    sibling = GetTiXmlPointer()->NextSibling(value);
  }

  if ((0 == sibling) && throwIfNoSiblings) {
    TICPPTHROW("No Siblings found with value, '" << value << "', After this Node ("
                                                 << Value()
                                                 << ")")
  }

  return NodeFactory(sibling, false);
}

Element* Node::NextSiblingElement(bool throwIfNoSiblings) const {
  return NextSiblingElement("", throwIfNoSiblings);
}

Element* Node::NextSiblingElement(const std::string& value,
                                  bool               throwIfNoSiblings) const {
  return NextSiblingElement(value.c_str(), throwIfNoSiblings);
}

Element* Node::NextSiblingElement(const char* value,
                                  bool        throwIfNoSiblings) const {
  TiXmlElement* sibling;

  if (0 == strlen(value)) {
    sibling = GetTiXmlPointer()->NextSiblingElement();
  } else {
    sibling = GetTiXmlPointer()->NextSiblingElement(value);
  }

  if (0 == sibling) {
    if (throwIfNoSiblings) {
      TICPPTHROW("No Element Siblings found with value, '"
                 << value
                 << "', After this Node ("
                 << Value()
                 << ")")
    } else {
      return 0;
    }
  }

  Element* temp = new Element(sibling);
  sibling->m_spawnedWrappers.push_back(temp);

  return temp;
}

Element* Node::FirstChildElement(bool throwIfNoChildren) const {
  return FirstChildElement("", throwIfNoChildren);
}

Element* Node::FirstChildElement(const std::string& value,
                                 bool               throwIfNoChildren) const {
  return FirstChildElement(value.c_str(), throwIfNoChildren);
}

Element* Node::FirstChildElement(const char* value, bool throwIfNoChildren) const {
  TiXmlElement* element;

  if (0 == strlen(value)) {
    element = GetTiXmlPointer()->FirstChildElement();
  } else {
    element = GetTiXmlPointer()->FirstChildElement(value);
  }

  if (0 == element) {
    if (throwIfNoChildren) {
      TICPPTHROW("Element (" << Value()
                             << ") does NOT contain a child with the value of '"
                             << value
                             << "'")
    } else {
      return 0;
    }
  }

  Element* temp = new Element(element);
  element->m_spawnedWrappers.push_back(temp);

  return temp;
}

int Node::Type() const { return GetTiXmlPointer()->Type(); }

Document* Node::GetDocument(bool throwIfNoDocument) const {
  TiXmlDocument* doc = GetTiXmlPointer()->GetDocument();

  if (0 == doc) {
    if (throwIfNoDocument) {
      TICPPTHROW("This node (" << Value() << ") is not linked under a document")
    } else {
      return 0;
    }
  }

  Document* temp = new Document(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

bool Node::NoChildren() const { return GetTiXmlPointer()->NoChildren(); }

Document* Node::ToDocument() const {
  TiXmlDocument* doc = GetTiXmlPointer()->ToDocument();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a Document")
  }

  Document* temp = new Document(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

Element* Node::ToElement() const {
  TiXmlElement* doc = GetTiXmlPointer()->ToElement();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a Element")
  }

  Element* temp = new Element(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

Comment* Node::ToComment() const {
  TiXmlComment* doc = GetTiXmlPointer()->ToComment();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a Comment")
  }

  Comment* temp = new Comment(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

Text* Node::ToText() const {
  TiXmlText* doc = GetTiXmlPointer()->ToText();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a Text")
  }

  Text* temp = new Text(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

Declaration* Node::ToDeclaration() const {
  TiXmlDeclaration* doc = GetTiXmlPointer()->ToDeclaration();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a Declaration")
  }

  Declaration* temp = new Declaration(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

StylesheetReference* Node::ToStylesheetReference() const {
  TiXmlStylesheetReference* doc = GetTiXmlPointer()->ToStylesheetReference();

  if (0 == doc) {
    TICPPTHROW("This node (" << Value() << ") is not a StylesheetReference")
  }

  StylesheetReference* temp = new StylesheetReference(doc);
  doc->m_spawnedWrappers.push_back(temp);

  return temp;
}

std::unique_ptr< Node > Node::Clone() const {
  TiXmlNode* node = GetTiXmlPointer()->Clone();

  if (0 == node) {
    TICPPTHROW("Node could not be cloned");
  }

  std::unique_ptr< Node > temp(NodeFactory(node, false, false));

  // Take ownership of the memory from TiXml
  temp->m_impRC->InitRef();

  return temp;
}

bool Node::Accept(TiXmlVisitor* visitor) const {
  return GetTiXmlPointer()->Accept(visitor);
}

//*****************************************************************************

Comment::Comment()
    : NodeImp< TiXmlComment >(new TiXmlComment()) {
  m_impRC->InitRef();
}

Comment::Comment(TiXmlComment* comment)
    : NodeImp< TiXmlComment >(comment) {}

Comment::Comment(const std::string& comment)
    : NodeImp< TiXmlComment >(new TiXmlComment()) {
  m_impRC->InitRef();
  m_tiXmlPointer->SetValue(comment);
}

//*****************************************************************************

Text::Text()
    : NodeImp< TiXmlText >(new TiXmlText("")) {
  m_impRC->InitRef();
}

Text::Text(const std::string& value)
    : NodeImp< TiXmlText >(new TiXmlText(value)) {
  m_impRC->InitRef();
}

Text::Text(TiXmlText* text)
    : NodeImp< TiXmlText >(text) {}

//*****************************************************************************

Document::Document()
    : NodeImp< TiXmlDocument >(new TiXmlDocument()) {
  m_impRC->InitRef();
}

Document::Document(TiXmlDocument* document)
    : NodeImp< TiXmlDocument >(document) {}

Document::Document(const char* documentName)
    : NodeImp< TiXmlDocument >(new TiXmlDocument(documentName)) {
  m_impRC->InitRef();
}

Document::Document(const std::string& documentName)
    : NodeImp< TiXmlDocument >(new TiXmlDocument(documentName)) {
  m_impRC->InitRef();
}

void Document::LoadFile(TiXmlEncoding encoding) {
  if (!m_tiXmlPointer->LoadFile(encoding)) {
    TICPPTHROW("Couldn't load " << m_tiXmlPointer->Value());
  }
}

void Document::SaveFile() const {
  if (!m_tiXmlPointer->SaveFile()) {
    TICPPTHROW("Couldn't save " << m_tiXmlPointer->Value());
  }
}

void Document::LoadFile(const std::string& filename, TiXmlEncoding encoding) {
  if (!m_tiXmlPointer->LoadFile(filename.c_str(), encoding)) {
    TICPPTHROW("Couldn't load " << filename);
  }
}

void Document::LoadFile(const char* filename, TiXmlEncoding encoding) {
  if (!m_tiXmlPointer->LoadFile(filename, encoding)) {
    TICPPTHROW("Couldn't load " << filename);
  }
}

void Document::SaveFile(const std::string& filename) const {
  if (!m_tiXmlPointer->SaveFile(filename.c_str())) {
    TICPPTHROW("Couldn't save " << filename);
  }
}

void Document::Parse(const std::string& xml,
                     bool               throwIfParseError,
                     TiXmlEncoding      encoding) {
  m_tiXmlPointer->Parse(xml.c_str(), 0, encoding);

  if (throwIfParseError && m_tiXmlPointer->Error()) {
    TICPPTHROW("Error parsing xml.");
  }
}

//*****************************************************************************

Element::Element()
    : NodeImp< TiXmlElement >(new TiXmlElement(
        "DefaultValueCausedByCreatingAnElementWithNoParameters")) {
  m_impRC->InitRef();
}

Element::Element(const std::string& value)
    : NodeImp< TiXmlElement >(new TiXmlElement(value)) {
  m_impRC->InitRef();
}

Element::Element(const char* value)
    : NodeImp< TiXmlElement >(new TiXmlElement(value)) {
  m_impRC->InitRef();
}

Element::Element(TiXmlElement* element)
    : NodeImp< TiXmlElement >(element) {}

Attribute* Element::FirstAttribute(bool throwIfNoAttributes) const {
  ValidatePointer();
  TiXmlAttribute* attribute = m_tiXmlPointer->FirstAttribute();

  if ((0 == attribute) && throwIfNoAttributes) {
    TICPPTHROW("This Element (" << Value() << ") has no attributes")
  }

  if (0 == attribute) {
    if (throwIfNoAttributes) {
      TICPPTHROW("Element (" << Value() << ") has no attributes")
    } else {
      return 0;
    }
  }

  Attribute* temp = new Attribute(attribute);
  attribute->m_spawnedWrappers.push_back(temp);

  return temp;
}

Attribute* Element::LastAttribute(bool throwIfNoAttributes) const {
  ValidatePointer();
  TiXmlAttribute* attribute = m_tiXmlPointer->LastAttribute();

  if ((0 == attribute) && throwIfNoAttributes) {
    TICPPTHROW("This Element (" << Value() << ") has no attributes")
  }

  if (0 == attribute) {
    if (throwIfNoAttributes) {
      TICPPTHROW("Element (" << Value() << ") has no attributes")
    } else {
      return 0;
    }
  }

  Attribute* temp = new Attribute(attribute);
  attribute->m_spawnedWrappers.push_back(temp);

  return temp;
}

std::string Element::GetAttributeOrDefault(const std::string& name,
                                           const std::string& defaultValue) const {
  std::string value;

  if (!GetAttributeImp(name, &value)) {
    return defaultValue;
  }

  return value;
}

std::string Element::GetAttribute(const std::string& name) const {
  return GetAttributeOrDefault(name, std::string());
}

bool Element::HasAttribute(const std::string& name) const {
  ValidatePointer();
  return (0 != m_tiXmlPointer->Attribute(name.c_str()));
}

void Element::RemoveAttribute(const std::string& name) {
  ValidatePointer();
  m_tiXmlPointer->RemoveAttribute(name.c_str());
}

bool Element::GetAttributeImp(const std::string& name, std::string* value) const {
  ValidatePointer();

  // Get value from TinyXML, if the attribute exists
  const char* retVal = m_tiXmlPointer->Attribute(name.c_str());

  // TinyXML returns nullptr if the attribute doesn't exist
  if (0 == retVal) {
    return false;
  } else {
    *value = retVal;
    return true;
  }
}

bool Element::GetTextImp(std::string* value) const {
  ValidatePointer();

  // Get value from TinyXML, if the attribute exists
  const char* retVal = m_tiXmlPointer->GetText();

  // TinyXML returns nullptr if the attribute doesn't exist
  if (0 == retVal) {
    return false;
  } else {
    *value = retVal;
    return true;
  }
}

//*****************************************************************************

Declaration::Declaration()
    : NodeImp< TiXmlDeclaration >(new TiXmlDeclaration()) {
  m_impRC->InitRef();
}

Declaration::Declaration(TiXmlDeclaration* declaration)
    : NodeImp< TiXmlDeclaration >(declaration) {}

Declaration::Declaration(const std::string& version,
                         const std::string& encoding,
                         const std::string& standalone)
    : NodeImp< TiXmlDeclaration >(
        new TiXmlDeclaration(version, encoding, standalone)) {
  m_impRC->InitRef();
}

std::string Declaration::Version() const { return m_tiXmlPointer->Version(); }

std::string Declaration::Encoding() const { return m_tiXmlPointer->Encoding(); }

std::string Declaration::Standalone() const {
  return m_tiXmlPointer->Standalone();
}

//*****************************************************************************

StylesheetReference::StylesheetReference()
    : NodeImp< TiXmlStylesheetReference >(new TiXmlStylesheetReference()) {
  m_impRC->InitRef();
}

StylesheetReference::StylesheetReference(
  TiXmlStylesheetReference* stylesheetReference)
    : NodeImp< TiXmlStylesheetReference >(stylesheetReference) {}

StylesheetReference::StylesheetReference(const std::string& type,
                                         const std::string& href)
    : NodeImp< TiXmlStylesheetReference >(
        new TiXmlStylesheetReference(type, href)) {
  m_impRC->InitRef();
}

std::string StylesheetReference::Type() const { return m_tiXmlPointer->Type(); }

std::string StylesheetReference::Href() const { return m_tiXmlPointer->Href(); }

//*****************************************************************************

Exception::Exception(const std::string& details)
    : m_details(details) {}

Exception::~Exception() throw() {}

const char* Exception::what() const throw() { return m_details.c_str(); }

//*****************************************************************************

TiCppRC::TiCppRC() {
  // Spawn reference counter for this object
  m_tiRC = new TiCppRCImp(this);
}

void TiCppRC::DeleteSpawnedWrappers() {
  std::vector< Base* >::reverse_iterator wrapper;

  for (wrapper = m_spawnedWrappers.rbegin(); wrapper != m_spawnedWrappers.rend();
       ++wrapper) {
    delete *wrapper;
  }

  m_spawnedWrappers.clear();
}

TiCppRC::~TiCppRC() {
  DeleteSpawnedWrappers();

  // Set pointer held by reference counter to nullptr
  this->m_tiRC->Nullify();

  // Decrement reference - so reference counter will delete itself if necessary
  this->m_tiRC->DecRef();
}

//*****************************************************************************

TiCppRCImp::TiCppRCImp(TiCppRC* tiCppRC)
    : m_count(1)
    , m_tiCppRC(tiCppRC) {}

void TiCppRCImp::IncRef() { m_count++; }

void TiCppRCImp::DecRef() {
  m_count--;

  if (0 == m_count) {
    delete m_tiCppRC;
    delete this;
  }
}

void TiCppRCImp::InitRef() { m_count = 1; }

void TiCppRCImp::Nullify() { m_tiCppRC = 0; }

TiCppRC* TiCppRCImp::Get() { return m_tiCppRC; }

bool TiCppRCImp::IsNull() { return 0 == m_tiCppRC; }

#endif  // TIXML_USE_TICPP
