| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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::createFromAscii(":") |
| + 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::createFromAscii(":") + 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::createFromAscii("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::createFromAscii("#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.compareToAscii("DOMSubtreeModified") == 0|| |
| aType.compareToAscii("DOMNodeInserted") == 0|| |
| aType.compareToAscii("DOMNodeRemoved") == 0|| |
| aType.compareToAscii("DOMNodeRemovedFromDocument") == 0|| |
| aType.compareToAscii("DOMNodeInsertedIntoDocument") == 0|| |
| aType.compareToAscii("DOMAttrModified") == 0|| |
| aType.compareToAscii("DOMCharacterDataModified") == 0) |
| { |
| pEvent = new events::CMutationEvent; |
| |
| } else if ( |
| aType.compareToAscii("DOMFocusIn") == 0|| |
| aType.compareToAscii("DOMFocusOut") == 0|| |
| aType.compareToAscii("DOMActivate") == 0) |
| { |
| pEvent = new events::CUIEvent; |
| } else if ( |
| aType.compareToAscii("click") == 0|| |
| aType.compareToAscii("mousedown") == 0|| |
| aType.compareToAscii("mouseup") == 0|| |
| aType.compareToAscii("mouseover") == 0|| |
| aType.compareToAscii("mousemove") == 0|| |
| aType.compareToAscii("mouseout") == 0 ) |
| { |
| 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); |
| } |
| } |