diff options
Diffstat (limited to 'unoxml/source/dom/document.cxx')
-rw-r--r-- | unoxml/source/dom/document.cxx | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/unoxml/source/dom/document.cxx b/unoxml/source/dom/document.cxx new file mode 100644 index 000000000000..43ce284f40ee --- /dev/null +++ b/unoxml/source/dom/document.cxx @@ -0,0 +1,1091 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <com/sun/star/uno/Sequence.h> + +#include "document.hxx" +#include "attr.hxx" +#include "element.hxx" +#include "cdatasection.hxx" +#include "documentfragment.hxx" +#include "text.hxx" +#include "cdatasection.hxx" +#include "comment.hxx" +#include "processinginstruction.hxx" +#include "entityreference.hxx" +#include "documenttype.hxx" +#include "elementlist.hxx" +#include "domimplementation.hxx" +#include <entity.hxx> +#include <notation.hxx> + +#include "../events/event.hxx" +#include "../events/mutationevent.hxx" +#include "../events/uievent.hxx" +#include "../events/mouseevent.hxx" +#include "../events/eventdispatcher.hxx" + +#include <string.h> + +#include <com/sun/star/xml/sax/FastToken.hpp> +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +namespace DOM +{ + static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument) + { + // find the doc type + xmlNodePtr cur = i_pDocument->children; + while (cur != NULL) + { + if ((cur->type == XML_DOCUMENT_TYPE_NODE) || + (cur->type == XML_DTD_NODE)) { + return cur; + } + } + return 0; + } + + /// get the pointer to the root element node of the document + static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument) + { + // find the document element + xmlNodePtr cur = i_pDocument->children; + while (cur != NULL) + { + if (cur->type == XML_ELEMENT_NODE) + break; + cur = cur->next; + } + return cur; + } + + CDocument::CDocument(xmlDocPtr const pDoc) + : CDocument_Base(*this, m_Mutex, + NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc)) + , m_aDocPtr(pDoc) + , m_streamListeners() + , m_pEventDispatcher(new events::CEventDispatcher()) + { + } + + ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc) + { + ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc)); + // add the doc itself to its nodemap! + xDoc->m_NodeMap.insert( + nodemap_t::value_type(reinterpret_cast<xmlNodePtr>(pDoc), + ::std::make_pair( + WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())), + xDoc.get()))); + return xDoc; + } + + CDocument::~CDocument() + { + ::osl::MutexGuard const g(m_Mutex); +#ifdef DBG_UTIL + // node map must be empty now, otherwise CDocument must not die! + for (nodemap_t::iterator i = m_NodeMap.begin(); + i != m_NodeMap.end(); ++i) + { + Reference<XNode> const xNode(i->second.first); + OSL_ENSURE(!xNode.is(), + "CDocument::~CDocument(): ERROR: live node in document node map!"); + } +#endif + xmlFreeDoc(m_aDocPtr); + } + + + events::CEventDispatcher & CDocument::GetEventDispatcher() + { + return *m_pEventDispatcher; + } + + ::rtl::Reference< CElement > CDocument::GetDocumentElement() + { + xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); + ::rtl::Reference< CElement > const xRet( + dynamic_cast<CElement*>(GetCNode(pNode).get())); + return xRet; + } + + void + CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode) + { + nodemap_t::iterator const i = m_NodeMap.find(pNode); + if (i != m_NodeMap.end()) { + // #i113681# consider this scenario: + // T1 calls ~CNode + // T2 calls getCNode: lookup will find i->second->first invalid + // so a new CNode is created and inserted + // T1 calls removeCNode: i->second->second now points to a + // different CNode instance! + // + // check that the CNode is the right one + CNode *const pCurrent = i->second.second; + if (pCurrent == pCNode) { + m_NodeMap.erase(i); + } + } + } + + /** NB: this is the CNode factory. + it is the only place where CNodes may be instantiated. + all CNodes must be registered at the m_NodeMap. + */ + ::rtl::Reference<CNode> + CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate) + { + if (0 == pNode) { + return 0; + } + //check whether there is already an instance for this node + nodemap_t::const_iterator const i = m_NodeMap.find(pNode); + if (i != m_NodeMap.end()) { + // #i113681# check that the CNode is still alive + uno::Reference<XNode> const xNode(i->second.first); + if (xNode.is()) + { + ::rtl::Reference<CNode> ret(i->second.second); + OSL_ASSERT(ret.is()); + return ret; + } + } + + if (!bCreate) { return 0; } + + // there is not yet an instance wrapping this node, + // create it and store it in the map + + ::rtl::Reference<CNode> pCNode; + switch (pNode->type) + { + case XML_ELEMENT_NODE: + // m_aNodeType = NodeType::ELEMENT_NODE; + pCNode = static_cast< CNode* >( + new CElement(*this, m_Mutex, pNode)); + break; + case XML_TEXT_NODE: + // m_aNodeType = NodeType::TEXT_NODE; + pCNode = static_cast< CNode* >( + new CText(*this, m_Mutex, pNode)); + break; + case XML_CDATA_SECTION_NODE: + // m_aNodeType = NodeType::CDATA_SECTION_NODE; + pCNode = static_cast< CNode* >( + new CCDATASection(*this, m_Mutex, pNode)); + break; + case XML_ENTITY_REF_NODE: + // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE; + pCNode = static_cast< CNode* >( + new CEntityReference(*this, m_Mutex, pNode)); + break; + case XML_ENTITY_NODE: + // m_aNodeType = NodeType::ENTITY_NODE; + pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex, + reinterpret_cast<xmlEntityPtr>(pNode))); + break; + case XML_PI_NODE: + // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE; + pCNode = static_cast< CNode* >( + new CProcessingInstruction(*this, m_Mutex, pNode)); + break; + case XML_COMMENT_NODE: + // m_aNodeType = NodeType::COMMENT_NODE; + pCNode = static_cast< CNode* >( + new CComment(*this, m_Mutex, pNode)); + break; + case XML_DOCUMENT_NODE: + // m_aNodeType = NodeType::DOCUMENT_NODE; + OSL_ENSURE(false, "CDocument::GetCNode is not supposed to" + " create a CDocument!!!"); + pCNode = static_cast< CNode* >(new CDocument( + reinterpret_cast<xmlDocPtr>(pNode))); + break; + case XML_DOCUMENT_TYPE_NODE: + case XML_DTD_NODE: + // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE; + pCNode = static_cast< CNode* >(new CDocumentType(*this, m_Mutex, + reinterpret_cast<xmlDtdPtr>(pNode))); + break; + case XML_DOCUMENT_FRAG_NODE: + // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE; + pCNode = static_cast< CNode* >( + new CDocumentFragment(*this, m_Mutex, pNode)); + break; + case XML_NOTATION_NODE: + // m_aNodeType = NodeType::NOTATION_NODE; + pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex, + reinterpret_cast<xmlNotationPtr>(pNode))); + break; + case XML_ATTRIBUTE_NODE: + // m_aNodeType = NodeType::ATTRIBUTE_NODE; + pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex, + reinterpret_cast<xmlAttrPtr>(pNode))); + break; + // unsupported node types + case XML_HTML_DOCUMENT_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + default: + break; + } + + if (pCNode != 0) { + bool const bInserted = m_NodeMap.insert( + nodemap_t::value_type(pNode, + ::std::make_pair(WeakReference<XNode>(pCNode.get()), + pCNode.get())) + ).second; + OSL_ASSERT(bInserted); + if (!bInserted) { + // if insertion failed, delete new instance and return null + return 0; + } + } + + OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!"); + return pCNode; + } + + + CDocument & CDocument::GetOwnerDocument() + { + return *this; + } + + void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler) + { + i_xHandler->startDocument(); + for (xmlNodePtr pChild = m_aNodePtr->children; + pChild != 0; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode = GetCNode(pChild); + OSL_ENSURE(pNode != 0, "CNode::get returned 0"); + pNode->saxify(i_xHandler); + } + i_xHandler->endDocument(); + } + + void CDocument::fastSaxify( Context& rContext ) + { + rContext.mxDocHandler->startDocument(); + for (xmlNodePtr pChild = m_aNodePtr->children; + pChild != 0; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode = GetCNode(pChild); + OSL_ENSURE(pNode != 0, "CNode::get returned 0"); + pNode->fastSaxify(rContext); + } + rContext.mxDocHandler->endDocument(); + } + + bool CDocument::IsChildTypeAllowed(NodeType const nodeType) + { + switch (nodeType) { + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_COMMENT_NODE: + return true; + case NodeType_ELEMENT_NODE: + // there may be only one! + return 0 == lcl_getDocumentRootPtr(m_aDocPtr); + case NodeType_DOCUMENT_TYPE_NODE: + // there may be only one! + return 0 == lcl_getDocumentType(m_aDocPtr); + default: + return false; + } + } + + + void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener ) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + m_streamListeners.insert(aListener); + } + + void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener ) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + m_streamListeners.erase(aListener); + } + + // IO context functions for libxml2 interaction + typedef struct { + Reference< XOutputStream > stream; + bool allowClose; + } IOContext; + + extern "C" { + // write callback + // int xmlOutputWriteCallback (void * context, const char * buffer, int len) + static int writeCallback(void *context, const char* buffer, int len){ + // create a sequence and write it to the stream + IOContext *pContext = static_cast<IOContext*>(context); + Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len); + pContext->stream->writeBytes(bs); + return len; + } + + // clsoe callback + //int xmlOutputCloseCallback (void * context) + static int closeCallback(void *context) + { + IOContext *pContext = static_cast<IOContext*>(context); + if (pContext->allowClose) { + pContext->stream->closeOutput(); + } + return 0; + } + } // extern "C" + + void SAL_CALL CDocument::start() + throw (RuntimeException) + { + listenerlist_t streamListeners; + { + ::osl::MutexGuard const g(m_Mutex); + + if (! m_rOutputStream.is()) { throw RuntimeException(); } + streamListeners = m_streamListeners; + } + + // notify listeners about start + listenerlist_t::const_iterator iter1 = streamListeners.begin(); + while (iter1 != streamListeners.end()) { + Reference< XStreamListener > aListener = *iter1; + aListener->started(); + ++iter1; + } + + { + ::osl::MutexGuard const g(m_Mutex); + + // check again! could have been reset... + if (! m_rOutputStream.is()) { throw RuntimeException(); } + + // setup libxml IO and write data to output stream + IOContext ioctx = {m_rOutputStream, false}; + xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO( + writeCallback, closeCallback, &ioctx, NULL); + xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL); + } + + // call listeners + listenerlist_t::const_iterator iter2 = streamListeners.begin(); + while (iter2 != streamListeners.end()) { + Reference< XStreamListener > aListener = *iter2; + aListener->closed(); + ++iter2; + } + } + + void SAL_CALL CDocument::terminate() + throw (RuntimeException) + { + // not supported + } + + void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream ) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + m_rOutputStream = aStream; + } + + Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + return m_rOutputStream; + } + + // Creates an Attr of the given name. + Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar *xName = (xmlChar*)o1.getStr(); + xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL); + ::rtl::Reference< CAttr > const pCAttr( + dynamic_cast< CAttr* >(GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)).get())); + pCAttr->m_bUnlinked = true; + return pCAttr.get(); + }; + + // Creates an attribute of the given qualified name and namespace URI. + Reference< XAttr > SAL_CALL CDocument::createAttributeNS( + const OUString& ns, const OUString& qname) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + // libxml does not allow a NS definition to be attached to an + // attribute node - which is a good thing, since namespaces are + // only defined as parts of element nodes + // thus the namespace data is stored in CAttr::m_pNamespace + sal_Int32 i = qname.indexOf(':'); + OString oPrefix, oName, oUri; + if (i != -1) + { + oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); + oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); + } + else + { + oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); + } + oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); + xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, + reinterpret_cast<xmlChar const*>(oName.getStr()), 0); + ::rtl::Reference< CAttr > const pCAttr( + dynamic_cast< CAttr* >(GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)).get())); + if (!pCAttr.is()) { throw RuntimeException(); } + // store the namespace data! + pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) ); + pCAttr->m_bUnlinked = true; + + return pCAttr.get(); + }; + + // Creates a CDATASection node whose value is the specified string. + Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString const oData( + ::rtl::OUStringToOString(data, RTL_TEXTENCODING_UTF8)); + xmlChar const*const pData = + reinterpret_cast<xmlChar const*>(oData.getStr()); + xmlNodePtr const pText = + xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr())); + Reference< XCDATASection > const xRet( + static_cast< XNode* >(GetCNode(pText).get()), + UNO_QUERY_THROW); + return xRet; + } + + // Creates a Comment node given the specified string. + Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar *xData = (xmlChar*)o1.getStr(); + xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData); + Reference< XComment > const xRet( + static_cast< XNode* >(GetCNode(pComment).get()), + UNO_QUERY_THROW); + return xRet; + } + + //Creates an empty DocumentFragment object. + Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment() + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr); + Reference< XDocumentFragment > const xRet( + static_cast< XNode* >(GetCNode(pFrag).get()), + UNO_QUERY_THROW); + return xRet; + } + + // Creates an element of the type specified. + Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8); + xmlChar *xName = (xmlChar*)o1.getStr(); + xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); + Reference< XElement > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY_THROW); + return xRet; + } + + // Creates an element of the given qualified name and namespace URI. + Reference< XElement > SAL_CALL CDocument::createElementNS( + const OUString& ns, const OUString& qname) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + sal_Int32 i = qname.indexOf(':'); + if (ns.getLength() == 0) throw RuntimeException(); + xmlChar *xPrefix; + xmlChar *xName; + OString o1, o2, o3; + if ( i != -1) { + o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); + xPrefix = (xmlChar*)o1.getStr(); + o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); + xName = (xmlChar*)o2.getStr(); + } else { + // default prefix + xPrefix = (xmlChar*)""; + o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); + xName = (xmlChar*)o2.getStr(); + } + o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); + xmlChar *xUri = (xmlChar*)o3.getStr(); + + // xmlNsPtr aNsPtr = xmlNewReconciledNs? + // xmlNsPtr aNsPtr = xmlNewGlobalNs? + xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); + xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix); + xmlSetNs(pNode, pNs); + Reference< XElement > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY_THROW); + return xRet; + } + + //Creates an EntityReference object. + Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar *xName = (xmlChar*)o1.getStr(); + xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName); + Reference< XEntityReference > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY_THROW); + return xRet; + } + + // Creates a ProcessingInstruction node given the specified name and + // data strings. + Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction( + const OUString& target, const OUString& data) + throw (RuntimeException, DOMException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8); + xmlChar *xTarget = (xmlChar*)o1.getStr(); + OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar *xData = (xmlChar*)o2.getStr(); + xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData); + pNode->doc = m_aDocPtr; + Reference< XProcessingInstruction > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY_THROW); + return xRet; + } + + // Creates a Text node given the specified string. + Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar *xData = (xmlChar*)o1.getStr(); + xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData); + Reference< XText > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY_THROW); + return xRet; + } + + // The Document Type Declaration (see DocumentType) associated with this + // document. + Reference< XDocumentType > SAL_CALL CDocument::getDoctype() + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr)); + Reference< XDocumentType > const xRet( + static_cast< XNode* >(GetCNode(pDocType).get()), + UNO_QUERY); + return xRet; + } + + // This is a convenience attribute that allows direct access to the child + // node that is the root element of the document. + Reference< XElement > SAL_CALL CDocument::getDocumentElement() + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); + if (!pNode) { return 0; } + Reference< XElement > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY); + return xRet; + } + + static xmlNodePtr + lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id) + { + if (cur == NULL) + return NULL; + // look in current node + if (cur->type == XML_ELEMENT_NODE) + { + xmlAttrPtr a = cur->properties; + while (a != NULL) + { + if (a->atype == XML_ATTRIBUTE_ID) { + if (strcmp((char*)a->children->content, (char*)id) == 0) + return cur; + } + a = a->next; + } + } + // look in children + xmlNodePtr result = lcl_search_element_by_id(cur->children, id); + if (result != NULL) + return result; + result = lcl_search_element_by_id(cur->next, id); + return result; + } + + // Returns the Element whose ID is given by elementId. + Reference< XElement > SAL_CALL + CDocument::getElementById(const OUString& elementId) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + // search the tree for an element with the given ID + OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8); + xmlChar *xId = (xmlChar*)o1.getStr(); + xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr); + if (!pStart) { return 0; } + xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId); + Reference< XElement > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY); + return xRet; + } + + + Reference< XNodeList > SAL_CALL + CDocument::getElementsByTagName(OUString const& rTagname) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + Reference< XNodeList > const xRet( + new CElementList(this->GetDocumentElement(), m_Mutex, rTagname)); + return xRet; + } + + Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS( + OUString const& rNamespaceURI, OUString const& rLocalName) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + Reference< XNodeList > const xRet( + new CElementList(this->GetDocumentElement(), m_Mutex, + rLocalName, &rNamespaceURI)); + return xRet; + } + + Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation() + throw (RuntimeException) + { + // does not need mutex currently + return Reference< XDOMImplementation >(CDOMImplementation::get()); + } + + // helper function to recursively import siblings + static void lcl_ImportSiblings( + Reference< XDocument > const& xTargetDocument, + Reference< XNode > const& xTargetParent, + Reference< XNode > const& xChild) + { + Reference< XNode > xSibling = xChild; + while (xSibling.is()) + { + Reference< XNode > const xTmp( + xTargetDocument->importNode(xSibling, sal_True)); + xTargetParent->appendChild(xTmp); + xSibling = xSibling->getNextSibling(); + } + } + + static Reference< XNode > + lcl_ImportNode( Reference< XDocument > const& xDocument, + Reference< XNode > const& xImportedNode, sal_Bool deep) + { + Reference< XNode > xNode; + NodeType aNodeType = xImportedNode->getNodeType(); + switch (aNodeType) + { + case NodeType_ATTRIBUTE_NODE: + { + Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW); + Reference< XAttr > const xNew = + xDocument->createAttribute(xAttr->getName()); + xNew->setValue(xAttr->getValue()); + xNode.set(xNew, UNO_QUERY); + break; + } + case NodeType_CDATA_SECTION_NODE: + { + Reference< XCDATASection > const xCData(xImportedNode, + UNO_QUERY_THROW); + Reference< XCDATASection > const xNewCData = + xDocument->createCDATASection(xCData->getData()); + xNode.set(xNewCData, UNO_QUERY); + break; + } + case NodeType_COMMENT_NODE: + { + Reference< XComment > const xComment(xImportedNode, + UNO_QUERY_THROW); + Reference< XComment > const xNewComment = + xDocument->createComment(xComment->getData()); + xNode.set(xNewComment, UNO_QUERY); + break; + } + case NodeType_DOCUMENT_FRAGMENT_NODE: + { + Reference< XDocumentFragment > const xFrag(xImportedNode, + UNO_QUERY_THROW); + Reference< XDocumentFragment > const xNewFrag = + xDocument->createDocumentFragment(); + xNode.set(xNewFrag, UNO_QUERY); + break; + } + case NodeType_ELEMENT_NODE: + { + Reference< XElement > const xElement(xImportedNode, + UNO_QUERY_THROW); + OUString const aNsUri = xImportedNode->getNamespaceURI(); + OUString const aNsPrefix = xImportedNode->getPrefix(); + OUString aQName = xElement->getTagName(); + Reference< XElement > xNewElement; + if (aNsUri.getLength() > 0) + { + if (aNsPrefix.getLength() > 0) { + aQName = aNsPrefix + OUString(RTL_CONSTASCII_USTRINGPARAM(":")) + + aQName; + } + xNewElement = xDocument->createElementNS(aNsUri, aQName); + } else { + xNewElement = xDocument->createElement(aQName); + } + + // get attributes + if (xElement->hasAttributes()) + { + Reference< XNamedNodeMap > attribs = xElement->getAttributes(); + for (sal_Int32 i = 0; i < attribs->getLength(); i++) + { + Reference< XAttr > const curAttr(attribs->item(i), + UNO_QUERY_THROW); + OUString const aAttrUri = curAttr->getNamespaceURI(); + OUString const aAttrPrefix = curAttr->getPrefix(); + OUString aAttrName = curAttr->getName(); + OUString const sValue = curAttr->getValue(); + if (aAttrUri.getLength() > 0) + { + if (aAttrPrefix.getLength() > 0) { + aAttrName = aAttrPrefix + + OUString(RTL_CONSTASCII_USTRINGPARAM(":")) + aAttrName; + } + xNewElement->setAttributeNS( + aAttrUri, aAttrName, sValue); + } else { + xNewElement->setAttribute(aAttrName, sValue); + } + } + } + xNode.set(xNewElement, UNO_QUERY); + break; + } + case NodeType_ENTITY_REFERENCE_NODE: + { + Reference< XEntityReference > const xRef(xImportedNode, + UNO_QUERY_THROW); + Reference< XEntityReference > const xNewRef( + xDocument->createEntityReference(xRef->getNodeName())); + xNode.set(xNewRef, UNO_QUERY); + break; + } + case NodeType_PROCESSING_INSTRUCTION_NODE: + { + Reference< XProcessingInstruction > const xPi(xImportedNode, + UNO_QUERY_THROW); + Reference< XProcessingInstruction > const xNewPi( + xDocument->createProcessingInstruction( + xPi->getTarget(), xPi->getData())); + xNode.set(xNewPi, UNO_QUERY); + break; + } + case NodeType_TEXT_NODE: + { + Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW); + Reference< XText > const xNewText( + xDocument->createTextNode(xText->getData())); + xNode.set(xNewText, UNO_QUERY); + break; + } + case NodeType_ENTITY_NODE: + case NodeType_DOCUMENT_NODE: + case NodeType_DOCUMENT_TYPE_NODE: + case NodeType_NOTATION_NODE: + default: + // can't be imported + throw RuntimeException(); + + } + if (deep) + { + // get children and import them + Reference< XNode > const xChild = xImportedNode->getFirstChild(); + if (xChild.is()) + { + lcl_ImportSiblings(xDocument, xNode, xChild); + } + } + + /* DOMNodeInsertedIntoDocument + * Fired when a node is being inserted into a document, + * either through direct insertion of the Node or insertion of a + * subtree in which it is contained. This event is dispatched after + * the insertion has taken place. The target of this event is the node + * being inserted. If the Node is being directly inserted the DOMNodeInserted + * event will fire before the DOMNodeInsertedIntoDocument event. + * Bubbles: No + * Cancelable: No + * Context Info: None + */ + if (xNode.is()) + { + Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY); + Reference< XMutationEvent > const event(xDocevent->createEvent( + OUString(RTL_CONSTASCII_USTRINGPARAM("DOMNodeInsertedIntoDocument"))), + UNO_QUERY_THROW); + event->initMutationEvent( + OUString::createFromAscii("DOMNodeInsertedIntoDocument") + , sal_True, sal_False, Reference< XNode >(), + OUString(), OUString(), OUString(), (AttrChangeType)0 ); + Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY); + xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY)); + } + + return xNode; + } + + Reference< XNode > SAL_CALL CDocument::importNode( + Reference< XNode > const& xImportedNode, sal_Bool deep) + throw (RuntimeException, DOMException) + { + if (!xImportedNode.is()) { throw RuntimeException(); } + + // NB: this whole operation inherently accesses 2 distinct documents. + // The imported node could even be from a different DOM implementation, + // so this implementation cannot make any assumptions about the + // locking strategy of the imported node. + // So the import takes no lock on this document; + // it only calls UNO methods on this document that temporarily + // lock the document, and UNO methods on the imported node that + // may temporarily lock the other document. + // As a consequence, the import is not atomic with regard to + // concurrent modifications of either document, but it should not + // deadlock. + // To ensure that no members are accessed, the implementation is in + // static non-member functions. + + Reference< XDocument > const xDocument(this); + // already in doc? + if (xImportedNode->getOwnerDocument() == xDocument) { + return xImportedNode; + } + + Reference< XNode > const xNode( + lcl_ImportNode(xDocument, xImportedNode, deep) ); + return xNode; + } + + + OUString SAL_CALL CDocument::getNodeName()throw (RuntimeException) + { + // does not need mutex currently + return OUString(RTL_CONSTASCII_USTRINGPARAM("#document")); + } + + OUString SAL_CALL CDocument::getNodeValue() throw (RuntimeException) + { + // does not need mutex currently + return OUString(); + } + + Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep) + throw (RuntimeException) + { + ::osl::MutexGuard const g(m_rMutex); + + OSL_ASSERT(0 != m_aNodePtr); + if (0 == m_aNodePtr) { + return 0; + } + xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0)); + if (0 == pClone) { return 0; } + Reference< XNode > const xRet( + static_cast<CNode*>(CDocument::CreateCDocument(pClone).get())); + return xRet; + } + + Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) throw (RuntimeException) + { + // does not need mutex currently + events::CEvent *pEvent = 0; + if ( + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMSubtreeModified")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMNodeInserted")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMNodeRemoved")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMNodeRemovedFromDocument")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMNodeInsertedIntoDocument")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMAttrModified")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMCharacterDataModified"))) + { + pEvent = new events::CMutationEvent; + + } else if ( + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMFocusIn")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMFocusOut")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("DOMActivate"))) + { + pEvent = new events::CUIEvent; + } else if ( + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("click")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("mousedown")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("mouseup")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("mouseover")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("mousemove")) || + aType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("mouseout")) ) + { + pEvent = new events::CMouseEvent; + } + else // generic event + { + pEvent = new events::CEvent; + } + return Reference< XEvent >(pEvent); + } + + // ::com::sun::star::xml::sax::XSAXSerializable + void SAL_CALL CDocument::serialize( + const Reference< XDocumentHandler >& i_xHandler, + const Sequence< beans::StringPair >& i_rNamespaces) + throw (RuntimeException, SAXException) + { + ::osl::MutexGuard const g(m_Mutex); + + // add new namespaces to root node + xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); + if (0 != pRoot) { + const beans::StringPair * pSeq = i_rNamespaces.getConstArray(); + for (const beans::StringPair *pNsDef = pSeq; + pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) { + OString prefix = OUStringToOString(pNsDef->First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(pNsDef->Second, + RTL_TEXTENCODING_UTF8); + // this will only add the ns if it does not exist already + xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()), + reinterpret_cast<const xmlChar*>(prefix.getStr())); + } + // eliminate duplicate namespace declarations + nscleanup(pRoot->children, pRoot); + } + saxify(i_xHandler); + } + + // ::com::sun::star::xml::sax::XFastSAXSerializable + void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler, + const Reference< XFastTokenHandler >& i_xTokenHandler, + const Sequence< beans::StringPair >& i_rNamespaces, + const Sequence< beans::Pair< rtl::OUString, sal_Int32 > >& i_rRegisterNamespaces ) + throw (SAXException, RuntimeException) + { + ::osl::MutexGuard const g(m_Mutex); + + // add new namespaces to root node + xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); + if (0 != pRoot) { + const beans::StringPair * pSeq = i_rNamespaces.getConstArray(); + for (const beans::StringPair *pNsDef = pSeq; + pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) { + OString prefix = OUStringToOString(pNsDef->First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(pNsDef->Second, + RTL_TEXTENCODING_UTF8); + // this will only add the ns if it does not exist already + xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()), + reinterpret_cast<const xmlChar*>(prefix.getStr())); + } + // eliminate duplicate namespace declarations + nscleanup(pRoot->children, pRoot); + } + + Context aContext(i_xHandler, + i_xTokenHandler); + + // register namespace ids + const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray(); + for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq; + pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs) + { + OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE, + "CDocument::fastSerialize(): invalid NS token id"); + aContext.maNamespaceMap[ pNs->First ] = pNs->Second; + } + + fastSaxify(aContext); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |