blob: 36bf6a59f02fe3db7c18b6a7d5c0a0c61228f4b2 [file] [log] [blame]
/**************************************************************
*
* 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);
}
}