1417734544
CAttr::getSpecified() should return true. CAttr::getValue() and CElement::setAttributeNode() could crash if no children. CAttributesMap::*NS() methods ignored namespaceURI parameter. CDATASection::getNodeValue() forwarded to wrong base class. CDocument needs to override cloneNode(). CDocument::importNode(), CDocumentBuilder::parseURI() deref null argument. CElement::getAttributes() should return an empty map. CElementList could be created for null element. CNode::insertBefore() did not set parent/children pointers. CNode::setPrefix() should only work on element/attribute. CXPathAPI had inverted check for null argument (xmlfix3 regression).
1062 lines
35 KiB
C++
1062 lines
35 KiB
C++
/*************************************************************************
|
|
*
|
|
* 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 <node.hxx>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <libxml/xmlstring.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
#include <rtl/uuid.h>
|
|
#include <rtl/instance.hxx>
|
|
#include <osl/mutex.hxx>
|
|
|
|
#include <com/sun/star/xml/sax/FastToken.hpp>
|
|
|
|
#include <document.hxx>
|
|
#include <attr.hxx>
|
|
#include <childlist.hxx>
|
|
|
|
#include "../events/eventdispatcher.hxx"
|
|
#include "../events/mutationevent.hxx"
|
|
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
namespace {
|
|
struct UnoTunnelId
|
|
: public ::rtl::StaticWithInit< Sequence<sal_Int8>, UnoTunnelId >
|
|
{
|
|
Sequence<sal_Int8> operator() ()
|
|
{
|
|
Sequence<sal_Int8> ret(16);
|
|
rtl_createUuid(
|
|
reinterpret_cast<sal_uInt8*>(ret.getArray()), 0, sal_True);
|
|
return ret;
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace DOM
|
|
{
|
|
void pushContext(Context& io_rContext)
|
|
{
|
|
io_rContext.maNamespaces.push_back(
|
|
io_rContext.maNamespaces.back());
|
|
}
|
|
|
|
void popContext(Context& io_rContext)
|
|
{
|
|
io_rContext.maNamespaces.pop_back();
|
|
}
|
|
|
|
void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
|
|
{
|
|
// add node's namespaces to current context
|
|
for (xmlNsPtr pNs = pNode->nsDef; pNs != 0; pNs = pNs->next) {
|
|
const xmlChar *pPrefix = pNs->prefix;
|
|
OString prefix(reinterpret_cast<const sal_Char*>(pPrefix),
|
|
strlen(reinterpret_cast<const char*>(pPrefix)));
|
|
const xmlChar *pHref = pNs->href;
|
|
OUString val(reinterpret_cast<const sal_Char*>(pHref),
|
|
strlen(reinterpret_cast<const char*>(pHref)),
|
|
RTL_TEXTENCODING_UTF8);
|
|
|
|
OSL_TRACE("Trying to add namespace %s (prefix %s)",
|
|
(const char*)pHref, (const char*)pPrefix);
|
|
|
|
Context::NamespaceMapType::iterator aIter=
|
|
io_rContext.maNamespaceMap.find(val);
|
|
if( aIter != io_rContext.maNamespaceMap.end() )
|
|
{
|
|
Context::Namespace aNS;
|
|
aNS.maPrefix = prefix;
|
|
aNS.mnToken = aIter->second;
|
|
aNS.maNamespaceURL = val;
|
|
|
|
io_rContext.maNamespaces.back().push_back(aNS);
|
|
|
|
OSL_TRACE("Added with token 0x%x", aIter->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
sal_Int32 getToken( const Context& rContext, const sal_Char* pToken )
|
|
{
|
|
const Sequence<sal_Int8> aSeq( (sal_Int8*)pToken, strlen( pToken ) );
|
|
return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
|
|
}
|
|
|
|
sal_Int32 getTokenWithPrefix( const Context& rContext, const sal_Char* pPrefix, const sal_Char* pName )
|
|
{
|
|
sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
|
|
OString prefix(pPrefix,
|
|
strlen(reinterpret_cast<const char*>(pPrefix)));
|
|
|
|
OSL_TRACE("getTokenWithPrefix(): prefix %s, name %s",
|
|
(const char*)pPrefix, (const char*)pName);
|
|
|
|
Context::NamespaceVectorType::value_type::const_iterator aIter;
|
|
if( (aIter=std::find_if(rContext.maNamespaces.back().begin(),
|
|
rContext.maNamespaces.back().end(),
|
|
boost::bind(std::equal_to<OString>(),
|
|
boost::bind(&Context::Namespace::getPrefix,
|
|
_1),
|
|
boost::cref(prefix)))) != rContext.maNamespaces.back().end() )
|
|
{
|
|
nNamespaceToken = aIter->mnToken;
|
|
sal_Int32 nNameToken = getToken( rContext, pName );
|
|
if( nNameToken != FastToken::DONTKNOW )
|
|
nNamespaceToken |= nNameToken;
|
|
}
|
|
|
|
return nNamespaceToken;
|
|
}
|
|
|
|
|
|
CNode::CNode(CDocument const& rDocument, ::osl::Mutex const& rMutex,
|
|
NodeType const& reNodeType, xmlNodePtr const& rpNode)
|
|
: m_bUnlinked(false)
|
|
, m_aNodeType(reNodeType)
|
|
, m_aNodePtr(rpNode)
|
|
// keep containing document alive
|
|
// (but not if this is a document; that would create a leak!)
|
|
, m_xDocument( (m_aNodePtr->type != XML_DOCUMENT_NODE)
|
|
? &const_cast<CDocument&>(rDocument) : 0 )
|
|
, m_rMutex(const_cast< ::osl::Mutex & >(rMutex))
|
|
{
|
|
OSL_ASSERT(m_aNodePtr);
|
|
}
|
|
|
|
void CNode::invalidate()
|
|
{
|
|
//remove from list if this wrapper goes away
|
|
if (m_aNodePtr != 0 && m_xDocument.is()) {
|
|
m_xDocument->RemoveCNode(m_aNodePtr, this);
|
|
}
|
|
// #i113663#: unlinked nodes will not be freed by xmlFreeDoc
|
|
if (m_bUnlinked) {
|
|
xmlFreeNode(m_aNodePtr);
|
|
}
|
|
m_aNodePtr = 0;
|
|
}
|
|
|
|
CNode::~CNode()
|
|
{
|
|
// if this is the document itself, the mutex is already freed!
|
|
if (NodeType_DOCUMENT_NODE == m_aNodeType) {
|
|
invalidate();
|
|
} else {
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
invalidate(); // other nodes are still alive so must lock mutex
|
|
}
|
|
}
|
|
|
|
CNode *
|
|
CNode::GetImplementation(uno::Reference<uno::XInterface> const& xNode)
|
|
{
|
|
uno::Reference<lang::XUnoTunnel> const xUnoTunnel(xNode, UNO_QUERY);
|
|
if (!xUnoTunnel.is()) { return 0; }
|
|
CNode *const pCNode( reinterpret_cast< CNode* >(
|
|
::sal::static_int_cast< sal_IntPtr >(
|
|
xUnoTunnel->getSomething(UnoTunnelId::get()))));
|
|
return pCNode;
|
|
}
|
|
|
|
CDocument & CNode::GetOwnerDocument()
|
|
{
|
|
OSL_ASSERT(m_xDocument.is());
|
|
return *m_xDocument; // needs overriding in CDocument!
|
|
}
|
|
|
|
|
|
static void lcl_nsexchange(
|
|
xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
|
|
{
|
|
// recursively exchange any references to oldNs with references to newNs
|
|
xmlNodePtr cur = aNode;
|
|
while (cur != 0)
|
|
{
|
|
if (cur->ns == oldNs)
|
|
cur->ns = newNs;
|
|
if (cur->type == XML_ELEMENT_NODE)
|
|
{
|
|
xmlAttrPtr curAttr = cur->properties;
|
|
while(curAttr != 0)
|
|
{
|
|
if (curAttr->ns == oldNs)
|
|
curAttr->ns = newNs;
|
|
curAttr = curAttr->next;
|
|
}
|
|
lcl_nsexchange(cur->children, oldNs, newNs);
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
/*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
|
|
{
|
|
xmlNodePtr cur = aNode;
|
|
|
|
//handle attributes
|
|
if (cur != NULL && cur->type == XML_ELEMENT_NODE)
|
|
{
|
|
xmlAttrPtr curAttr = cur->properties;
|
|
while(curAttr != 0)
|
|
{
|
|
if (curAttr->ns != NULL)
|
|
{
|
|
xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
|
|
if (ns != NULL)
|
|
curAttr->ns = ns;
|
|
}
|
|
curAttr = curAttr->next;
|
|
}
|
|
}
|
|
|
|
while (cur != NULL)
|
|
{
|
|
nscleanup(cur->children, cur);
|
|
if (cur->ns != NULL)
|
|
{
|
|
xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
|
|
if (ns != NULL && ns != cur->ns && strcmp((char*)ns->href, (char*)cur->ns->href)==0)
|
|
{
|
|
xmlNsPtr curDef = cur->nsDef;
|
|
xmlNsPtr *refp = &(cur->nsDef); // insert point
|
|
while (curDef != NULL)
|
|
{
|
|
ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
|
|
if (ns != NULL && ns != curDef && strcmp((char*)ns->href, (char*)curDef->href)==0)
|
|
{
|
|
// reconnect ns pointers in sub-tree to newly found ns before
|
|
// removing redundant nsdecl to prevent dangling pointers.
|
|
lcl_nsexchange(cur, curDef, ns);
|
|
*refp = curDef->next;
|
|
xmlFreeNs(curDef);
|
|
curDef = *refp;
|
|
} else {
|
|
refp = &(curDef->next);
|
|
curDef = curDef->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
|
|
{
|
|
if (!i_xHandler.is()) throw RuntimeException();
|
|
// default: do nothing
|
|
}
|
|
|
|
void CNode::fastSaxify(Context& io_rContext)
|
|
{
|
|
if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
|
|
// default: do nothing
|
|
}
|
|
|
|
bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/)
|
|
{
|
|
// default: no children allowed
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Adds the node newChild to the end of the list of children of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::appendChild(
|
|
Reference< XNode > const& xNewChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
::osl::ClearableMutexGuard guard(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) { return 0; }
|
|
|
|
CNode *const pNewChild(CNode::GetImplementation(xNewChild));
|
|
if (!pNewChild) { throw RuntimeException(); }
|
|
xmlNodePtr const cur = pNewChild->GetNodePtr();
|
|
if (!cur) { throw RuntimeException(); }
|
|
|
|
// error checks:
|
|
// from other document
|
|
if (cur->doc != m_aNodePtr->doc) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
|
|
throw e;
|
|
}
|
|
// same node
|
|
if (cur == m_aNodePtr) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
if (cur->parent != NULL) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
if (!IsChildTypeAllowed(pNewChild->m_aNodeType)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
// check whether this is an attribute node; it needs special handling
|
|
xmlNodePtr res = NULL;
|
|
if (cur->type == XML_ATTRIBUTE_NODE)
|
|
{
|
|
xmlChar const*const pChildren((cur->children)
|
|
? cur->children->content
|
|
: reinterpret_cast<xmlChar const*>(""));
|
|
CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
|
|
if (!pCAttr) { throw RuntimeException(); }
|
|
xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
|
|
if (pNs) {
|
|
res = reinterpret_cast<xmlNodePtr>(
|
|
xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
|
|
} else {
|
|
res = reinterpret_cast<xmlNodePtr>(
|
|
xmlNewProp(m_aNodePtr, cur->name, pChildren));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = xmlAddChild(m_aNodePtr, cur);
|
|
|
|
// libxml can do optimization when appending nodes.
|
|
// if res != cur, something was optimized and the newchild-wrapper
|
|
// should be updated
|
|
if (res && (cur != res)) {
|
|
pNewChild->invalidate(); // cur has been freed
|
|
}
|
|
}
|
|
|
|
if (!res) { return 0; }
|
|
|
|
// use custom ns cleanup instead of
|
|
// xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
|
|
// because that will not remove unneeded ns decls
|
|
nscleanup(res, m_aNodePtr);
|
|
|
|
::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
|
|
|
|
if (!pNode.is()) { return 0; }
|
|
|
|
// dispatch DOMNodeInserted event, target is the new node
|
|
// this node is the related node
|
|
// does bubble
|
|
pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
|
|
Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
|
|
Reference< XMutationEvent > event(docevent->createEvent(
|
|
OUString::createFromAscii("DOMNodeInserted")), UNO_QUERY);
|
|
event->initMutationEvent(OUString::createFromAscii("DOMNodeInserted")
|
|
, sal_True, sal_False,
|
|
this,
|
|
OUString(), OUString(), OUString(), (AttrChangeType)0 );
|
|
|
|
// the following dispatch functions use only UNO interfaces
|
|
// and call event listeners, so release mutex to prevent deadlocks.
|
|
guard.clear();
|
|
|
|
dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
|
|
// dispatch subtree modified for this node
|
|
dispatchSubtreeModified();
|
|
|
|
return pNode.get();
|
|
}
|
|
|
|
/**
|
|
Returns a duplicate of this node, i.e., serves as a generic copy
|
|
constructor for nodes.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
|
|
xmlCopyNode(m_aNodePtr, (bDeep) ? 1 : 0));
|
|
if (!pNode.is()) { return 0; }
|
|
pNode->m_bUnlinked = true; // not linked yet
|
|
return pNode.get();
|
|
}
|
|
|
|
/**
|
|
A NamedNodeMap containing the attributes of this node (if it is an Element)
|
|
or null otherwise.
|
|
*/
|
|
Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
|
|
throw (RuntimeException)
|
|
{
|
|
// return empty reference; only element node may override this impl
|
|
return Reference< XNamedNodeMap>();
|
|
}
|
|
|
|
/**
|
|
A NodeList that contains all children of this node.
|
|
*/
|
|
Reference< XNodeList > SAL_CALL CNode::getChildNodes()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
|
|
return xNodeList;
|
|
}
|
|
|
|
/**
|
|
The first child of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getFirstChild()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
GetOwnerDocument().GetCNode(m_aNodePtr->children).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
The last child of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getLastChild()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr)).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
Returns the local part of the qualified name of this node.
|
|
*/
|
|
OUString SAL_CALL CNode::getLocalName()
|
|
throw (RuntimeException)
|
|
{
|
|
// see CElement/CAttr
|
|
return ::rtl::OUString();
|
|
}
|
|
|
|
|
|
/**
|
|
The namespace URI of this node, or null if it is unspecified.
|
|
*/
|
|
OUString SAL_CALL CNode::getNamespaceURI()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
OUString aURI;
|
|
if (m_aNodePtr != NULL &&
|
|
(m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
|
|
m_aNodePtr->ns != NULL)
|
|
{
|
|
const xmlChar* xHref = m_aNodePtr->ns->href;
|
|
aURI = OUString((sal_Char*)xHref, strlen((char*)xHref), RTL_TEXTENCODING_UTF8);
|
|
}
|
|
return aURI;
|
|
}
|
|
|
|
/**
|
|
The node immediately following this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getNextSibling()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
GetOwnerDocument().GetCNode(m_aNodePtr->next).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
The name of this node, depending on its type; see the table above.
|
|
*/
|
|
OUString SAL_CALL CNode::getNodeName()
|
|
throw (RuntimeException)
|
|
{
|
|
/*
|
|
Interface nodeName nodeValue attributes
|
|
--------------------------------------------------------------------------------------
|
|
Attr name of attribute value of attribute null
|
|
CDATASection "#cdata-section" content of the CDATA Section null
|
|
Comment "#comment" content of the comment null
|
|
Document "#document" null null
|
|
DocumentFragment "#document-fragment" null null
|
|
DocumentType document type name null null
|
|
Element tag name null NamedNodeMap
|
|
Entity entity name null null
|
|
EntityReference name of entity null null
|
|
referenced
|
|
Notation notation name null null
|
|
Processing\ target entire content excluding null
|
|
Instruction the target
|
|
Text "#text" content of the text node null
|
|
*/
|
|
OUString aName;
|
|
return aName;
|
|
}
|
|
|
|
/**
|
|
A code representing the type of the underlying object, as defined above.
|
|
*/
|
|
NodeType SAL_CALL CNode::getNodeType()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
return m_aNodeType;
|
|
}
|
|
|
|
/**
|
|
The value of this node, depending on its type; see the table above.
|
|
*/
|
|
OUString SAL_CALL CNode::getNodeValue()
|
|
throw (RuntimeException)
|
|
{
|
|
OUString aValue;
|
|
return aValue;
|
|
}
|
|
|
|
/**
|
|
The Document object associated with this node.
|
|
*/
|
|
Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XDocument > const xDoc(& GetOwnerDocument());
|
|
return xDoc;
|
|
}
|
|
|
|
/**
|
|
The parent of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getParentNode()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
GetOwnerDocument().GetCNode(m_aNodePtr->parent).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
The namespace prefix of this node, or null if it is unspecified.
|
|
*/
|
|
OUString SAL_CALL CNode::getPrefix()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
OUString aPrefix;
|
|
if (m_aNodePtr != NULL &&
|
|
(m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
|
|
m_aNodePtr->ns != NULL)
|
|
{
|
|
const xmlChar* xPrefix = m_aNodePtr->ns->prefix;
|
|
if( xPrefix != NULL )
|
|
aPrefix = OUString((sal_Char*)xPrefix, strlen((char*)xPrefix), RTL_TEXTENCODING_UTF8);
|
|
}
|
|
return aPrefix;
|
|
|
|
}
|
|
|
|
/**
|
|
The node immediately preceding this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getPreviousSibling()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
GetOwnerDocument().GetCNode(m_aNodePtr->prev).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
Returns whether this node (if it is an element) has any attributes.
|
|
*/
|
|
sal_Bool SAL_CALL CNode::hasAttributes()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
return (m_aNodePtr != NULL && m_aNodePtr->properties != NULL);
|
|
}
|
|
|
|
/**
|
|
Returns whether this node has any children.
|
|
*/
|
|
sal_Bool SAL_CALL CNode::hasChildNodes()
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
return (m_aNodePtr != NULL && m_aNodePtr->children != NULL);
|
|
}
|
|
|
|
/**
|
|
Inserts the node newChild before the existing child node refChild.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::insertBefore(
|
|
const Reference< XNode >& newChild, const Reference< XNode >& refChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
|
|
|
|
if (newChild->getOwnerDocument() != getOwnerDocument()) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
|
|
throw e;
|
|
}
|
|
if (refChild->getParentNode() != Reference< XNode >(this)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
::osl::ClearableMutexGuard guard(m_rMutex);
|
|
|
|
CNode *const pNewNode(CNode::GetImplementation(newChild));
|
|
CNode *const pRefNode(CNode::GetImplementation(refChild));
|
|
if (!pNewNode || !pRefNode) { throw RuntimeException(); }
|
|
xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
|
|
xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
|
|
if (!pNewChild || !pRefChild) { throw RuntimeException(); }
|
|
|
|
if (pNewChild == m_aNodePtr) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
// already has parent
|
|
if (pNewChild->parent != NULL)
|
|
{
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
// attributes are unordered anyway, so just do appendChild
|
|
if (XML_ATTRIBUTE_NODE == pNewChild->type) {
|
|
guard.clear();
|
|
return appendChild(newChild);
|
|
}
|
|
|
|
xmlNodePtr cur = m_aNodePtr->children;
|
|
|
|
//search child before which to insert
|
|
while (cur != NULL)
|
|
{
|
|
if (cur == pRefChild) {
|
|
// insert before
|
|
pNewChild->next = cur;
|
|
pNewChild->prev = cur->prev;
|
|
cur->prev = pNewChild;
|
|
if (pNewChild->prev != NULL) {
|
|
pNewChild->prev->next = pNewChild;
|
|
}
|
|
pNewChild->parent = cur->parent;
|
|
if (pNewChild->parent->children == cur) {
|
|
pNewChild->parent->children = pNewChild;
|
|
}
|
|
// do not update parent->last here!
|
|
pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
|
|
break;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
return refChild;
|
|
}
|
|
|
|
/**
|
|
Tests whether the DOM implementation implements a specific feature and
|
|
that feature is supported by this node.
|
|
*/
|
|
sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
|
|
throw (RuntimeException)
|
|
{
|
|
// XXX
|
|
return sal_False;
|
|
}
|
|
|
|
/**
|
|
Puts all Text nodes in the full depth of the sub-tree underneath this
|
|
Node, including attribute nodes, into a "normal" form where only structure
|
|
(e.g., elements, comments, processing instructions, CDATA sections, and
|
|
entity references) separates Text nodes, i.e., there are neither adjacent
|
|
Text nodes nor empty Text nodes.
|
|
*/
|
|
void SAL_CALL CNode::normalize()
|
|
throw (RuntimeException)
|
|
{
|
|
//XXX combine adjacent text nodes and remove empty ones
|
|
}
|
|
|
|
/**
|
|
Removes the child node indicated by oldChild from the list of children,
|
|
and returns it.
|
|
*/
|
|
Reference< XNode > SAL_CALL
|
|
CNode::removeChild(const Reference< XNode >& xOldChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
if (!xOldChild.is()) {
|
|
throw RuntimeException();
|
|
}
|
|
|
|
if (xOldChild->getOwnerDocument() != getOwnerDocument()) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
|
|
throw e;
|
|
}
|
|
if (xOldChild->getParentNode() != Reference< XNode >(this)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
::osl::ClearableMutexGuard guard(m_rMutex);
|
|
|
|
if (!m_aNodePtr) { throw RuntimeException(); }
|
|
|
|
Reference<XNode> xReturn( xOldChild );
|
|
|
|
::rtl::Reference<CNode> const pOld(CNode::GetImplementation(xOldChild));
|
|
if (!pOld.is()) { throw RuntimeException(); }
|
|
xmlNodePtr const old = pOld->GetNodePtr();
|
|
if (!old) { throw RuntimeException(); }
|
|
|
|
if( old->type == XML_ATTRIBUTE_NODE )
|
|
{
|
|
xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
|
|
xmlRemoveProp( pAttr );
|
|
pOld->invalidate(); // freed by xmlRemoveProp
|
|
xReturn.clear();
|
|
}
|
|
else
|
|
{
|
|
xmlUnlinkNode(old);
|
|
pOld->m_bUnlinked = true;
|
|
}
|
|
|
|
/*DOMNodeRemoved
|
|
* Fired when a node is being removed from its parent node.
|
|
* This event is dispatched before the node is removed from the tree.
|
|
* The target of this event is the node being removed.
|
|
* Bubbles: Yes
|
|
* Cancelable: No
|
|
* Context Info: relatedNode holds the parent node
|
|
*/
|
|
Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
|
|
Reference< XMutationEvent > event(docevent->createEvent(
|
|
OUString::createFromAscii("DOMNodeRemoved")), UNO_QUERY);
|
|
event->initMutationEvent(OUString::createFromAscii("DOMNodeRemoved"),
|
|
sal_True,
|
|
sal_False,
|
|
this,
|
|
OUString(), OUString(), OUString(), (AttrChangeType)0 );
|
|
|
|
// the following dispatch functions use only UNO interfaces
|
|
// and call event listeners, so release mutex to prevent deadlocks.
|
|
guard.clear();
|
|
|
|
dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
|
|
// subtree modified for this node
|
|
dispatchSubtreeModified();
|
|
|
|
return xReturn;
|
|
}
|
|
|
|
/**
|
|
Replaces the child node oldChild with newChild in the list of children,
|
|
and returns the oldChild node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::replaceChild(
|
|
Reference< XNode > const& xNewChild,
|
|
Reference< XNode > const& xOldChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
if (!xOldChild.is() || !xNewChild.is()) {
|
|
throw RuntimeException();
|
|
}
|
|
|
|
if (xNewChild->getOwnerDocument() != getOwnerDocument()) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
|
|
throw e;
|
|
}
|
|
if (xOldChild->getParentNode() != Reference< XNode >(this)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
::osl::ClearableMutexGuard guard(m_rMutex);
|
|
|
|
/*
|
|
Reference< XNode > aNode = removeChild(oldChild);
|
|
appendChild(newChild);
|
|
*/
|
|
::rtl::Reference<CNode> const pOldNode(
|
|
CNode::GetImplementation(xOldChild));
|
|
::rtl::Reference<CNode> const pNewNode(
|
|
CNode::GetImplementation(xNewChild));
|
|
if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
|
|
xmlNodePtr const pOld = pOldNode->GetNodePtr();
|
|
xmlNodePtr const pNew = pNewNode->GetNodePtr();
|
|
if (!pOld || !pNew) { throw RuntimeException(); }
|
|
|
|
if (pNew == m_aNodePtr) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
// already has parent
|
|
if (pNew->parent != NULL) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
if( pOld->type == XML_ATTRIBUTE_NODE )
|
|
{
|
|
// can only replace attribute with attribute
|
|
if ( pOld->type != pNew->type )
|
|
{
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
xmlAttrPtr pAttr = (xmlAttrPtr)pOld;
|
|
xmlRemoveProp( pAttr );
|
|
pOldNode->invalidate(); // freed by xmlRemoveProp
|
|
appendChild(xNewChild);
|
|
}
|
|
else
|
|
{
|
|
|
|
xmlNodePtr cur = m_aNodePtr->children;
|
|
//find old node in child list
|
|
while (cur != NULL)
|
|
{
|
|
if(cur == pOld)
|
|
{
|
|
// exchange nodes
|
|
pNew->prev = pOld->prev;
|
|
if (pNew->prev != NULL)
|
|
pNew->prev->next = pNew;
|
|
pNew->next = pOld->next;
|
|
if (pNew->next != NULL)
|
|
pNew->next->prev = pNew;
|
|
pNew->parent = pOld->parent;
|
|
if(pNew->parent->children == pOld)
|
|
pNew->parent->children = pNew;
|
|
if(pNew->parent->last == pOld)
|
|
pNew->parent->last = pNew;
|
|
pOld->next = NULL;
|
|
pOld->prev = NULL;
|
|
pOld->parent = NULL;
|
|
pOldNode->m_bUnlinked = true;
|
|
pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
guard.clear(); // release for calling event handlers
|
|
dispatchSubtreeModified();
|
|
|
|
return xOldChild;
|
|
}
|
|
|
|
void CNode::dispatchSubtreeModified()
|
|
{
|
|
// only uses UNO interfaces => needs no mutex
|
|
|
|
// dispatch DOMSubtreeModified
|
|
// target is _this_ node
|
|
Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
|
|
Reference< XMutationEvent > event(docevent->createEvent(
|
|
OUString::createFromAscii("DOMSubtreeModified")), UNO_QUERY);
|
|
event->initMutationEvent(
|
|
OUString::createFromAscii("DOMSubtreeModified"), sal_True,
|
|
sal_False, Reference< XNode >(),
|
|
OUString(), OUString(), OUString(), (AttrChangeType)0 );
|
|
dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
|
|
}
|
|
|
|
/**
|
|
The value of this node, depending on its type; see the table above.
|
|
*/
|
|
void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
// use specific node implememntation
|
|
// if we end up down here, something went wrong
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
|
|
throw e;
|
|
}
|
|
|
|
/**
|
|
The namespace prefix of this node, or null if it is unspecified.
|
|
*/
|
|
void SAL_CALL CNode::setPrefix(const OUString& prefix)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
if ((0 == m_aNodePtr) ||
|
|
((m_aNodePtr->type != XML_ELEMENT_NODE) &&
|
|
(m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
|
|
{
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
|
|
throw e;
|
|
}
|
|
OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
|
|
xmlChar *pBuf = (xmlChar*)o1.getStr();
|
|
if (m_aNodePtr != NULL && m_aNodePtr->ns != NULL)
|
|
{
|
|
xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
|
|
m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
|
|
}
|
|
|
|
}
|
|
|
|
// --- XEventTarget
|
|
void SAL_CALL CNode::addEventListener(const OUString& eventType,
|
|
const Reference< com::sun::star::xml::dom::events::XEventListener >& listener,
|
|
sal_Bool useCapture)
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
CDocument & rDocument(GetOwnerDocument());
|
|
events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
|
|
rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
|
|
}
|
|
|
|
void SAL_CALL CNode::removeEventListener(const OUString& eventType,
|
|
const Reference< com::sun::star::xml::dom::events::XEventListener >& listener,
|
|
sal_Bool useCapture)
|
|
throw (RuntimeException)
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
CDocument & rDocument(GetOwnerDocument());
|
|
events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
|
|
rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
|
|
}
|
|
|
|
sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
|
|
throw(RuntimeException, EventException)
|
|
{
|
|
CDocument * pDocument;
|
|
events::CEventDispatcher * pDispatcher;
|
|
xmlNodePtr pNode;
|
|
{
|
|
::osl::MutexGuard const g(m_rMutex);
|
|
|
|
pDocument = & GetOwnerDocument();
|
|
pDispatcher = & pDocument->GetEventDispatcher();
|
|
pNode = m_aNodePtr;
|
|
}
|
|
// this calls event listeners, do not call with locked mutex
|
|
pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
|
|
return sal_True;
|
|
}
|
|
|
|
::sal_Int64 SAL_CALL
|
|
CNode::getSomething(Sequence< ::sal_Int8 > const& rId)
|
|
throw (RuntimeException)
|
|
{
|
|
if ((rId.getLength() == 16) &&
|
|
(0 == rtl_compareMemory(UnoTunnelId::get().getConstArray(),
|
|
rId.getConstArray(), 16)))
|
|
{
|
|
return ::sal::static_int_cast< sal_Int64 >(
|
|
reinterpret_cast< sal_IntPtr >(this) );
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|