arabica/include/DOM/Simple/DocumentImpl.hpp

571 lines
21 KiB
C++

#ifndef JEZUK_SimpleDOM_DOCUMENTIMPL_H
#define JEZUK_SimpleDOM_DOCUMENTIMPL_H
#include <DOM/Document.hpp>
#include <DOM/Simple/ElementNSImpl.hpp>
#include <DOM/Simple/ProcessingInstructionImpl.hpp>
#include <DOM/Simple/DocumentFragmentImpl.hpp>
#include <DOM/Simple/CommentImpl.hpp>
#include <DOM/Simple/TextImpl.hpp>
#include <DOM/Simple/CDATASectionImpl.hpp>
#include <DOM/Simple/AttrNSImpl.hpp>
#include <DOM/Simple/EntityReferenceImpl.hpp>
#include <DOM/Simple/EntityImpl.hpp>
#include <DOM/Simple/NotationImpl.hpp>
#include <DOM/Simple/ElementByTagImpl.hpp>
#include <DOM/Simple/NodeImpl.hpp>
#include <XML/XMLCharacterClasses.hpp>
#include <set>
#include <list>
#include <algorithm>
namespace Arabica
{
namespace SimpleDOM
{
template<class stringT, class string_adaptorT>
class valueIs : public std::unary_function<AttrImpl<stringT, string_adaptorT>*, bool>
{
public:
valueIs(const stringT& value) : value_(value) { }
bool operator()(const AttrImpl<stringT, string_adaptorT>* node) const
{
return (node->getNodeValue() == value_);
} // operator()
private:
stringT value_;
}; // class valueIs
template<class stringT, class string_adaptorT>
class DocumentImpl : public DOM::Document_impl<stringT, string_adaptorT>,
public NodeImplWithChildren<stringT, string_adaptorT>
{
typedef NodeImpl<stringT, string_adaptorT> NodeImplT;
typedef AttrImpl<stringT, string_adaptorT> AttrImplT;
typedef ElementImpl<stringT, string_adaptorT> ElementImplT;
typedef NodeImplWithChildren<stringT, string_adaptorT> NodeWithChildrenT;
public:
typedef DOM::Node_impl<stringT, string_adaptorT> DOMNode_implT;
typedef DOM::Attr_impl<stringT, string_adaptorT> DOMAttr_implT;
typedef DOM::Element_impl<stringT, string_adaptorT> DOMElement_implT;
typedef DOM::Document_impl<stringT, string_adaptorT> DOMDocument_implT;
typedef DOM::DocumentType_impl<stringT, string_adaptorT> DOMDocumentType_implT;
typedef DOM::DOMImplementation<stringT, string_adaptorT> DOMDOMImplementationT;
DocumentImpl() :
NodeWithChildrenT(0),
documentElement_(0),
documentType_(0),
domImplementation_(),
namespaceURI_(),
qualifiedName_(),
changesCount_(0),
refCount_(0),
empty_()
{
NodeImplT::setOwnerDoc(this);
} // DocumentBaseImpl
DocumentImpl(DOMDOMImplementationT domImpl) :
NodeWithChildrenT(0),
documentElement_(0),
documentType_(0),
domImplementation_(domImpl),
namespaceURI_(),
qualifiedName_(),
changesCount_(0),
refCount_(0)
{
NodeImplT::setOwnerDoc(this);
} // DocumentBaseImpl
DocumentImpl(const stringT& namespaceURI,
const stringT& qualifiedName,
DOMDocumentType_implT* docType,
DOMDOMImplementationT domImpl) :
NodeWithChildrenT(0),
documentElement_(0),
documentType_(0),
domImplementation_(domImpl),
namespaceURI_(namespaceURI),
qualifiedName_(qualifiedName),
changesCount_(0),
refCount_(0)
{
NodeImplT::setOwnerDoc(this);
if(docType)
{
if(docType->getOwnerDocument() != 0)
throw DOM::DOMException(DOM::DOMException::WRONG_DOCUMENT_ERR);
appendChild(docType);
} // if(docType)
} // DocumentBaseImpl
virtual ~DocumentImpl()
{
for(typename std::set<NodeImplT*>::iterator n = orphans_.begin(); n != orphans_.end(); ++n)
delete *n;
} // ~DocumentImpl
/////////////////////////////////////////////////////////////////////
// Ref counting
virtual void addRef()
{
++refCount_;
} // addRef
virtual void releaseRef()
{
if(--refCount_ == 0)
delete this;
} // releaseRef
/////////////////////////////////////////////////////////////////////
// DOM::Document functions
virtual DOMDocumentType_implT* getDoctype() const
{
return documentType_;
} // getDocType
virtual DOMDOMImplementationT getImplementation() const
{
return domImplementation_;
} // getImplementation
virtual DOMElement_implT* getDocumentElement() const
{
return documentElement_;
} // getDocumentElement
virtual DOMElement_implT* createElement(const stringT& tagName) const
{
checkName(tagName);
return createElement_nocheck(tagName);
} // createElement
DOMElement_implT* createElement_nocheck(const stringT& tagName) const
{
ElementImplT* n =
new ElementImplT(const_cast<DocumentImpl*>(this), tagName);
orphaned(n);
return n;
} // createElement_nocheck
virtual DOM::DocumentFragment_impl<stringT, string_adaptorT>* createDocumentFragment() const
{
DocumentFragmentImpl<stringT, string_adaptorT>* n = new DocumentFragmentImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this));
orphaned(n);
return n;
} // createDocumentFragment
virtual DOM::Text_impl<stringT, string_adaptorT>* createTextNode(const stringT& data) const
{
TextImpl<stringT, string_adaptorT>* n = new TextImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), data);
orphaned(n);
return n;
} // createTextNode
virtual DOM::Comment_impl<stringT, string_adaptorT>* createComment(const stringT& data) const
{
CommentImpl<stringT, string_adaptorT>* n = new CommentImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), data);
orphaned(n);
return n;
} // createComment
virtual DOM::CDATASection_impl<stringT, string_adaptorT>* createCDATASection(const stringT& data) const
{
CDATASectionImpl<stringT, string_adaptorT>* n = new CDATASectionImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), data);
orphaned(n);
return n;
} // createCDATASection
virtual DOM::ProcessingInstruction_impl<stringT, string_adaptorT>* createProcessingInstruction(const stringT& target, const stringT& data) const
{
checkName(target);
checkChars(data);
return createProcessingInstruction_nocheck(target, data);
} // createProcessingInstruction
DOM::ProcessingInstruction_impl<stringT, string_adaptorT>* createProcessingInstruction_nocheck(const stringT& target, const stringT& data) const
{
ProcessingInstructionImpl<stringT, string_adaptorT>* n = new ProcessingInstructionImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), target, data);
orphaned(n);
return n;
} // createProcessingInstruction_nocheck
virtual DOMAttr_implT* createAttribute(const stringT& name) const
{
checkName(name);
return createAttribute_nocheck(name);
} // createAttribute
DOMAttr_implT* createAttribute_nocheck(const stringT& name) const
{
AttrImpl<stringT, string_adaptorT>* n = new AttrImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), name);
orphaned(n);
return n;
} // createAttribute_nocheck
virtual DOM::EntityReference_impl<stringT, string_adaptorT>* createEntityReference(const stringT& name) const
{
checkName(name);
return createEntityReference_nocheck(name);
} // createEntityRef
DOM::EntityReference_impl<stringT, string_adaptorT>* createEntityReference_nocheck(const stringT& name) const
{
EntityReferenceImpl<stringT, string_adaptorT>* n = new EntityReferenceImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), name);
if((documentType_ != 0) && (documentType_->getEntities()->getNamedItem(name) != 0))
{
DOMNode_implT* entity = documentType_->getEntities()->getNamedItem(name);
for(DOMNode_implT* child = entity->getFirstChild(); child != 0; child = child->getNextSibling())
n->appendChild(importNode(child, true));
} // if ...
orphaned(n);
n->setReadOnly(true);
return n;
} // createEntityReference
virtual DOM::NodeList_impl<stringT, string_adaptorT>* getElementsByTagName(const stringT& tagname) const
{
return new ElementByTagList<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this),
const_cast<DocumentImpl*>(this),
tagname);
} // getElementsByTagName
virtual DOMNode_implT* importNode(DOMNode_implT* importedNode, bool deep) const
{
DOMNode_implT* newNode = 0;
switch(importedNode->getNodeType())
{
case DOM::Node_base::ATTRIBUTE_NODE:
if(string_adaptorT::empty(importedNode->getLocalName()))
newNode = createAttribute_nocheck(importedNode->getNodeName());
else
newNode = createAttributeNS_nocheck(importedNode->getNamespaceURI(), importedNode->getNodeName());
deep = true;
break;
case DOM::Node_base::DOCUMENT_FRAGMENT_NODE:
newNode = createDocumentFragment();
break;
case DOM::Node_base::DOCUMENT_NODE:
throw DOM::DOMException(DOM::DOMException::NOT_SUPPORTED_ERR);
case DOM::Node_base::DOCUMENT_TYPE_NODE:
throw DOM::DOMException(DOM::DOMException::NOT_SUPPORTED_ERR);
case DOM::Node_base::ELEMENT_NODE:
{
DOMElement_implT* elem;
if(string_adaptorT::empty(importedNode->getLocalName()))
elem = createElement_nocheck(importedNode->getNodeName());
else
elem = createElementNS_nocheck(importedNode->getNamespaceURI(), importedNode->getNodeName());
const DOM::NamedNodeMap_impl<stringT, string_adaptorT>* attrs = importedNode->getAttributes();
if(attrs)
for(unsigned int i = 0; i < attrs->getLength(); ++i)
{
DOMAttr_implT* a = dynamic_cast<DOMAttr_implT*>(attrs->item(i));
if(a->getSpecified())
{
DOMAttr_implT* newA = dynamic_cast<DOMAttr_implT*>(importNode(a, true));
if(string_adaptorT::empty(a->getLocalName()))
elem->setAttributeNode(newA);
else
elem->setAttributeNodeNS(newA);
} // if ...
} // for
newNode = elem;
}
break;
case DOM::Node_base::ENTITY_NODE:
{
DOM::Entity_impl<stringT, string_adaptorT>* entity = dynamic_cast<DOM::Entity_impl<stringT, string_adaptorT>*>(importedNode);
newNode = new EntityImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this),
entity->getNodeName(),
entity->getPublicId(),
entity->getSystemId(),
entity->getNotationName());
}
break;
case DOM::Node_base::ENTITY_REFERENCE_NODE:
newNode = createEntityReference_nocheck(importedNode->getNodeName());
deep = false;
break;
case DOM::Node_base::NOTATION_NODE:
{
DOM::Notation_impl<stringT, string_adaptorT>* entity = dynamic_cast<DOM::Notation_impl<stringT, string_adaptorT>*>(importedNode);
newNode = new NotationImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this),
entity->getNodeName(),
entity->getPublicId(),
entity->getSystemId());
}
break;
case DOM::Node_base::PROCESSING_INSTRUCTION_NODE:
newNode = createProcessingInstruction_nocheck(importedNode->getNodeName(), importedNode->getNodeValue());
break;
case DOM::Node_base::TEXT_NODE:
newNode = createTextNode(importedNode->getNodeValue());
break;
case DOM::Node_base::CDATA_SECTION_NODE:
newNode = createCDATASection(importedNode->getNodeValue());
break;
case DOM::Node_base::COMMENT_NODE:
newNode = createComment(importedNode->getNodeValue());
break;
default:
throw std::runtime_error("Bad node type value in importNode");
} // switch
if(deep)
{
for(DOMNode_implT* child = importedNode->getFirstChild(); child != 0; child = child->getNextSibling())
newNode->appendChild(importNode(child, true));
} // if(deep)
if(newNode->getNodeType() == DOM::Node_base::ENTITY_NODE)
dynamic_cast<NodeImplT*>(newNode)->setReadOnly(true);
orphaned(dynamic_cast<NodeImplT*>(newNode));
return newNode;
} // importNode
virtual DOMElement_implT* createElementNS(const stringT& namespaceURI, const stringT& qualifiedName) const
{
checkName(qualifiedName);
return createElementNS_nocheck(namespaceURI, qualifiedName);
} // createElementNS
DOMElement_implT* createElementNS_nocheck(const stringT& namespaceURI, const stringT& qualifiedName) const
{
ElementNSImpl<stringT, string_adaptorT>* n =
new ElementNSImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), namespaceURI, !string_adaptorT::empty(namespaceURI), qualifiedName);
orphaned(n);
return n;
} // createElementNS
virtual DOMAttr_implT* createAttributeNS(const stringT& namespaceURI, const stringT& qualifiedName) const
{
checkName(qualifiedName);
return createAttributeNS_nocheck(namespaceURI, qualifiedName);
} // createAttributeNS
virtual DOMAttr_implT* createAttributeNS_nocheck(const stringT& namespaceURI, const stringT& qualifiedName) const
{
AttrNSImpl<stringT, string_adaptorT>* n = new AttrNSImpl<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this), namespaceURI, !string_adaptorT::empty(namespaceURI), qualifiedName);
orphaned(n);
return n;
} // createAttributeNS_nocheck
virtual DOM::NodeList_impl<stringT, string_adaptorT>* getElementsByTagNameNS(const stringT& namespaceURI, const stringT& localName) const
{
return new ElementByTagList<stringT, string_adaptorT>(const_cast<DocumentImpl*>(this),
const_cast<DocumentImpl*>(this),
namespaceURI, localName);
} // getElementsByTagNameNS
virtual DOMElement_implT* getElementById(const stringT& elementId) const
{
typename std::set<AttrImplT*>::const_iterator i = std::find_if(idNodes_.begin(), idNodes_.end(), valueIs<stringT, string_adaptorT>(elementId));
if(i == idNodes_.end())
return 0;
return (*i)->getOwnerElement();
} // getElementById
////////////////////////////////////////////////////////
// DOM Node methods
virtual typename DOM::Node_base::Type getNodeType() const
{
return DOM::Node_base::DOCUMENT_NODE;
} // getNodeType
virtual DOMNode_implT* getParentNode() const { return 0; }
virtual DOMDocument_implT* getOwnerDocument() const { return 0; }
virtual const stringT& getNodeName() const
{
static const stringT doc = string_adaptorT::construct_from_utf8("#document");
return doc;
} // getNodeName
virtual DOMNode_implT* insertBefore(DOMNode_implT* newChild, DOMNode_implT* refChild)
{
if((newChild->getNodeType() == DOM::Node_base::ELEMENT_NODE) && (documentElement_ != 0))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
if((newChild->getNodeType() == DOM::Node_base::DOCUMENT_TYPE_NODE) && (documentType_ != 0))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
DOMNode_implT* result = NodeWithChildrenT::insertBefore(newChild, refChild);
if((newChild->getNodeType() == DOM::Node_base::ELEMENT_NODE) && (documentElement_ == 0))
documentElement_ = dynamic_cast<DOMElement_implT*>(newChild);
if((newChild->getNodeType() == DOM::Node_base::DOCUMENT_TYPE_NODE) && (documentType_ == 0))
documentType_ = dynamic_cast<DOMDocumentType_implT*>(newChild);
return result;
} // insertBefore
virtual DOMNode_implT* replaceChild(DOMNode_implT* newChild, DOMNode_implT* oldChild)
{
checkChildType(newChild);
if((newChild->getNodeType() == DOM::Node_base::DOCUMENT_TYPE_NODE) && (documentType_ == oldChild))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
if((newChild->getNodeType() == DOM::Node_base::ELEMENT_NODE) &&
(documentElement_ != 0) &&
(documentElement_ != oldChild))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
DOMNode_implT* result = NodeWithChildrenT::replaceChild(newChild, oldChild);
if((newChild->getNodeType() == DOM::Node_base::ELEMENT_NODE) &&
((documentElement_ == 0) || (documentElement_ == oldChild)))
documentElement_ = dynamic_cast<DOMElement_implT*>(newChild);
return result;
} // replaceChild
virtual DOMNode_implT* removeChild(DOMNode_implT* oldChild)
{
if((documentType_ == oldChild))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
DOMNode_implT* result = NodeWithChildrenT::removeChild(oldChild);
if(documentElement_ == oldChild)
documentElement_ = static_cast<DOMElement_implT*>(0);
return result;
} // removeChild
virtual DOMNode_implT* appendChild(DOMNode_implT* newChild)
{
return insertBefore(newChild, 0);
} // appendChild
virtual DOMNode_implT* cloneNode(bool deep) const
{
DocumentImpl* clone = new DocumentImpl(namespaceURI_, qualifiedName_, 0, domImplementation_);
if(documentType_ != 0)
{
DocumentTypeImpl<stringT, string_adaptorT>* dt = dynamic_cast<DocumentTypeImpl<stringT, string_adaptorT>*>(documentType_->cloneNode(true));
dt->setOwnerDoc(clone);
clone->appendChild(dt);
}
if(deep)
for(DOMNode_implT* child = NodeWithChildrenT::getFirstChild(); child != 0; child = child->getNextSibling())
if((documentType_ != child) && (child != clone->getDocumentElement()))
clone->appendChild(clone->importNode(child, true));
return clone;
} // cloneNode
////////////////////////////
// extensions
void markChanged() { ++changesCount_; }
unsigned long changes() const { return changesCount_; }
void orphaned(NodeImplT* node) const
{
orphans_.insert(node);
} // orphaned
bool isOrphaned(NodeImplT* node) const
{
return orphans_.find(node) != orphans_.end();
} // isOrphaned
void purge(NodeImplT* node)
{
orphans_.erase(node);
delete node;
} // purge
void adopted(NodeImplT* node)
{
typename std::set<NodeImplT*>::iterator n = orphans_.find(node);
if(n != orphans_.end())
orphans_.erase(n);
} // adopted
void setElementId(AttrImplT* attr)
{
idNodes_.insert(attr);
} // setElementId
void removeElementId(AttrImplT* attr)
{
typename std::set<AttrImplT*>::iterator n = idNodes_.find(attr);
if(n != idNodes_.end())
idNodes_.erase(n);
} // removeElementId
stringT const* stringPool(const stringT& str) const
{
typename std::list<stringT>::const_iterator i = std::find(stringPool_.begin(), stringPool_.end(), str);
if(i != stringPool_.end())
return &(*i);
stringPool_.push_back(str);
return &(stringPool_.back());
} // stringPool
const stringT& empty_string() const { return empty_; }
private:
void checkChildType(DOMNode_implT* child)
{
typename DOM::Node_base::Type type = child->getNodeType();
if((type != DOM::Node_base::ELEMENT_NODE) &&
(type != DOM::Node_base::PROCESSING_INSTRUCTION_NODE) &&
(type != DOM::Node_base::COMMENT_NODE) &&
(type != DOM::Node_base::DOCUMENT_TYPE_NODE))
throw DOM::DOMException(DOM::DOMException::HIERARCHY_REQUEST_ERR);
} // checkChildType
void checkName(const stringT& str) const
{
if(string_adaptorT::length(str) == 0)
throw DOM::DOMException(DOM::DOMException::INVALID_CHARACTER_ERR);
typedef typename string_adaptorT::const_iterator const_iterator;
const_iterator i = string_adaptorT::begin(str);
const_iterator ie = string_adaptorT::end(str);
for( ; i != ie; ++i)
if(!XML::is_name_char(*i))
throw DOM::DOMException(DOM::DOMException::INVALID_CHARACTER_ERR);
} // checkName
void checkChars(const stringT& str) const
{
typedef typename string_adaptorT::const_iterator const_iterator;
const_iterator i = string_adaptorT::begin(str);
const_iterator ie = string_adaptorT::end(str);
for( ; i != ie; ++i)
if(!XML::is_char(*i))
throw DOM::DOMException(DOM::DOMException::INVALID_CHARACTER_ERR);
} // checkChars
private:
DOMElement_implT* documentElement_;
DOMDocumentType_implT* documentType_;
DOMDOMImplementationT domImplementation_;
stringT namespaceURI_;
stringT qualifiedName_;
unsigned long changesCount_;
unsigned long refCount_;
mutable std::set<NodeImplT*> orphans_;
std::set<AttrImplT*> idNodes_;
mutable std::list<stringT> stringPool_;
const stringT empty_;
}; // class DocumentImpl
} // namespace SAX2DOM
} // namespace Arabica
#endif
// end of file