views:

25

answers:

2

I want to wrap part of the TinyXML library in some custom classes in my project because I only need some of its functionality and I do not wish to expose everything.

I have a problem where my XMLDocument::AddNode(...) function is basically doing the same thing that my XMLNode class is meant for. I wondered if anyone could give me some design tips on how to wrap the TinyXML library so I don't break encapsulation and my wrapper classes don't expose the TinyXML library to the rest of my code.

class XMLNode
    {
    public:
        XMLNode::XMLNode();
        XMLNode::XMLNode(const std::string& element);  // Creates a TiXMLElement object
        XMLNode::XMLNode(XMLNode& nodycopy);
        XMLNode::~XMLNode();

        XMLNode GetFirstChild();
        XMLNode GetNextSibling();

        std::string GetNodeValue();
        std::string GetNodeName();

        void AddNodeText(const std::string& text);
        void AddNodeAttribute();

    private:
        std::string value;
        std::string nodename;

        TiXmlElement* thisnode;
    };

//These functions do the same as my AddNode does from XMLDocument, it seems rather silly...
    XMLNode::XMLNode(const std::string& str_element) 
{
    thisnode = new TiXmlElement(str_element.c_str());
    nodename = str_element;
}

void XMLNode::AddNodeText(const std::string& nodetext)
{
    TiXmlText* text = new TiXmlText(nodetext.c_str());
    thisnode->LinkEndChild(text);
    value = nodetext;
}


class XMLDocument
    {
    public:
        XMLDocument::XMLDocument(const std::string& documentname);
        XMLDocument::~XMLDocument();

        void SaveToFile(std::string filename);
        std::string basicXML(std::string rootnode);

        void AddNode(XMLNode& node);
        XMLNode GetXPathNode(const std::string& node);
        void AppendCloneNodeAsChild(XMLNode& nodetoappend); 

        void SetRoot(XMLNode& rootnode);
    private:
        TiXmlDocument document;
        TiXmlElement root;
        TiXmlElement currentelement;

    };

void XMLDocument::AddNode(XMLNode& node)  // This function does over what XMLNode class is actually for... I just want to add the node to the XMLDocument
{
    std::string val = node.GetNodeName();
    TiXmlElement* el = new TiXmlElement(val.c_str());
    TiXmlText * txt = new TiXmlText(node.GetNodeValue().c_str());
    el->LinkEndChild(txt);
    document.LinkEndChild(el);
}

Can anybody give me some suggestions on how to wrap this properly and only expose the functionality of TinyXML that I want?

A: 

There are 2 ways you can solve this.

  1. Deriving it from XMLDocument
  2. Having object of XMLDocument and forward the call to it.
Vinay
+1  A: 

You already encapsulate the TinyXml document - now you just need to delegate to it where you want, to expose only a subset of the function it provides.

Replace this

void XMLDocument::AddNode(XMLNode& node)  // This function does over what XMLNode class is actually for... I just want to add the node to the XMLDocument
{
    std::string val = node.GetNodeName();
    TiXmlElement* el = new TiXmlElement(val.c_str());
    TiXmlText * txt = new TiXmlText(node.GetNodeValue().c_str());
    el->LinkEndChild(txt);
    document.LinkEndChild(el);
}

with this

void XMLDocument::AddNode(XMLNode& node) 
{
    document.AddNode(node);
}

This is perfectly legitimate since you don't want to expose the full TiXmlDocument to your class's clients. Think of how std::queue and std::stack act as adapters on underlying containers.

Steve Townsend
But my XMLNode is not compatible with the TiXmlElement that document.AddNode uses...
Tony
@Tony - I see, but since `XmlNode` encapsulates `TiXmlElement`, can't you just call `document.AddNode(*node.thisnode);` ?
Steve Townsend
So you'd expose the TiXmlElement? I suppose there isn't many options really, although I guess exposing it kind of breaks the idea of a wrapper...
Tony
@Tony - `TiXmlElement` is still only used internally, in `XMLDocument::AddNode`
Steve Townsend