46716bcf7f
(and in the next patch, an actual CDocument, not just 0 :)
1095 lines
37 KiB
C++
1095 lines
37 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 <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 "element.hxx"
|
|
#include "text.hxx"
|
|
#include "cdatasection.hxx"
|
|
#include "entityreference.hxx"
|
|
#include "entity.hxx"
|
|
#include "processinginstruction.hxx"
|
|
#include "comment.hxx"
|
|
#include "document.hxx"
|
|
#include "documenttype.hxx"
|
|
#include "documentfragment.hxx"
|
|
#include "notation.hxx"
|
|
#include "childlist.hxx"
|
|
#include "attr.hxx"
|
|
|
|
#include "../events/eventdispatcher.hxx"
|
|
#include "../events/mutationevent.hxx"
|
|
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
|
|
namespace {
|
|
//see CNode::remove
|
|
struct NodeMutex: public ::rtl::Static<osl::Mutex, NodeMutex> {};
|
|
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;
|
|
}
|
|
|
|
typedef std::map< const xmlNodePtr,
|
|
::std::pair< WeakReference<XNode>, CNode* > > nodemap_t;
|
|
static nodemap_t g_theNodeMap;
|
|
|
|
void CNode::removeCNode(xmlNodePtr const pNode, CNode const*const pCNode)
|
|
{
|
|
::osl::MutexGuard guard(NodeMutex::get());
|
|
nodemap_t::iterator const i = g_theNodeMap.find(pNode);
|
|
if (i != g_theNodeMap.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) {
|
|
g_theNodeMap.erase(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
::rtl::Reference<CNode>
|
|
CNode::getCNode(xmlNodePtr const pNode, bool const bCreate)
|
|
{
|
|
if (0 == pNode) {
|
|
return 0;
|
|
}
|
|
//see CNode::remove
|
|
::osl::MutexGuard guard(NodeMutex::get());
|
|
//check whether there is already an instance for this node
|
|
nodemap_t::const_iterator const i = g_theNodeMap.find(pNode);
|
|
if (i != g_theNodeMap.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(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_TEXT_NODE:
|
|
// m_aNodeType = NodeType::TEXT_NODE;
|
|
pCNode = static_cast< CNode* >(new CText(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_CDATA_SECTION_NODE:
|
|
// m_aNodeType = NodeType::CDATA_SECTION_NODE;
|
|
pCNode = static_cast< CNode* >(new CCDATASection(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_ENTITY_REF_NODE:
|
|
// m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
|
|
pCNode = static_cast< CNode* >(new CEntityReference(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_ENTITY_NODE:
|
|
// m_aNodeType = NodeType::ENTITY_NODE;
|
|
pCNode = static_cast< CNode* >(new CEntity(*(CDocument*)0,
|
|
reinterpret_cast<xmlEntityPtr>(pNode)));
|
|
break;
|
|
case XML_PI_NODE:
|
|
// m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
|
|
pCNode = static_cast< CNode* >(new CProcessingInstruction(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_COMMENT_NODE:
|
|
// m_aNodeType = NodeType::COMMENT_NODE;
|
|
pCNode = static_cast< CNode* >(new CComment(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_DOCUMENT_NODE:
|
|
// m_aNodeType = NodeType::DOCUMENT_NODE;
|
|
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(*(CDocument*)0,
|
|
reinterpret_cast<xmlDtdPtr>(pNode)));
|
|
break;
|
|
case XML_DOCUMENT_FRAG_NODE:
|
|
// m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
|
|
pCNode = static_cast< CNode* >(new CDocumentFragment(*(CDocument*)0, pNode));
|
|
break;
|
|
case XML_NOTATION_NODE:
|
|
// m_aNodeType = NodeType::NOTATION_NODE;
|
|
pCNode = static_cast< CNode* >(new CNotation(*(CDocument*)0,
|
|
reinterpret_cast<xmlNotationPtr>(pNode)));
|
|
break;
|
|
case XML_ATTRIBUTE_NODE:
|
|
// m_aNodeType = NodeType::ATTRIBUTE_NODE;
|
|
pCNode = static_cast< CNode* >(new CAttr(*(CDocument*)0,
|
|
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 = g_theNodeMap.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 CNode::getCNode!");
|
|
return pCNode;
|
|
}
|
|
|
|
xmlNodePtr CNode::getNodePtr(const Reference< XNode >& aNode)
|
|
{
|
|
try {
|
|
CNode *const pNode = GetImplementation(aNode);
|
|
if( pNode )
|
|
return pNode->m_aNodePtr;
|
|
}
|
|
catch(...) {}
|
|
return 0;
|
|
}
|
|
|
|
|
|
CNode::CNode(CDocument const& rDocument,
|
|
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 )
|
|
{
|
|
OSL_ASSERT(m_aNodePtr);
|
|
}
|
|
|
|
void CNode::invalidate()
|
|
{
|
|
//remove from list if this wrapper goes away
|
|
if (m_aNodePtr != 0) {
|
|
CNode::removeCNode(m_aNodePtr, this);
|
|
}
|
|
// #i113663#: unlinked nodes will not be freed by xmlFreeDoc
|
|
if (m_bUnlinked) {
|
|
xmlFreeNode(m_aNodePtr);
|
|
}
|
|
m_aNodePtr = 0;
|
|
}
|
|
|
|
CNode::~CNode()
|
|
{
|
|
invalidate();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
::rtl::Reference< CDocument > CNode::GetOwnerDocument_Impl()
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
::rtl::Reference< CDocument > const xDoc(
|
|
dynamic_cast<CDocument *>(CNode::getCNode(
|
|
reinterpret_cast<xmlNodePtr>(m_aNodePtr->doc)).get()));
|
|
return xDoc;
|
|
}
|
|
|
|
|
|
static void _nsexchange(const xmlNodePtr aNode, xmlNsPtr oldNs, xmlNsPtr 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;
|
|
}
|
|
_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.
|
|
_nsexchange(cur, curDef, ns);
|
|
*refp = curDef->next;
|
|
xmlFreeNs(curDef);
|
|
curDef = *refp;
|
|
} else {
|
|
refp = &(curDef->next);
|
|
curDef = curDef->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
void SAL_CALL CNode::saxify(
|
|
const Reference< XDocumentHandler >& i_xHandler) {
|
|
if (!i_xHandler.is()) throw RuntimeException();
|
|
// default: do nothing
|
|
}
|
|
|
|
void SAL_CALL CNode::fastSaxify(Context& io_rContext) {
|
|
if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
|
|
// default: do nothing
|
|
}
|
|
|
|
/**
|
|
Adds the node newChild to the end of the list of children of this node.
|
|
*/
|
|
Reference< XNode > CNode::appendChild(const Reference< XNode >& newChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
::rtl::Reference<CNode> pNode;
|
|
if (m_aNodePtr != NULL) {
|
|
xmlNodePtr cur = CNode::getNodePtr(newChild.get());
|
|
|
|
// 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;
|
|
}
|
|
// already has parant and is not attribute
|
|
if (cur->parent != NULL && cur->type != XML_ATTRIBUTE_NODE) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
// check whether this is an attribute node so we remove it's
|
|
// carrier node if it has one
|
|
xmlNodePtr res = NULL;
|
|
if (cur->type == XML_ATTRIBUTE_NODE)
|
|
{
|
|
if (cur->parent != NULL)
|
|
{
|
|
if (m_aNodePtr->type != XML_ELEMENT_NODE ||
|
|
strcmp((char*)cur->parent->name, "__private") != 0)
|
|
{
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
xmlNsPtr pAttrNs = cur->ns;
|
|
xmlNsPtr pParentNs = xmlSearchNs(m_aNodePtr->doc, m_aNodePtr, pAttrNs->prefix);
|
|
if (pParentNs == NULL || strcmp((char*)pParentNs->href, (char*)pAttrNs->href) != 0)
|
|
pParentNs = xmlNewNs(m_aNodePtr, pAttrNs->href, pAttrNs->prefix);
|
|
|
|
if (cur->children != NULL)
|
|
res = (xmlNodePtr)xmlNewNsProp(m_aNodePtr, pParentNs, cur->name, cur->children->content);
|
|
else
|
|
res = (xmlNodePtr)xmlNewProp(m_aNodePtr, cur->name, (xmlChar*) "");
|
|
|
|
xmlFreeNode(cur->parent);
|
|
cur->parent = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (cur->children != NULL)
|
|
res = (xmlNodePtr)xmlNewProp(m_aNodePtr, cur->name, cur->children->content);
|
|
else
|
|
res = (xmlNodePtr)xmlNewProp(m_aNodePtr, cur->name, (xmlChar*) "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = xmlAddChild(m_aNodePtr, cur);
|
|
}
|
|
|
|
// libxml can do optimizations, when appending nodes.
|
|
// if res != cur, something was optimized and the newchild-wrapper
|
|
// should be updated
|
|
if (res && (cur != res)) {
|
|
::rtl::Reference<CNode> pCNode(CNode::getCNode(cur));
|
|
OSL_ASSERT(pCNode.is());
|
|
if (pCNode.is()) {
|
|
pCNode->invalidate(); // cur has been freed
|
|
}
|
|
}
|
|
|
|
// use custom ns cleanup instaead of
|
|
// xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
|
|
// because that will not remove unneeded ns decls
|
|
_nscleanup(res, m_aNodePtr);
|
|
|
|
pNode = CNode::getCNode(res);
|
|
}
|
|
//XXX check for errors
|
|
|
|
// dispatch DOMNodeInserted event, target is the new node
|
|
// this node is the related node
|
|
// does bubble
|
|
if (pNode.is())
|
|
{
|
|
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,
|
|
Reference< XNode >(CNode::getCNode(m_aNodePtr).get()),
|
|
OUString(), OUString(), OUString(), (AttrChangeType)0 );
|
|
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 > CNode::cloneNode(sal_Bool bDeep)
|
|
throw (RuntimeException)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
::rtl::Reference<CNode> const pNode = CNode::getCNode(
|
|
xmlCopyNode(m_aNodePtr, (bDeep) ? 1 : 0));
|
|
pNode->m_bUnlinked = true; // not linked yet
|
|
//XXX check for errors
|
|
return pNode.get();
|
|
}
|
|
|
|
/**
|
|
A NamedNodeMap containing the attributes of this node (if it is an Element)
|
|
or null otherwise.
|
|
*/
|
|
Reference< XNamedNodeMap > CNode::getAttributes()
|
|
throw (RuntimeException)
|
|
{
|
|
// return empty reference
|
|
// only element node may override this impl
|
|
return Reference< XNamedNodeMap>();
|
|
|
|
// get all children that are attributes
|
|
/* --> CElement
|
|
Reference< NamedNodeMap > aNodeMap(new AttributeNamedNodeMap(m_aNodePtr), UNO_QUERY);
|
|
return aNodeMap;
|
|
*/
|
|
}
|
|
|
|
/**
|
|
A NodeList that contains all children of this node.
|
|
*/
|
|
Reference< XNodeList > CNode::getChildNodes()
|
|
throw (RuntimeException)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNodeList > const xNodeList(new CChildList(this));
|
|
return xNodeList;
|
|
}
|
|
|
|
/**
|
|
The first child of this node.
|
|
*/
|
|
Reference< XNode > CNode::getFirstChild()
|
|
throw (RuntimeException)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
CNode::getCNode(m_aNodePtr->children).get());
|
|
return xNode;
|
|
}
|
|
|
|
/**
|
|
The last child of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getLastChild()
|
|
throw (RuntimeException)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
CNode::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)
|
|
{
|
|
OUString aName;
|
|
/*
|
|
--> Element / Attribute
|
|
if(m_aNodePtr != NULL && (m_aNodeType == NodeType::ATTRIBUTE_NODE
|
|
|| m_aNodeType == NodeType::ELEMENT_NODE))
|
|
{
|
|
aName = OUString(m_aNodePtr->name, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
//XXX error checking
|
|
*/
|
|
return aName;
|
|
}
|
|
|
|
|
|
/**
|
|
The namespace URI of this node, or null if it is unspecified.
|
|
*/
|
|
OUString SAL_CALL CNode::getNamespaceURI()
|
|
throw (RuntimeException)
|
|
{
|
|
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)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(CNode::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)
|
|
{
|
|
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)
|
|
{
|
|
Reference< XDocument > const xDoc(GetOwnerDocument_Impl().get());
|
|
return xDoc;
|
|
}
|
|
|
|
/**
|
|
The parent of this node.
|
|
*/
|
|
Reference< XNode > SAL_CALL CNode::getParentNode()
|
|
throw (RuntimeException)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
CNode::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)
|
|
{
|
|
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)
|
|
{
|
|
if (0 == m_aNodePtr) {
|
|
return 0;
|
|
}
|
|
Reference< XNode > const xNode(
|
|
CNode::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)
|
|
{
|
|
return (m_aNodePtr != NULL && m_aNodePtr->properties != NULL);
|
|
}
|
|
|
|
/**
|
|
Returns whether this node has any children.
|
|
*/
|
|
sal_Bool SAL_CALL CNode::hasChildNodes()
|
|
throw (RuntimeException)
|
|
{
|
|
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->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;
|
|
}
|
|
|
|
xmlNodePtr pRefChild = CNode::getNodePtr(refChild.get());
|
|
xmlNodePtr pNewChild = CNode::getNodePtr(newChild.get());
|
|
xmlNodePtr cur = m_aNodePtr->children;
|
|
|
|
//search cild 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;
|
|
::rtl::Reference<CNode> const pNode(CNode::getCNode(pNewChild));
|
|
pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
|
|
}
|
|
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 >& oldChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
if (!oldChild.is()) {
|
|
throw RuntimeException();
|
|
}
|
|
|
|
if (oldChild->getParentNode() != Reference< XNode >(this)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
Reference<XNode> xReturn( oldChild );
|
|
|
|
xmlNodePtr old = CNode::getNodePtr(oldChild);
|
|
::rtl::Reference<CNode> const pOld( CNode::getCNode(old) );
|
|
pOld->m_bUnlinked = true;
|
|
|
|
if( old->type == XML_ATTRIBUTE_NODE )
|
|
{
|
|
xmlAttrPtr pAttr = (xmlAttrPtr) old;
|
|
xmlRemoveProp( pAttr );
|
|
pOld->invalidate(); // freed by xmlRemoveProp
|
|
xReturn.clear();
|
|
}
|
|
else
|
|
{
|
|
xmlUnlinkNode(old);
|
|
}
|
|
|
|
/*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
|
|
*/
|
|
if (oldChild.is())
|
|
{
|
|
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,
|
|
Reference< XNode >(CNode::getCNode(m_aNodePtr).get()),
|
|
OUString(), OUString(), OUString(), (AttrChangeType)0 );
|
|
dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
|
|
|
|
// subtree modofied 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(
|
|
const Reference< XNode >& newChild, const Reference< XNode >& oldChild)
|
|
throw (RuntimeException, DOMException)
|
|
{
|
|
if (!oldChild.is()) {
|
|
throw RuntimeException();
|
|
}
|
|
// XXX check node types
|
|
|
|
if (oldChild->getParentNode() != Reference< XNode >(this)) {
|
|
DOMException e;
|
|
e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
|
|
throw e;
|
|
}
|
|
|
|
/*
|
|
Reference< XNode > aNode = removeChild(oldChild);
|
|
appendChild(newChild);
|
|
*/
|
|
xmlNodePtr pOld = CNode::getNodePtr(oldChild);
|
|
xmlNodePtr pNew = CNode::getNodePtr(newChild);
|
|
|
|
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 );
|
|
::rtl::Reference<CNode> const pOldNode( CNode::getCNode(pOld) );
|
|
pOldNode->invalidate(); // freed by xmlRemoveProp
|
|
appendChild( newChild );
|
|
}
|
|
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;
|
|
::rtl::Reference<CNode> const pOldNode(CNode::getCNode(pOld));
|
|
pOldNode->m_bUnlinked = true;
|
|
::rtl::Reference<CNode> const pNewNode(CNode::getCNode(pNew));
|
|
pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
|
|
dispatchSubtreeModified();
|
|
|
|
return oldChild;
|
|
}
|
|
|
|
void CNode::dispatchSubtreeModified()
|
|
{
|
|
// 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)
|
|
{
|
|
OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
|
|
xmlChar *pBuf = (xmlChar*)o1.getStr();
|
|
// XXX copy buf?
|
|
// XXX free old string? (leak?)
|
|
if (m_aNodePtr != NULL && m_aNodePtr->ns != NULL)
|
|
{
|
|
m_aNodePtr->ns->prefix = 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)
|
|
{
|
|
events::CEventDispatcher & rDispatcher(
|
|
m_xDocument->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)
|
|
{
|
|
events::CEventDispatcher & rDispatcher(
|
|
m_xDocument->GetEventDispatcher());
|
|
rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
|
|
}
|
|
|
|
sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
|
|
throw(RuntimeException, EventException)
|
|
{
|
|
events::CEventDispatcher & rDispatcher(
|
|
m_xDocument->GetEventDispatcher());
|
|
rDispatcher.dispatchEvent(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;
|
|
}
|
|
}
|
|
|