mirror of
https://github.com/jezhiggins/arabica
synced 2024-12-29 22:23:27 +01:00
507 lines
17 KiB
C++
507 lines
17 KiB
C++
#ifndef JEZUK_DOM_SimpleDOM_NODE_H
|
|
#define JEZUK_DOM_SimpleDOM_NODE_H
|
|
|
|
////////////////////////////
|
|
// C++ DOM definition
|
|
//
|
|
// $Id$
|
|
////////////////////////////
|
|
|
|
#include <DOM/Node.h>
|
|
#include <DOM/DOMException.h>
|
|
#include <deque>
|
|
#include <algorithm>
|
|
|
|
//#include <iostream>
|
|
|
|
namespace SimpleDOM
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
template<class stringT, class string_adaptorT> class DocumentImpl;
|
|
|
|
template<class stringT, class string_adaptorT>
|
|
class NodeImpl : virtual public DOM::Node_impl<stringT>
|
|
{
|
|
typedef DOM::Node_impl<stringT> NodeT;
|
|
public:
|
|
NodeImpl(DocumentImpl<stringT, string_adaptorT>* ownerDoc) :
|
|
parentNode_(0),
|
|
ownerDoc_(ownerDoc),
|
|
prevSibling_(0),
|
|
nextSibling_(0),
|
|
readOnly_(false)
|
|
{
|
|
//std::cout << std::endl << "born " << this << std::endl;
|
|
} // NodeImpl
|
|
|
|
virtual ~NodeImpl()
|
|
{
|
|
//std::cout << std::endl << "die " << this << std::endl;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
// Ref counting
|
|
virtual void addRef()
|
|
{
|
|
if(ownerDoc_)
|
|
ownerDoc_->addRef();
|
|
} // addRef
|
|
virtual void releaseRef()
|
|
{
|
|
if(ownerDoc_)
|
|
ownerDoc_->releaseRef();
|
|
} // releaseRef
|
|
|
|
///////////////////////////////////////////////////////
|
|
// Node methods
|
|
virtual const stringT& getNodeName() const = 0;
|
|
|
|
virtual const stringT& getNodeValue() const { return ownerDoc_->empty_string(); }
|
|
virtual void setNodeValue(const stringT& nodeValue) { throwIfReadOnly(); }
|
|
|
|
virtual DOM::Node_base::Type getNodeType() const = 0;
|
|
|
|
virtual DOM::Node_impl<stringT>* getParentNode() const { return parentNode_; }
|
|
|
|
virtual DOM::NodeList_impl<stringT>* getChildNodes() const = 0;
|
|
|
|
virtual DOM::Node_impl<stringT>* getFirstChild() const = 0;
|
|
virtual DOM::Node_impl<stringT>* getLastChild() const = 0;
|
|
|
|
virtual DOM::Node_impl<stringT>* getPreviousSibling() const { return prevSibling_; }
|
|
virtual DOM::Node_impl<stringT>* getNextSibling() const { return nextSibling_; }
|
|
|
|
virtual DOM::NamedNodeMap_impl<stringT>* getAttributes() const { return 0; }
|
|
|
|
virtual DOM::Document_impl<stringT>* getOwnerDocument() const { return ownerDoc_; }
|
|
|
|
virtual DOM::Node_impl<stringT>* insertBefore(DOM::Node_impl<stringT>* newChild, DOM::Node_impl<stringT>* refChild) = 0;
|
|
virtual DOM::Node_impl<stringT>* replaceChild(DOM::Node_impl<stringT>* newChild, DOM::Node_impl<stringT>* oldChild) = 0;
|
|
virtual DOM::Node_impl<stringT>* removeChild(DOM::Node_impl<stringT>* oldChild) = 0;
|
|
virtual DOM::Node_impl<stringT>* appendChild(DOM::Node_impl<stringT>* newChild) = 0;
|
|
virtual void purgeChild(DOM::Node_impl<stringT>* oldChild) = 0;
|
|
|
|
virtual bool hasChildNodes() const = 0;
|
|
|
|
virtual DOM::Node_impl<stringT>* cloneNode(bool deep) const = 0;
|
|
|
|
virtual void normalize()
|
|
{
|
|
DOM::Node_impl<stringT>*child = getFirstChild();
|
|
while(child != 0)
|
|
{
|
|
DOM::Node_impl<stringT>*next = child->getNextSibling();
|
|
|
|
if(child->getNodeType() == DOM::Node<stringT>::TEXT_NODE)
|
|
{
|
|
DOM::Text_impl<stringT>* textNode = dynamic_cast<DOM::Text_impl<stringT>*>(child);
|
|
while((next != 0) && (next->getNodeType() == DOM::Node<stringT>::TEXT_NODE))
|
|
{
|
|
textNode->appendData(next->getNodeValue());
|
|
removeChild(next);
|
|
next = textNode->getNextSibling();
|
|
} // while
|
|
if(string_adaptorT::empty(textNode->getData()))
|
|
removeChild(textNode);
|
|
}
|
|
else
|
|
child->normalize();
|
|
|
|
child = next;
|
|
} // while
|
|
|
|
DOM::NamedNodeMap_impl<stringT>* attrs = getAttributes();
|
|
if(attrs)
|
|
for(unsigned int i = 0; i < attrs->getLength(); ++i)
|
|
attrs->item(i)->normalize();
|
|
} // normalize
|
|
|
|
virtual bool isSupported(const stringT& feature, const stringT& version) const
|
|
{
|
|
return false;
|
|
} // isSupported
|
|
|
|
virtual const stringT& getNamespaceURI() const { return ownerDoc_->empty_string(); }
|
|
virtual const stringT& getPrefix() const { return ownerDoc_->empty_string(); }
|
|
virtual void setPrefix(const stringT& prefix) { }
|
|
virtual const stringT& getLocalName() const { return ownerDoc_->empty_string(); }
|
|
|
|
// additional methods - since C++ std::string (and by implication
|
|
// stringT) don't differenciate between a null string and an empty string,
|
|
// but the DOM recommendation does, I have to introduce these three methods
|
|
// to disambiguate. If they return false, the corresponding attribute should be
|
|
// considered null. If they return true, the attribute has been set EVEN IF
|
|
// it has been set to the empty string
|
|
virtual bool hasNamespaceURI() const { return false; }
|
|
virtual bool hasPrefix() const { return false; }
|
|
|
|
virtual bool hasAttributes() const { return false; }
|
|
|
|
/////////////////////////////////////////////////////
|
|
// implementation specific method
|
|
void setParentNode(NodeImpl<stringT, string_adaptorT>* parent)
|
|
{
|
|
if(!parent && parentNode_ && ownerDoc_)
|
|
ownerDoc_->orphaned(this);
|
|
if(!parentNode_ && parent && ownerDoc_)
|
|
ownerDoc_->adopted(this);
|
|
parentNode_ = parent;
|
|
} // setParentNode
|
|
|
|
NodeImpl<stringT, string_adaptorT>* getFirst() { return dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(getFirstChild()); }
|
|
|
|
NodeImpl<stringT, string_adaptorT>* getPrev() { return prevSibling_; }
|
|
|
|
void setPrev(NodeImpl<stringT, string_adaptorT>* prevSibling)
|
|
{
|
|
prevSibling_ = prevSibling;
|
|
} // setPreviousSibling
|
|
|
|
NodeImpl<stringT, string_adaptorT>* getNext() { return nextSibling_; }
|
|
|
|
void setNext(NodeImpl<stringT, string_adaptorT>* nextSibling)
|
|
{
|
|
nextSibling_ = nextSibling;
|
|
} // setNextSibling
|
|
|
|
DocumentImpl<stringT, string_adaptorT>* getOwnerDoc() const { return ownerDoc_; }
|
|
|
|
virtual void setOwnerDoc(DocumentImpl<stringT, string_adaptorT>* ownerDoc)
|
|
{
|
|
ownerDoc_ = ownerDoc;
|
|
for(NodeImpl<stringT, string_adaptorT>*child = getFirst(); child != 0; child = child->getNext())
|
|
child->setOwnerDoc(ownerDoc);
|
|
} // setOwnerDocument
|
|
|
|
bool getReadOnly() const { return readOnly_; }
|
|
|
|
virtual void setReadOnly(bool readOnly)
|
|
{
|
|
readOnly_ = true;
|
|
for(NodeImpl<stringT, string_adaptorT>*child = getFirst(); child != 0; child = child->getNext())
|
|
child->setReadOnly(readOnly_);
|
|
} // setReadOnly
|
|
|
|
void throwIfReadOnly() const
|
|
{
|
|
if(readOnly_)
|
|
throw DOM::DOMException(DOM::DOMException::NO_MODIFICATION_ALLOWED_ERR);
|
|
} // throwIfReadOnly
|
|
|
|
protected:
|
|
NodeImpl<stringT, string_adaptorT>* parentNode_;
|
|
DocumentImpl<stringT, string_adaptorT>* ownerDoc_;
|
|
NodeImpl<stringT, string_adaptorT>* prevSibling_;
|
|
NodeImpl<stringT, string_adaptorT>* nextSibling_;
|
|
bool readOnly_;
|
|
}; // class NodeImpl
|
|
|
|
template<class stringT, class string_adaptorT>
|
|
class ChildlessNodeImpl : public NodeImpl<stringT, string_adaptorT>
|
|
{
|
|
public:
|
|
ChildlessNodeImpl(DocumentImpl<stringT, string_adaptorT>* ownerDoc) :
|
|
NodeImpl<stringT, string_adaptorT>(ownerDoc)
|
|
{
|
|
} // ChildlessNodeImpl
|
|
|
|
///////////////////////////////////////////////////////
|
|
// Node methods
|
|
virtual DOM::NodeList_impl<stringT>* getChildNodes() const { return 0; }
|
|
|
|
virtual DOM::Node_impl<stringT>* getFirstChild() const { return 0; }
|
|
virtual DOM::Node_impl<stringT>* getLastChild() const { return 0; }
|
|
|
|
virtual DOM::Node_impl<stringT>* insertBefore(DOM::Node_impl<stringT>*newChild, DOM::Node_impl<stringT>*refChild)
|
|
{
|
|
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
|
|
} // insertBefore
|
|
|
|
virtual DOM::Node_impl<stringT>* replaceChild(DOM::Node_impl<stringT>*newChild, DOM::Node_impl<stringT>*oldChild)
|
|
{
|
|
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
|
|
} // insertBefore
|
|
|
|
virtual DOM::Node_impl<stringT>* removeChild(DOM::Node_impl<stringT>*oldChild)
|
|
{
|
|
throw DOM::DOMException(DOM::DOMException::NOT_FOUND_ERR);
|
|
} // removeChild
|
|
|
|
virtual DOM::Node_impl<stringT>* appendChild(DOM::Node_impl<stringT>*newChild)
|
|
{
|
|
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
|
|
} // appendChild
|
|
|
|
virtual void purgeChild(DOM::Node_impl<stringT>* oldChild)
|
|
{
|
|
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
|
|
} // purgeChild
|
|
|
|
virtual bool hasChildNodes() const { return false; }
|
|
}; // class ChildlessNodeImpl
|
|
|
|
template<class stringT, class string_adaptorT>
|
|
class NodeImplWithChildren : public NodeImpl<stringT, string_adaptorT>,
|
|
public DOM::NodeList_impl<stringT>
|
|
{
|
|
typedef NodeImpl<stringT, string_adaptorT> NodeT;
|
|
public:
|
|
NodeImplWithChildren(DocumentImpl<stringT, string_adaptorT>* ownerDoc) :
|
|
NodeImpl<stringT, string_adaptorT>(ownerDoc)
|
|
{
|
|
} // NodeImplWithChildren
|
|
|
|
virtual ~NodeImplWithChildren()
|
|
{
|
|
for(typename NodeListT::iterator i = nodes_.begin(); i != nodes_.end(); ++i)
|
|
delete (*i);
|
|
} // ~NodeImpl
|
|
|
|
///////////////////////////////////////////////////////
|
|
// Ref counting
|
|
virtual void addRef()
|
|
{
|
|
NodeImpl<stringT, string_adaptorT>::addRef();
|
|
} // addRef
|
|
virtual void releaseRef()
|
|
{
|
|
NodeImpl<stringT, string_adaptorT>::releaseRef();
|
|
} // releaseRef
|
|
|
|
///////////////////////////////////////////////////////
|
|
// Node methods
|
|
virtual DOM::NodeList_impl<stringT>* getChildNodes() const
|
|
{
|
|
return const_cast<DOM::NodeList_impl<stringT>*>(static_cast<const DOM::NodeList_impl<stringT>*>(this));
|
|
} // getChildNodes
|
|
|
|
virtual DOM::Node_impl<stringT>* getFirstChild() const
|
|
{
|
|
if(nodes_.size())
|
|
return nodes_[0];
|
|
return 0;
|
|
} // getFirstChild
|
|
|
|
virtual DOM::Node_impl<stringT>* getLastChild() const
|
|
{
|
|
if(nodes_.size())
|
|
return *nodes_.rbegin();
|
|
return 0;
|
|
} // getLastChild
|
|
|
|
virtual DOM::Node_impl<stringT>* insertBefore(DOM::Node_impl<stringT>*newChild, DOM::Node_impl<stringT>*refChild)
|
|
{
|
|
return do_insertBefore(dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(newChild),
|
|
dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(refChild));
|
|
} // insertBefore
|
|
|
|
virtual DOM::Node_impl<stringT>* replaceChild(DOM::Node_impl<stringT>*newChild, DOM::Node_impl<stringT>*oldChild)
|
|
{
|
|
return do_replaceChild(dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(newChild),
|
|
dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(oldChild));
|
|
} // replaceChild
|
|
|
|
virtual DOM::Node_impl<stringT>* removeChild(DOM::Node_impl<stringT>* oldChild)
|
|
{
|
|
return do_removeChild(dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(oldChild));
|
|
} // removeChild
|
|
|
|
virtual DOM::Node_impl<stringT>* appendChild(DOM::Node_impl<stringT>* newChild)
|
|
{
|
|
return do_appendChild(dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(newChild));
|
|
} // appendChild
|
|
|
|
virtual void purgeChild(DOM::Node_impl<stringT>* oldChild)
|
|
{
|
|
do_purgeChild(dynamic_cast<NodeImpl<stringT, string_adaptorT>*>(oldChild));
|
|
} // purgeChild
|
|
|
|
virtual bool hasChildNodes() const
|
|
{
|
|
return nodes_.size() != 0;
|
|
} // hasChildNodes
|
|
|
|
///////////////////////////////////////////////////////
|
|
// NodeList methods
|
|
virtual DOM::Node_impl<stringT>* item(unsigned int index) const
|
|
{
|
|
if(index >= nodes_.size())
|
|
return 0;
|
|
|
|
return nodes_[index];
|
|
} // item
|
|
|
|
virtual unsigned int getLength() const
|
|
{
|
|
return static_cast<unsigned int>(nodes_.size());
|
|
} // getLength
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// implementation
|
|
protected:
|
|
NodeImpl<stringT, string_adaptorT>* do_insertBefore(NodeImpl<stringT, string_adaptorT>* newChild, NodeImpl<stringT, string_adaptorT>* refChild)
|
|
{
|
|
NodeT::throwIfReadOnly();
|
|
|
|
if(newChild->getNodeType() == DOM::Node<stringT>::DOCUMENT_FRAGMENT_NODE)
|
|
{
|
|
for(NodeImpl<stringT, string_adaptorT>* child = newChild->getFirst(); child != 0; child = newChild->getFirst())
|
|
insertBefore(newChild->removeChild(child), refChild);
|
|
return newChild;
|
|
} // if ...
|
|
|
|
checkCanAdd(newChild);
|
|
removeIfRequired(newChild);
|
|
if(refChild)
|
|
{
|
|
nodes_.insert(findChild(refChild), newChild);
|
|
|
|
NodeImpl<stringT, string_adaptorT>* prev = refChild->getPrev();
|
|
if(prev != 0)
|
|
prev->setNext(newChild);
|
|
newChild->setPrev(prev);
|
|
newChild->setNext(refChild);
|
|
refChild->setPrev(newChild);
|
|
}
|
|
else
|
|
{
|
|
if(!nodes_.empty())
|
|
{
|
|
(*nodes_.rbegin())->setNext(newChild);
|
|
newChild->setPrev(*nodes_.rbegin());
|
|
} //
|
|
nodes_.push_back(newChild);
|
|
}
|
|
|
|
newChild->setParentNode(this);
|
|
|
|
markChanged();
|
|
|
|
return newChild;
|
|
} // insertBefore
|
|
|
|
NodeImpl<stringT, string_adaptorT>* do_replaceChild(NodeImpl<stringT, string_adaptorT>* newChild, NodeImpl<stringT, string_adaptorT>* oldChild)
|
|
{
|
|
NodeT::throwIfReadOnly();
|
|
|
|
if(newChild->getNodeType() == DOM::Node<stringT>::DOCUMENT_FRAGMENT_NODE)
|
|
{
|
|
// not exception safe - but the it's not specified to be :(
|
|
DOM::Node_impl<stringT>* lc = newChild->getLastChild();
|
|
replaceChild(newChild->removeChild(lc), oldChild);
|
|
insertBefore(newChild, lc);
|
|
return newChild;
|
|
} // if ...
|
|
|
|
checkCanAdd(newChild);
|
|
typename std::deque<NodeImpl<stringT, string_adaptorT>*>::iterator result = findChild(oldChild);
|
|
removeIfRequired(newChild);
|
|
*result = newChild;
|
|
newChild->setParentNode(this);
|
|
|
|
NodeImpl<stringT, string_adaptorT>* prev = oldChild->getPrev();
|
|
NodeImpl<stringT, string_adaptorT>* next = oldChild->getNext();
|
|
newChild->setPrev(prev);
|
|
newChild->setNext(next);
|
|
if(prev != 0)
|
|
prev->setNext(newChild);
|
|
if(next != 0)
|
|
next->setPrev(newChild);
|
|
|
|
oldChild->setParentNode(0);
|
|
oldChild->setPrev(0);
|
|
oldChild->setNext(0);
|
|
|
|
markChanged();
|
|
|
|
return oldChild;
|
|
} // replaceChild
|
|
|
|
NodeImpl<stringT, string_adaptorT>* do_removeChild(NodeImpl<stringT, string_adaptorT>* oldChild)
|
|
{
|
|
NodeT::throwIfReadOnly();
|
|
|
|
nodes_.erase(findChild(oldChild));
|
|
|
|
NodeImpl<stringT, string_adaptorT>* prev = oldChild->getPrev();
|
|
NodeImpl<stringT, string_adaptorT>* next = oldChild->getNext();
|
|
if(prev != 0)
|
|
prev->setNext(next);
|
|
if(next != 0)
|
|
next->setPrev(prev);
|
|
|
|
oldChild->setParentNode(0);
|
|
oldChild->setPrev(0);
|
|
oldChild->setNext(0);
|
|
|
|
markChanged();
|
|
|
|
return oldChild;
|
|
} // removeChild
|
|
|
|
NodeImpl<stringT, string_adaptorT>* do_appendChild(NodeImpl<stringT, string_adaptorT>* newChild)
|
|
{
|
|
return do_insertBefore(newChild, 0);
|
|
} // appendChild
|
|
|
|
void do_purgeChild(NodeImpl<stringT, string_adaptorT>* oldChild)
|
|
{
|
|
oldChild = do_removeChild(oldChild);
|
|
NodeT::ownerDoc_->purge(oldChild);
|
|
} // do_purgeChild
|
|
|
|
private:
|
|
typedef std::deque<NodeImpl<stringT, string_adaptorT>*> NodeListT;
|
|
|
|
typename NodeListT::iterator findChild(NodeImpl<stringT, string_adaptorT>* refChild)
|
|
{
|
|
typename NodeListT::iterator result = std::find(nodes_.begin(), nodes_.end(), refChild);
|
|
if(result == nodes_.end())
|
|
throw DOM::DOMException(DOM::DOMException::NOT_FOUND_ERR);
|
|
return result;
|
|
} // findChild
|
|
|
|
void removeIfRequired(NodeImpl<stringT, string_adaptorT>* newNode) const
|
|
{
|
|
if(newNode->getParentNode() != 0)
|
|
newNode->getParentNode()->removeChild(newNode);
|
|
} // removeIfRequired
|
|
|
|
void checkCanAdd(NodeImpl<stringT, string_adaptorT>* child)
|
|
{
|
|
DocumentImpl<stringT, string_adaptorT>* childDoc = child->getOwnerDoc();
|
|
if(childDoc == 0)
|
|
{
|
|
child->setOwnerDoc(NodeT::getOwnerDoc());
|
|
return;
|
|
} //
|
|
|
|
if(child->getNodeType() == DOM::Node<stringT>::DOCUMENT_NODE)
|
|
{
|
|
if(childDoc != dynamic_cast<DocumentImpl<stringT, string_adaptorT>*>(this))
|
|
throw DOM::DOMException(DOM::DOMException::WRONG_DOCUMENT_ERR);
|
|
return;
|
|
} // if(parent is a Document)
|
|
|
|
if(NodeT::getOwnerDocument() && childDoc != NodeT::getOwnerDocument())
|
|
throw DOM::DOMException(DOM::DOMException::WRONG_DOCUMENT_ERR);
|
|
|
|
checkChildType(child);
|
|
} // checkCanAdd
|
|
|
|
virtual void checkChildType(DOM::Node_impl<stringT>* child) = 0;
|
|
|
|
void markChanged()
|
|
{
|
|
if(NodeT::ownerDoc_)
|
|
NodeT::ownerDoc_->markChanged();
|
|
} // markChanged
|
|
|
|
NodeListT nodes_;
|
|
}; // class NodeImplWithChildren
|
|
|
|
} // namespace DOM
|
|
|
|
#endif // JEZUK_DOM_NODE_H
|
|
// end of file
|