#ifndef JEZUK_DOM_SimpleDOM_NODE_H #define JEZUK_DOM_SimpleDOM_NODE_H //////////////////////////// // C++ DOM definition // // $Id$ //////////////////////////// #include #include #include #include //#include namespace SimpleDOM { //////////////////////////////////////////////////////////////////// template class DocumentImpl; template class NodeImpl : virtual public DOM::Node_impl { typedef DOM::Node_impl NodeT; public: NodeImpl(DocumentImpl* 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 stringT getNodeValue() const { return stringT(); } virtual void setNodeValue(const stringT& nodeValue) { throwIfReadOnly(); } virtual DOM::Node_base::Type getNodeType() const = 0; virtual DOM::Node_impl* getParentNode() const { return parentNode_; } virtual DOM::NodeList_impl* getChildNodes() const = 0; virtual DOM::Node_impl* getFirstChild() const = 0; virtual DOM::Node_impl* getLastChild() const = 0; virtual DOM::Node_impl* getPreviousSibling() const { return prevSibling_; } virtual DOM::Node_impl* getNextSibling() const { return nextSibling_; } virtual DOM::NamedNodeMap_impl* getAttributes() const { return 0; } virtual DOM::Document_impl* getOwnerDocument() const { return ownerDoc_; } virtual DOM::Node_impl* insertBefore(DOM::Node_impl* newChild, DOM::Node_impl* refChild) = 0; virtual DOM::Node_impl* replaceChild(DOM::Node_impl* newChild, DOM::Node_impl* oldChild) = 0; virtual DOM::Node_impl* removeChild(DOM::Node_impl* oldChild) = 0; virtual DOM::Node_impl* appendChild(DOM::Node_impl* newChild) = 0; virtual void purgeChild(DOM::Node_impl* oldChild) = 0; virtual bool hasChildNodes() const = 0; virtual DOM::Node_impl* cloneNode(bool deep) const = 0; virtual void normalize() { DOM::Node_impl*child = getFirstChild(); while(child != 0) { DOM::Node_impl*next = child->getNextSibling(); if(child->getNodeType() == DOM::Node::TEXT_NODE) { DOM::Text_impl* textNode = dynamic_cast*>(child); while((next != 0) && (next->getNodeType() == DOM::Node::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* 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 stringT getNamespaceURI() const { return stringT(); } virtual stringT getPrefix() const { return stringT(); } virtual void setPrefix(const stringT& prefix) { } virtual stringT getLocalName() const { return stringT(); } // 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* parent) { if(!parent && parentNode_ && ownerDoc_) ownerDoc_->orphaned(this); if(!parentNode_ && parent && ownerDoc_) ownerDoc_->adopted(this); parentNode_ = parent; } // setParentNode NodeImpl* getFirst() { return dynamic_cast*>(getFirstChild()); } NodeImpl* getPrev() { return prevSibling_; } void setPrev(NodeImpl* prevSibling) { prevSibling_ = prevSibling; } // setPreviousSibling NodeImpl* getNext() { return nextSibling_; } void setNext(NodeImpl* nextSibling) { nextSibling_ = nextSibling; } // setNextSibling DocumentImpl* getOwnerDoc() const { return ownerDoc_; } virtual void setOwnerDoc(DocumentImpl* ownerDoc) { ownerDoc_ = ownerDoc; for(NodeImpl*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*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* parentNode_; DocumentImpl* ownerDoc_; NodeImpl* prevSibling_; NodeImpl* nextSibling_; bool readOnly_; }; // class NodeImpl template class ChildlessNodeImpl : public NodeImpl { public: ChildlessNodeImpl(DocumentImpl* ownerDoc) : NodeImpl(ownerDoc) { } // ChildlessNodeImpl /////////////////////////////////////////////////////// // Node methods virtual DOM::NodeList_impl* getChildNodes() const { return 0; } virtual DOM::Node_impl* getFirstChild() const { return 0; } virtual DOM::Node_impl* getLastChild() const { return 0; } virtual DOM::Node_impl* insertBefore(DOM::Node_impl*newChild, DOM::Node_impl*refChild) { throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR); } // insertBefore virtual DOM::Node_impl* replaceChild(DOM::Node_impl*newChild, DOM::Node_impl*oldChild) { throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR); } // insertBefore virtual DOM::Node_impl* removeChild(DOM::Node_impl*oldChild) { throw DOM::DOMException(DOM::DOMException::NOT_FOUND_ERR); } // removeChild virtual DOM::Node_impl* appendChild(DOM::Node_impl*newChild) { throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR); } // appendChild virtual void purgeChild(DOM::Node_impl* oldChild) { throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR); } // purgeChild virtual bool hasChildNodes() const { return false; } }; // class ChildlessNodeImpl template class NodeImplWithChildren : public NodeImpl, public DOM::NodeList_impl { typedef NodeImpl NodeT; public: NodeImplWithChildren(DocumentImpl* ownerDoc) : NodeImpl(ownerDoc) { } // NodeImplWithChildren virtual ~NodeImplWithChildren() { for(typename NodeListT::iterator i = nodes_.begin(); i != nodes_.end(); ++i) delete (*i); } // ~NodeImpl /////////////////////////////////////////////////////// // Ref counting virtual void addRef() { NodeImpl::addRef(); } // addRef virtual void releaseRef() { NodeImpl::releaseRef(); } // releaseRef /////////////////////////////////////////////////////// // Node methods virtual DOM::NodeList_impl* getChildNodes() const { return const_cast*>(static_cast*>(this)); } // getChildNodes virtual DOM::Node_impl* getFirstChild() const { if(nodes_.size()) return nodes_[0]; return 0; } // getFirstChild virtual DOM::Node_impl* getLastChild() const { if(nodes_.size()) return *nodes_.rbegin(); return 0; } // getLastChild virtual DOM::Node_impl* insertBefore(DOM::Node_impl*newChild, DOM::Node_impl*refChild) { return do_insertBefore(dynamic_cast*>(newChild), dynamic_cast*>(refChild)); } // insertBefore virtual DOM::Node_impl* replaceChild(DOM::Node_impl*newChild, DOM::Node_impl*oldChild) { return do_replaceChild(dynamic_cast*>(newChild), dynamic_cast*>(oldChild)); } // replaceChild virtual DOM::Node_impl* removeChild(DOM::Node_impl* oldChild) { return do_removeChild(dynamic_cast*>(oldChild)); } // removeChild virtual DOM::Node_impl* appendChild(DOM::Node_impl* newChild) { return do_appendChild(dynamic_cast*>(newChild)); } // appendChild virtual void purgeChild(DOM::Node_impl* oldChild) { do_purgeChild(dynamic_cast*>(oldChild)); } // purgeChild virtual bool hasChildNodes() const { return nodes_.size() != 0; } // hasChildNodes /////////////////////////////////////////////////////// // NodeList methods virtual DOM::Node_impl* item(unsigned int index) const { if(index >= nodes_.size()) return 0; return nodes_[index]; } // item virtual unsigned int getLength() const { return static_cast(nodes_.size()); } // getLength ///////////////////////////////////////////////////////////// // implementation protected: NodeImpl* do_insertBefore(NodeImpl* newChild, NodeImpl* refChild) { NodeT::throwIfReadOnly(); if(newChild->getNodeType() == DOM::Node::DOCUMENT_FRAGMENT_NODE) { for(NodeImpl* 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* 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* do_replaceChild(NodeImpl* newChild, NodeImpl* oldChild) { NodeT::throwIfReadOnly(); if(newChild->getNodeType() == DOM::Node::DOCUMENT_FRAGMENT_NODE) { // not exception safe - but the it's not specified to be :( DOM::Node_impl* lc = newChild->getLastChild(); replaceChild(newChild->removeChild(lc), oldChild); insertBefore(newChild, lc); return newChild; } // if ... checkCanAdd(newChild); typename std::deque*>::iterator result = findChild(oldChild); removeIfRequired(newChild); *result = newChild; newChild->setParentNode(this); NodeImpl* prev = oldChild->getPrev(); NodeImpl* 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* do_removeChild(NodeImpl* oldChild) { NodeT::throwIfReadOnly(); nodes_.erase(findChild(oldChild)); NodeImpl* prev = oldChild->getPrev(); NodeImpl* 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* do_appendChild(NodeImpl* newChild) { return do_insertBefore(newChild, 0); } // appendChild void do_purgeChild(NodeImpl* oldChild) { oldChild = do_removeChild(oldChild); NodeT::ownerDoc_->purge(oldChild); } // do_purgeChild private: typedef std::deque*> NodeListT; typename NodeListT::iterator findChild(NodeImpl* 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* newNode) const { if(newNode->getParentNode() != 0) newNode->getParentNode()->removeChild(newNode); } // removeIfRequired void checkCanAdd(NodeImpl* child) { DocumentImpl* childDoc = child->getOwnerDoc(); if(childDoc == 0) { child->setOwnerDoc(NodeT::getOwnerDoc()); return; } // if(child->getNodeType() == DOM::Node::DOCUMENT_NODE) { if(childDoc != dynamic_cast*>(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* 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