diff options
Diffstat (limited to 'unoxml/source')
60 files changed, 14299 insertions, 0 deletions
diff --git a/unoxml/source/dom/attr.cxx b/unoxml/source/dom/attr.cxx new file mode 100644 index 000000000..4988aa421 --- /dev/null +++ b/unoxml/source/dom/attr.cxx @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "attr.hxx" + +#include <string.h> + +#include <memory> + +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <com/sun/star/xml/dom/events/XMutationEvent.hpp> + +#include "document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; + +namespace DOM +{ + CAttr::CAttr(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlAttrPtr const pAttr) + : CAttr_Base(rDocument, rMutex, + NodeType_ATTRIBUTE_NODE, reinterpret_cast<xmlNodePtr>(pAttr)) + , m_aAttrPtr(pAttr) + { + } + + xmlNsPtr CAttr::GetNamespace(xmlNodePtr const pNode) + { + if (!m_pNamespace) + { + return nullptr; + } + xmlChar const*const pUri(reinterpret_cast<xmlChar const*>( + m_pNamespace->first.getStr())); + xmlChar const*const pPrefix(reinterpret_cast<xmlChar const*>( + m_pNamespace->second.getStr())); + xmlNsPtr pNs = xmlSearchNs(pNode->doc, pNode, pPrefix); + if (pNs && (0 != xmlStrcmp(pNs->href, pUri))) { + return pNs; + } + pNs = xmlNewNs(pNode, pUri, pPrefix); + if (pNs) { + return pNs; + } + pNs = xmlSearchNsByHref(pNode->doc, pNode, pUri); + // if (!pNs) hmm... now what? throw? + if (!pNs) { + SAL_WARN("unoxml", "CAttr: cannot create namespace"); + } + return pNs; + } + + bool CAttr::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) + { + switch (nodeType) { + case NodeType_TEXT_NODE: + case NodeType_ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } + } + + OUString SAL_CALL CAttr::getNodeName() + { + return getName(); + } + OUString SAL_CALL CAttr::getNodeValue() + { + return getValue(); + } + OUString SAL_CALL CAttr::getLocalName() + { + return getName(); + } + + + /** + Returns the name of this attribute. + */ + OUString SAL_CALL CAttr::getName() + { + ::osl::MutexGuard const g(m_rMutex); + + if ((nullptr == m_aNodePtr) || (nullptr == m_aAttrPtr)) { + return OUString(); + } + OUString const aName(reinterpret_cast<char const *>(m_aAttrPtr->name), + strlen(reinterpret_cast<char const *>(m_aAttrPtr->name)), RTL_TEXTENCODING_UTF8); + return aName; + } + + /** + The Element node this attribute is attached to or null if this + attribute is not in use. + */ + Reference< XElement > SAL_CALL CAttr::getOwnerElement() + { + ::osl::MutexGuard const g(m_rMutex); + + if ((nullptr == m_aNodePtr) || (nullptr == m_aAttrPtr)) { + return nullptr; + } + if (nullptr == m_aAttrPtr->parent) { + return nullptr; + } + Reference< XElement > const xRet( + static_cast< XNode* >(GetOwnerDocument().GetCNode( + m_aAttrPtr->parent).get()), + UNO_QUERY_THROW); + return xRet; + } + + /** + If this attribute was explicitly given a value in the original + document, this is true; otherwise, it is false. + */ + sal_Bool SAL_CALL CAttr::getSpecified() + { + // FIXME if this DOM implementation supported DTDs it would need + // to check that this attribute is not default or something + return true; + } + + /** + On retrieval, the value of the attribute is returned as a string. + */ + OUString SAL_CALL CAttr::getValue() + { + ::osl::MutexGuard const g(m_rMutex); + + if ((nullptr == m_aNodePtr) || (nullptr == m_aAttrPtr)) { + return OUString(); + } + if (nullptr == m_aAttrPtr->children) { + return OUString(); + } + char const*const pContent(reinterpret_cast<char const*>(m_aAttrPtr->children->content)); + return OUString(pContent, strlen(pContent), RTL_TEXTENCODING_UTF8); + } + + /** + Sets the value of the attribute from a string. + */ + void SAL_CALL CAttr::setValue(const OUString& value) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if ((nullptr == m_aNodePtr) || (nullptr == m_aAttrPtr)) { + return; + } + + // remember old value (for mutation event) + OUString sOldValue = getValue(); + + OString o1 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); + xmlChar const * pValue = reinterpret_cast<xmlChar const *>(o1.getStr()); + // this does not work if the attribute was created anew + // xmlNodePtr pNode = m_aAttrPtr->parent; + // xmlSetProp(pNode, m_aAttrPtr->name, pValue); + std::shared_ptr<xmlChar const> const buffer( + xmlEncodeEntitiesReentrant(m_aAttrPtr->doc, pValue), xmlFree); + xmlFreeNodeList(m_aAttrPtr->children); + m_aAttrPtr->children = + xmlStringGetNodeList(m_aAttrPtr->doc, buffer.get()); + xmlNodePtr tmp = m_aAttrPtr->children; + while (tmp != nullptr) { + tmp->parent = m_aNodePtr; + tmp->doc = m_aAttrPtr->doc; + if (tmp->next == nullptr) + m_aNodePtr->last = tmp; + tmp = tmp->next; + } + + // dispatch DOM events to signal change in attribute value + // dispatch DomAttrModified + DOMSubtreeModified + OUString sEventName( "DOMAttrModified" ); + Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); + Reference< XMutationEvent > event(docevent->createEvent(sEventName),UNO_QUERY); + event->initMutationEvent( + sEventName, true, false, + Reference<XNode>( static_cast<XAttr*>( this ) ), + sOldValue, value, getName(), AttrChangeType_MODIFICATION ); + + guard.clear(); // release mutex before calling event handlers + + dispatchEvent(event); + dispatchSubtreeModified(); + } + + void SAL_CALL CAttr::setPrefix(const OUString& prefix) + { + ::osl::MutexGuard const g(m_rMutex); + + if (!m_aNodePtr) { return; } + + if (m_pNamespace) + { + OSL_ASSERT(!m_aNodePtr->parent); + m_pNamespace->second = + OUStringToOString(prefix, RTL_TEXTENCODING_UTF8); + } + else + { + CNode::setPrefix(prefix); + } + } + + OUString SAL_CALL CAttr::getPrefix() + { + ::osl::MutexGuard const g(m_rMutex); + + if (!m_aNodePtr) { return OUString(); } + + if (m_pNamespace) + { + OSL_ASSERT(!m_aNodePtr->parent); + OUString const ret(OStringToOUString( + m_pNamespace->second, RTL_TEXTENCODING_UTF8)); + return ret; + } + else + { + return CNode::getPrefix(); + } + } + + OUString SAL_CALL CAttr::getNamespaceURI() + { + ::osl::MutexGuard const g(m_rMutex); + + if (!m_aNodePtr) { return OUString(); } + + if (m_pNamespace) + { + OSL_ASSERT(!m_aNodePtr->parent); + OUString const ret(OStringToOUString( + m_pNamespace->first, RTL_TEXTENCODING_UTF8)); + return ret; + } + else + { + return CNode::getNamespaceURI(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/attr.hxx b/unoxml/source/dom/attr.hxx new file mode 100644 index 000000000..f30b25896 --- /dev/null +++ b/unoxml/source/dom/attr.hxx @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <memory> + +#include <libxml/tree.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XAttr.hpp> + +#include <node.hxx> + +namespace DOM +{ + typedef ::std::pair< OString, OString > stringpair_t; + + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XAttr > CAttr_Base; + + class CAttr + : public CAttr_Base + { + friend class CDocument; + + xmlAttrPtr m_aAttrPtr; + ::std::unique_ptr< stringpair_t > m_pNamespace; + + CAttr(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlAttrPtr const pAttr); + + public: + /// return the libxml namespace corresponding to m_pNamespace on pNode + xmlNsPtr GetNamespace(xmlNodePtr const pNode); + + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const*) override; + + /** + Returns the name of this attribute. + */ + virtual OUString SAL_CALL getName() override; + + /** + The Element node this attribute is attached to or null if this + attribute is not in use. + */ + virtual css::uno::Reference< css::xml::dom::XElement > SAL_CALL getOwnerElement() override; + + /** + If this attribute was explicitly given a value in the original + document, this is true; otherwise, it is false. + */ + virtual sal_Bool SAL_CALL getSpecified() override; + + /** + On retrieval, the value of the attribute is returned as a string. + */ + virtual OUString SAL_CALL getValue() override; + + /** + Sets the value of the attribute from a string. + */ + + virtual void SAL_CALL setValue(const OUString& value) override; + + // resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + virtual OUString SAL_CALL getLocalName() override; + + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getNamespaceURI() override; + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override; + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return setValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override; + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/attributesmap.cxx b/unoxml/source/dom/attributesmap.cxx new file mode 100644 index 000000000..575fa96c2 --- /dev/null +++ b/unoxml/source/dom/attributesmap.cxx @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "attributesmap.hxx" + +#include <string.h> + +#include <com/sun/star/xml/dom/DOMException.hpp> + +#include "element.hxx" +#include "document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CAttributesMap::CAttributesMap(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex) + : m_pElement(pElement) + , m_rMutex(rMutex) + { + } + + /** + The number of nodes in this map. + */ + sal_Int32 SAL_CALL CAttributesMap::getLength() + { + ::osl::MutexGuard const g(m_rMutex); + + sal_Int32 count = 0; + xmlNodePtr pNode = m_pElement->GetNodePtr(); + if (pNode != nullptr) + { + xmlAttrPtr cur = pNode->properties; + while (cur != nullptr) + { + count++; + cur = cur->next; + } + } + return count; + } + + /** + Retrieves a node specified by local name + */ + Reference< XNode > SAL_CALL + CAttributesMap::getNamedItem(OUString const& name) + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNode > aNode; + xmlNodePtr pNode = m_pElement->GetNodePtr(); + if (pNode != nullptr) + { + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const * pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlAttrPtr cur = pNode->properties; + while (cur != nullptr) + { + if( strcmp(reinterpret_cast<char const *>(pName), reinterpret_cast<char const *>(cur->name)) == 0) + { + aNode = m_pElement->GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(cur)); + break; + } + cur = cur->next; + } + } + return aNode; + } + + /** + Retrieves a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CAttributesMap::getNamedItemNS( + OUString const& namespaceURI, OUString const& localName) + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNode > aNode; + xmlNodePtr pNode = m_pElement->GetNodePtr(); + if (pNode != nullptr) + { + OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); + xmlChar const * pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + xmlChar const* pSearchNs = + reinterpret_cast<xmlChar const*>(o2.getStr()); + xmlNsPtr const pNs = xmlSearchNsByHref(pNode->doc, pNode, pSearchNs); + xmlAttrPtr cur = pNode->properties; + while (cur != nullptr && pNs != nullptr) + { + if( strcmp(reinterpret_cast<char const *>(pName), reinterpret_cast<char const *>(cur->name)) == 0 && + cur->ns == pNs) + { + aNode = m_pElement->GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(cur)); + break; + } + cur = cur->next; + } + } + return aNode; + } + + /** + Returns the indexth item in the map. + */ + Reference< XNode > SAL_CALL + CAttributesMap::item(sal_Int32 index) + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNode > aNode; + xmlNodePtr pNode = m_pElement->GetNodePtr(); + if (pNode != nullptr) + { + xmlAttrPtr cur = pNode->properties; + sal_Int32 count = 0; + while (cur != nullptr) + { + if (count == index) + { + aNode = m_pElement->GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(cur)); + break; + } + count++; + cur = cur->next; + } + } + return aNode; + } + + /** + Removes a node specified by name. + */ + Reference< XNode > SAL_CALL + CAttributesMap::removeNamedItem(OUString const& name) + { + // no MutexGuard needed: m_pElement is const + Reference< XAttr > const xAttr(m_pElement->getAttributeNode(name)); + if (!xAttr.is()) { + throw DOMException( + "CAttributesMap::removeNamedItem: no such attribute", + static_cast<OWeakObject*>(this), + DOMExceptionType_NOT_FOUND_ERR); + } + return m_pElement->removeAttributeNode(xAttr); + } + + /** + // Removes a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CAttributesMap::removeNamedItemNS( + OUString const& namespaceURI, OUString const& localName) + { + // no MutexGuard needed: m_pElement is const + Reference< XAttr > const xAttr( + m_pElement->getAttributeNodeNS(namespaceURI, localName)); + if (!xAttr.is()) { + throw DOMException( + "CAttributesMap::removeNamedItemNS: no such attribute", + static_cast<OWeakObject*>(this), + DOMExceptionType_NOT_FOUND_ERR); + } + return m_pElement->removeAttributeNode(xAttr); + } + + /** + // Adds a node using its nodeName attribute. + */ + Reference< XNode > SAL_CALL + CAttributesMap::setNamedItem(Reference< XNode > const& xNode) + { + Reference< XAttr > const xAttr(xNode, UNO_QUERY); + if (!xNode.is()) { + throw DOMException( + "CAttributesMap::setNamedItem: XAttr argument expected", + static_cast<OWeakObject*>(this), + DOMExceptionType_HIERARCHY_REQUEST_ERR); + } + // no MutexGuard needed: m_pElement is const + return m_pElement->setAttributeNode(xAttr); + } + + /** + Adds a node using its namespaceURI and localName. + */ + Reference< XNode > SAL_CALL + CAttributesMap::setNamedItemNS(Reference< XNode > const& xNode) + { + Reference< XAttr > const xAttr(xNode, UNO_QUERY); + if (!xNode.is()) { + throw DOMException( + "CAttributesMap::setNamedItemNS: XAttr argument expected", + static_cast<OWeakObject*>(this), + DOMExceptionType_HIERARCHY_REQUEST_ERR); + } + // no MutexGuard needed: m_pElement is const + return m_pElement->setAttributeNodeNS(xAttr); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/attributesmap.hxx b/unoxml/source/dom/attributesmap.hxx new file mode 100644 index 000000000..55f4c6795 --- /dev/null +++ b/unoxml/source/dom/attributesmap.hxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ref.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> + +#include <cppuhelper/implbase.hxx> + +#include "element.hxx" + +namespace DOM +{ + class CElement; + + class CAttributesMap + : public cppu::WeakImplHelper< css::xml::dom::XNamedNodeMap > + { + private: + ::rtl::Reference<CElement> const m_pElement; + ::osl::Mutex & m_rMutex; + + public: + CAttributesMap(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex); + + /** + The number of nodes in this map. + */ + virtual sal_Int32 SAL_CALL getLength() override; + + /** + Retrieves a node specified by local name + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNamedItem(OUString const& name) override; + + /** + Retrieves a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + Returns the indexth item in the map. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL item(sal_Int32 index) override; + + /** + Removes a node specified by name. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + removeNamedItem(OUString const& name) override; + + /** + // Removes a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + // Adds a node using its nodeName attribute. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItem(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + + /** + Adds a node using its namespaceURI and localName. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItemNS(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/cdatasection.cxx b/unoxml/source/dom/cdatasection.cxx new file mode 100644 index 000000000..47dc77389 --- /dev/null +++ b/unoxml/source/dom/cdatasection.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "cdatasection.hxx" + +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; + +namespace DOM +{ + CCDATASection::CCDATASection( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CCDATASection_Base(rDocument, rMutex, + NodeType_CDATA_SECTION_NODE, pNode) + { + } + + void CCDATASection::saxify(const Reference< XDocumentHandler >& i_xHandler) + { + if (!i_xHandler.is()) throw RuntimeException(); + Reference< XExtendedDocumentHandler > xExtended(i_xHandler, UNO_QUERY); + if (xExtended.is()) { + xExtended->startCDATA(); + i_xHandler->characters(getData()); + xExtended->endCDATA(); + } + } + + OUString SAL_CALL CCDATASection::getNodeName() + { + return "#cdata-section"; + } + + OUString SAL_CALL CCDATASection::getNodeValue() + { + return CCharacterData::getData(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/cdatasection.hxx b/unoxml/source/dom/cdatasection.hxx new file mode 100644 index 000000000..0cd181ce6 --- /dev/null +++ b/unoxml/source/dom/cdatasection.hxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XCDATASection.hpp> + +#include <cppuhelper/implbase.hxx> +#include "text.hxx" + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CText, css::xml::dom::XCDATASection > + CCDATASection_Base; + + class CCDATASection + : public CCDATASection_Base + { + friend class CDocument; + + CCDATASection(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + virtual css::uno::Reference< css::xml::dom::XText > SAL_CALL splitText(sal_Int32 offset) override + { + return CText::splitText(offset); + } + + // --- delegations for XCharacterData + virtual void SAL_CALL appendData(const OUString& arg) override + { + CCharacterData::appendData(arg); + } + virtual void SAL_CALL deleteData(sal_Int32 offset, sal_Int32 count) override + { + CCharacterData::deleteData(offset, count); + } + virtual OUString SAL_CALL getData() override + { + return CCharacterData::getData(); + } + virtual sal_Int32 SAL_CALL getLength() override + { + return CCharacterData::getLength(); + } + virtual void SAL_CALL insertData(sal_Int32 offset, const OUString& arg) override + { + CCharacterData::insertData(offset, arg); + } + virtual void SAL_CALL replaceData(sal_Int32 offset, sal_Int32 count, const OUString& arg) override + { + CCharacterData::replaceData(offset, count, arg); + } + virtual void SAL_CALL setData(const OUString& data) override + { + CCharacterData::setData(data); + } + virtual OUString SAL_CALL subStringData(sal_Int32 offset, sal_Int32 count) override + { + return CCharacterData::subStringData(offset, count); + } + + + // --- overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CText::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/characterdata.cxx b/unoxml/source/dom/characterdata.cxx new file mode 100644 index 000000000..d46ff5d7c --- /dev/null +++ b/unoxml/source/dom/characterdata.cxx @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "characterdata.hxx" + +#include <string.h> + +#include <memory> + +#include <osl/diagnose.h> + +#include <com/sun/star/xml/dom/DOMException.hpp> +#include <com/sun/star/xml/dom/events/XDocumentEvent.hpp> +#include <com/sun/star/xml/dom/events/XMutationEvent.hpp> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; + +namespace DOM +{ + + CCharacterData::CCharacterData( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + NodeType const& reNodeType, xmlNodePtr const& rpNode) + : CCharacterData_Base(rDocument, rMutex, reNodeType, rpNode) + { + } + + void CCharacterData::dispatchEvent_Impl( + OUString const& prevValue, OUString const& newValue) + { + Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); + Reference< XMutationEvent > event(docevent->createEvent( + "DOMCharacterDataModified"), UNO_QUERY); + event->initMutationEvent( + "DOMCharacterDataModified", + true, false, Reference< XNode >(), + prevValue, newValue, OUString(), AttrChangeType(0) ); + dispatchEvent(event); + dispatchSubtreeModified(); + } + + /** + Append the string to the end of the character data of the node. + */ + void SAL_CALL CCharacterData::appendData(const OUString& arg) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (m_aNodePtr != nullptr) + { + OUString oldValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + xmlNodeAddContent(m_aNodePtr, reinterpret_cast<const xmlChar*>(OUStringToOString(arg, RTL_TEXTENCODING_UTF8).getStr())); + OUString newValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent_Impl(oldValue, newValue); + } + } + + /** + Remove a range of 16-bit units from the node. + */ + void SAL_CALL CCharacterData::deleteData(sal_Int32 offset, sal_Int32 count) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (m_aNodePtr == nullptr) + return; + + // get current data + std::shared_ptr<xmlChar const> const pContent( + xmlNodeGetContent(m_aNodePtr), xmlFree); + OString aData(reinterpret_cast<char const*>(pContent.get())); + OUString tmp(OStringToOUString(aData, RTL_TEXTENCODING_UTF8)); + if (offset > tmp.getLength() || offset < 0 || count < 0) { + DOMException e; + e.Code = DOMExceptionType_INDEX_SIZE_ERR; + throw e; + } + if ((offset+count) > tmp.getLength()) + count = tmp.getLength() - offset; + + OUString tmp2 = OUString::Concat(tmp.subView(0, offset)) + tmp.subView(offset+count); + OUString oldValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + xmlNodeSetContent(m_aNodePtr, reinterpret_cast<const xmlChar*>(OUStringToOString(tmp2, RTL_TEXTENCODING_UTF8).getStr())); + OUString newValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent_Impl(oldValue, newValue); + + } + + + /** + Return the character data of the node that implements this interface. + */ + OUString SAL_CALL CCharacterData::getData() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aData; + if (m_aNodePtr != nullptr) + { + OSL_ENSURE(m_aNodePtr->content, "character data node with NULL content, please inform lars.oppermann@sun.com!"); + if (m_aNodePtr->content != nullptr) + { + aData = OUString(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + } + } + return aData; + } + + /** + The number of 16-bit units that are available through data and the + substringData method below. + */ + sal_Int32 SAL_CALL CCharacterData::getLength() + { + ::osl::MutexGuard const g(m_rMutex); + + sal_Int32 length = 0; + if (m_aNodePtr != nullptr) + { + OUString aData(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + length = aData.getLength(); + } + return length; + } + + /** + Insert a string at the specified 16-bit unit offset. + */ + void SAL_CALL CCharacterData::insertData(sal_Int32 offset, const OUString& arg) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (m_aNodePtr == nullptr) + return; + + // get current data + std::shared_ptr<xmlChar const> const pContent( + xmlNodeGetContent(m_aNodePtr), xmlFree); + OString aData(reinterpret_cast<char const*>(pContent.get())); + OUString tmp(OStringToOUString(aData, RTL_TEXTENCODING_UTF8)); + if (offset > tmp.getLength() || offset < 0) { + DOMException e; + e.Code = DOMExceptionType_INDEX_SIZE_ERR; + throw e; + } + + OUString tmp2 = tmp.subView(0, offset) + + arg + + tmp.subView(offset); + OUString oldValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + xmlNodeSetContent(m_aNodePtr, reinterpret_cast<const xmlChar*>(OUStringToOString(tmp2, RTL_TEXTENCODING_UTF8).getStr())); + OUString newValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent_Impl(oldValue, newValue); + + } + + + /** + Replace the characters starting at the specified 16-bit unit offset + with the specified string. + */ + void SAL_CALL CCharacterData::replaceData(sal_Int32 offset, sal_Int32 count, const OUString& arg) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (m_aNodePtr == nullptr) + return; + + // get current data + std::shared_ptr<xmlChar const> const pContent( + xmlNodeGetContent(m_aNodePtr), xmlFree); + OString aData(reinterpret_cast<char const*>(pContent.get())); + OUString tmp(OStringToOUString(aData, RTL_TEXTENCODING_UTF8)); + if (offset > tmp.getLength() || offset < 0 || count < 0){ + DOMException e; + e.Code = DOMExceptionType_INDEX_SIZE_ERR; + throw e; + } + if ((offset+count) > tmp.getLength()) + count = tmp.getLength() - offset; + + OUString tmp2 = tmp.subView(0, offset) + + arg + + tmp.subView(offset+count); + OUString oldValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + xmlNodeSetContent(m_aNodePtr, reinterpret_cast<const xmlChar*>(OUStringToOString(tmp2, RTL_TEXTENCODING_UTF8).getStr())); + OUString newValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent_Impl(oldValue, newValue); + + } + + /** + Set the character data of the node that implements this interface. + */ + void SAL_CALL CCharacterData::setData(const OUString& data) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (m_aNodePtr != nullptr) + { + OUString oldValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + xmlNodeSetContent(m_aNodePtr, reinterpret_cast<const xmlChar*>(OUStringToOString(data, RTL_TEXTENCODING_UTF8).getStr())); + OUString newValue(reinterpret_cast<char*>(m_aNodePtr->content), strlen(reinterpret_cast<char*>(m_aNodePtr->content)), RTL_TEXTENCODING_UTF8); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent_Impl(oldValue, newValue); + } + } + + /** + Extracts a range of data from the node. + */ + OUString SAL_CALL CCharacterData::subStringData(sal_Int32 offset, sal_Int32 count) + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aStr; + if (m_aNodePtr != nullptr) + { + // get current data + std::shared_ptr<xmlChar const> const pContent( + xmlNodeGetContent(m_aNodePtr), xmlFree); + OString aData(reinterpret_cast<char const*>(pContent.get())); + OUString tmp(OStringToOUString(aData, RTL_TEXTENCODING_UTF8)); + if (offset > tmp.getLength() || offset < 0 || count < 0) { + DOMException e; + e.Code = DOMExceptionType_INDEX_SIZE_ERR; + throw e; + } + aStr = tmp.copy(offset, count); + } + return aStr; + } + + +} // namespace DOM + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/characterdata.hxx b/unoxml/source/dom/characterdata.hxx new file mode 100644 index 000000000..7728f65ef --- /dev/null +++ b/unoxml/source/dom/characterdata.hxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <sal/types.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XCharacterData.hpp> + +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XCharacterData > + CCharacterData_Base; + + class CCharacterData + : public CCharacterData_Base + { + + protected: + CCharacterData(CDocument const& rDocument, ::osl::Mutex const& rMutex, + css::xml::dom::NodeType const& reNodeType, xmlNodePtr const& rpNode); + + void dispatchEvent_Impl( + OUString const& prevValue, OUString const& newValue); + + public: + /** + Append the string to the end of the character data of the node. + */ + virtual void SAL_CALL appendData(const OUString& arg) override; + + /** + Remove a range of 16-bit units from the node. + */ + virtual void SAL_CALL deleteData(sal_Int32 offset, sal_Int32 count) override; + + /** + Return the character data of the node that implements this interface. + */ + virtual OUString SAL_CALL getData() override; + + /** + The number of 16-bit units that are available through data and the + substringData method below. + */ + virtual sal_Int32 SAL_CALL getLength() override; + + /** + Insert a string at the specified 16-bit unit offset. + */ + virtual void SAL_CALL insertData(sal_Int32 offset, const OUString& arg) override; + + /** + Replace the characters starting at the specified 16-bit unit offset + with the specified string. + */ + virtual void SAL_CALL replaceData(sal_Int32 offset, sal_Int32 count, const OUString& arg) override; + + /** + Set the character data of the node that implements this interface. + */ + virtual void SAL_CALL setData(const OUString& data) override; + + /** + Extracts a range of data from the node. + */ + virtual OUString SAL_CALL subStringData(sal_Int32 offset, sal_Int32 count) override; + + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual OUString SAL_CALL getNodeName() override + { + return CNode::getNodeName(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual OUString SAL_CALL getNodeValue() override + { + return getData(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return setData(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/childlist.cxx b/unoxml/source/dom/childlist.cxx new file mode 100644 index 000000000..6f62f0144 --- /dev/null +++ b/unoxml/source/dom/childlist.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "childlist.hxx" + +#include <libxml/tree.h> + +#include <node.hxx> +#include "document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CChildList::CChildList(::rtl::Reference<CNode> const& pBase, + ::osl::Mutex & rMutex) + : m_pNode(pBase) + , m_rMutex(rMutex) + { + } + + /** + The number of nodes in the list. + */ + sal_Int32 SAL_CALL CChildList::getLength() + { + ::osl::MutexGuard const g(m_rMutex); + + sal_Int32 length = 0; + if (m_pNode != nullptr) + { + xmlNodePtr cur = m_pNode->GetNodePtr(); + if (nullptr != cur) { + cur = cur->children; + } + while (cur != nullptr) + { + length++; + cur = cur->next; + } + } + return length; + + } + /** + Returns the indexth item in the collection. + */ + Reference< XNode > SAL_CALL CChildList::item(sal_Int32 index) + { + ::osl::MutexGuard const g(m_rMutex); + + if (m_pNode != nullptr) + { + xmlNodePtr cur = m_pNode->GetNodePtr(); + if (nullptr != cur) { + cur = cur->children; + } + while (cur != nullptr) + { + if (index-- == 0) { + return m_pNode->GetOwnerDocument().GetCNode(cur); + } + cur = cur->next; + } + } + return nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/childlist.hxx b/unoxml/source/dom/childlist.hxx new file mode 100644 index 000000000..091a8e450 --- /dev/null +++ b/unoxml/source/dom/childlist.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ref.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> + +#include <cppuhelper/implbase.hxx> + +#include <node.hxx> + +namespace DOM +{ + class CNode; + + class CChildList + : public cppu::WeakImplHelper< css::xml::dom::XNodeList > + { + private: + ::rtl::Reference<CNode> const m_pNode; + ::osl::Mutex & m_rMutex; + + public: + CChildList(::rtl::Reference<CNode> const& pBase, + ::osl::Mutex & rMutex); + + /** + The number of nodes in the list. + */ + virtual sal_Int32 SAL_CALL getLength() override; + /** + Returns the indexth item in the collection. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL item(sal_Int32 index) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/comment.cxx b/unoxml/source/dom/comment.cxx new file mode 100644 index 000000000..30bcb30aa --- /dev/null +++ b/unoxml/source/dom/comment.cxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "comment.hxx" + +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; + +namespace DOM +{ + CComment::CComment(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CComment_Base(rDocument, rMutex, NodeType_COMMENT_NODE, pNode) + { + } + + void CComment::saxify( + const Reference< XDocumentHandler >& i_xHandler) { + if (!i_xHandler.is()) throw RuntimeException(); + Reference< XExtendedDocumentHandler > xExtended(i_xHandler, UNO_QUERY); + if (xExtended.is()) { + xExtended->comment(getData()); + } + } + + OUString SAL_CALL CComment::getNodeName() + { + return "#comment"; + } + + OUString SAL_CALL CComment::getNodeValue() + { + return CCharacterData::getData(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/comment.hxx b/unoxml/source/dom/comment.hxx new file mode 100644 index 000000000..94b8f1f58 --- /dev/null +++ b/unoxml/source/dom/comment.hxx @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XComment.hpp> + +#include <cppuhelper/implbase.hxx> +#include "characterdata.hxx" + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CCharacterData, css::xml::dom::XComment > + CComment_Base; + + class CComment + : public CComment_Base + { + friend class CDocument; + + CComment(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + // --- delegations for XCharacterData + virtual void SAL_CALL appendData(const OUString& arg) override + { + CCharacterData::appendData(arg); + } + virtual void SAL_CALL deleteData(sal_Int32 offset, sal_Int32 count) override + { + CCharacterData::deleteData(offset, count); + } + virtual OUString SAL_CALL getData() override + { + return CCharacterData::getData(); + } + virtual sal_Int32 SAL_CALL getLength() override + { + return CCharacterData::getLength(); + } + virtual void SAL_CALL insertData(sal_Int32 offset, const OUString& arg) override + { + CCharacterData::insertData(offset, arg); + } + virtual void SAL_CALL replaceData(sal_Int32 offset, sal_Int32 count, const OUString& arg) override + { + CCharacterData::replaceData(offset, count, arg); + } + virtual void SAL_CALL setData(const OUString& data) override + { + CCharacterData::setData(data); + } + virtual OUString SAL_CALL subStringData(sal_Int32 offset, sal_Int32 count) override + { + return CCharacterData::subStringData(offset, count); + } + + + // --- overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CCharacterData::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CCharacterData::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CCharacterData::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CCharacterData::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CCharacterData::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CCharacterData::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CCharacterData::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CCharacterData::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CCharacterData::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CCharacterData::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CCharacterData::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CCharacterData::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CCharacterData::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CCharacterData::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CCharacterData::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CCharacterData::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CCharacterData::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CCharacterData::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CCharacterData::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CCharacterData::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CCharacterData::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CCharacterData::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CCharacterData::setPrefix(prefix); + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/document.cxx b/unoxml/source/dom/document.cxx new file mode 100644 index 000000000..413f76481 --- /dev/null +++ b/unoxml/source/dom/document.cxx @@ -0,0 +1,1027 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#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 "comment.hxx" +#include "processinginstruction.hxx" +#include "entityreference.hxx" +#include "documenttype.hxx" +#include "elementlist.hxx" +#include "domimplementation.hxx" +#include "entity.hxx" +#include "notation.hxx" + +#include <event.hxx> +#include <mutationevent.hxx> +#include <uievent.hxx> +#include <mouseevent.hxx> +#include <eventdispatcher.hxx> + +#include <string.h> + +#include <osl/diagnose.h> + +#include <com/sun/star/xml/sax/FastToken.hpp> + +using namespace css; +using namespace css::io; +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; +using namespace css::xml::sax; + +namespace DOM +{ + static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument) + { + // find the doc type + xmlNodePtr cur = i_pDocument->children; + while (cur != nullptr) + { + if ((cur->type == XML_DOCUMENT_TYPE_NODE) || + (cur->type == XML_DTD_NODE)) { + return cur; + } + } + return nullptr; + } + + /// 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 != nullptr) + { + 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_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.emplace( + 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 (const auto& rEntry : m_NodeMap) + { + Reference<XNode> const xNode(rEntry.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()) + return; + + // #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 (nullptr == pNode) { + return nullptr; + } + //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 nullptr; } + + // 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 = new CElement(*this, m_Mutex, pNode); + break; + case XML_TEXT_NODE: + // m_aNodeType = NodeType::TEXT_NODE; + pCNode = new CText(*this, m_Mutex, pNode); + break; + case XML_CDATA_SECTION_NODE: + // m_aNodeType = NodeType::CDATA_SECTION_NODE; + pCNode = new CCDATASection(*this, m_Mutex, pNode); + break; + case XML_ENTITY_REF_NODE: + // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE; + pCNode = new CEntityReference(*this, m_Mutex, pNode); + break; + case XML_ENTITY_NODE: + // m_aNodeType = NodeType::ENTITY_NODE; + pCNode = new CEntity(*this, m_Mutex, reinterpret_cast<xmlEntityPtr>(pNode)); + break; + case XML_PI_NODE: + // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE; + pCNode = new CProcessingInstruction(*this, m_Mutex, pNode); + break; + case XML_COMMENT_NODE: + // m_aNodeType = NodeType::COMMENT_NODE; + pCNode = 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 = new CDocument(reinterpret_cast<xmlDocPtr>(pNode)); + break; + case XML_DOCUMENT_TYPE_NODE: + case XML_DTD_NODE: + // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE; + pCNode = new CDocumentType(*this, m_Mutex, reinterpret_cast<xmlDtdPtr>(pNode)); + break; + case XML_DOCUMENT_FRAG_NODE: + // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE; + pCNode = new CDocumentFragment(*this, m_Mutex, pNode); + break; + case XML_NOTATION_NODE: + // m_aNodeType = NodeType::NOTATION_NODE; + pCNode = new CNotation(*this, m_Mutex, reinterpret_cast<xmlNotationPtr>(pNode)); + break; + case XML_ATTRIBUTE_NODE: + // m_aNodeType = NodeType::ATTRIBUTE_NODE; + pCNode = 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 != nullptr) { + bool const bInserted = m_NodeMap.emplace( + pNode, + ::std::make_pair(WeakReference<XNode>(pCNode), pCNode.get()) + ).second; + OSL_ASSERT(bInserted); + if (!bInserted) { + // if insertion failed, delete new instance and return null + return nullptr; + } + } + + 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 != nullptr; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode = GetCNode(pChild); + OSL_ENSURE(pNode != nullptr, "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 != nullptr; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode = GetCNode(pChild); + OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); + pNode->fastSaxify(rContext); + } + rContext.mxDocHandler->endDocument(); + } + + bool CDocument::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const pReplacedNodeType) + { + switch (nodeType) { + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_COMMENT_NODE: + return true; + case NodeType_ELEMENT_NODE: + // there may be only one! + return (pReplacedNodeType && *pReplacedNodeType == nodeType) + || nullptr == lcl_getDocumentRootPtr(m_aDocPtr); + case NodeType_DOCUMENT_TYPE_NODE: + // there may be only one! + return (pReplacedNodeType && *pReplacedNodeType == nodeType) + || nullptr == lcl_getDocumentType(m_aDocPtr); + default: + return false; + } + } + + + void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener ) + { + ::osl::MutexGuard const g(m_Mutex); + + m_streamListeners.insert(aListener); + } + + void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener ) + { + ::osl::MutexGuard const g(m_Mutex); + + m_streamListeners.erase(aListener); + } + + namespace { + + // IO context functions for libxml2 interaction + struct IOContext { + Reference< XOutputStream > stream; + bool allowClose; + }; + + } + + 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; + } + + // close 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() + { + listenerlist_t streamListeners; + { + ::osl::MutexGuard const g(m_Mutex); + + if (! m_rOutputStream.is()) { throw RuntimeException(); } + streamListeners = m_streamListeners; + } + + // notify listeners about start + for (const Reference< XStreamListener >& aListener : streamListeners) { + aListener->started(); + } + + { + ::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, nullptr); + xmlSaveFileTo(pOut, m_aNodePtr->doc, nullptr); + } + + // call listeners + for (const Reference< XStreamListener >& aListener : streamListeners) { + aListener->closed(); + } + } + + void SAL_CALL CDocument::terminate() + { + // not supported + } + + void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream ) + { + ::osl::MutexGuard const g(m_Mutex); + + m_rOutputStream = aStream; + } + + Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() + { + ::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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr); + ::rtl::Reference< CAttr > const pCAttr( + dynamic_cast< CAttr* >(GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)).get())); + if (!pCAttr.is()) { throw RuntimeException(); } + pCAttr->m_bUnlinked = true; + return pCAttr; + }; + + // Creates an attribute of the given qualified name and namespace URI. + Reference< XAttr > SAL_CALL CDocument::createAttributeNS( + const OUString& ns, const OUString& qname) + { + ::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.subView(0, i), RTL_TEXTENCODING_UTF8); + oName = OUStringToOString(qname.subView(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()), nullptr); + ::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; + }; + + // Creates a CDATASection node whose value is the specified string. + Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data) + { + ::osl::MutexGuard const g(m_Mutex); + + OString const oData( + OUStringToOString(data, RTL_TEXTENCODING_UTF8)); + xmlChar const*const pData = + reinterpret_cast<xmlChar const*>(oData.getStr()); + xmlNodePtr const pText = + xmlNewCDataBlock(m_aDocPtr, pData, oData.getLength()); + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, pData); + 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() + { + ::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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr); + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + sal_Int32 i = qname.indexOf(':'); + if (ns.isEmpty()) throw RuntimeException(); + xmlChar const *pPrefix; + xmlChar const *pName; + OString o1, o2, o3; + if ( i != -1) { + o1 = OUStringToOString(qname.subView(0, i), RTL_TEXTENCODING_UTF8); + pPrefix = reinterpret_cast<xmlChar const *>(o1.getStr()); + o2 = OUStringToOString(qname.subView(i+1), RTL_TEXTENCODING_UTF8); + pName = reinterpret_cast<xmlChar const *>(o2.getStr()); + } else { + // default prefix + pPrefix = reinterpret_cast<xmlChar const *>(""); + o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); + pName = reinterpret_cast<xmlChar const *>(o2.getStr()); + } + o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); + xmlChar const *pUri = reinterpret_cast<xmlChar const *>(o3.getStr()); + + // xmlNsPtr aNsPtr = xmlNewReconciledNs? + // xmlNsPtr aNsPtr = xmlNewGlobalNs? + xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr); + xmlNsPtr const pNs = xmlNewNs(pNode, pUri, pPrefix); + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, pName); + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8); + xmlChar const *pTarget = reinterpret_cast<xmlChar const *>(o1.getStr()); + OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar const *pData = reinterpret_cast<xmlChar const *>(o2.getStr()); + xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, pTarget, pData); + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); + xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, pData); + 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() + { + ::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() + { + ::osl::MutexGuard const g(m_Mutex); + + xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); + if (!pNode) { return nullptr; } + 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 == nullptr) + return nullptr; + // look in current node + if (cur->type == XML_ELEMENT_NODE) + { + xmlAttrPtr a = cur->properties; + while (a != nullptr) + { + if (a->atype == XML_ATTRIBUTE_ID) { + if (strcmp(reinterpret_cast<char*>(a->children->content), reinterpret_cast<char const *>(id)) == 0) + return cur; + } + a = a->next; + } + } + // look in children + xmlNodePtr result = lcl_search_element_by_id(cur->children, id); + if (result != nullptr) + 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) + { + ::osl::MutexGuard const g(m_Mutex); + + // search the tree for an element with the given ID + OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8); + xmlChar const *pId = reinterpret_cast<xmlChar const *>(o1.getStr()); + xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr); + if (!pStart) { return nullptr; } + xmlNodePtr const pNode = lcl_search_element_by_id(pStart, pId); + Reference< XElement > const xRet( + static_cast< XNode* >(GetCNode(pNode).get()), + UNO_QUERY); + return xRet; + } + + + Reference< XNodeList > SAL_CALL + CDocument::getElementsByTagName(OUString const& rTagname) + { + ::osl::MutexGuard const g(m_Mutex); + + Reference< XNodeList > const xRet( + new CElementList(GetDocumentElement(), m_Mutex, rTagname)); + return xRet; + } + + Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS( + OUString const& rNamespaceURI, OUString const& rLocalName) + { + ::osl::MutexGuard const g(m_Mutex); + + Reference< XNodeList > const xRet( + new CElementList(GetDocumentElement(), m_Mutex, + rLocalName, &rNamespaceURI)); + return xRet; + } + + Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation() + { + // 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, true)); + xTargetParent->appendChild(xTmp); + xSibling = xSibling->getNextSibling(); + } + } + + static Reference< XNode > + lcl_ImportNode( Reference< XDocument > const& xDocument, + Reference< XNode > const& xImportedNode, 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 = xNew; + break; + } + case NodeType_CDATA_SECTION_NODE: + { + Reference< XCDATASection > const xCData(xImportedNode, + UNO_QUERY_THROW); + Reference< XCDATASection > const xNewCData = + xDocument->createCDATASection(xCData->getData()); + xNode = xNewCData; + break; + } + case NodeType_COMMENT_NODE: + { + Reference< XComment > const xComment(xImportedNode, + UNO_QUERY_THROW); + Reference< XComment > const xNewComment = + xDocument->createComment(xComment->getData()); + xNode = xNewComment; + break; + } + case NodeType_DOCUMENT_FRAGMENT_NODE: + { + Reference< XDocumentFragment > const xFrag(xImportedNode, + UNO_QUERY_THROW); + Reference< XDocumentFragment > const xNewFrag = + xDocument->createDocumentFragment(); + xNode = xNewFrag; + 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.isEmpty()) + { + if (!aNsPrefix.isEmpty()) { + aQName = aNsPrefix + ":" + 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.isEmpty()) + { + if (!aAttrPrefix.isEmpty()) { + aAttrName = aAttrPrefix + ":" + aAttrName; + } + xNewElement->setAttributeNS( + aAttrUri, aAttrName, sValue); + } else { + xNewElement->setAttribute(aAttrName, sValue); + } + } + } + xNode = xNewElement; + break; + } + case NodeType_ENTITY_REFERENCE_NODE: + { + Reference< XEntityReference > const xRef(xImportedNode, + UNO_QUERY_THROW); + Reference< XEntityReference > const xNewRef( + xDocument->createEntityReference(xRef->getNodeName())); + xNode = xNewRef; + 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 = xNewPi; + break; + } + case NodeType_TEXT_NODE: + { + Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW); + Reference< XText > const xNewText( + xDocument->createTextNode(xText->getData())); + xNode = xNewText; + 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( + "DOMNodeInsertedIntoDocument"), UNO_QUERY_THROW); + event->initMutationEvent( + "DOMNodeInsertedIntoDocument", true, false, Reference< XNode >(), + OUString(), OUString(), OUString(), AttrChangeType(0) ); + Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY); + xDocET->dispatchEvent(event); + } + + return xNode; + } + + Reference< XNode > SAL_CALL CDocument::importNode( + Reference< XNode > const& xImportedNode, sal_Bool deep) + { + 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() + { + // does not need mutex currently + return "#document"; + } + + OUString SAL_CALL CDocument::getNodeValue() + { + // does not need mutex currently + return OUString(); + } + + Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep) + { + ::osl::MutexGuard const g(m_rMutex); + + OSL_ASSERT(nullptr != m_aNodePtr); + if (nullptr == m_aNodePtr) { + return nullptr; + } + xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, bDeep ? 1 : 0)); + if (nullptr == pClone) { return nullptr; } + Reference< XNode > const xRet( + static_cast<CNode*>(CDocument::CreateCDocument(pClone).get())); + return xRet; + } + + Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) + { + // does not need mutex currently + rtl::Reference<events::CEvent> pEvent; + if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved" + || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified" + || aType == "DOMCharacterDataModified") + { + pEvent = new events::CMutationEvent; + + } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate") + { + pEvent = new events::CUIEvent; + } else if ( aType == "click" || aType == "mousedown" || aType == "mouseup" + || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" ) + { + pEvent = new events::CMouseEvent; + } + else // generic event + { + pEvent = new events::CEvent; + } + return pEvent; + } + + // css::xml::sax::XSAXSerializable + void SAL_CALL CDocument::serialize( + const Reference< XDocumentHandler >& i_xHandler, + const Sequence< beans::StringPair >& i_rNamespaces) + { + ::osl::MutexGuard const g(m_Mutex); + + // add new namespaces to root node + xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); + if (nullptr != pRoot) { + for (const beans::StringPair& rNsDef : i_rNamespaces) { + OString prefix = OUStringToOString(rNsDef.First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(rNsDef.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); + } + + // css::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< OUString, sal_Int32 > >& i_rRegisterNamespaces ) + { + ::osl::MutexGuard const g(m_Mutex); + + // add new namespaces to root node + xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); + if (nullptr != pRoot) { + for (const beans::StringPair& rNsDef : i_rNamespaces) { + OString prefix = OUStringToOString(rNsDef.First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(rNsDef.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, + dynamic_cast<sax_fastparser::FastTokenHandlerBase*>(i_xTokenHandler.get())); + + // register namespace ids + for (const beans::Pair<OUString,sal_Int32>& rNs : i_rRegisterNamespaces) + { + OSL_ENSURE(rNs.Second >= FastToken::NAMESPACE, + "CDocument::fastSerialize(): invalid NS token id"); + aContext.maNamespaceMap[ rNs.First ] = rNs.Second; + } + + fastSaxify(aContext); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/document.hxx b/unoxml/source/dom/document.hxx new file mode 100644 index 000000000..450b9200f --- /dev/null +++ b/unoxml/source/dom/document.hxx @@ -0,0 +1,338 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <memory> +#include <unordered_map> + +#include <libxml/tree.h> + +#include <sal/types.h> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/weakref.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/beans/StringPair.hpp> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XAttr.hpp> +#include <com/sun/star/xml/dom/XElement.hpp> +#include <com/sun/star/xml/dom/XDOMImplementation.hpp> +#include <com/sun/star/xml/dom/events/XDocumentEvent.hpp> +#include <com/sun/star/xml/dom/events/XEvent.hpp> +#include <com/sun/star/xml/sax/XSAXSerializable.hpp> +#include <com/sun/star/xml/sax/XFastSAXSerializable.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> +#include <com/sun/star/xml/sax/XFastDocumentHandler.hpp> +#include <com/sun/star/io/XActiveDataSource.hpp> +#include <com/sun/star/io/XActiveDataControl.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XStreamListener.hpp> +#include <o3tl/sorted_vector.hxx> + +#include <node.hxx> + +namespace DOM +{ + namespace events { + class CEventDispatcher; + } + + class CElement; + + typedef ::cppu::ImplInheritanceHelper< + CNode, css::xml::dom::XDocument, css::xml::dom::events::XDocumentEvent, + css::io::XActiveDataControl, css::io::XActiveDataSource, + css::xml::sax::XSAXSerializable, css::xml::sax::XFastSAXSerializable> + CDocument_Base; + + class CDocument + : public CDocument_Base + { + + private: + /// this Mutex is used for synchronization of all UNO wrapper + /// objects that belong to this document + ::osl::Mutex m_Mutex; + /// the libxml document: freed in destructor + /// => all UNO wrapper objects must keep the CDocument alive + xmlDocPtr const m_aDocPtr; + + // datacontrol/source state + typedef o3tl::sorted_vector< css::uno::Reference< css::io::XStreamListener > > listenerlist_t; + listenerlist_t m_streamListeners; + css::uno::Reference< css::io::XOutputStream > m_rOutputStream; + + typedef std::unordered_map< xmlNodePtr, + ::std::pair< css::uno::WeakReference<css::xml::dom::XNode>, CNode* > > nodemap_t; + nodemap_t m_NodeMap; + + ::std::unique_ptr<events::CEventDispatcher> const m_pEventDispatcher; + + explicit CDocument(xmlDocPtr const pDocPtr); + + + public: + /// factory: only way to create instance! + static ::rtl::Reference<CDocument> + CreateCDocument(xmlDocPtr const pDoc); + + virtual ~CDocument() override; + + // needed by CXPathAPI + ::osl::Mutex & GetMutex() { return m_Mutex; } + + events::CEventDispatcher & GetEventDispatcher(); + ::rtl::Reference< CElement > GetDocumentElement(); + + /// get UNO wrapper instance for a libxml node + ::rtl::Reference<CNode> GetCNode( + xmlNodePtr const pNode, bool const bCreate = true); + /// remove a UNO wrapper instance + void RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode); + + virtual CDocument & GetOwnerDocument() override; + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + virtual void fastSaxify( Context& rContext ) override; + + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const* pReplacedNodeType) override; + + /** + Creates an Attr of the given name. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL createAttribute(const OUString& name) override; + + /** + Creates an attribute of the given qualified name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL createAttributeNS(const OUString& namespaceURI, const OUString& qualifiedName) override; + + /** + Creates a CDATASection node whose value is the specified string. + */ + virtual css::uno::Reference< css::xml::dom::XCDATASection > SAL_CALL createCDATASection(const OUString& data) override; + + /** + Creates a Comment node given the specified string. + */ + virtual css::uno::Reference< css::xml::dom::XComment > SAL_CALL createComment(const OUString& data) override; + + /** + Creates an empty DocumentFragment object. + */ + virtual css::uno::Reference< css::xml::dom::XDocumentFragment > SAL_CALL createDocumentFragment() override; + + /** + Creates an element of the type specified. + */ + virtual css::uno::Reference< css::xml::dom::XElement > SAL_CALL createElement(const OUString& tagName) override; + + /** + Creates an element of the given qualified name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XElement > SAL_CALL createElementNS(const OUString& namespaceURI, const OUString& qualifiedName) override; + + /** + Creates an EntityReference object. + */ + virtual css::uno::Reference< css::xml::dom::XEntityReference > SAL_CALL createEntityReference(const OUString& name) override; + + /** + Creates a ProcessingInstruction node given the specified name and + data strings. + */ + virtual css::uno::Reference< css::xml::dom::XProcessingInstruction > SAL_CALL createProcessingInstruction( + const OUString& target, const OUString& data) override; + + /** + Creates a Text node given the specified string. + */ + virtual css::uno::Reference< css::xml::dom::XText > SAL_CALL createTextNode(const OUString& data) override; + + /** + The Document Type Declaration (see DocumentType) associated with this + document. + */ + virtual css::uno::Reference< css::xml::dom::XDocumentType > SAL_CALL getDoctype() override; + + /** + This is a convenience attribute that allows direct access to the child + node that is the root element of the document. + */ + virtual css::uno::Reference< css::xml::dom::XElement > SAL_CALL getDocumentElement() override; + + /** + Returns the Element whose ID is given by elementId. + */ + virtual css::uno::Reference< css::xml::dom::XElement > SAL_CALL getElementById(const OUString& elementId) override; + + /** + Returns a NodeList of all the Elements with a given tag name in the + order in which they are encountered in a preorder traversal of the + Document tree. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getElementsByTagName(const OUString& tagname) override; + + /** + Returns a NodeList of all the Elements with a given local name and + namespace URI in the order in which they are encountered in a preorder + traversal of the Document tree. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getElementsByTagNameNS(const OUString& namespaceURI, const OUString& localName) override; + + /** + The DOMImplementation object that handles this document. + */ + virtual css::uno::Reference< css::xml::dom::XDOMImplementation > SAL_CALL getImplementation() override; + + /** + Imports a node from another document to this document. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL importNode(const css::uno::Reference< css::xml::dom::XNode >& importedNode, sal_Bool deep) override; + + // XDocumentEvent + virtual css::uno::Reference< css::xml::dom::events::XEvent > SAL_CALL createEvent(const OUString& eventType) override; + + // XActiveDataControl, + // see https://api.libreoffice.org/docs/common/ref/com/sun/star/io/XActiveDataControl.html + virtual void SAL_CALL addListener(const css::uno::Reference< css::io::XStreamListener >& aListener ) override; + virtual void SAL_CALL removeListener(const css::uno::Reference< css::io::XStreamListener >& aListener ) override; + virtual void SAL_CALL start() override; + virtual void SAL_CALL terminate() override; + + // XActiveDataSource + // see https://api.libreoffice.org/docs/common/ref/com/sun/star/io/XActiveDataSource.html + virtual void SAL_CALL setOutputStream( const css::uno::Reference< css::io::XOutputStream >& aStream ) override; + virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream() override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + // css::xml::sax::XSAXSerializable + virtual void SAL_CALL serialize( + const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler, + const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces) override; + + // css::xml::sax::XFastSAXSerializable + virtual void SAL_CALL fastSerialize( const css::uno::Reference< css::xml::sax::XFastDocumentHandler >& handler, + const css::uno::Reference< css::xml::sax::XFastTokenHandler >& tokenHandler, + const css::uno::Sequence< css::beans::StringPair >& i_rNamespaces, + const css::uno::Sequence< css::beans::Pair< OUString, sal_Int32 > >& namespaces ) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documentbuilder.cxx b/unoxml/source/dom/documentbuilder.cxx new file mode 100644 index 000000000..90de083f2 --- /dev/null +++ b/unoxml/source/dom/documentbuilder.cxx @@ -0,0 +1,429 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "documentbuilder.hxx" + +#include <string.h> + +#include <libxml/xmlerror.h> + +#include <memory> + +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + +#include <comphelper/processfactory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/xml/sax/SAXParseException.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + +#include <ucbhelper/content.hxx> +#include <ucbhelper/commandenvironment.hxx> + +#include "document.hxx" + +using namespace css::io; +using namespace css::lang; +using namespace css::ucb; +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; +using namespace ucbhelper; +using css::task::XInteractionHandler; +using css::xml::sax::InputSource; + + +namespace DOM +{ + namespace { + + class CDefaultEntityResolver : public cppu::WeakImplHelper< XEntityResolver > + { + public: + virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId ) override + { + InputSource is; + is.sPublicId = sPublicId; + is.sSystemId = sSystemId; + is.sEncoding.clear(); + + try { + Reference< XCommandEnvironment > aEnvironment( + new CommandEnvironment(Reference< XInteractionHandler >(), + Reference< XProgressHandler >() )); + Content aContent(sSystemId, aEnvironment, comphelper::getProcessComponentContext()); + + is.aInputStream = aContent.openStream(); + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION( "unoxml", "exception in default entity resolver"); + is.aInputStream.clear(); + } + return is; + } + + }; + + } + + CDocumentBuilder::CDocumentBuilder() + : m_xEntityResolver(new CDefaultEntityResolver) + { + // init libxml. libxml will protect itself against multiple + // initializations so there is no problem here if this gets + // called multiple times. + xmlInitParser(); + } + + Sequence< OUString > SAL_CALL CDocumentBuilder::getSupportedServiceNames() + { + return { "com.sun.star.xml.dom.DocumentBuilder" }; + } + + OUString SAL_CALL CDocumentBuilder::getImplementationName() + { + return "com.sun.star.comp.xml.dom.DocumentBuilder"; + } + + sal_Bool SAL_CALL CDocumentBuilder::supportsService(const OUString& aServiceName) + { + return cppu::supportsService(this, aServiceName); + } + + Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation() + { + + return Reference< XDOMImplementation >(); + } + + sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware() + { + return true; + } + + sal_Bool SAL_CALL CDocumentBuilder::isValidating() + { + return false; + } + + Reference< XDocument > SAL_CALL CDocumentBuilder::newDocument() + { + std::scoped_lock const g(m_Mutex); + + // create a new document + xmlDocPtr pDocument = xmlNewDoc(reinterpret_cast<const xmlChar*>("1.0")); + return CDocument::CreateCDocument(pDocument); + } + + static OUString make_error_message(xmlParserCtxtPtr ctxt) + { + return OUString(ctxt->lastError.message, strlen(ctxt->lastError.message), RTL_TEXTENCODING_ASCII_US) + + "Line: " + + OUString::number(static_cast<sal_Int32>(ctxt->lastError.line)) + + "\nColumn: " + + OUString::number(static_cast<sal_Int32>(ctxt->lastError.int2)); + } + + // -- callbacks and context struct for parsing from stream + // -- c-linkage, so the callbacks can be used by libxml + extern "C" { + + namespace { + + // context struct passed to IO functions + typedef struct context { + Reference< XInputStream > rInputStream; + bool close; + bool freeOnClose; + } context_t; + + } + + static int xmlIO_read_func( void *context, char *buffer, int len) + { + // get the context... + context_t *pctx = static_cast<context_t*>(context); + if (!pctx->rInputStream.is()) + return -1; + try { + // try to read the requested number of bytes + Sequence< sal_Int8 > chunk(len); + int nread = pctx->rInputStream->readBytes(chunk, len); + + // copy bytes to the provided buffer + memcpy(buffer, chunk.getConstArray(), nread); + return nread; + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION( "unoxml", ""); + return -1; + } + } + + static int xmlIO_close_func(void* context) + { + // get the context... + context_t *pctx = static_cast<context_t*>(context); + if (!pctx->rInputStream.is()) + return 0; + try + { + if (pctx->close) + pctx->rInputStream->closeInput(); + if (pctx->freeOnClose) + delete pctx; + return 0; + } catch (const css::uno::Exception&) { + TOOLS_WARN_EXCEPTION( "unoxml", ""); + return -1; + } + } + + static xmlParserInputPtr resolve_func(void *ctx, + const xmlChar *publicId, + const xmlChar *systemId) + { + // get the CDocumentBuilder object + xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(ctx); + CDocumentBuilder *builder = static_cast< CDocumentBuilder* >(ctxt->_private); + Reference< XEntityResolver > resolver = builder->getEntityResolver(); + OUString sysid; + if (systemId != nullptr) + sysid = OUString(reinterpret_cast<char const *>(systemId), strlen(reinterpret_cast<char const *>(systemId)), RTL_TEXTENCODING_UTF8); + OUString pubid; + if (publicId != nullptr) + pubid = OUString(reinterpret_cast<char const *>(publicId), strlen(reinterpret_cast<char const *>(publicId)), RTL_TEXTENCODING_UTF8); + + // resolve the entity + InputSource src = resolver->resolveEntity(pubid, sysid); + + // create IO context on heap because this call will no longer be on the stack + // when IO is actually performed through the callbacks. The close function must + // free the memory which is indicated by the freeOnClose field in the context struct + context_t *c = new context_t; + c->rInputStream = src.aInputStream; + c->close = true; + c->freeOnClose = true; + + // set up the inputBuffer and inputPtr for libxml + xmlParserInputBufferPtr pBuffer = + xmlParserInputBufferCreateIO(xmlIO_read_func, xmlIO_close_func, c, XML_CHAR_ENCODING_NONE); + xmlParserInputPtr pInput = + xmlNewIOInputStream(ctxt, pBuffer, XML_CHAR_ENCODING_NONE); + return pInput; + } + +#if 0 + static xmlParserInputPtr external_entity_loader(const char *URL, const char * /*ID*/, xmlParserCtxtPtr ctxt) + { + // just call our resolver function using the URL as systemId + return resolve_func(ctxt, 0, (const xmlChar*)URL); + } +#endif + + // default warning handler does not trigger assertion + static void warning_func(void * ctx, const char * /*msg*/, ...) + { + try + { + xmlParserCtxtPtr const pctx = static_cast<xmlParserCtxtPtr>(ctx); + + SAL_INFO( + "unoxml", + "libxml2 warning: " + << make_error_message(pctx)); + + CDocumentBuilder * const pDocBuilder = static_cast<CDocumentBuilder*>(pctx->_private); + + if (pDocBuilder->getErrorHandler().is()) // if custom error handler is set (using setErrorHandler ()) + { + // Prepare SAXParseException to be passed to custom XErrorHandler::warning function + css::xml::sax::SAXParseException saxex; + saxex.Message = make_error_message(pctx); + saxex.LineNumber = static_cast<sal_Int32>(pctx->lastError.line); + saxex.ColumnNumber = static_cast<sal_Int32>(pctx->lastError.int2); + + // Call custom warning function + pDocBuilder->getErrorHandler()->warning(::css::uno::Any(saxex)); + } + } + catch (const css::uno::Exception &) + { + // Protect lib2xml from UNO Exception + TOOLS_WARN_EXCEPTION("unoxml", "DOM::warning_func"); + } + } + + // default error handler triggers assertion + static void error_func(void * ctx, const char * /*msg*/, ...) + { + try + { + xmlParserCtxtPtr const pctx = static_cast<xmlParserCtxtPtr>(ctx); + SAL_WARN( + "unoxml", + "libxml2 error: " + << make_error_message(pctx)); + + CDocumentBuilder * const pDocBuilder = static_cast<CDocumentBuilder*>(pctx->_private); + + if (pDocBuilder->getErrorHandler().is()) // if custom error handler is set (using setErrorHandler ()) + { + // Prepare SAXParseException to be passed to custom XErrorHandler::error function + css::xml::sax::SAXParseException saxex; + saxex.Message = make_error_message(pctx); + saxex.LineNumber = static_cast<sal_Int32>(pctx->lastError.line); + saxex.ColumnNumber = static_cast<sal_Int32>(pctx->lastError.int2); + + // Call custom warning function + pDocBuilder->getErrorHandler()->error(::css::uno::Any(saxex)); + } + } + catch (const css::uno::Exception &) + { + // Protect lib2xml from UNO Exception + TOOLS_WARN_EXCEPTION("unoxml", "DOM::error_func"); + } + } + } // extern "C" + + static void throwEx(xmlParserCtxtPtr ctxt) + { + css::xml::sax::SAXParseException saxex; + saxex.Message = make_error_message(ctxt); + saxex.LineNumber = static_cast<sal_Int32>(ctxt->lastError.line); + saxex.ColumnNumber = static_cast<sal_Int32>(ctxt->lastError.int2); + throw saxex; + } + + namespace { + + struct XmlFreeParserCtxt { + void operator ()(xmlParserCtxt * p) const { xmlFreeParserCtxt(p); } + }; + + } + + Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is) + { + if (!is.is()) { + throw RuntimeException(); + } + + std::scoped_lock const g(m_Mutex); + + // IO context struct. Must outlive pContext, as destroying that via + // xmlFreeParserCtxt may still access this context_t + context_t c; + c.rInputStream = is; + // we did not open the stream, thus we do not close it. + c.close = false; + c.freeOnClose = false; + + std::unique_ptr<xmlParserCtxt, XmlFreeParserCtxt> const pContext( + xmlNewParserCtxt()); + + // register error functions to prevent errors being printed + // on the console + pContext->_private = this; + pContext->sax->error = error_func; + pContext->sax->warning = warning_func; + pContext->sax->resolveEntity = resolve_func; + + xmlDocPtr const pDoc = xmlCtxtReadIO(pContext.get(), + xmlIO_read_func, xmlIO_close_func, &c, nullptr, nullptr, 0); + + if (pDoc == nullptr) { + throwEx(pContext.get()); + } + return CDocument::CreateCDocument(pDoc); + } + + Reference< XDocument > SAL_CALL CDocumentBuilder::parseURI(const OUString& sUri) + { + std::scoped_lock const g(m_Mutex); + + std::unique_ptr<xmlParserCtxt, XmlFreeParserCtxt> const pContext( + xmlNewParserCtxt()); + pContext->_private = this; + pContext->sax->error = error_func; + pContext->sax->warning = warning_func; + pContext->sax->resolveEntity = resolve_func; + // xmlSetExternalEntityLoader(external_entity_loader); + OString oUri = OUStringToOString(sUri, RTL_TEXTENCODING_UTF8); + char *uri = const_cast<char*>(oUri.getStr()); + xmlDocPtr pDoc = xmlCtxtReadFile(pContext.get(), uri, nullptr, 0); + + Reference< XDocument > xRet; + + // if we failed to parse the URI as a simple file, lets try via a ucb stream. + // For Android file:///assets/ URLs which must go via the osl/ file API. + if (pDoc == nullptr) { + Reference < XSimpleFileAccess3 > xStreamAccess( + SimpleFileAccess::create( comphelper::getProcessComponentContext() ) ); + Reference< XInputStream > xInStream = xStreamAccess->openFileRead( sUri ); + if (!xInStream.is()) + throwEx(pContext.get()); + + // loop over every layout entry in current file + xRet = parse( xInStream ); + + xInStream->closeInput(); + xInStream.clear(); + + } else + xRet = CDocument::CreateCDocument(pDoc).get(); + + return xRet; + } + + void SAL_CALL + CDocumentBuilder::setEntityResolver(Reference< XEntityResolver > const& xER) + { + std::scoped_lock const g(m_Mutex); + + m_xEntityResolver = xER; + } + + Reference< XEntityResolver > CDocumentBuilder::getEntityResolver() + { + std::scoped_lock const g(m_Mutex); + + return m_xEntityResolver; + } + + void SAL_CALL + CDocumentBuilder::setErrorHandler(Reference< XErrorHandler > const& xEH) + { + std::scoped_lock const g(m_Mutex); + + m_xErrorHandler = xEH; + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CDocumentBuilder_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new DOM::CDocumentBuilder()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documentbuilder.hxx b/unoxml/source/dom/documentbuilder.hxx new file mode 100644 index 000000000..b29e0410e --- /dev/null +++ b/unoxml/source/dom/documentbuilder.hxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> + +#include <com/sun/star/xml/dom/XDocumentBuilder.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/dom/XDOMImplementation.hpp> +#include <com/sun/star/xml/sax/XEntityResolver.hpp> +#include <com/sun/star/xml/sax/XErrorHandler.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <mutex> + +namespace DOM +{ + typedef ::cppu::WeakImplHelper + < css::xml::dom::XDocumentBuilder + , css::lang::XServiceInfo + > CDocumentBuilder_Base; + + class CDocumentBuilder + : public CDocumentBuilder_Base + { + private: + std::mutex m_Mutex; + css::uno::Reference< css::xml::sax::XEntityResolver > m_xEntityResolver; + css::uno::Reference< css::xml::sax::XErrorHandler > m_xErrorHandler; + + public: + + // ctor + explicit CDocumentBuilder(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + /** + Obtain an instance of a DOMImplementation object. + */ + virtual css::uno::Reference< css::xml::dom::XDOMImplementation > SAL_CALL getDOMImplementation() override; + + /** + Indicates whether or not this parser is configured to understand + namespaces. + */ + virtual sal_Bool SAL_CALL isNamespaceAware() override; + + /** + Indicates whether or not this parser is configured to validate XML + documents. + */ + virtual sal_Bool SAL_CALL isValidating() override; + + /** + Obtain a new instance of a DOM Document object to build a DOM tree + with. + */ + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL newDocument() override; + + /** + Parse the content of the given InputStream as an XML document and + return a new DOM Document object. + */ + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL parse(const css::uno::Reference< css::io::XInputStream >& is) override; + + /** + Parse the content of the given URI as an XML document and return + a new DOM Document object. + */ + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL parseURI(const OUString& uri) override; + + /** + Specify the EntityResolver to be used to resolve entities present + in the XML document to be parsed. + */ + virtual void SAL_CALL setEntityResolver(const css::uno::Reference< css::xml::sax::XEntityResolver >& er) override; + + /// @throws css::uno::RuntimeException + css::uno::Reference< css::xml::sax::XEntityResolver > getEntityResolver(); + + + /** + Specify the ErrorHandler to be used to report errors present in + the XML document to be parsed. + */ + virtual void SAL_CALL setErrorHandler(const css::uno::Reference< css::xml::sax::XErrorHandler >& eh) override; + + /* + Get the ErrorHandler to be used to report errors present in + the XML document to be parsed. + */ + + const css::uno::Reference< css::xml::sax::XErrorHandler >& getErrorHandler() const + { + return m_xErrorHandler; + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documentfragment.cxx b/unoxml/source/dom/documentfragment.cxx new file mode 100644 index 000000000..dd3ed3c18 --- /dev/null +++ b/unoxml/source/dom/documentfragment.cxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "documentfragment.hxx" + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CDocumentFragment::CDocumentFragment( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CDocumentFragment_Base(rDocument, rMutex, + NodeType_DOCUMENT_FRAGMENT_NODE, pNode) + { + } + + bool CDocumentFragment::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) + { + switch (nodeType) { + case NodeType_ELEMENT_NODE: + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_COMMENT_NODE: + case NodeType_TEXT_NODE: + case NodeType_CDATA_SECTION_NODE: + case NodeType_ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } + } + + OUString SAL_CALL CDocumentFragment::getNodeName() + { + return "#document-fragment"; + } + OUString SAL_CALL CDocumentFragment::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documentfragment.hxx b/unoxml/source/dom/documentfragment.hxx new file mode 100644 index 000000000..c71edc86a --- /dev/null +++ b/unoxml/source/dom/documentfragment.hxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XDocumentFragment.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XDocumentFragment > + CDocumentFragment_Base; + + class CDocumentFragment + : public CDocumentFragment_Base + { + friend class CDocument; + + CDocumentFragment( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const*) override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documenttype.cxx b/unoxml/source/dom/documenttype.cxx new file mode 100644 index 000000000..d79e2d0d0 --- /dev/null +++ b/unoxml/source/dom/documenttype.cxx @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "documenttype.hxx" + +#include <string.h> + +#include <osl/diagnose.h> + +#include "entitiesmap.hxx" +#include "notationsmap.hxx" + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + + CDocumentType::CDocumentType( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlDtdPtr const pDtd) + : CDocumentType_Base(rDocument, rMutex, + NodeType_DOCUMENT_TYPE_NODE, reinterpret_cast<xmlNodePtr>(pDtd)) + , m_aDtdPtr(pDtd) + { + } + + /** + A NamedNodeMap containing the general entities, both external and + internal, declared in the DTD. + */ + css::uno::Reference< XNamedNodeMap > SAL_CALL CDocumentType::getEntities() + { + ::osl::MutexGuard const g(m_rMutex); + + css::uno::Reference< XNamedNodeMap > aMap; + if (m_aDtdPtr != nullptr) + { + aMap.set(new CEntitiesMap); + } + return aMap; + } + + /** + The internal subset as a string, or null if there is none. + */ + OUString SAL_CALL CDocumentType::getInternalSubset() + { + OSL_ENSURE(false, + "CDocumentType::getInternalSubset: not implemented (#i113683#)"); + return OUString(); + } + + /** + The name of DTD; i.e., the name immediately following the DOCTYPE + keyword. + */ + OUString SAL_CALL CDocumentType::getName() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aName; + if (m_aDtdPtr != nullptr) + { + aName = OUString(reinterpret_cast<char const *>(m_aDtdPtr->name), strlen(reinterpret_cast<char const *>(m_aDtdPtr->name)), RTL_TEXTENCODING_UTF8); + } + return aName; + } + + /** + A NamedNodeMap containing the notations declared in the DTD. + */ + css::uno::Reference< XNamedNodeMap > SAL_CALL CDocumentType::getNotations() + { + ::osl::MutexGuard const g(m_rMutex); + + css::uno::Reference< XNamedNodeMap > aMap; + if (m_aDtdPtr != nullptr) + { + aMap.set(new CNotationsMap); + } + return aMap; + } + + /** + The public identifier of the external subset. + */ + OUString SAL_CALL CDocumentType::getPublicId() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aId; + if (m_aDtdPtr != nullptr) + { + aId = OUString(reinterpret_cast<char const *>(m_aDtdPtr->name), strlen(reinterpret_cast<char const *>(m_aDtdPtr->ExternalID)), RTL_TEXTENCODING_UTF8); + } + return aId; + } + + /** + The system identifier of the external subset. + */ + OUString SAL_CALL CDocumentType::getSystemId() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aId; + if (m_aDtdPtr != nullptr) + { + aId = OUString(reinterpret_cast<char const *>(m_aDtdPtr->name), strlen(reinterpret_cast<char const *>(m_aDtdPtr->SystemID)), RTL_TEXTENCODING_UTF8); + } + return aId; + } + + OUString SAL_CALL CDocumentType::getNodeName() + { + return getName(); + } + + OUString SAL_CALL CDocumentType::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/documenttype.hxx b/unoxml/source/dom/documenttype.hxx new file mode 100644 index 000000000..48f857bf7 --- /dev/null +++ b/unoxml/source/dom/documenttype.hxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <sal/types.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XDocumentType.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XDocumentType > + CDocumentType_Base; + + class CDocumentType + : public CDocumentType_Base + { + private: + friend class CDocument; + + xmlDtdPtr m_aDtdPtr; + + CDocumentType(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlDtdPtr const pDtd); + + public: + /** + A NamedNodeMap containing the general entities, both external and + internal, declared in the DTD. + */ + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getEntities() override; + + /** + The internal subset as a string, or null if there is none. + */ + virtual OUString SAL_CALL getInternalSubset() override; + + /** + The name of DTD; i.e., the name immediately following the DOCTYPE + keyword. + */ + virtual OUString SAL_CALL getName() override; + + /** + A NamedNodeMap containing the notations declared in the DTD. + */ + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getNotations() override; + + /** + The public identifier of the external subset. + */ + virtual OUString SAL_CALL getPublicId() override; + + /** + The system identifier of the external subset. + */ + virtual OUString SAL_CALL getSystemId() override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/domimplementation.cxx b/unoxml/source/dom/domimplementation.cxx new file mode 100644 index 000000000..0bd7e5940 --- /dev/null +++ b/unoxml/source/dom/domimplementation.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "domimplementation.hxx" + +#include <osl/diagnose.h> +#include <rtl/ref.hxx> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CDOMImplementation* CDOMImplementation::get() + { + // why the heck is this thing static? + // perhaps it would be helpful to know what the implementation should + // do to answer this question... + static rtl::Reference<CDOMImplementation> xDOMImplementation = new CDOMImplementation; + return &*xDOMImplementation; + } + + // there is just 1 static instance, so these must not delete it! + void SAL_CALL CDOMImplementation::acquire() noexcept { } + void SAL_CALL CDOMImplementation::release() noexcept { } + + /** + Creates a DOM Document object of the specified type with its document element. + */ + Reference <XDocument > SAL_CALL CDOMImplementation::createDocument( + OUString const& /*rNamespaceURI*/, + OUString const& /*rQualifiedName*/, + Reference< XDocumentType > const& /*xDoctype*/) + { + OSL_ENSURE(false, + "CDOMImplementation::createDocument: not implemented (#i113683#)"); + return Reference<XDocument>(); + } + + /** + Creates an empty DocumentType node. + */ + Reference< XDocumentType > SAL_CALL CDOMImplementation::createDocumentType( + OUString const& /*rQualifiedName*/, + OUString const& /*rPublicId*/, OUString const& /*rSystemId*/) + { + OSL_ENSURE(false, "CDOMImplementation::createDocumentType: " + "not implemented (#i113683#)"); + return Reference<XDocumentType>(); + } + + /** + Test if the DOM implementation implements a specific feature. + */ + sal_Bool SAL_CALL + CDOMImplementation::hasFeature(OUString const& /*feature*/, OUString const& /*ver*/) + { + OSL_ENSURE(false, + "CDOMImplementation::hasFeature: not implemented (#i113683#)"); + return false; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/domimplementation.hxx b/unoxml/source/dom/domimplementation.hxx new file mode 100644 index 000000000..955a4e32e --- /dev/null +++ b/unoxml/source/dom/domimplementation.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/dom/XDocumentType.hpp> +#include <com/sun/star/xml/dom/XDOMImplementation.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace DOM +{ + class CDOMImplementation + : public cppu::WeakImplHelper< css::xml::dom::XDOMImplementation > + { + + public: + static CDOMImplementation* get(); + + // there is just 1 static instance, so these must not delete it! + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + /** + Creates a DOM Document object of the specified type with its document element. + */ + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL createDocument(const OUString& namespaceURI, const OUString& qualifiedName, const css::uno::Reference< css::xml::dom::XDocumentType >& doctype) override; + + /** + Creates an empty DocumentType node. + */ + virtual css::uno::Reference< css::xml::dom::XDocumentType > SAL_CALL createDocumentType(const OUString& qualifiedName, const OUString& publicId, const OUString& systemId) override; + + /** + Test if the DOM implementation implements a specific feature. + */ + virtual sal_Bool SAL_CALL hasFeature(const OUString& feature, const OUString& ver) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/element.cxx b/unoxml/source/dom/element.cxx new file mode 100644 index 000000000..61109de6b --- /dev/null +++ b/unoxml/source/dom/element.cxx @@ -0,0 +1,756 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "element.hxx" + +#include <string.h> + +#include <memory> + +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/xml/dom/DOMException.hpp> +#include <com/sun/star/xml/dom/events/XMutationEvent.hpp> +#include <com/sun/star/xml/sax/FastToken.hpp> + +#include <comphelper/attributelist.hxx> +#include <comphelper/servicehelper.hxx> + +#include <node.hxx> +#include "attr.hxx" +#include "elementlist.hxx" +#include "attributesmap.hxx" +#include "document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; +using namespace css::xml::sax; + +namespace DOM +{ + + CElement::CElement(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CElement_Base(rDocument, rMutex, NodeType_ELEMENT_NODE, pNode) + { + } + + void CElement::saxify(const Reference< XDocumentHandler >& i_xHandler) + { + if (!i_xHandler.is()) throw RuntimeException(); + rtl::Reference<comphelper::AttributeList> pAttrs = + new comphelper::AttributeList(); + OUString type = ""; + // add namespace definitions to attributes + for (xmlNsPtr pNs = m_aNodePtr->nsDef; pNs != nullptr; pNs = pNs->next) { + const xmlChar *pPrefix = pNs->prefix ? pNs->prefix : reinterpret_cast<const xmlChar*>(""); + OUString prefix(reinterpret_cast<const char*>(pPrefix), + strlen(reinterpret_cast<const char*>(pPrefix)), + RTL_TEXTENCODING_UTF8); + OUString name = (prefix.isEmpty()) + ? OUString( "xmlns" ) : "xmlns:" + prefix; + const xmlChar *pHref = pNs->href; + OUString val(reinterpret_cast<const char*>(pHref), + strlen(reinterpret_cast<const char*>(pHref)), + RTL_TEXTENCODING_UTF8); + pAttrs->AddAttribute(name, type, val); + } + // add attributes + for (xmlAttrPtr pAttr = m_aNodePtr->properties; + pAttr != nullptr; pAttr = pAttr->next) { + ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)); + OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); + OUString prefix = pNode->getPrefix(); + OUString name = (prefix.isEmpty()) + ? pNode->getLocalName() + : prefix + ":" + pNode->getLocalName(); + OUString val = pNode->getNodeValue(); + pAttrs->AddAttribute(name, type, val); + } + OUString prefix = getPrefix(); + OUString name = (prefix.isEmpty()) + ? getLocalName() + : prefix + ":" + getLocalName(); + i_xHandler->startElement(name, pAttrs); + // recurse + for (xmlNodePtr pChild = m_aNodePtr->children; + pChild != nullptr; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode( + GetOwnerDocument().GetCNode(pChild)); + OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); + pNode->saxify(i_xHandler); + } + i_xHandler->endElement(name); + } + + void CElement::fastSaxify( Context& i_rContext ) + { + if (!i_rContext.mxDocHandler.is()) throw RuntimeException(); + pushContext(i_rContext); + addNamespaces(i_rContext,m_aNodePtr); + + // add attributes + i_rContext.mxAttribList->clear(); + for (xmlAttrPtr pAttr = m_aNodePtr->properties; + pAttr != nullptr; pAttr = pAttr->next) { + ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)); + OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); + + const xmlChar* pName = pAttr->name; + sal_Int32 nAttributeToken=FastToken::DONTKNOW; + + if( pAttr->ns && strlen(reinterpret_cast<char const *>(pAttr->ns->prefix)) ) + nAttributeToken = getTokenWithPrefix( i_rContext, + reinterpret_cast<char const *>(pAttr->ns->prefix), + reinterpret_cast<char const *>(pName) ); + else + nAttributeToken = getToken( i_rContext, reinterpret_cast<char const *>(pName) ); + + if( nAttributeToken != FastToken::DONTKNOW ) + i_rContext.mxAttribList->add( nAttributeToken, + OUStringToOString(pNode->getNodeValue(), + RTL_TEXTENCODING_UTF8)); + } + + const xmlChar* pPrefix = (m_aNodePtr->ns && m_aNodePtr->ns->prefix) ? m_aNodePtr->ns->prefix : reinterpret_cast<const xmlChar*>(""); + const xmlChar* pName = m_aNodePtr->name; + sal_Int32 nElementToken=FastToken::DONTKNOW; + if( strlen(reinterpret_cast<char const *>(pPrefix)) ) + nElementToken = getTokenWithPrefix( i_rContext, reinterpret_cast<char const *>(pPrefix), reinterpret_cast<char const *>(pName) ); + else + nElementToken = getToken( i_rContext, reinterpret_cast<char const *>(pName) ); + + Reference<XFastContextHandler> xParentHandler(i_rContext.mxCurrentHandler); + try + { + Reference< XFastAttributeList > xAttr( i_rContext.mxAttribList ); + if( nElementToken == FastToken::DONTKNOW ) + { + const OUString aNamespace; + const OUString aElementName( reinterpret_cast<char const *>(pPrefix), + strlen(reinterpret_cast<char const *>(pPrefix)), + RTL_TEXTENCODING_UTF8 ); + + if( xParentHandler.is() ) + i_rContext.mxCurrentHandler = xParentHandler->createUnknownChildContext( aNamespace, aElementName, xAttr ); + else + i_rContext.mxCurrentHandler = i_rContext.mxDocHandler->createUnknownChildContext( aNamespace, aElementName, xAttr ); + + if( i_rContext.mxCurrentHandler.is() ) + i_rContext.mxCurrentHandler->startUnknownElement( aNamespace, aElementName, xAttr ); + } + else + { + if( xParentHandler.is() ) + i_rContext.mxCurrentHandler = xParentHandler->createFastChildContext( nElementToken, xAttr ); + else + i_rContext.mxCurrentHandler = i_rContext.mxDocHandler->createFastChildContext( nElementToken, xAttr ); + + if( i_rContext.mxCurrentHandler.is() ) + i_rContext.mxCurrentHandler->startFastElement( nElementToken, xAttr ); + } + } + catch( Exception& ) + {} + + // recurse + for (xmlNodePtr pChild = m_aNodePtr->children; + pChild != nullptr; pChild = pChild->next) { + ::rtl::Reference<CNode> const pNode( + GetOwnerDocument().GetCNode(pChild)); + OSL_ENSURE(pNode != nullptr, "CNode::get returned 0"); + pNode->fastSaxify(i_rContext); + } + + if( i_rContext.mxCurrentHandler.is() ) try + { + if( nElementToken != FastToken::DONTKNOW ) + i_rContext.mxCurrentHandler->endFastElement( nElementToken ); + else + { + const OUString aElementName( reinterpret_cast<char const *>(pPrefix), + strlen(reinterpret_cast<char const *>(pPrefix)), + RTL_TEXTENCODING_UTF8 ); + + i_rContext.mxCurrentHandler->endUnknownElement( "", aElementName ); + } + } + catch( Exception& ) + {} + + // restore after children have been processed + i_rContext.mxCurrentHandler = xParentHandler; + popContext(i_rContext); + } + + bool CElement::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) + { + switch (nodeType) { + case NodeType_ELEMENT_NODE: + case NodeType_TEXT_NODE: + case NodeType_COMMENT_NODE: + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_CDATA_SECTION_NODE: + case NodeType_ENTITY_REFERENCE_NODE: + return true; + case NodeType_ATTRIBUTE_NODE: + /* this is not really allowed by the DOM spec, but this + implementation has evidently supported it (by special case + handling, so the attribute does not actually become a child) + so allow it for backward compatibility */ + return true; + default: + return false; + } + } + + + /** + Retrieves an attribute value by name. + return empty string if attribute is not set + */ + OUString SAL_CALL CElement::getAttribute(OUString const& name) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + // search properties + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + std::shared_ptr<xmlChar const> const pValue( + xmlGetProp(m_aNodePtr, reinterpret_cast<xmlChar const *>(o1.getStr())), xmlFree); + OUString const ret( pValue + ? OUString(reinterpret_cast<char const*>(pValue.get()), + strlen(reinterpret_cast<char const*>(pValue.get())), + RTL_TEXTENCODING_UTF8) + : OUString() ); + return ret; + } + + /** + Retrieves an attribute node by name. + */ + Reference< XAttr > SAL_CALL CElement::getAttributeNode(OUString const& name) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const*const pName = + reinterpret_cast<xmlChar const*>(o1.getStr()); + xmlAttrPtr const pAttr = xmlHasProp(m_aNodePtr, pName); + if (nullptr == pAttr) { + return nullptr; + } + Reference< XAttr > const xRet( + static_cast< XNode* >(GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)).get()), + UNO_QUERY_THROW); + return xRet; + } + + /** + Retrieves an Attr node by local name and namespace URI. + */ + Reference< XAttr > SAL_CALL CElement::getAttributeNodeNS( + const OUString& namespaceURI, const OUString& localName) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); + xmlChar const*const pName = + reinterpret_cast<xmlChar const*>(o1.getStr()); + OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + xmlChar const*const pNS = + reinterpret_cast<xmlChar const*>(o2.getStr()); + xmlAttrPtr const pAttr = xmlHasNsProp(m_aNodePtr, pName, pNS); + if (nullptr == pAttr) { + return nullptr; + } + Reference< XAttr > const xRet( + static_cast< XNode* >(GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr)).get()), + UNO_QUERY_THROW); + return xRet; + } + + /** + Retrieves an attribute value by local name and namespace URI. + return empty string if attribute is not set + */ + OUString SAL_CALL + CElement::getAttributeNS( + OUString const& namespaceURI, OUString const& localName) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); + xmlChar const*const pName = + reinterpret_cast<xmlChar const*>(o1.getStr()); + OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + xmlChar const*const pNS = + reinterpret_cast<xmlChar const*>(o2.getStr()); + std::shared_ptr<xmlChar const> const pValue( + xmlGetNsProp(m_aNodePtr, pName, pNS), xmlFree); + if (nullptr == pValue) { + return OUString(); + } + OUString const ret(reinterpret_cast<char const*>(pValue.get()), + strlen(reinterpret_cast<char const*>(pValue.get())), + RTL_TEXTENCODING_UTF8); + return ret; + } + + /** + Returns a NodeList of all descendant Elements with a given tag name, + in the order in which they are + encountered in a preorder traversal of this Element tree. + */ + Reference< XNodeList > SAL_CALL + CElement::getElementsByTagName(OUString const& rLocalName) + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNodeList > const xList( + new CElementList(this, m_rMutex, rLocalName)); + return xList; + } + + /** + Returns a NodeList of all the descendant Elements with a given local + name and namespace URI in the order in which they are encountered in + a preorder traversal of this Element tree. + */ + Reference< XNodeList > SAL_CALL + CElement::getElementsByTagNameNS( + OUString const& rNamespaceURI, OUString const& rLocalName) + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNodeList > const xList( + new CElementList(this, m_rMutex, rLocalName, &rNamespaceURI)); + return xList; + } + + /** + The name of the element. + */ + OUString SAL_CALL CElement::getTagName() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + OUString const ret(reinterpret_cast<char const *>(m_aNodePtr->name), + strlen(reinterpret_cast<char const *>(m_aNodePtr->name)), RTL_TEXTENCODING_UTF8); + return ret; + } + + + /** + Returns true when an attribute with a given name is specified on this + element or has a default value, false otherwise. + */ + sal_Bool SAL_CALL CElement::hasAttribute(OUString const& name) + { + ::osl::MutexGuard const g(m_rMutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + return (m_aNodePtr != nullptr && xmlHasProp(m_aNodePtr, pName) != nullptr); + } + + /** + Returns true when an attribute with a given local name and namespace + URI is specified on this element or has a default value, false otherwise. + */ + sal_Bool SAL_CALL CElement::hasAttributeNS( + OUString const& namespaceURI, OUString const& localName) + { + ::osl::MutexGuard const g(m_rMutex); + + OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + xmlChar const *pNs = reinterpret_cast<xmlChar const *>(o2.getStr()); + return (m_aNodePtr != nullptr && xmlHasNsProp(m_aNodePtr, pName, pNs) != nullptr); + } + + /** + Removes an attribute by name. + */ + void SAL_CALL CElement::removeAttribute(OUString const& name) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return; + } + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const*const pName = + reinterpret_cast<xmlChar const*>(o1.getStr()); + xmlAttrPtr const pAttr = xmlHasProp(m_aNodePtr, pName); + if (0 == xmlUnsetProp(m_aNodePtr, pName)) { + ::rtl::Reference<CNode> const pCNode(GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr), false)); + if (pCNode.is()) { + pCNode->invalidate(); // freed by xmlUnsetProp + } + } + } + + /** + Removes an attribute by local name and namespace URI. + */ + void SAL_CALL CElement::removeAttributeNS( + OUString const& namespaceURI, OUString const& localName) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return; + } + OString o1 = OUStringToOString(localName, RTL_TEXTENCODING_UTF8); + xmlChar const*const pName = + reinterpret_cast<xmlChar const*>(o1.getStr()); + OString o2 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + xmlChar const*const pURI = + reinterpret_cast<xmlChar const*>(o2.getStr()); + xmlNsPtr const pNs = + xmlSearchNsByHref(m_aNodePtr->doc, m_aNodePtr, pURI); + xmlAttrPtr const pAttr = xmlHasNsProp(m_aNodePtr, pName, pURI); + if (0 == xmlUnsetNsProp(m_aNodePtr, pNs, pName)) { + ::rtl::Reference<CNode> const pCNode(GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(pAttr), false)); + if (pCNode.is()) { + pCNode->invalidate(); // freed by xmlUnsetNsProp + } + } + } + + /** + Removes the specified attribute node. + */ + Reference< XAttr > SAL_CALL + CElement::removeAttributeNode(Reference< XAttr > const& oldAttr) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + + ::rtl::Reference<CNode> const pCNode( + comphelper::getFromUnoTunnel<CNode>(Reference<XNode>(oldAttr))); + if (!pCNode.is()) { throw RuntimeException(); } + + xmlNodePtr const pNode = pCNode->GetNodePtr(); + xmlAttrPtr const pAttr = reinterpret_cast<xmlAttrPtr>(pNode); + if (!pAttr) { throw RuntimeException(); } + + if (pAttr->parent != m_aNodePtr) + { + DOMException e; + e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; + throw e; + } + if (pAttr->doc != m_aNodePtr->doc) + { + DOMException e; + e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR; + throw e; + } + + Reference< XAttr > aAttr; + if (!oldAttr->getNamespaceURI().isEmpty()) { + OUStringBuffer qname(oldAttr->getPrefix()); + if (!qname.isEmpty()) { + qname.append(':'); + } + qname.append(oldAttr->getName()); + aAttr = GetOwnerDocument().createAttributeNS( + oldAttr->getNamespaceURI(), qname.makeStringAndClear()); + } else { + aAttr = GetOwnerDocument().createAttribute(oldAttr->getName()); + } + aAttr->setValue(oldAttr->getValue()); + xmlRemoveProp(pAttr); + pCNode->invalidate(); // freed by xmlRemoveProp + + return aAttr; + } + + /** + Adds a new attribute node. + */ + Reference< XAttr > + CElement::setAttributeNode_Impl_Lock( + Reference< XAttr > const& xNewAttr, bool const bNS) + { + if (xNewAttr->getOwnerDocument() != getOwnerDocument()) { + DOMException e; + e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR; + throw e; + } + + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (nullptr == m_aNodePtr) { + throw RuntimeException(); + } + + // get the implementation + CAttr *const pCAttr = dynamic_cast<CAttr*>( + comphelper::getFromUnoTunnel<CNode>(xNewAttr)); + if (!pCAttr) { throw RuntimeException(); } + xmlAttrPtr const pAttr = + reinterpret_cast<xmlAttrPtr>(pCAttr->GetNodePtr()); + if (!pAttr) { throw RuntimeException(); } + + // check whether the attribute is not in use by another element + if (pAttr->parent) { + DOMException e; + e.Code = DOMExceptionType_INUSE_ATTRIBUTE_ERR; + throw e; + } + + xmlAttrPtr res = nullptr; + xmlChar const*const pContent( + (pAttr->children) ? pAttr->children->content : nullptr); + + if (bNS) { + xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) ); + res = xmlNewNsProp(m_aNodePtr, pNs, pAttr->name, pContent); + } else { + res = xmlNewProp(m_aNodePtr, pAttr->name, pContent); + } + + // get the new attr node + Reference< XAttr > const xAttr( + static_cast< XNode* >(GetOwnerDocument().GetCNode( + reinterpret_cast<xmlNodePtr>(res)).get()), + UNO_QUERY_THROW); + + // attribute addition event + // dispatch DOMAttrModified event + Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); + Reference< XMutationEvent > event(docevent->createEvent( + "DOMAttrModified"), UNO_QUERY); + event->initMutationEvent("DOMAttrModified", + true, false, xAttr, + OUString(), xAttr->getValue(), xAttr->getName(), + AttrChangeType_ADDITION); + + guard.clear(); // release mutex before calling event handlers + + dispatchEvent(event); + dispatchSubtreeModified(); + + return xAttr; + } + + Reference< XAttr > + CElement::setAttributeNode(const Reference< XAttr >& newAttr) + { + return setAttributeNode_Impl_Lock(newAttr, false); + } + + /** + Adds a new attribute. + */ + Reference< XAttr > + CElement::setAttributeNodeNS(const Reference< XAttr >& newAttr) + { + return setAttributeNode_Impl_Lock(newAttr, true); + } + + /** + Adds a new attribute. + */ + void SAL_CALL + CElement::setAttribute(OUString const& name, OUString const& value) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); + xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr()); + OString o2 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); + xmlChar const *pValue = reinterpret_cast<xmlChar const *>(o2.getStr()); + + if (nullptr == m_aNodePtr) { + throw RuntimeException(); + } + OUString oldValue; + AttrChangeType aChangeType = AttrChangeType_MODIFICATION; + std::shared_ptr<xmlChar const> const pOld( + xmlGetProp(m_aNodePtr, pName), xmlFree); + if (pOld == nullptr) { + aChangeType = AttrChangeType_ADDITION; + xmlNewProp(m_aNodePtr, pName, pValue); + } else { + oldValue = OUString(reinterpret_cast<char const*>(pOld.get()), + strlen(reinterpret_cast<char const*>(pOld.get())), + RTL_TEXTENCODING_UTF8); + xmlSetProp(m_aNodePtr, pName, pValue); + } + + // dispatch DOMAttrModified event + Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); + Reference< XMutationEvent > event(docevent->createEvent( + "DOMAttrModified"), UNO_QUERY); + event->initMutationEvent("DOMAttrModified", + true, false, + getAttributeNode(name), + oldValue, value, name, aChangeType); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent(event); + dispatchSubtreeModified(); + } + + /** + Adds a new attribute. + */ + void SAL_CALL + CElement::setAttributeNS(OUString const& namespaceURI, + OUString const& qualifiedName, OUString const& value) + { + if (namespaceURI.isEmpty()) throw RuntimeException(); + + ::osl::ClearableMutexGuard guard(m_rMutex); + + OString o1, o2, o3, o4, o5; + xmlChar const *pPrefix = nullptr; + xmlChar const *pLName = nullptr; + o1 = OUStringToOString(qualifiedName, RTL_TEXTENCODING_UTF8); + xmlChar const *pQName = reinterpret_cast<xmlChar const *>(o1.getStr()); + sal_Int32 idx = qualifiedName.indexOf(':'); + if (idx != -1) + { + o2 = OUStringToOString( + qualifiedName.subView(0,idx), + RTL_TEXTENCODING_UTF8); + pPrefix = reinterpret_cast<xmlChar const *>(o2.getStr()); + o3 = OUStringToOString( + qualifiedName.subView(idx+1), + RTL_TEXTENCODING_UTF8); + pLName = reinterpret_cast<xmlChar const *>(o3.getStr()); + } else { + pPrefix = reinterpret_cast<xmlChar const *>(""); + pLName = pQName; + } + o4 = OUStringToOString(namespaceURI, RTL_TEXTENCODING_UTF8); + o5 = OUStringToOString(value, RTL_TEXTENCODING_UTF8); + xmlChar const *pURI= reinterpret_cast<xmlChar const *>(o4.getStr()); + xmlChar const *pValue = reinterpret_cast<xmlChar const *>(o5.getStr()); + + if (nullptr == m_aNodePtr) { + throw RuntimeException(); + } + + //find the right namespace + xmlNsPtr pNs = xmlSearchNs(m_aNodePtr->doc, m_aNodePtr, pPrefix); + // if no namespace found, create a new one + if (pNs == nullptr) { + pNs = xmlNewNs(m_aNodePtr, pURI, pPrefix); + } + + if (strcmp(reinterpret_cast<char const *>(pNs->href), reinterpret_cast<char const *>(pURI)) != 0) { + // ambiguous ns prefix + throw RuntimeException(); + } + + // found namespace matches + + OUString oldValue; + AttrChangeType aChangeType = AttrChangeType_MODIFICATION; + std::shared_ptr<xmlChar const> const pOld( + xmlGetNsProp(m_aNodePtr, pLName, pNs->href), xmlFree); + if (pOld == nullptr) { + aChangeType = AttrChangeType_ADDITION; + xmlNewNsProp(m_aNodePtr, pNs, pLName, pValue); + } else { + oldValue = OUString(reinterpret_cast<char const*>(pOld.get()), + strlen(reinterpret_cast<char const*>(pOld.get())), + RTL_TEXTENCODING_UTF8); + xmlSetNsProp(m_aNodePtr, pNs, pLName, pValue); + } + // dispatch DOMAttrModified event + Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY); + Reference< XMutationEvent > event(docevent->createEvent( + "DOMAttrModified"), UNO_QUERY); + event->initMutationEvent( + "DOMAttrModified", true, false, + getAttributeNodeNS(namespaceURI, OUString(reinterpret_cast<char const *>(pLName), strlen(reinterpret_cast<char const *>(pLName)), RTL_TEXTENCODING_UTF8)), + oldValue, value, qualifiedName, aChangeType); + + guard.clear(); // release mutex before calling event handlers + dispatchEvent(event); + dispatchSubtreeModified(); + } + + Reference< XNamedNodeMap > SAL_CALL + CElement::getAttributes() + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNamedNodeMap > const xMap( + new CAttributesMap(this, m_rMutex)); + return xMap; + } + + OUString SAL_CALL CElement::getNodeName() + { + return getLocalName(); + } + + OUString SAL_CALL CElement::getLocalName() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aName; + if (m_aNodePtr != nullptr) + { + const xmlChar* pName = m_aNodePtr->name; + aName = OUString(reinterpret_cast<const char*>(pName), strlen(reinterpret_cast<const char*>(pName)), RTL_TEXTENCODING_UTF8); + } + return aName; + } + + OUString SAL_CALL CElement::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/element.hxx b/unoxml/source/dom/element.hxx new file mode 100644 index 000000000..3810e0935 --- /dev/null +++ b/unoxml/source/dom/element.hxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> +#include <com/sun/star/xml/dom/NodeType.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper<CNode, css::xml::dom::XElement > CElement_Base; + + class CElement + : public CElement_Base + { + private: + friend class CDocument; + + css::uno::Reference< css::xml::dom::XAttr > setAttributeNode_Impl_Lock( + css::uno::Reference< css::xml::dom::XAttr > const& xNewAttr, bool const bNS); + + protected: + CElement(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + virtual void fastSaxify( Context& i_rContext ) override; + + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const*) override; + + /** + Retrieves an attribute value by name. + */ + virtual OUString SAL_CALL getAttribute(const OUString& name) override; + + /** + Retrieves an attribute node by name. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL getAttributeNode(const OUString& name) override; + + /** + Retrieves an Attr node by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL getAttributeNodeNS(const OUString& namespaceURI, const OUString& localName) override; + + /** + Retrieves an attribute value by local name and namespace URI. + */ + virtual OUString SAL_CALL getAttributeNS(const OUString& namespaceURI, const OUString& localName) override; + + /** + Returns a NodeList of all descendant Elements with a given tag name, + in the order in which they are + encountered in a preorder traversal of this Element tree. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getElementsByTagName(const OUString& name) override; + + /** + Returns a NodeList of all the descendant Elements with a given local + name and namespace URI in the order in which they are encountered in + a preorder traversal of this Element tree. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getElementsByTagNameNS(const OUString& namespaceURI, + const OUString& localName) override; + + /** + The name of the element. + */ + virtual OUString SAL_CALL getTagName() override; + + /** + Returns true when an attribute with a given name is specified on this + element or has a default value, false otherwise. + */ + virtual sal_Bool SAL_CALL hasAttribute(const OUString& name) override; + + /** + Returns true when an attribute with a given local name and namespace + URI is specified on this element or has a default value, false otherwise. + */ + virtual sal_Bool SAL_CALL hasAttributeNS(const OUString& namespaceURI, const OUString& localName) override; + + /** + Removes an attribute by name. + */ + virtual void SAL_CALL removeAttribute(const OUString& name) override; + + /** + Removes the specified attribute node. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL removeAttributeNode(const css::uno::Reference< css::xml::dom::XAttr >& oldAttr) override; + + /** + Removes an attribute by local name and namespace URI. + */ + virtual void SAL_CALL removeAttributeNS(const OUString& namespaceURI, const OUString& localName) override; + + /** + Adds a new attribute. + */ + virtual void SAL_CALL setAttribute(const OUString& name, const OUString& value) override; + + /** + Adds a new attribute node. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL setAttributeNode(const css::uno::Reference< css::xml::dom::XAttr >& newAttr) override; + + /** + Adds a new attribute. + */ + virtual css::uno::Reference< css::xml::dom::XAttr > SAL_CALL setAttributeNodeNS(const css::uno::Reference< css::xml::dom::XAttr >& newAttr) override; + + /** + Adds a new attribute. + */ + virtual void SAL_CALL setAttributeNS( + const OUString& namespaceURI, const OUString& qualifiedName, const OUString& value) override; + + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override; + virtual OUString SAL_CALL getLocalName() override; + + // resolve uno inheritance problems... + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/elementlist.cxx b/unoxml/source/dom/elementlist.cxx new file mode 100644 index 000000000..c8fc60437 --- /dev/null +++ b/unoxml/source/dom/elementlist.cxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "elementlist.hxx" + +#include <string.h> +#include <string_view> + +#include <cppuhelper/implbase.hxx> +#include <o3tl/safeint.hxx> +#include <tools/diagnose_ex.h> + +#include "element.hxx" +#include "document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; + +namespace +{ + class WeakEventListener : public ::cppu::WeakImplHelper<css::xml::dom::events::XEventListener> + { + private: + css::uno::WeakReference<css::xml::dom::events::XEventListener> mxOwner; + + public: + explicit WeakEventListener(const css::uno::Reference<css::xml::dom::events::XEventListener>& rOwner) + : mxOwner(rOwner) + { + } + + virtual void SAL_CALL handleEvent(const css::uno::Reference<css::xml::dom::events::XEvent>& rEvent) override + { + css::uno::Reference<css::xml::dom::events::XEventListener> xOwner(mxOwner.get(), + css::uno::UNO_QUERY); + if (xOwner.is()) + xOwner->handleEvent(rEvent); + } + }; +} + +namespace DOM +{ + + static xmlChar* lcl_initXmlString(std::u16string_view rString) + { + OString const os = + OUStringToOString(rString, RTL_TEXTENCODING_UTF8); + xmlChar *const pRet = new xmlChar[os.getLength() + 1]; + strcpy(reinterpret_cast<char*>(pRet), os.getStr()); + return pRet; + } + + CElementList::CElementList(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex, + std::u16string_view rName, OUString const*const pURI) + : m_xImpl(new CElementListImpl(pElement, rMutex, rName, pURI)) + { + if (pElement.is()) { + m_xImpl->registerListener(*pElement); + } + } + + CElementListImpl::CElementListImpl(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex, + std::u16string_view rName, OUString const*const pURI) + : m_pElement(pElement) + , m_rMutex(rMutex) + , m_pName(lcl_initXmlString(rName)) + , m_pURI(pURI ? lcl_initXmlString(*pURI) : nullptr) + , m_bRebuild(true) + { + } + + CElementListImpl::~CElementListImpl() + { + if (m_xEventListener.is() && m_pElement.is()) + { + Reference< XEventTarget > xTarget = m_pElement; + assert(xTarget.is()); + if (!xTarget.is()) + return; + xTarget->removeEventListener("DOMSubtreeModified", m_xEventListener, false/*capture*/); + } + } + + void CElementListImpl::registerListener(CElement & rElement) + { + try { + Reference< XEventTarget > const xTarget( + static_cast<XElement*>(& rElement), UNO_QUERY_THROW); + m_xEventListener = new WeakEventListener(this); + xTarget->addEventListener("DOMSubtreeModified", m_xEventListener, false/*capture*/); + } catch (const Exception &){ + TOOLS_WARN_EXCEPTION( "unoxml", "Exception caught while registering NodeList as listener"); + } + } + + void CElementListImpl::buildlist(xmlNodePtr pNode, bool start) + { + // bail out if no rebuild is needed + if (start) { + if (!m_bRebuild) + { + return; + } else { + m_nodevector.clear(); + m_bRebuild = false; // don't rebuild until tree is mutated + } + } + + while (pNode != nullptr ) + { + if (pNode->type == XML_ELEMENT_NODE && + (strcmp(reinterpret_cast<char const *>(pNode->name), reinterpret_cast<char*>(m_pName.get())) == 0)) + { + if (!m_pURI) { + m_nodevector.push_back(pNode); + } else { + if (pNode->ns != nullptr && (0 == + strcmp(reinterpret_cast<char const *>(pNode->ns->href), reinterpret_cast<char*>(m_pURI.get())))) + { + m_nodevector.push_back(pNode); + } + } + } + if (pNode->children != nullptr) buildlist(pNode->children, false); + + if (!start) pNode = pNode->next; + else break; // fold back + } + } + + /** + The number of nodes in the list. + */ + sal_Int32 SAL_CALL CElementListImpl::getLength() + { + ::osl::MutexGuard const g(m_rMutex); + + if (!m_pElement.is()) { return 0; } + + // this has to be 'live' + buildlist(m_pElement->GetNodePtr()); + return m_nodevector.size(); + } + /** + Returns the indexth item in the collection. + */ + Reference< XNode > SAL_CALL CElementListImpl::item(sal_Int32 index) + { + if (index < 0) throw RuntimeException(); + + ::osl::MutexGuard const g(m_rMutex); + + if (!m_pElement.is()) { return nullptr; } + + buildlist(m_pElement->GetNodePtr()); + if (m_nodevector.size() <= o3tl::make_unsigned(index)) { + throw RuntimeException(); + } + return m_pElement->GetOwnerDocument().GetCNode(m_nodevector[index]); + } + + // tree mutations can change the list + void SAL_CALL CElementListImpl::handleEvent(Reference< XEvent > const&) + { + ::osl::MutexGuard const g(m_rMutex); + + m_bRebuild = true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/elementlist.hxx b/unoxml/source/dom/elementlist.hxx new file mode 100644 index 000000000..66ce3beda --- /dev/null +++ b/unoxml/source/dom/elementlist.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <vector> +#include <string_view> +#include <memory> + +#include <libxml/tree.h> + +#include <sal/types.h> +#include <rtl/ref.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/dom/events/XEvent.hpp> +#include <com/sun/star/xml/dom/events/XEventListener.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace DOM +{ + class CElement; + + class CElementListImpl + : public cppu::WeakImplHelper< css::xml::dom::XNodeList, + css::xml::dom::events::XEventListener > + { + private: + /** @short proxy weak binding to forward Events to ourself without + an ownership cycle + */ + css::uno::Reference< css::xml::dom::events::XEventListener > m_xEventListener; + + ::rtl::Reference<CElement> const m_pElement; + ::osl::Mutex & m_rMutex; + ::std::unique_ptr<xmlChar[]> const m_pName; + ::std::unique_ptr<xmlChar[]> const m_pURI; + bool m_bRebuild; + std::vector< xmlNodePtr > m_nodevector; + + void buildlist(xmlNodePtr pNode, bool start=true); + + public: + CElementListImpl(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex, + std::u16string_view rName, OUString const*const pURI); + + void registerListener(CElement & rElement); + + virtual ~CElementListImpl() override; + + /** + The number of nodes in the list. + */ + virtual sal_Int32 SAL_CALL getLength() override; + /** + Returns the indexth item in the collection. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL item(sal_Int32 index) override; + + // XEventListener + virtual void SAL_CALL handleEvent(const css::uno::Reference< css::xml::dom::events::XEvent >& evt) override; + }; + + class CElementList + : public cppu::WeakImplHelper< css::xml::dom::XNodeList, + css::xml::dom::events::XEventListener > + { + private: + rtl::Reference<CElementListImpl> m_xImpl; + public: + CElementList(::rtl::Reference<CElement> const& pElement, + ::osl::Mutex & rMutex, + std::u16string_view rName, OUString const*const pURI = nullptr); + + /** + The number of nodes in the list. + */ + virtual sal_Int32 SAL_CALL getLength() override + { + return m_xImpl->getLength(); + } + /** + Returns the indexth item in the collection. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL item(sal_Int32 index) override + { + return m_xImpl->item(index); + } + + // XEventListener + virtual void SAL_CALL handleEvent(const css::uno::Reference< css::xml::dom::events::XEvent >& evt) override + { + m_xImpl->handleEvent(evt); + } + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entitiesmap.cxx b/unoxml/source/dom/entitiesmap.cxx new file mode 100644 index 000000000..d5c65e7c2 --- /dev/null +++ b/unoxml/source/dom/entitiesmap.cxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "entitiesmap.hxx" + +#include <osl/diagnose.h> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CEntitiesMap::CEntitiesMap() + { + } + + /** + The number of nodes in this map. + */ + sal_Int32 SAL_CALL CEntitiesMap::getLength() + { + OSL_ENSURE(false, + "CEntitiesMap::getLength: not implemented (#i113683#)"); + return 0; + } + + /** + Retrieves a node specified by local name + */ + Reference< XNode > SAL_CALL + CEntitiesMap::getNamedItem(OUString const& /*name*/) + { + OSL_ENSURE(false, + "CEntitiesMap::getNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Retrieves a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::getNamedItemNS( + OUString const& /*namespaceURI*/, OUString const& /*localName*/) + { + OSL_ENSURE(false, + "CEntitiesMap::getNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Returns the indexth item in the map. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::item(sal_Int32 /*index*/) + { + OSL_ENSURE(false, "CEntitiesMap::item: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Removes a node specified by name. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::removeNamedItem(OUString const& /*name*/) + { + OSL_ENSURE(false, + "CEntitiesMap::removeNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + // Removes a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::removeNamedItemNS( + OUString const& /*namespaceURI*/, OUString const& /*localName*/) + { + OSL_ENSURE(false, + "CEntitiesMap::removeNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + // Adds a node using its nodeName attribute. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::setNamedItem(Reference< XNode > const& /*arg*/) + { + OSL_ENSURE(false, + "CEntitiesMap::setNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Adds a node using its namespaceURI and localName. + */ + Reference< XNode > SAL_CALL + CEntitiesMap::setNamedItemNS(Reference< XNode > const& /*arg*/) + { + OSL_ENSURE(false, + "CEntitiesMap::setNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entitiesmap.hxx b/unoxml/source/dom/entitiesmap.hxx new file mode 100644 index 000000000..7349851dd --- /dev/null +++ b/unoxml/source/dom/entitiesmap.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace DOM +{ + class CDocumentType; + + class CEntitiesMap + : public cppu::WeakImplHelper< css::xml::dom::XNamedNodeMap > + { + public: + CEntitiesMap(); + + /** + The number of nodes in this map. + */ + virtual sal_Int32 SAL_CALL getLength() override; + + /** + Retrieves a node specified by local name + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + getNamedItem(const OUString& name) override; + + /** + Retrieves a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + Returns the indexth item in the map. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + item(sal_Int32 index) override; + + /** + Removes a node specified by name. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + removeNamedItem(OUString const& name) override; + + /** + // Removes a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + // Adds a node using its nodeName attribute. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItem(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + + /** + Adds a node using its namespaceURI and localName. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItemNS(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entity.cxx b/unoxml/source/dom/entity.cxx new file mode 100644 index 000000000..ccc8a0872 --- /dev/null +++ b/unoxml/source/dom/entity.cxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "entity.hxx" + +#include <osl/diagnose.h> + +#include <string.h> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + + CEntity::CEntity(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlEntityPtr const pEntity) + : CEntity_Base(rDocument, rMutex, + NodeType_ENTITY_NODE, reinterpret_cast<xmlNodePtr>(pEntity)) + , m_aEntityPtr(pEntity) + { + } + + bool CEntity::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) + { + switch (nodeType) { + case NodeType_ELEMENT_NODE: + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_COMMENT_NODE: + case NodeType_TEXT_NODE: + case NodeType_CDATA_SECTION_NODE: + case NodeType_ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } + } + + /** + For unparsed entities, the name of the notation for the entity. + */ + OUString SAL_CALL CEntity::getNotationName() + { + OSL_ENSURE(false, + "CEntity::getNotationName: not implemented (#i113683#)"); + return OUString(); + } + + /** + The public identifier associated with the entity, if specified. + */ + OUString SAL_CALL CEntity::getPublicId() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aID; + if(m_aEntityPtr != nullptr) + { + aID = OUString(reinterpret_cast<char const *>(m_aEntityPtr->ExternalID), strlen(reinterpret_cast<char const *>(m_aEntityPtr->ExternalID)), RTL_TEXTENCODING_UTF8); + } + return aID; + } + + /** + The system identifier associated with the entity, if specified. + */ + OUString SAL_CALL CEntity::getSystemId() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aID; + if(m_aEntityPtr != nullptr) + { + aID = OUString(reinterpret_cast<char const *>(m_aEntityPtr->SystemID), strlen(reinterpret_cast<char const *>(m_aEntityPtr->SystemID)), RTL_TEXTENCODING_UTF8); + } + return aID; + } + OUString SAL_CALL CEntity::getNodeName() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aName; + if (m_aNodePtr != nullptr) + { + const xmlChar* pName = m_aNodePtr->name; + aName = OUString(reinterpret_cast<char const *>(pName), strlen(reinterpret_cast<char const *>(pName)), RTL_TEXTENCODING_UTF8); + } + return aName; + } + OUString SAL_CALL CEntity::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entity.hxx b/unoxml/source/dom/entity.hxx new file mode 100644 index 000000000..2668adb68 --- /dev/null +++ b/unoxml/source/dom/entity.hxx @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <sal/types.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XEntity.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XEntity > CEntity_Base; + + class CEntity + : public CEntity_Base + { + private: + friend class CDocument; + + xmlEntityPtr m_aEntityPtr; + + CEntity(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlEntityPtr const pEntity); + + public: + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const*) override; + + /** + For unparsed entities, the name of the notation for the entity. + */ + virtual OUString SAL_CALL getNotationName() override; + + /** + The public identifier associated with the entity, if specified. + */ + virtual OUString SAL_CALL getPublicId() override; + + /** + The system identifier associated with the entity, if specified. + */ + virtual OUString SAL_CALL getSystemId() override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entityreference.cxx b/unoxml/source/dom/entityreference.cxx new file mode 100644 index 000000000..a3a06a238 --- /dev/null +++ b/unoxml/source/dom/entityreference.cxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "entityreference.hxx" + +#include <string.h> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CEntityReference::CEntityReference( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CEntityReference_Base(rDocument, rMutex, + NodeType_ENTITY_REFERENCE_NODE, pNode) + { + } + + bool CEntityReference::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const) + { + switch (nodeType) { + case NodeType_ELEMENT_NODE: + case NodeType_PROCESSING_INSTRUCTION_NODE: + case NodeType_COMMENT_NODE: + case NodeType_TEXT_NODE: + case NodeType_CDATA_SECTION_NODE: + case NodeType_ENTITY_REFERENCE_NODE: + return true; + default: + return false; + } + } + + OUString SAL_CALL CEntityReference::getNodeName() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aName; + if (m_aNodePtr != nullptr) + { + const xmlChar* pName = m_aNodePtr->name; + aName = OUString(reinterpret_cast<char const *>(pName), strlen(reinterpret_cast<char const *>(pName)), RTL_TEXTENCODING_UTF8); + } + return aName; + } + + OUString SAL_CALL CEntityReference::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/entityreference.hxx b/unoxml/source/dom/entityreference.hxx new file mode 100644 index 000000000..2ed9f568b --- /dev/null +++ b/unoxml/source/dom/entityreference.hxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XEntityReference.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XEntityReference > + CEntityReference_Base; + + class CEntityReference + : public CEntityReference_Base + { + private: + friend class CDocument; + + CEntityReference( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + virtual bool IsChildTypeAllowed(css::xml::dom::NodeType const nodeType, + css::xml::dom::NodeType const*) override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/node.cxx b/unoxml/source/dom/node.cxx new file mode 100644 index 000000000..32cbedc0e --- /dev/null +++ b/unoxml/source/dom/node.cxx @@ -0,0 +1,984 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <node.hxx> + +#include <string.h> + +#include <libxml/xmlstring.h> + +#include <algorithm> + +#include <osl/mutex.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <com/sun/star/xml/dom/DOMException.hpp> +#include <com/sun/star/xml/dom/events/XMutationEvent.hpp> +#include <com/sun/star/xml/sax/FastToken.hpp> + +#include <comphelper/servicehelper.hxx> + +#include "document.hxx" +#include "attr.hxx" +#include "childlist.hxx" + +#include <eventdispatcher.hxx> + +using namespace css; +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; +using namespace css::xml::sax; + +namespace DOM +{ + void pushContext(Context& io_rContext) + { + // Explicitly use a temp. variable. + // Windows/VC++ seems to mess up if .back() is directly passed as + // parameter. i.e. Don't use push_back( .back() ); + Context::NamespaceVectorType::value_type aVal = io_rContext.maNamespaces.back(); + io_rContext.maNamespaces.push_back( aVal ); + } + + 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 != nullptr; pNs = pNs->next) { + const xmlChar *pPrefix = pNs->prefix; + // prefix can be NULL when xmlns attribute is empty (xmlns="") + OString prefix(reinterpret_cast<const char*>(pPrefix), + pPrefix ? strlen(reinterpret_cast<const char*>(pPrefix)) : 0); + const xmlChar *pHref = pNs->href; + OUString val(reinterpret_cast<const char*>(pHref), + strlen(reinterpret_cast<const char*>(pHref)), + RTL_TEXTENCODING_UTF8); + + 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; + + io_rContext.maNamespaces.back().push_back(aNS); + + SAL_INFO("unoxml", "Added with token " << aIter->second); + } + } + } + + sal_Int32 getToken( const Context& rContext, const char* pToken ) + { + const Sequence<sal_Int8> aSeq( reinterpret_cast<sal_Int8 const *>(pToken), strlen( pToken ) ); + return rContext.mxTokenHandler->getTokenFromUTF8( aSeq ); + } + + sal_Int32 getTokenWithPrefix( const Context& rContext, const char* pPrefix, const char* pName ) + { + sal_Int32 nNamespaceToken = FastToken::DONTKNOW; + OString prefix(pPrefix, + strlen(pPrefix)); + + SAL_INFO("unoxml", "getTokenWithPrefix(): prefix " << pPrefix << ", name " << pName); + + Context::NamespaceVectorType::value_type::const_iterator aIter; + if( (aIter=std::find_if(rContext.maNamespaces.back().begin(), + rContext.maNamespaces.back().end(), + [&prefix](const Context::Namespace &aNamespace){ return aNamespace.getPrefix() == prefix; } )) != + rContext.maNamespaces.back().end() ) + { + nNamespaceToken = aIter->mnToken; + sal_Int32 nNameToken = getToken( rContext, pName ); + if( nNameToken == FastToken::DONTKNOW ) + nNamespaceToken = FastToken::DONTKNOW; + else + 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) : nullptr ) + , 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 != nullptr && 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 = nullptr; + } + + 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 + } + } + + const css::uno::Sequence< sal_Int8 > & CNode::getUnoTunnelId() noexcept + { + static const comphelper::UnoIdInit theCNodeUnoTunnelId; + return theCNodeUnoTunnelId.getSeq(); + } + + 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 != nullptr) + { + if (cur->ns == oldNs) + cur->ns = newNs; + if (cur->type == XML_ELEMENT_NODE) + { + xmlAttrPtr curAttr = cur->properties; + while(curAttr != nullptr) + { + 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 != nullptr && cur->type == XML_ELEMENT_NODE) + { + xmlAttrPtr curAttr = cur->properties; + while(curAttr != nullptr) + { + if (curAttr->ns != nullptr) + { + xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix); + if (ns != nullptr) + curAttr->ns = ns; + } + curAttr = curAttr->next; + } + } + + while (cur != nullptr) + { + nscleanup(cur->children, cur); + if (cur->ns != nullptr) + { + xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix); + if (ns != nullptr && ns != cur->ns && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(cur->ns->href))==0) + { + xmlNsPtr curDef = cur->nsDef; + xmlNsPtr *refp = &(cur->nsDef); // insert point + while (curDef != nullptr) + { + ns = xmlSearchNs(cur->doc, aParent, curDef->prefix); + if (ns != nullptr && ns != curDef && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(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*/, NodeType const*const) + { + // 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) + { + ::osl::ClearableMutexGuard guard(m_rMutex); + + if (nullptr == m_aNodePtr) { return nullptr; } + + CNode *const pNewChild(comphelper::getFromUnoTunnel<CNode>(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 != nullptr) { + DOMException e; + e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; + throw e; + } + if (!IsChildTypeAllowed(pNewChild->m_aNodeType, nullptr)) { + DOMException e; + e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; + throw e; + } + + // check whether this is an attribute node; it needs special handling + xmlNodePtr res = nullptr; + 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 nullptr; } + + // 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 nullptr; } + + // 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( + "DOMNodeInserted"), UNO_QUERY); + event->initMutationEvent("DOMNodeInserted", true, 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(event); + // dispatch subtree modified for this node + dispatchSubtreeModified(); + + return pNode; + } + + /** + 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) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode( + xmlCopyNode(m_aNodePtr, bDeep ? 1 : 0)); + if (!pNode.is()) { return nullptr; } + pNode->m_bUnlinked = true; // not linked yet + return pNode; + } + + /** + A NamedNodeMap containing the attributes of this node (if it is an Element) + or null otherwise. + */ + Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes() + { + // 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() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex)); + return xNodeList; + } + + /** + The first child of this node. + */ + Reference< XNode > SAL_CALL CNode::getFirstChild() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + return GetOwnerDocument().GetCNode(m_aNodePtr->children); + } + + /** + The last child of this node. + */ + Reference< XNode > SAL_CALL CNode::getLastChild() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + return GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr)); + } + + /** + Returns the local part of the qualified name of this node. + */ + OUString SAL_CALL CNode::getLocalName() + { + // see CElement/CAttr + return OUString(); + } + + + /** + The namespace URI of this node, or null if it is unspecified. + */ + OUString SAL_CALL CNode::getNamespaceURI() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aURI; + if (m_aNodePtr != nullptr && + (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) && + m_aNodePtr->ns != nullptr) + { + const xmlChar* pHref = m_aNodePtr->ns->href; + aURI = OUString(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8); + } + return aURI; + } + + /** + The node immediately following this node. + */ + Reference< XNode > SAL_CALL CNode::getNextSibling() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + return GetOwnerDocument().GetCNode(m_aNodePtr->next); + } + + /** + The name of this node, depending on its type; see the table above. + */ + OUString SAL_CALL CNode::getNodeName() + { + /* + 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 + */ + return OUString(); + } + + /** + A code representing the type of the underlying object, as defined above. + */ + NodeType SAL_CALL CNode::getNodeType() + { + ::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() + { + return OUString(); + } + + /** + The Document object associated with this node. + */ + Reference< XDocument > SAL_CALL CNode::getOwnerDocument() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + Reference< XDocument > const xDoc(& GetOwnerDocument()); + return xDoc; + } + + /** + The parent of this node. + */ + Reference< XNode > SAL_CALL CNode::getParentNode() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + return GetOwnerDocument().GetCNode(m_aNodePtr->parent); + } + + /** + The namespace prefix of this node, or null if it is unspecified. + */ + OUString SAL_CALL CNode::getPrefix() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aPrefix; + if (m_aNodePtr != nullptr && + (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) && + m_aNodePtr->ns != nullptr) + { + const xmlChar* pPrefix = m_aNodePtr->ns->prefix; + if( pPrefix != nullptr ) + aPrefix = OUString(reinterpret_cast<char const *>(pPrefix), strlen(reinterpret_cast<char const *>(pPrefix)), RTL_TEXTENCODING_UTF8); + } + return aPrefix; + + } + + /** + The node immediately preceding this node. + */ + Reference< XNode > SAL_CALL CNode::getPreviousSibling() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return nullptr; + } + return GetOwnerDocument().GetCNode(m_aNodePtr->prev); + } + + /** + Returns whether this node (if it is an element) has any attributes. + */ + sal_Bool SAL_CALL CNode::hasAttributes() + { + ::osl::MutexGuard const g(m_rMutex); + + return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr); + } + + /** + Returns whether this node has any children. + */ + sal_Bool SAL_CALL CNode::hasChildNodes() + { + ::osl::MutexGuard const g(m_rMutex); + + return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr); + } + + /** + Inserts the node newChild before the existing child node refChild. + */ + Reference< XNode > SAL_CALL CNode::insertBefore( + const Reference< XNode >& newChild, const Reference< XNode >& refChild) + { + 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(comphelper::getFromUnoTunnel<CNode>(newChild)); + CNode *const pRefNode(comphelper::getFromUnoTunnel<CNode>(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 != nullptr) + { + DOMException e; + e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; + throw e; + } + if (!IsChildTypeAllowed(pNewNode->m_aNodeType, nullptr)) { + 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 != nullptr) + { + if (cur == pRefChild) { + // insert before + pNewChild->next = cur; + pNewChild->prev = cur->prev; + cur->prev = pNewChild; + if (pNewChild->prev != nullptr) { + 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*/) + { + OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)"); + return 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() + { + //XXX combine adjacent text nodes and remove empty ones + OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)"); + } + + /** + 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) + { + 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(comphelper::getFromUnoTunnel<CNode>(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( + "DOMNodeRemoved"), UNO_QUERY); + event->initMutationEvent("DOMNodeRemoved", + true, + 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(event); + // 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) + { + 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); + + ::rtl::Reference<CNode> const pOldNode( + comphelper::getFromUnoTunnel<CNode>(xOldChild)); + ::rtl::Reference<CNode> const pNewNode( + comphelper::getFromUnoTunnel<CNode>(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 != nullptr) { + DOMException e; + e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR; + throw e; + } + if (!IsChildTypeAllowed(pNewNode->m_aNodeType, &pOldNode->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 = reinterpret_cast<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 != nullptr) + { + if(cur == pOld) + { + // exchange nodes + pNew->prev = pOld->prev; + if (pNew->prev != nullptr) + pNew->prev->next = pNew; + pNew->next = pOld->next; + if (pNew->next != nullptr) + pNew->next->prev = pNew; + pNew->parent = pOld->parent; + assert(pNew->parent && "coverity[var_deref_op] pNew->parent cannot be NULL here"); + if(pNew->parent->children == pOld) + pNew->parent->children = pNew; + if(pNew->parent->last == pOld) + pNew->parent->last = pNew; + pOld->next = nullptr; + pOld->prev = nullptr; + pOld->parent = nullptr; + 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( + "DOMSubtreeModified"), UNO_QUERY); + event->initMutationEvent( + "DOMSubtreeModified", true, + false, Reference< XNode >(), + OUString(), OUString(), OUString(), AttrChangeType(0) ); + dispatchEvent(event); + } + + /** + The value of this node, depending on its type; see the table above. + */ + void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/) + { + // use specific node implementation + // 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) + { + ::osl::MutexGuard const g(m_rMutex); + + if ((nullptr == 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 const *pBuf = reinterpret_cast<xmlChar const *>(o1.getStr()); + if (m_aNodePtr != nullptr && m_aNodePtr->ns != nullptr) + { + 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< css::xml::dom::events::XEventListener >& listener, + sal_Bool useCapture) + { + ::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< css::xml::dom::events::XEventListener >& listener, + sal_Bool useCapture) + { + ::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) + { + 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 true; + } + + ::sal_Int64 SAL_CALL + CNode::getSomething(Sequence< ::sal_Int8 > const& rId) + { + return comphelper::getSomethingImpl(rId, this); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/notation.cxx b/unoxml/source/dom/notation.cxx new file mode 100644 index 000000000..352d5b002 --- /dev/null +++ b/unoxml/source/dom/notation.cxx @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "notation.hxx" + +#include <string.h> + +#include <osl/diagnose.h> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CNotation::CNotation(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNotationPtr const pNotation) + : CNotation_Base(rDocument, rMutex, + NodeType_NOTATION_NODE, reinterpret_cast<xmlNodePtr>(pNotation)) + { + } + + OUString SAL_CALL CNotation::getPublicId() + { + OSL_ENSURE(false, + "CNotation::getPublicId: not implemented (#i113683#)"); + return OUString(); + } + + /** + The system identifier of this notation. + */ + OUString SAL_CALL CNotation::getSystemId() + { + OSL_ENSURE(false, + "CNotation::getSystemId: not implemented (#i113683#)"); + return OUString(); + } + + + OUString SAL_CALL CNotation::getNodeName() + { + ::osl::MutexGuard const g(m_rMutex); + + OUString aName; + if (m_aNodePtr != nullptr) + { + const xmlChar* pName = m_aNodePtr->name; + aName = OUString(reinterpret_cast<char const *>(pName), strlen(reinterpret_cast<char const *>(pName)), RTL_TEXTENCODING_UTF8); + } + return aName; + } + + OUString SAL_CALL CNotation::getNodeValue() + { + return OUString(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/notation.hxx b/unoxml/source/dom/notation.hxx new file mode 100644 index 000000000..2a95200f5 --- /dev/null +++ b/unoxml/source/dom/notation.hxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNotation.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef cppu::ImplInheritanceHelper< CNode, css::xml::dom::XNotation > CNotation_Base; + + class CNotation + : public CNotation_Base + { + private: + friend class CDocument; + + CNotation(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNotationPtr const pNotation); + + /** + The public identifier of this notation. + */ + virtual OUString SAL_CALL getPublicId() override; + + /** + The system identifier of this notation. + */ + virtual OUString SAL_CALL getSystemId() override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CNode::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/notationsmap.cxx b/unoxml/source/dom/notationsmap.cxx new file mode 100644 index 000000000..07cb851b3 --- /dev/null +++ b/unoxml/source/dom/notationsmap.cxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "notationsmap.hxx" + +#include <osl/diagnose.h> + +using namespace css::uno; +using namespace css::xml::dom; + +namespace DOM +{ + CNotationsMap::CNotationsMap() + { + } + + /** + The number of nodes in this map. + */ + sal_Int32 SAL_CALL CNotationsMap::getLength() + { + OSL_ENSURE(false, + "CNotationsMap::getLength: not implemented (#i113683#)"); + return 0; + } + + /** + Retrieves a node specified by local name + */ + Reference< XNode > SAL_CALL + CNotationsMap::getNamedItem(OUString const& /*name*/) + { + OSL_ENSURE(false, + "CNotationsMap::getNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Retrieves a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CNotationsMap::getNamedItemNS( + OUString const& /*namespaceURI*/, OUString const& /*localName*/) + { + OSL_ENSURE(false, + "CNotationsMap::getNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Returns the indexth item in the map. + */ + Reference< XNode > SAL_CALL + CNotationsMap::item(sal_Int32 /*index*/) + { + OSL_ENSURE(false, "CNotationsMap::item: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Removes a node specified by name. + */ + Reference< XNode > SAL_CALL + CNotationsMap::removeNamedItem(OUString const& /*name*/) + { + OSL_ENSURE(false, + "CNotationsMap::removeNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + // Removes a node specified by local name and namespace URI. + */ + Reference< XNode > SAL_CALL + CNotationsMap::removeNamedItemNS( + OUString const& /*namespaceURI*/, OUString const& /*localName*/) + { + OSL_ENSURE(false, + "CNotationsMap::removeNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + // Adds a node using its nodeName attribute. + */ + Reference< XNode > SAL_CALL + CNotationsMap::setNamedItem(Reference< XNode > const& /*arg*/) + { + OSL_ENSURE(false, + "CNotationsMap::setNamedItem: not implemented (#i113683#)"); + return Reference< XNode >(); + } + + /** + Adds a node using its namespaceURI and localName. + */ + Reference< XNode > SAL_CALL + CNotationsMap::setNamedItemNS(Reference< XNode > const& /*arg*/) + { + OSL_ENSURE(false, + "CNotationsMap::setNamedItemNS: not implemented (#i113683#)"); + return Reference< XNode >(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/notationsmap.hxx b/unoxml/source/dom/notationsmap.hxx new file mode 100644 index 000000000..9dac7d968 --- /dev/null +++ b/unoxml/source/dom/notationsmap.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNamedNodeMap.hpp> + +#include <cppuhelper/implbase.hxx> + +namespace DOM +{ + class CDocumentType; + + class CNotationsMap + : public cppu::WeakImplHelper< css::xml::dom::XNamedNodeMap > + { + public: + CNotationsMap(); + + /** + The number of nodes in this map. + */ + virtual sal_Int32 SAL_CALL getLength() override; + + /** + Retrieves a node specified by local name + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + getNamedItem(OUString const& name) override; + + /** + Retrieves a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + Returns the indexth item in the map. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + item(sal_Int32 index) override; + + /** + Removes a node specified by name. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + removeNamedItem(OUString const& name) override; + + /** + // Removes a node specified by local name and namespace URI. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeNamedItemNS( + OUString const& namespaceURI, OUString const& localName) override; + + /** + // Adds a node using its nodeName attribute. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItem(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + + /** + Adds a node using its namespaceURI and localName. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL + setNamedItemNS(css::uno::Reference< css::xml::dom::XNode > const& arg) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/processinginstruction.cxx b/unoxml/source/dom/processinginstruction.cxx new file mode 100644 index 000000000..9eda0b904 --- /dev/null +++ b/unoxml/source/dom/processinginstruction.cxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "processinginstruction.hxx" + +#include <string.h> + +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; + +namespace DOM +{ + CProcessingInstruction::CProcessingInstruction( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CProcessingInstruction_Base(rDocument, rMutex, + NodeType_PROCESSING_INSTRUCTION_NODE, pNode) + { + } + + void CProcessingInstruction::saxify( + const Reference< XDocumentHandler >& i_xHandler) { + if (!i_xHandler.is()) throw RuntimeException(); + Reference< XExtendedDocumentHandler > xExtended(i_xHandler, UNO_QUERY); + if (xExtended.is()) { + xExtended->processingInstruction(getTarget(), getData()); + } + } + + /** + The content of this processing instruction. + */ + OUString SAL_CALL + CProcessingInstruction::getData() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + + char const*const pContent( + reinterpret_cast<char const*>(m_aNodePtr->content)); + if (nullptr == pContent) { + return OUString(); + } + OUString const ret(pContent, strlen(pContent), RTL_TEXTENCODING_UTF8); + return ret; + } + + /** + The target of this processing instruction. + */ + OUString SAL_CALL + CProcessingInstruction::getTarget() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + + char const*const pName( + reinterpret_cast<char const*>(m_aNodePtr->name)); + if (nullptr == pName) { + return OUString(); + } + OUString const ret(pName, strlen(pName), RTL_TEXTENCODING_UTF8); + return ret; + } + + /** + The content of this processing instruction. + */ + void SAL_CALL CProcessingInstruction::setData(OUString const& rData) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + throw RuntimeException(); + } + + OString const data( + OUStringToOString(rData, RTL_TEXTENCODING_UTF8)); + xmlChar const*const pData( + reinterpret_cast<xmlChar const*>(data.getStr()) ); + xmlFree(m_aNodePtr->content); + m_aNodePtr->content = xmlStrdup(pData); + } + + OUString SAL_CALL + CProcessingInstruction::getNodeName() + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_aNodePtr) { + return OUString(); + } + + char const*const pName = + reinterpret_cast<char const*>(m_aNodePtr->name); + OUString const ret(pName, strlen(pName), RTL_TEXTENCODING_UTF8); + return ret; + } + + OUString SAL_CALL CProcessingInstruction::getNodeValue() + { + return getData(); + } + + void SAL_CALL + CProcessingInstruction::setNodeValue(OUString const& rNodeValue) + { + return setData(rNodeValue); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/processinginstruction.hxx b/unoxml/source/dom/processinginstruction.hxx new file mode 100644 index 000000000..e2c027816 --- /dev/null +++ b/unoxml/source/dom/processinginstruction.hxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XProcessingInstruction.hpp> + +#include <cppuhelper/implbase.hxx> +#include <node.hxx> + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CNode, css::xml::dom::XProcessingInstruction > + CProcessingInstruction_Base; + + class CProcessingInstruction + : public CProcessingInstruction_Base + { + private: + friend class CDocument; + + CProcessingInstruction( + CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + /** + The content of this processing instruction. + */ + virtual OUString SAL_CALL getData() override; + + /** + The target of this processing instruction. + */ + virtual OUString SAL_CALL getTarget() override; + + /** + The content of this processing instruction. + */ + virtual void SAL_CALL setData(const OUString& data) override; + + // ---- resolve uno inheritance problems... + // overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + virtual OUString SAL_CALL getNodeValue() override; + virtual void SAL_CALL setNodeValue(OUString const& rNodeValue) override; + + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CNode::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CNode::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CNode::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CNode::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CNode::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CNode::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CNode::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CNode::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CNode::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CNode::getNodeType(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CNode::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CNode::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CNode::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CNode::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CNode::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CNode::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CNode::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CNode::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CNode::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CNode::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CNode::setPrefix(prefix); + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/saxbuilder.cxx b/unoxml/source/dom/saxbuilder.cxx new file mode 100644 index 000000000..61b7a496a --- /dev/null +++ b/unoxml/source/dom/saxbuilder.cxx @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "saxbuilder.hxx" + +#include <com/sun/star/xml/dom/DocumentBuilder.hpp> +#include <com/sun/star/xml/sax/SAXException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <sax/fastattribs.hxx> +#include <xmloff/xmlimp.hxx> + +using namespace css::lang; +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; + +namespace DOM +{ + CSAXDocumentBuilder::CSAXDocumentBuilder(const Reference< XComponentContext >& ctx) + : m_xContext(ctx) + , m_aState( SAXDocumentBuilderState_READY) + {} + + Sequence< OUString > SAL_CALL CSAXDocumentBuilder::getSupportedServiceNames() + { + return { "com.sun.star.xml.dom.SAXDocumentBuilder" }; + } + + OUString SAL_CALL CSAXDocumentBuilder::getImplementationName() + { + return "com.sun.star.comp.xml.dom.SAXDocumentBuilder"; + } + + sal_Bool SAL_CALL CSAXDocumentBuilder::supportsService(const OUString& aServiceName) + { + return cppu::supportsService(this, aServiceName); + } + + SAXDocumentBuilderState SAL_CALL CSAXDocumentBuilder::getState() + { + std::scoped_lock g(m_Mutex); + + return m_aState; + } + + void SAL_CALL CSAXDocumentBuilder::reset() + { + std::scoped_lock g(m_Mutex); + + m_aDocument.clear(); + m_aFragment.clear(); + while (!m_aNodeStack.empty()) m_aNodeStack.pop(); + m_aState = SAXDocumentBuilderState_READY; + } + + Reference< XDocument > SAL_CALL CSAXDocumentBuilder::getDocument() + { + std::scoped_lock g(m_Mutex); + + if (m_aState != SAXDocumentBuilderState_DOCUMENT_FINISHED) + throw RuntimeException(); + + return m_aDocument; + } + + Reference< XDocumentFragment > SAL_CALL CSAXDocumentBuilder::getDocumentFragment() + { + std::scoped_lock g(m_Mutex); + + if (m_aState != SAXDocumentBuilderState_FRAGMENT_FINISHED) + throw RuntimeException(); + return m_aFragment; + } + + void SAL_CALL CSAXDocumentBuilder::startDocumentFragment(const Reference< XDocument >& ownerDoc) + { + std::scoped_lock g(m_Mutex); + + // start a new document fragment and push it onto the stack + // we have to be in a clean state to do this + if (m_aState != SAXDocumentBuilderState_READY) + throw RuntimeException(); + + m_aDocument = ownerDoc; + Reference< XDocumentFragment > aFragment = m_aDocument->createDocumentFragment(); + m_aNodeStack.push(aFragment); + m_aFragment = aFragment; + m_aState = SAXDocumentBuilderState_BUILDING_FRAGMENT; + } + + void SAL_CALL CSAXDocumentBuilder::endDocumentFragment() + { + std::scoped_lock g(m_Mutex); + + // there should only be the document left on the node stack + if (m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + throw RuntimeException(); + + Reference< XNode > aNode = m_aNodeStack.top(); + if ( aNode->getNodeType() != NodeType_DOCUMENT_FRAGMENT_NODE) + throw RuntimeException(); + m_aNodeStack.pop(); + m_aState = SAXDocumentBuilderState_FRAGMENT_FINISHED; + } + + //XFastDocumentHandler + void SAL_CALL CSAXDocumentBuilder::startDocument() + { + std::scoped_lock g(m_Mutex); + + // start a new document and push it onto the stack + // we have to be in a clean state to do this + if (m_aState != SAXDocumentBuilderState_READY) + throw SAXException(); + + Reference< XDocumentBuilder > aBuilder(DocumentBuilder::create(m_xContext)); + Reference< XDocument > aDocument = aBuilder->newDocument(); + m_aNodeStack.push(aDocument); + m_aDocument = aDocument; + m_aState = SAXDocumentBuilderState_BUILDING_DOCUMENT; + } + + void SAL_CALL CSAXDocumentBuilder::endDocument() + { + std::scoped_lock g(m_Mutex); + + // there should only be the document left on the node stack + if (m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT) + throw SAXException(); + + Reference< XNode > aNode = m_aNodeStack.top(); + if ( aNode->getNodeType() != NodeType_DOCUMENT_NODE) + throw SAXException(); + m_aNodeStack.pop(); + m_aState = SAXDocumentBuilderState_DOCUMENT_FINISHED; + } + + void SAL_CALL CSAXDocumentBuilder::processingInstruction( const OUString& rTarget, const OUString& rData ) + { + std::scoped_lock g(m_Mutex); + + // append PI node to the current top + if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + throw SAXException(); + + Reference< XProcessingInstruction > aInstruction = m_aDocument->createProcessingInstruction( + rTarget, rData); + m_aNodeStack.top()->appendChild(aInstruction); + } + + void SAL_CALL CSAXDocumentBuilder::setDocumentLocator( const Reference< XLocator >& ) + { + } + + void SAL_CALL CSAXDocumentBuilder::startFastElement( sal_Int32 nElement , const Reference< XFastAttributeList >& xAttribs ) + { + std::scoped_lock g(m_Mutex); + + if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + { + throw SAXException(); + } + + Reference< XElement > aElement; + const OUString& aPrefix(SvXMLImport::getNamespacePrefixFromToken(nElement, nullptr)); + const OUString& aURI( SvXMLImport::getNamespaceURIFromToken( nElement ) ); + OUString aQualifiedName( SvXMLImport::getNameFromToken( nElement ) ); + if( !aPrefix.isEmpty() ) + aQualifiedName = aPrefix + SvXMLImport::aNamespaceSeparator + aQualifiedName; + + if ( !aURI.isEmpty() ) + { + // found a URI for prefix + // qualified name + aElement = m_aDocument->createElementNS( aURI, aQualifiedName ); + } + else + { + // no URI for prefix + aElement = m_aDocument->createElement( aQualifiedName ); + } + aElement.set( m_aNodeStack.top()->appendChild(aElement), UNO_QUERY); + m_aNodeStack.push(aElement); + + if (xAttribs.is()) + setElementFastAttributes(aElement, xAttribs); + } + + // For arbitrary meta elements + void SAL_CALL CSAXDocumentBuilder::startUnknownElement( const OUString& rNamespace, const OUString& rName, const Reference< XFastAttributeList >& xAttribs ) + { + std::scoped_lock g(m_Mutex); + + if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + { + throw SAXException(); + } + + Reference< XElement > aElement; + if ( !rNamespace.isEmpty() ) + aElement = m_aDocument->createElementNS( rNamespace, rName ); + else + aElement = m_aDocument->createElement( rName ); + + aElement.set( m_aNodeStack.top()->appendChild(aElement), UNO_QUERY); + m_aNodeStack.push(aElement); + + if (!xAttribs.is()) + return; + + setElementFastAttributes(aElement, xAttribs); + const Sequence< css::xml::Attribute > unknownAttribs = xAttribs->getUnknownAttributes(); + for ( const auto& rUnknownAttrib : unknownAttribs ) + { + const OUString& rAttrValue = rUnknownAttrib.Value; + const OUString& rAttrName = rUnknownAttrib.Name; + const OUString& rAttrNamespace = rUnknownAttrib.NamespaceURL; + if ( !rAttrNamespace.isEmpty() ) + aElement->setAttributeNS( rAttrNamespace, rAttrName, rAttrValue ); + else + aElement->setAttribute( rAttrName, rAttrValue ); + } + } + + void CSAXDocumentBuilder::setElementFastAttributes(const Reference< XElement >& aElement, const Reference< XFastAttributeList >& xAttribs) + { + for (auto &it : sax_fastparser::castToFastAttributeList( xAttribs )) + { + sal_Int32 nAttrToken = it.getToken(); + const OUString& aAttrPrefix(SvXMLImport::getNamespacePrefixFromToken(nAttrToken, nullptr)); + const OUString& aAttrURI( SvXMLImport::getNamespaceURIFromToken( nAttrToken ) ); + OUString aAttrQualifiedName( SvXMLImport::getNameFromToken( nAttrToken ) ); + if( !aAttrPrefix.isEmpty() ) + aAttrQualifiedName = aAttrPrefix + SvXMLImport::aNamespaceSeparator + aAttrQualifiedName; + + if ( !aAttrURI.isEmpty() ) + aElement->setAttributeNS( aAttrURI, aAttrQualifiedName, it.toString() ); + else + aElement->setAttribute( aAttrQualifiedName, it.toString() ); + } + } + + void SAL_CALL CSAXDocumentBuilder::endFastElement( sal_Int32 nElement ) + { + std::scoped_lock g(m_Mutex); + + // pop the current element from the stack + if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + throw SAXException(); + + Reference< XNode > aNode(m_aNodeStack.top()); + if (aNode->getNodeType() != NodeType_ELEMENT_NODE) + throw SAXException(); + + Reference< XElement > aElement(aNode, UNO_QUERY); + if( aElement->getPrefix() != SvXMLImport::getNamespacePrefixFromToken(nElement, nullptr) || + aElement->getTagName() != SvXMLImport::getNameFromToken( nElement ) ) // consistency check + throw SAXException(); + + // pop it + m_aNodeStack.pop(); + } + + + void SAL_CALL CSAXDocumentBuilder::endUnknownElement( const OUString& /*rNamespace*/, const OUString& rName ) + { + std::scoped_lock g(m_Mutex); + + // pop the current element from the stack + if ( m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + throw SAXException(); + + Reference< XNode > aNode(m_aNodeStack.top()); + if (aNode->getNodeType() != NodeType_ELEMENT_NODE) + throw SAXException(); + + Reference< XElement > aElement(aNode, UNO_QUERY); + OUString aRefName; + const OUString& aPrefix = aElement->getPrefix(); + if (!aPrefix.isEmpty()) + aRefName = aPrefix + SvXMLImport::aNamespaceSeparator + aElement->getTagName(); + else + aRefName = aElement->getTagName(); + if (aRefName != rName) // consistency check + throw SAXException(); + + // pop it + m_aNodeStack.pop(); + } + + Reference< XFastContextHandler > SAL_CALL CSAXDocumentBuilder::createFastChildContext( sal_Int32/* nElement */, const Reference< XFastAttributeList >&/* xAttribs */ ) + { + return nullptr; + } + + + Reference< XFastContextHandler > SAL_CALL CSAXDocumentBuilder::createUnknownChildContext( const OUString&/* rNamespace */, const OUString&/* rName */, const Reference< XFastAttributeList >&/* xAttribs */ ) + { + return nullptr; + } + + void SAL_CALL CSAXDocumentBuilder::characters( const OUString& rChars ) + { + std::scoped_lock g(m_Mutex); + + // append text node to the current top element + if (m_aState != SAXDocumentBuilderState_BUILDING_DOCUMENT && + m_aState != SAXDocumentBuilderState_BUILDING_FRAGMENT) + throw SAXException(); + + Reference< XText > aText = m_aDocument->createTextNode(rChars); + m_aNodeStack.top()->appendChild(aText); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CSAXDocumentBuilder_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new DOM::CSAXDocumentBuilder(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/saxbuilder.hxx b/unoxml/source/dom/saxbuilder.hxx new file mode 100644 index 000000000..83b82912e --- /dev/null +++ b/unoxml/source/dom/saxbuilder.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <mutex> +#include <stack> + +#include <sal/types.h> +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> + +#include <com/sun/star/xml/dom/XSAXDocumentBuilder2.hpp> +#include <com/sun/star/xml/dom/SAXDocumentBuilderState.hpp> +#include <com/sun/star/xml/dom/XDocument.hpp> +#include <com/sun/star/xml/dom/XDocumentFragment.hpp> +#include <com/sun/star/xml/sax/XLocator.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace DOM +{ + class CSAXDocumentBuilder + : public ::cppu::WeakImplHelper< css::xml::dom::XSAXDocumentBuilder2, css::lang::XServiceInfo > + { + + private: + std::mutex m_Mutex; + const css::uno::Reference< css::uno::XComponentContext> m_xContext; + + css::xml::dom::SAXDocumentBuilderState m_aState; + std::stack< css::uno::Reference< css::xml::dom::XNode > > m_aNodeStack; + + css::uno::Reference< css::xml::dom::XDocument > m_aDocument; + css::uno::Reference< css::xml::dom::XDocumentFragment > m_aFragment; + + + public: + explicit CSAXDocumentBuilder(const css::uno::Reference< css::uno::XComponentContext >& ); + static void setElementFastAttributes(const css::uno::Reference< css::xml::dom::XElement >& aElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttribs); + + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + // XFastDocumentHandler + virtual void SAL_CALL startDocument() override; + virtual void SAL_CALL endDocument() override; + virtual void SAL_CALL processingInstruction( const OUString& rTarget, const OUString& rData ) override; + virtual void SAL_CALL setDocumentLocator( const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override; + + // XFastContextHandler + virtual void SAL_CALL startFastElement( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; + virtual void SAL_CALL startUnknownElement( const OUString& Namespace, const OUString& Name, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; + virtual void SAL_CALL endFastElement( sal_Int32 Element ) override; + virtual void SAL_CALL endUnknownElement( const OUString& Namespace, const OUString& Name ) override; + virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; + virtual css::uno::Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& Namespace, const OUString& Name, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs ) override; + virtual void SAL_CALL characters( const OUString& aChars ) override; + + // XSAXDocumentBuilder + virtual css::xml::dom::SAXDocumentBuilderState SAL_CALL getState() override; + virtual void SAL_CALL reset() override; + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getDocument() override; + virtual css::uno::Reference< css::xml::dom::XDocumentFragment > SAL_CALL getDocumentFragment() override; + virtual void SAL_CALL startDocumentFragment(const css::uno::Reference< css::xml::dom::XDocument >& ownerDoc) override; + virtual void SAL_CALL endDocumentFragment() override; + + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/text.cxx b/unoxml/source/dom/text.cxx new file mode 100644 index 000000000..08db01ba3 --- /dev/null +++ b/unoxml/source/dom/text.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "text.hxx" + +#include <osl/diagnose.h> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::sax; + +namespace DOM +{ + CText::CText(CDocument const& rDocument, ::osl::Mutex const& rMutex, + NodeType const& reNodeType, xmlNodePtr const& rpNode) + : CText_Base(rDocument, rMutex, reNodeType, rpNode) + { + } + + CText::CText(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode) + : CText_Base(rDocument, rMutex, NodeType_TEXT_NODE, pNode) + { + } + + void CText::saxify( + const Reference< XDocumentHandler >& i_xHandler) { + if (!i_xHandler.is()) throw RuntimeException(); + i_xHandler->characters(getData()); + } + + void CText::fastSaxify( Context& io_rContext ) + { + if (io_rContext.mxCurrentHandler.is()) + { + try + { + io_rContext.mxCurrentHandler->characters( getData() ); + } + catch( Exception& ) + {} + } + } + + OUString SAL_CALL CText::getNodeName() + { + return "#text"; + } + + Reference< XText > SAL_CALL CText::splitText(sal_Int32 /*offset*/) + { + OSL_FAIL("CText::splitText: not implemented (#i113683#)"); + return Reference< XText >(this); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/dom/text.hxx b/unoxml/source/dom/text.hxx new file mode 100644 index 000000000..d594b1586 --- /dev/null +++ b/unoxml/source/dom/text.hxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <libxml/tree.h> + +#include <sal/types.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XText.hpp> + +#include "characterdata.hxx" + +namespace DOM +{ + typedef ::cppu::ImplInheritanceHelper< CCharacterData, css::xml::dom::XText > CText_Base; + + class CText + : public CText_Base + { + private: + friend class CDocument; + + protected: + CText(CDocument const& rDocument, ::osl::Mutex const& rMutex, + css::xml::dom::NodeType const& reNodeType, xmlNodePtr const& rpNode); + CText(CDocument const& rDocument, ::osl::Mutex const& rMutex, + xmlNodePtr const pNode); + + public: + + virtual void saxify(const css::uno::Reference< css::xml::sax::XDocumentHandler >& i_xHandler) override; + + virtual void fastSaxify( Context& io_rContext ) override; + + // Breaks this node into two nodes at the specified offset, keeping + // both in the tree as siblings. + virtual css::uno::Reference< css::xml::dom::XText > SAL_CALL splitText(sal_Int32 offset) override; + + + // --- delegations for XCharacterData + virtual void SAL_CALL appendData(const OUString& arg) override + { + CCharacterData::appendData(arg); + } + virtual void SAL_CALL deleteData(sal_Int32 offset, sal_Int32 count) override + { + CCharacterData::deleteData(offset, count); + } + virtual OUString SAL_CALL getData() override + { + return CCharacterData::getData(); + } + virtual sal_Int32 SAL_CALL getLength() override + { + return CCharacterData::getLength(); + } + virtual void SAL_CALL insertData(sal_Int32 offset, const OUString& arg) override + { + CCharacterData::insertData(offset, arg); + } + virtual void SAL_CALL replaceData(sal_Int32 offset, sal_Int32 count, const OUString& arg) override + { + CCharacterData::replaceData(offset, count, arg); + } + virtual void SAL_CALL setData(const OUString& data) override + { + CCharacterData::setData(data); + } + virtual OUString SAL_CALL subStringData(sal_Int32 offset, sal_Int32 count) override + { + return CCharacterData::subStringData(offset, count); + } + + + // --- overrides for XNode base + virtual OUString SAL_CALL getNodeName() override; + + // --- resolve uno inheritance problems... + // --- delegation for XNode base. + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL appendChild(const css::uno::Reference< css::xml::dom::XNode >& newChild) override + { + return CCharacterData::appendChild(newChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL cloneNode(sal_Bool deep) override + { + return CCharacterData::cloneNode(deep); + } + virtual css::uno::Reference< css::xml::dom::XNamedNodeMap > SAL_CALL getAttributes() override + { + return CCharacterData::getAttributes(); + } + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getChildNodes() override + { + return CCharacterData::getChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getFirstChild() override + { + return CCharacterData::getFirstChild(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getLastChild() override + { + return CCharacterData::getLastChild(); + } + virtual OUString SAL_CALL getLocalName() override + { + return CCharacterData::getLocalName(); + } + virtual OUString SAL_CALL getNamespaceURI() override + { + return CCharacterData::getNamespaceURI(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getNextSibling() override + { + return CCharacterData::getNextSibling(); + } + virtual css::xml::dom::NodeType SAL_CALL getNodeType() override + { + return CCharacterData::getNodeType(); + } + virtual OUString SAL_CALL getNodeValue() override + { + return CCharacterData::getNodeValue(); + } + virtual css::uno::Reference< css::xml::dom::XDocument > SAL_CALL getOwnerDocument() override + { + return CCharacterData::getOwnerDocument(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getParentNode() override + { + return CCharacterData::getParentNode(); + } + virtual OUString SAL_CALL getPrefix() override + { + return CCharacterData::getPrefix(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL getPreviousSibling() override + { + return CCharacterData::getPreviousSibling(); + } + virtual sal_Bool SAL_CALL hasAttributes() override + { + return CCharacterData::hasAttributes(); + } + virtual sal_Bool SAL_CALL hasChildNodes() override + { + return CCharacterData::hasChildNodes(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL insertBefore( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& refChild) override + { + return CCharacterData::insertBefore(newChild, refChild); + } + virtual sal_Bool SAL_CALL isSupported(const OUString& feature, const OUString& ver) override + { + return CCharacterData::isSupported(feature, ver); + } + virtual void SAL_CALL normalize() override + { + CCharacterData::normalize(); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL removeChild(const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CCharacterData::removeChild(oldChild); + } + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL replaceChild( + const css::uno::Reference< css::xml::dom::XNode >& newChild, const css::uno::Reference< css::xml::dom::XNode >& oldChild) override + { + return CCharacterData::replaceChild(newChild, oldChild); + } + virtual void SAL_CALL setNodeValue(const OUString& nodeValue) override + { + return CCharacterData::setNodeValue(nodeValue); + } + virtual void SAL_CALL setPrefix(const OUString& prefix) override + { + return CCharacterData::setPrefix(prefix); + } + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/events/event.cxx b/unoxml/source/events/event.cxx new file mode 100644 index 000000000..e3b092ff3 --- /dev/null +++ b/unoxml/source/events/event.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <event.hxx> + +using namespace css::uno; +using namespace css::xml::dom::events; + +namespace DOM::events +{ + + CEvent::CEvent() + : m_canceled(false) + , m_phase(PhaseType_CAPTURING_PHASE) + , m_bubbles(false) + , m_cancelable(true) + { + } + + CEvent::~CEvent() + { + } + + OUString SAL_CALL CEvent::getType() + { + std::unique_lock const g(m_Mutex); + return m_eventType; + } + + Reference< XEventTarget > SAL_CALL + CEvent::getTarget() + { + std::unique_lock const g(m_Mutex); + return m_target; + } + + Reference< XEventTarget > SAL_CALL + CEvent::getCurrentTarget() + { + std::unique_lock const g(m_Mutex); + return m_currentTarget; + } + + PhaseType SAL_CALL CEvent::getEventPhase() + { + std::unique_lock const g(m_Mutex); + return m_phase; + } + + sal_Bool SAL_CALL CEvent::getBubbles() + { + std::unique_lock const g(m_Mutex); + return m_bubbles; + } + + sal_Bool SAL_CALL CEvent::getCancelable() + { + std::unique_lock const g(m_Mutex); + return m_cancelable; + } + + css::util::Time SAL_CALL + CEvent::getTimeStamp() + { + std::unique_lock const g(m_Mutex); + return m_time; + } + + void SAL_CALL CEvent::stopPropagation() + { + std::unique_lock const g(m_Mutex); + if (m_cancelable) { m_canceled = true; } + } + + void SAL_CALL CEvent::preventDefault() + { + } + + void SAL_CALL + CEvent::initEvent(OUString const& eventTypeArg, sal_Bool canBubbleArg, + sal_Bool cancelableArg) + { + std::unique_lock const g(m_Mutex); + + m_eventType = eventTypeArg; + m_bubbles = canBubbleArg; + m_cancelable = cancelableArg; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/events/eventdispatcher.cxx b/unoxml/source/events/eventdispatcher.cxx new file mode 100644 index 000000000..201f682f7 --- /dev/null +++ b/unoxml/source/events/eventdispatcher.cxx @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <eventdispatcher.hxx> + +#include <event.hxx> +#include <mutationevent.hxx> +#include <uievent.hxx> +#include <mouseevent.hxx> + +#include "../dom/document.hxx" + +#include <osl/mutex.hxx> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; + +namespace DOM::events { + + void CEventDispatcher::addListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture) + { + TypeListenerMap *const pTMap = bCapture + ? (& m_CaptureListeners) : (& m_TargetListeners); + + // get the multimap for the specified type + ListenerMap *pMap = nullptr; + auto tIter = pTMap->find(aType); + if (tIter == pTMap->end()) { + // the map has to be created + auto const pair = pTMap->emplace(aType, ListenerMap()); + pMap = & pair.first->second; + } else { + pMap = & tIter->second; + } + assert(pMap != nullptr); + pMap->emplace(pNode, aListener); + } + + void CEventDispatcher::removeListener(xmlNodePtr pNode, const OUString& aType, const Reference<XEventListener>& aListener, bool bCapture) + { + TypeListenerMap *const pTMap = bCapture + ? (& m_CaptureListeners) : (& m_TargetListeners); + + // get the multimap for the specified type + auto tIter = pTMap->find(aType); + if (tIter == pTMap->end()) + return; + + ListenerMap & rMap = tIter->second; + // find listeners of specified type for specified node + ListenerMap::iterator iter = rMap.find(pNode); + while (iter != rMap.end() && iter->first == pNode) + { + // erase all references to specified listener + if (iter->second.is() && iter->second == aListener) + { + iter = rMap.erase(iter); + } + else + ++iter; + } + } + + CEventDispatcher::~CEventDispatcher() + { + } + + void CEventDispatcher::callListeners( + TypeListenerMap const& rTMap, + xmlNodePtr const pNode, + const OUString& aType, Reference< XEvent > const& xEvent) + { + // get the multimap for the specified type + TypeListenerMap::const_iterator tIter = rTMap.find(aType); + if (tIter != rTMap.end()) { + ListenerMap const& rMap = tIter->second; + auto iterRange = rMap.equal_range(pNode); + for( auto iter = iterRange.first; iter != iterRange.second; ++iter ) + { + if(iter->second.is()) + (iter->second)->handleEvent(xEvent); + } + } + } + + void CEventDispatcher::dispatchEvent( + DOM::CDocument & rDocument, ::osl::Mutex & rMutex, + xmlNodePtr const pNode, Reference<XNode> const& xNode, + Reference< XEvent > const& i_xEvent) const + { + TypeListenerMap captureListeners; + TypeListenerMap targetListeners; + { + ::osl::MutexGuard g(rMutex); + + captureListeners = m_CaptureListeners; + targetListeners = m_TargetListeners; + } + + if (captureListeners.empty() && targetListeners.empty()) + return; + + rtl::Reference<CEvent> pEvent; // pointer to internal event representation + + OUString const aType = i_xEvent->getType(); + if (aType == "DOMSubtreeModified" || + aType == "DOMNodeInserted" || + aType == "DOMNodeRemoved" || + aType == "DOMNodeRemovedFromDocument" || + aType == "DOMNodeInsertedIntoDocument" || + aType == "DOMAttrModified" || + aType == "DOMCharacterDataModified" ) + { + Reference< XMutationEvent > const aMEvent(i_xEvent, + UNO_QUERY_THROW); + // dispatch a mutation event + // we need to clone the event in order to have complete control + // over the implementation + rtl::Reference<CMutationEvent> pMEvent = new CMutationEvent; + pMEvent->initMutationEvent( + aType, aMEvent->getBubbles(), aMEvent->getCancelable(), + aMEvent->getRelatedNode(), aMEvent->getPrevValue(), + aMEvent->getNewValue(), aMEvent->getAttrName(), + aMEvent->getAttrChange()); + pEvent = pMEvent; + } else if ( // UIEvent + aType == "DOMFocusIn" || + aType == "DOMFocusOut" || + aType == "DOMActivate" ) + { + Reference< XUIEvent > const aUIEvent(i_xEvent, UNO_QUERY_THROW); + rtl::Reference<CUIEvent> pUIEvent = new CUIEvent; + pUIEvent->initUIEvent(aType, + aUIEvent->getBubbles(), aUIEvent->getCancelable(), + aUIEvent->getView(), aUIEvent->getDetail()); + pEvent = pUIEvent; + } else if ( // MouseEvent + aType == "click" || + aType == "mousedown" || + aType == "mouseup" || + aType == "mouseover" || + aType == "mousemove" || + aType == "mouseout" ) + { + Reference< XMouseEvent > const aMouseEvent(i_xEvent, + UNO_QUERY_THROW); + rtl::Reference<CMouseEvent> pMouseEvent = new CMouseEvent; + pMouseEvent->initMouseEvent(aType, + aMouseEvent->getBubbles(), aMouseEvent->getCancelable(), + aMouseEvent->getView(), aMouseEvent->getDetail(), + aMouseEvent->getScreenX(), aMouseEvent->getScreenY(), + aMouseEvent->getClientX(), aMouseEvent->getClientY(), + aMouseEvent->getCtrlKey(), aMouseEvent->getAltKey(), + aMouseEvent->getShiftKey(), aMouseEvent->getMetaKey(), + aMouseEvent->getButton(), aMouseEvent->getRelatedTarget()); + pEvent = pMouseEvent; + } + else // generic event + { + pEvent = new CEvent; + pEvent->initEvent( + aType, i_xEvent->getBubbles(), i_xEvent->getCancelable()); + } + pEvent->m_target.set(xNode, UNO_QUERY_THROW); + pEvent->m_currentTarget = i_xEvent->getCurrentTarget(); + pEvent->m_time = i_xEvent->getTimeStamp(); + + // create the reference to the private event implementation + // that will be dispatched to the listeners + Reference< XEvent > const xEvent(pEvent); + + // build the path from target node to the root + typedef std::vector< ::std::pair<Reference<XEventTarget>, xmlNodePtr> > + NodeVector_t; + NodeVector_t captureVector; + { + ::osl::MutexGuard g(rMutex); + + xmlNodePtr cur = pNode; + while (cur != nullptr) + { + Reference< XEventTarget > const xRef( + rDocument.GetCNode(cur)); + captureVector.emplace_back(xRef, cur); + cur = cur->parent; + } + } + + // the capture vector now holds the node path from target to root + // first we must search for capture listeners in order root to + // to target. after that, any target listeners have to be called + // then bubbeling phase listeners are called in target to root + // order + // start at the root + NodeVector_t::const_reverse_iterator rinode = + const_cast<NodeVector_t const&>(captureVector).rbegin(); + if (rinode == const_cast<NodeVector_t const&>(captureVector).rend()) + return; + + // capturing phase: + pEvent->m_phase = PhaseType_CAPTURING_PHASE; + while (rinode != + const_cast<NodeVector_t const&>(captureVector).rend()) + { + pEvent->m_currentTarget = rinode->first; + callListeners(captureListeners, rinode->second, aType, xEvent); + if (pEvent->m_canceled) return; + ++rinode; + } + + NodeVector_t::const_iterator inode = captureVector.begin(); + + // target phase + pEvent->m_phase = PhaseType_AT_TARGET; + pEvent->m_currentTarget = inode->first; + callListeners(targetListeners, inode->second, aType, xEvent); + if (pEvent->m_canceled) return; + // bubbeling phase + ++inode; + if (i_xEvent->getBubbles()) { + pEvent->m_phase = PhaseType_BUBBLING_PHASE; + while (inode != captureVector.end()) + { + pEvent->m_currentTarget = inode->first; + callListeners(targetListeners, + inode->second, aType, xEvent); + if (pEvent->m_canceled) return; + ++inode; + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/events/mouseevent.cxx b/unoxml/source/events/mouseevent.cxx new file mode 100644 index 000000000..4ae8a1b55 --- /dev/null +++ b/unoxml/source/events/mouseevent.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <mouseevent.hxx> + +using namespace css::uno; +using namespace css::xml::dom::events; +using namespace css::xml::dom::views; + +namespace DOM::events +{ + CMouseEvent::CMouseEvent() + : m_screenX(0) + , m_screenY(0) + , m_clientX(0) + , m_clientY(0) + , m_ctrlKey(false) + , m_shiftKey(false) + , m_altKey(false) + , m_metaKey(false) + , m_button(0) + { + } + + sal_Int32 SAL_CALL CMouseEvent::getScreenX() + { + std::unique_lock const g(m_Mutex); + return m_screenX; + } + sal_Int32 SAL_CALL CMouseEvent::getScreenY() + { + std::unique_lock const g(m_Mutex); + return m_screenY; + } + sal_Int32 SAL_CALL CMouseEvent::getClientX() + { + std::unique_lock const g(m_Mutex); + return m_clientX; + } + sal_Int32 SAL_CALL CMouseEvent::getClientY() + { + std::unique_lock const g(m_Mutex); + return m_clientY; + } + sal_Bool SAL_CALL CMouseEvent::getCtrlKey() + { + std::unique_lock const g(m_Mutex); + return m_ctrlKey; + } + sal_Bool SAL_CALL CMouseEvent::getShiftKey() + { + std::unique_lock const g(m_Mutex); + return m_shiftKey; + } + sal_Bool SAL_CALL CMouseEvent::getAltKey() + { + std::unique_lock const g(m_Mutex); + return m_altKey; + } + sal_Bool SAL_CALL CMouseEvent::getMetaKey() + { + std::unique_lock const g(m_Mutex); + return m_metaKey; + } + sal_Int16 SAL_CALL CMouseEvent::getButton() + { + std::unique_lock const g(m_Mutex); + return m_button; + } + Reference< XEventTarget > SAL_CALL CMouseEvent::getRelatedTarget() + { + return Reference< XEventTarget >(); + } + + void SAL_CALL CMouseEvent::initMouseEvent( + const OUString& typeArg, + sal_Bool canBubbleArg, + sal_Bool cancelableArg, + const Reference< XAbstractView >& viewArg, + sal_Int32 detailArg, + sal_Int32 screenXArg, + sal_Int32 screenYArg, + sal_Int32 clientXArg, + sal_Int32 clientYArg, + sal_Bool ctrlKeyArg, + sal_Bool altKeyArg, + sal_Bool shiftKeyArg, + sal_Bool metaKeyArg, + sal_Int16 buttonArg, + const Reference< XEventTarget >& /*relatedTargetArg*/) + { + CUIEvent::initUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); + std::unique_lock const g(m_Mutex); + m_screenX = screenXArg; + m_screenY = screenYArg; + m_clientX = clientXArg; + m_clientY = clientYArg; + m_ctrlKey = ctrlKeyArg; + m_altKey = altKeyArg; + m_shiftKey = shiftKeyArg; + m_metaKey = metaKeyArg; + m_button = buttonArg; + } + + // delegate to CUIEvent, since we are inheriting from CUIEvent and XUIEvent + Reference< XAbstractView > SAL_CALL CMouseEvent::getView() + { + return CUIEvent::getView(); + } + + sal_Int32 SAL_CALL CMouseEvent::getDetail() + { + return CUIEvent::getDetail(); + } + + void SAL_CALL CMouseEvent::initUIEvent(const OUString& typeArg, + sal_Bool canBubbleArg, + sal_Bool cancelableArg, + const Reference< XAbstractView >& viewArg, + sal_Int32 detailArg) + { + CUIEvent::initUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); + } + + OUString SAL_CALL CMouseEvent::getType() + { + return CUIEvent::getType(); + } + + Reference< XEventTarget > SAL_CALL CMouseEvent::getTarget() + { + return CUIEvent::getTarget(); + } + + Reference< XEventTarget > SAL_CALL CMouseEvent::getCurrentTarget() + { + return CUIEvent::getCurrentTarget(); + } + + PhaseType SAL_CALL CMouseEvent::getEventPhase() + { + return CUIEvent::getEventPhase(); + } + + sal_Bool SAL_CALL CMouseEvent::getBubbles() + { + return CEvent::getBubbles(); + } + + sal_Bool SAL_CALL CMouseEvent::getCancelable() + { + return CUIEvent::getCancelable(); + } + + css::util::Time SAL_CALL CMouseEvent::getTimeStamp() + { + return CUIEvent::getTimeStamp(); + } + + void SAL_CALL CMouseEvent::stopPropagation() + { + CUIEvent::stopPropagation(); + } + + void SAL_CALL CMouseEvent::preventDefault() + { + CUIEvent::preventDefault(); + } + + void SAL_CALL CMouseEvent::initEvent(const OUString& eventTypeArg, sal_Bool canBubbleArg, + sal_Bool cancelableArg) + { + // base initializer + CUIEvent::initEvent(eventTypeArg, canBubbleArg, cancelableArg); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/events/mutationevent.cxx b/unoxml/source/events/mutationevent.cxx new file mode 100644 index 000000000..8ef91f0e3 --- /dev/null +++ b/unoxml/source/events/mutationevent.cxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <mutationevent.hxx> + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::dom::events; + +namespace DOM::events +{ + CMutationEvent::CMutationEvent() + : m_attrChangeType(AttrChangeType_MODIFICATION) + { + } + + CMutationEvent::~CMutationEvent() + { + } + + Reference< XNode > SAL_CALL CMutationEvent::getRelatedNode() + { + std::unique_lock const g(m_Mutex); + return m_relatedNode; + } + + OUString SAL_CALL CMutationEvent::getPrevValue() + { + std::unique_lock const g(m_Mutex); + return m_prevValue; + } + + OUString SAL_CALL CMutationEvent::getNewValue() + { + std::unique_lock const g(m_Mutex); + return m_newValue; + } + + OUString SAL_CALL CMutationEvent::getAttrName() + { + std::unique_lock const g(m_Mutex); + return m_attrName; + } + + AttrChangeType SAL_CALL CMutationEvent::getAttrChange() + { + std::unique_lock const g(m_Mutex); + return m_attrChangeType; + } + + void SAL_CALL CMutationEvent::initMutationEvent(const OUString& typeArg, + sal_Bool canBubbleArg, sal_Bool cancelableArg, + const Reference< XNode >& relatedNodeArg, const OUString& prevValueArg, + const OUString& newValueArg, const OUString& attrNameArg, + AttrChangeType attrChangeArg) + { + CEvent::initEvent(typeArg, canBubbleArg, cancelableArg); + + std::unique_lock const g(m_Mutex); + + m_relatedNode = relatedNodeArg; + m_prevValue = prevValueArg; + m_newValue = newValueArg; + m_attrName = attrNameArg; + m_attrChangeType = attrChangeArg; + } + + // delegate to CEvent, since we are inheriting from CEvent and XEvent + OUString SAL_CALL CMutationEvent::getType() + { + return CEvent::getType(); + } + + Reference< XEventTarget > SAL_CALL CMutationEvent::getTarget() + { + return CEvent::getTarget(); + } + + Reference< XEventTarget > SAL_CALL CMutationEvent::getCurrentTarget() + { + return CEvent::getCurrentTarget(); + } + + PhaseType SAL_CALL CMutationEvent::getEventPhase() + { + return CEvent::getEventPhase(); + } + + sal_Bool SAL_CALL CMutationEvent::getBubbles() + { + return CEvent::getBubbles(); + } + + sal_Bool SAL_CALL CMutationEvent::getCancelable() + { + return CEvent::getCancelable(); + } + + css::util::Time SAL_CALL CMutationEvent::getTimeStamp() + { + return CEvent::getTimeStamp(); + } + + void SAL_CALL CMutationEvent::stopPropagation() + { + CEvent::stopPropagation(); + } + void SAL_CALL CMutationEvent::preventDefault() + { + CEvent::preventDefault(); + } + + void SAL_CALL CMutationEvent::initEvent(const OUString& eventTypeArg, sal_Bool canBubbleArg, + sal_Bool cancelableArg) + { + // base initializer + CEvent::initEvent(eventTypeArg, canBubbleArg, cancelableArg); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/events/uievent.cxx b/unoxml/source/events/uievent.cxx new file mode 100644 index 000000000..be61ca5cf --- /dev/null +++ b/unoxml/source/events/uievent.cxx @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <uievent.hxx> + +using namespace css::uno; +using namespace css::xml::dom::events; +using namespace css::xml::dom::views; + +namespace DOM::events +{ + CUIEvent::CUIEvent() + : m_detail(0) + { + } + + Reference< XAbstractView > SAL_CALL + CUIEvent::getView() + { + std::unique_lock const g(m_Mutex); + return m_view; + } + + sal_Int32 SAL_CALL CUIEvent::getDetail() + { + std::unique_lock const g(m_Mutex); + return m_detail; + } + + void SAL_CALL CUIEvent::initUIEvent(const OUString& typeArg, + sal_Bool canBubbleArg, + sal_Bool cancelableArg, + const Reference< XAbstractView >& viewArg, + sal_Int32 detailArg) + { + CEvent::initEvent(typeArg, canBubbleArg, cancelableArg); + std::unique_lock const g(m_Mutex); + m_view = viewArg; + m_detail = detailArg; + } + + + // delegate to CEvent, since we are inheriting from CEvent and XEvent + OUString SAL_CALL CUIEvent::getType() + { + return CEvent::getType(); + } + + Reference< XEventTarget > SAL_CALL CUIEvent::getTarget() + { + return CEvent::getTarget(); + } + + Reference< XEventTarget > SAL_CALL CUIEvent::getCurrentTarget() + { + return CEvent::getCurrentTarget(); + } + + PhaseType SAL_CALL CUIEvent::getEventPhase() + { + return CEvent::getEventPhase(); + } + + sal_Bool SAL_CALL CUIEvent::getBubbles() + { + return CEvent::getBubbles(); + } + + sal_Bool SAL_CALL CUIEvent::getCancelable() + { + // mutation events cannot be canceled + return false; + } + + css::util::Time SAL_CALL CUIEvent::getTimeStamp() + { + return CEvent::getTimeStamp(); + } + + void SAL_CALL CUIEvent::stopPropagation() + { + CEvent::stopPropagation(); + } + void SAL_CALL CUIEvent::preventDefault() + { + CEvent::preventDefault(); + } + + void SAL_CALL CUIEvent::initEvent(const OUString& eventTypeArg, sal_Bool canBubbleArg, + sal_Bool cancelableArg) + { + // base initializer + CEvent::initEvent(eventTypeArg, canBubbleArg, cancelableArg); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/rdf/CBlankNode.cxx b/unoxml/source/rdf/CBlankNode.cxx new file mode 100644 index 000000000..6e0140cc9 --- /dev/null +++ b/unoxml/source/rdf/CBlankNode.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/rdf/XBlankNode.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + + +/// anonymous implementation namespace +namespace { + +class CBlankNode: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::rdf::XBlankNode> +{ +public: + CBlankNode(); + + // css::lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::lang::XInitialization: + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override; + + // css::rdf::XNode: + virtual OUString SAL_CALL getStringValue() override; + +private: + CBlankNode(CBlankNode const&) = delete; + CBlankNode& operator=(CBlankNode const&) = delete; + + OUString m_NodeID; +}; + +CBlankNode::CBlankNode() +{} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL CBlankNode::getImplementationName() +{ + return "CBlankNode"; +} + +sal_Bool SAL_CALL CBlankNode::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL CBlankNode::getSupportedServiceNames() +{ + return { "com.sun.star.rdf.BlankNode" }; +} + +// css::lang::XInitialization: +void SAL_CALL CBlankNode::initialize(const css::uno::Sequence< css::uno::Any > & aArguments) +{ + if (aArguments.getLength() != 1) { + throw css::lang::IllegalArgumentException( + "CBlankNode::initialize: must give exactly 1 argument", *this, 1); + } + + OUString arg; + if (!(aArguments[0] >>= arg)) { + throw css::lang::IllegalArgumentException( + "CBlankNode::initialize: argument must be string", *this, 0); + } + + //FIXME: what is legal? + if (arg.isEmpty()) { + throw css::lang::IllegalArgumentException( + "CBlankNode::initialize: argument is not valid blank node ID", *this, 0); + } + m_NodeID = arg; +} + +// css::rdf::XNode: +OUString SAL_CALL CBlankNode::getStringValue() +{ + return m_NodeID; +} + +} // closing anonymous implementation namespace + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CBlankNode_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CBlankNode()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/rdf/CLiteral.cxx b/unoxml/source/rdf/CLiteral.cxx new file mode 100644 index 000000000..b1c756883 --- /dev/null +++ b/unoxml/source/rdf/CLiteral.cxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/rdf/XLiteral.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + + +/// anonymous implementation namespace +namespace { + +class CLiteral: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::rdf::XLiteral> +{ +public: + explicit CLiteral(); + + // css::lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::lang::XInitialization: + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override; + + // css::rdf::XNode: + virtual OUString SAL_CALL getStringValue() override; + + // css::rdf::XLiteral: + virtual OUString SAL_CALL getValue() override; + virtual OUString SAL_CALL getLanguage() override; + virtual css::uno::Reference< css::rdf::XURI > SAL_CALL getDatatype() override; + +private: + CLiteral(CLiteral const&) = delete; + CLiteral& operator=(CLiteral const&) = delete; + + OUString m_Value; + OUString m_Language; + css::uno::Reference< css::rdf::XURI > m_xDatatype; +}; + +CLiteral::CLiteral() +{} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL CLiteral::getImplementationName() +{ + return "CLiteral"; +} + +sal_Bool SAL_CALL CLiteral::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL CLiteral::getSupportedServiceNames() +{ + return { "com.sun.star.rdf.Literal" }; +} + +// css::lang::XInitialization: +void SAL_CALL CLiteral::initialize(const css::uno::Sequence< css::uno::Any > & aArguments) +{ + const sal_Int32 len( aArguments.getLength() ); + if (len < 1 || len > 2) { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: must give 1 or 2 argument(s)", *this, 2); + } + + OUString arg0; + if (!(aArguments[0] >>= arg0)) { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: argument must be string", *this, 0); + } + //FIXME: what is legal? + if (!(true)) { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: argument is not valid literal value", *this, 0); + } + m_Value = arg0; + + if (len <= 1) + return; + + OUString arg1; + css::uno::Reference< css::rdf::XURI > xURI; + if (aArguments[1] >>= arg1) { + if (arg1.isEmpty()) { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: argument is not valid language", *this, 1); + } + m_Language = arg1; + } else if (aArguments[1] >>= xURI) { + if (!xURI.is()) { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: argument is null", *this, 1); + } + m_xDatatype = xURI; + } else { + throw css::lang::IllegalArgumentException( + "CLiteral::initialize: argument must be string or URI", *this, 1); + } +} + +// css::rdf::XNode: +OUString SAL_CALL CLiteral::getStringValue() +{ + if (!m_Language.isEmpty()) { + return m_Value + "@" + m_Language; + } else if (m_xDatatype.is()) { + return m_Value + "^^" + m_xDatatype->getStringValue(); + } else { + return m_Value; + } +} + +// css::rdf::XLiteral: +OUString SAL_CALL CLiteral::getValue() +{ + return m_Value; +} + +OUString SAL_CALL CLiteral::getLanguage() +{ + return m_Language; +} + +css::uno::Reference< css::rdf::XURI > SAL_CALL CLiteral::getDatatype() +{ + return m_xDatatype; +} + +} // closing anonymous implementation namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CLiteral_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CLiteral()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/rdf/CURI.cxx b/unoxml/source/rdf/CURI.cxx new file mode 100644 index 000000000..488aa3e90 --- /dev/null +++ b/unoxml/source/rdf/CURI.cxx @@ -0,0 +1,807 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/rdf/XURI.hpp> +#include <com/sun/star/rdf/URIs.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> + + +/// anonymous implementation namespace +namespace { + +class CURI: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, + css::lang::XInitialization, + css::rdf::XURI> +{ +public: + explicit CURI(); + + // css::lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::lang::XInitialization: + virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override; + + // css::rdf::XNode: + virtual OUString SAL_CALL getStringValue() override; + + // css::rdf::XURI: + virtual OUString SAL_CALL getLocalName() override; + virtual OUString SAL_CALL getNamespace() override; + +private: + CURI(CURI const&) = delete; + CURI& operator=(CURI const&) = delete; + + /// handle css.rdf.URIs + void initFromConstant(const sal_Int16 i_Constant); + + OUString m_Namespace; + OUString m_LocalName; +}; + +CURI::CURI() +{} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL CURI::getImplementationName() +{ + return "CURI"; +} + +sal_Bool SAL_CALL CURI::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > SAL_CALL CURI::getSupportedServiceNames() +{ + return { "com.sun.star.rdf.URI" }; +} + +const char s_nsXSD [] = "http://www.w3.org/2001/XMLSchema-datatypes#"; +const char s_nsRDF [] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; +const char s_nsRDFs [] = "http://www.w3.org/2000/01/rdf-schema#"; +const char s_nsOWL [] = "http://www.w3.org/2002/07/owl#"; +const char s_nsPkg [] = + "http://docs.oasis-open.org/ns/office/1.2/meta/pkg#"; +const char s_nsODF [] = + "http://docs.oasis-open.org/ns/office/1.2/meta/odf#"; +const char s_nsLO_EXT [] = + "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0odf#"; + +void CURI::initFromConstant(const sal_Int16 i_Constant) +{ + const char *ns(nullptr); + const char *ln(nullptr); + switch (i_Constant) + { + case css::rdf::URIs::XSD_NCNAME: + ns = s_nsXSD; + ln = "NCName"; + break; + + case css::rdf::URIs::XSD_STRING: + ns = s_nsXSD; + ln = "string"; + break; + + case css::rdf::URIs::XSD_NORMALIZEDSTRING: + ns = s_nsXSD; + ln = "normalizedString"; + break; + + case css::rdf::URIs::XSD_BOOLEAN: + ns = s_nsXSD; + ln = "boolean"; + break; + + case css::rdf::URIs::XSD_DECIMAL: + ns = s_nsXSD; + ln = "decimal"; + break; + + case css::rdf::URIs::XSD_FLOAT: + ns = s_nsXSD; + ln = "float"; + break; + + case css::rdf::URIs::XSD_DOUBLE: + ns = s_nsXSD; + ln = "double"; + break; + + case css::rdf::URIs::XSD_INTEGER: + ns = s_nsXSD; + ln = "integer"; + break; + + case css::rdf::URIs::XSD_NONNEGATIVEINTEGER: + ns = s_nsXSD; + ln = "nonNegativeInteger"; + break; + + case css::rdf::URIs::XSD_POSITIVEINTEGER: + ns = s_nsXSD; + ln = "positiveInteger"; + break; + + case css::rdf::URIs::XSD_NONPOSITIVEINTEGER: + ns = s_nsXSD; + ln = "nonPositiveInteger"; + break; + + case css::rdf::URIs::XSD_NEGATIVEINTEGER: + ns = s_nsXSD; + ln = "negativeInteger"; + break; + + case css::rdf::URIs::XSD_LONG: + ns = s_nsXSD; + ln = "long"; + break; + + case css::rdf::URIs::XSD_INT: + ns = s_nsXSD; + ln = "int"; + break; + + case css::rdf::URIs::XSD_SHORT: + ns = s_nsXSD; + ln = "short"; + break; + + case css::rdf::URIs::XSD_BYTE: + ns = s_nsXSD; + ln = "byte"; + break; + + case css::rdf::URIs::XSD_UNSIGNEDLONG: + ns = s_nsXSD; + ln = "unsignedLong"; + break; + + case css::rdf::URIs::XSD_UNSIGNEDINT: + ns = s_nsXSD; + ln = "unsignedInt"; + break; + + case css::rdf::URIs::XSD_UNSIGNEDSHORT: + ns = s_nsXSD; + ln = "unsignedShort"; + break; + + case css::rdf::URIs::XSD_UNSIGNEDBYTE: + ns = s_nsXSD; + ln = "unsignedByte"; + break; + + case css::rdf::URIs::XSD_HEXBINARY: + ns = s_nsXSD; + ln = "hexBinary"; + break; + + case css::rdf::URIs::XSD_BASE64BINARY: + ns = s_nsXSD; + ln = "base64Binary"; + break; + + case css::rdf::URIs::XSD_DATETIME: + ns = s_nsXSD; + ln = "dateTime"; + break; + + case css::rdf::URIs::XSD_TIME: + ns = s_nsXSD; + ln = "time"; + break; + + case css::rdf::URIs::XSD_DATE: + ns = s_nsXSD; + ln = "date"; + break; + + case css::rdf::URIs::XSD_GYEARMONTH: + ns = s_nsXSD; + ln = "gYearMonth"; + break; + + case css::rdf::URIs::XSD_GYEAR: + ns = s_nsXSD; + ln = "gYear"; + break; + + case css::rdf::URIs::XSD_GMONTHDAY: + ns = s_nsXSD; + ln = "gMonthDay"; + break; + + case css::rdf::URIs::XSD_GDAY: + ns = s_nsXSD; + ln = "gDay"; + break; + + case css::rdf::URIs::XSD_GMONTH: + ns = s_nsXSD; + ln = "gMonth"; + break; + + case css::rdf::URIs::XSD_ANYURI: + ns = s_nsXSD; + ln = "anyURI"; + break; + + case css::rdf::URIs::XSD_TOKEN: + ns = s_nsXSD; + ln = "token"; + break; + + case css::rdf::URIs::XSD_LANGUAGE: + ns = s_nsXSD; + ln = "language"; + break; + + case css::rdf::URIs::XSD_NMTOKEN: + ns = s_nsXSD; + ln = "NMTOKEN"; + break; + + case css::rdf::URIs::XSD_NAME: + ns = s_nsXSD; + ln = "Name"; + break; + + case css::rdf::URIs::XSD_DURATION: + ns = s_nsXSD; + ln = "duration"; + break; + + case css::rdf::URIs::XSD_QNAME: + ns = s_nsXSD; + ln = "QName"; + break; + + case css::rdf::URIs::XSD_NOTATION: + ns = s_nsXSD; + ln = "NOTATION"; + break; + + case css::rdf::URIs::XSD_NMTOKENS: + ns = s_nsXSD; + ln = "NMTOKENS"; + break; + + case css::rdf::URIs::XSD_ID: + ns = s_nsXSD; + ln = "ID"; + break; + + case css::rdf::URIs::XSD_IDREF: + ns = s_nsXSD; + ln = "IDREF"; + break; + + case css::rdf::URIs::XSD_IDREFS: + ns = s_nsXSD; + ln = "IDREFS"; + break; + + case css::rdf::URIs::XSD_ENTITY: + ns = s_nsXSD; + ln = "ENTITY"; + break; + + case css::rdf::URIs::XSD_ENTITIES: + ns = s_nsXSD; + ln = "ENTITIES"; + break; + + case css::rdf::URIs::RDF_TYPE: + ns = s_nsRDF; + ln = "type"; + break; + + case css::rdf::URIs::RDF_SUBJECT: + ns = s_nsRDF; + ln = "subject"; + break; + + case css::rdf::URIs::RDF_PREDICATE: + ns = s_nsRDF; + ln = "predicate"; + break; + + case css::rdf::URIs::RDF_OBJECT: + ns = s_nsRDF; + ln = "object"; + break; + + case css::rdf::URIs::RDF_PROPERTY: + ns = s_nsRDF; + ln = "Property"; + break; + + case css::rdf::URIs::RDF_STATEMENT: + ns = s_nsRDF; + ln = "Statement"; + break; + + case css::rdf::URIs::RDF_VALUE: + ns = s_nsRDF; + ln = "value"; + break; + + case css::rdf::URIs::RDF_FIRST: + ns = s_nsRDF; + ln = "first"; + break; + + case css::rdf::URIs::RDF_REST: + ns = s_nsRDF; + ln = "rest"; + break; + + case css::rdf::URIs::RDF_NIL: + ns = s_nsRDF; + ln = "nil"; + break; + + case css::rdf::URIs::RDF_XMLLITERAL: + ns = s_nsRDF; + ln = "XMLLiteral"; + break; + + case css::rdf::URIs::RDF_ALT: + ns = s_nsRDF; + ln = "Alt"; + break; + + case css::rdf::URIs::RDF_BAG: + ns = s_nsRDF; + ln = "Bag"; + break; + + case css::rdf::URIs::RDF_LIST: + ns = s_nsRDF; + ln = "List"; + break; + + case css::rdf::URIs::RDF_SEQ: + ns = s_nsRDF; + ln = "Seq"; + break; + + case css::rdf::URIs::RDF_1: + ns = s_nsRDF; + ln = "_1"; + break; + + case css::rdf::URIs::RDFS_COMMENT: + ns = s_nsRDFs; + ln = "comment"; + break; + + case css::rdf::URIs::RDFS_LABEL: + ns = s_nsRDFs; + ln = "label"; + break; + + case css::rdf::URIs::RDFS_DOMAIN: + ns = s_nsRDFs; + ln = "domain"; + break; + + case css::rdf::URIs::RDFS_RANGE: + ns = s_nsRDFs; + ln = "range"; + break; + + case css::rdf::URIs::RDFS_SUBCLASSOF: + ns = s_nsRDFs; + ln = "subClassOf"; + break; + + case css::rdf::URIs::RDFS_LITERAL: + ns = s_nsRDFs; + ln = "Literal"; + break; + + case css::rdf::URIs::OWL_CLASS: + ns = s_nsOWL; + ln = "Class"; + break; + + case css::rdf::URIs::OWL_OBJECTPROPERTY: + ns = s_nsOWL; + ln = "ObjectProperty"; + break; + + case css::rdf::URIs::OWL_DATATYPEPROPERTY: + ns = s_nsOWL; + ln = "DatatypeProperty"; + break; + + case css::rdf::URIs::OWL_FUNCTIONALPROPERTY: + ns = s_nsOWL; + ln = "FunctionalProperty"; + break; + + case css::rdf::URIs::OWL_THING: + ns = s_nsOWL; + ln = "Thing"; + break; + + case css::rdf::URIs::OWL_NOTHING: + ns = s_nsOWL; + ln = "Nothing"; + break; + + case css::rdf::URIs::OWL_INDIVIDUAL: + ns = s_nsOWL; + ln = "Individual"; + break; + + case css::rdf::URIs::OWL_EQUIVALENTCLASS: + ns = s_nsOWL; + ln = "equivalentClass"; + break; + + case css::rdf::URIs::OWL_EQUIVALENTPROPERTY: + ns = s_nsOWL; + ln = "equivalentProperty"; + break; + + case css::rdf::URIs::OWL_SAMEAS: + ns = s_nsOWL; + ln = "sameAs"; + break; + + case css::rdf::URIs::OWL_DIFFERENTFROM: + ns = s_nsOWL; + ln = "differentFrom"; + break; + + case css::rdf::URIs::OWL_ALLDIFFERENT: + ns = s_nsOWL; + ln = "AllDifferent"; + break; + + case css::rdf::URIs::OWL_DISTINCTMEMBERS: + ns = s_nsOWL; + ln = "distinctMembers"; + break; + + case css::rdf::URIs::OWL_INVERSEOF: + ns = s_nsOWL; + ln = "inverseOf"; + break; + + case css::rdf::URIs::OWL_TRANSITIVEPROPERTY: + ns = s_nsOWL; + ln = "TransitiveProperty"; + break; + + case css::rdf::URIs::OWL_SYMMETRICPROPERTY: + ns = s_nsOWL; + ln = "SymmetricProperty"; + break; + + case css::rdf::URIs::OWL_INVERSEFUNCTIONALPROPERTY: + ns = s_nsOWL; + ln = "InverseFunctionalProperty"; + break; + + case css::rdf::URIs::OWL_RESTRICTION: + ns = s_nsOWL; + ln = "Restriction"; + break; + + case css::rdf::URIs::OWL_ONPROPERTY: + ns = s_nsOWL; + ln = "onProperty"; + break; + + case css::rdf::URIs::OWL_ALLVALUESFROM: + ns = s_nsOWL; + ln = "allValuesFrom"; + break; + + case css::rdf::URIs::OWL_SOMEVALUESFROM: + ns = s_nsOWL; + ln = "someValuesFrom"; + break; + + case css::rdf::URIs::OWL_MINCARDINALITY: + ns = s_nsOWL; + ln = "minCardinality"; + break; + + case css::rdf::URIs::OWL_MAXCARDINALITY: + ns = s_nsOWL; + ln = "maxCardinality"; + break; + + case css::rdf::URIs::OWL_CARDINALITY: + ns = s_nsOWL; + ln = "cardinality"; + break; + + case css::rdf::URIs::OWL_ONTOLOGY: + ns = s_nsOWL; + ln = "Ontology"; + break; + + case css::rdf::URIs::OWL_IMPORTS: + ns = s_nsOWL; + ln = "imports"; + break; + + case css::rdf::URIs::OWL_VERSIONINFO: + ns = s_nsOWL; + ln = "versionInfo"; + break; + + case css::rdf::URIs::OWL_PRIORVERSION: + ns = s_nsOWL; + ln = "priorVersion"; + break; + + case css::rdf::URIs::OWL_BACKWARDCOMPATIBLEWITH: + ns = s_nsOWL; + ln = "backwardCompatibleWith"; + break; + + case css::rdf::URIs::OWL_INCOMPATIBLEWITH: + ns = s_nsOWL; + ln = "incompatibleWith"; + break; + + case css::rdf::URIs::OWL_DEPRECATEDCLASS: + ns = s_nsOWL; + ln = "DeprecatedClass"; + break; + + case css::rdf::URIs::OWL_DEPRECATEDPROPERTY: + ns = s_nsOWL; + ln = "DeprecatedProperty"; + break; + + case css::rdf::URIs::OWL_ANNOTATIONPROPERTY: + ns = s_nsOWL; + ln = "AnnotationProperty"; + break; + + case css::rdf::URIs::OWL_ONTOLOGYPROPERTY: + ns = s_nsOWL; + ln = "OntologyProperty"; + break; + + case css::rdf::URIs::OWL_ONEOF: + ns = s_nsOWL; + ln = "oneOf"; + break; + + case css::rdf::URIs::OWL_DATARANGE: + ns = s_nsOWL; + ln = "dataRange"; + break; + + case css::rdf::URIs::OWL_DISJOINTWITH: + ns = s_nsOWL; + ln = "disjointWith"; + break; + + case css::rdf::URIs::OWL_UNIONOF: + ns = s_nsOWL; + ln = "unionOf"; + break; + + case css::rdf::URIs::OWL_COMPLEMENTOF: + ns = s_nsOWL; + ln = "complementOf"; + break; + + case css::rdf::URIs::OWL_INTERSECTIONOF: + ns = s_nsOWL; + ln = "intersectionOf"; + break; + + case css::rdf::URIs::OWL_HASVALUE: + ns = s_nsOWL; + ln = "hasValue"; + break; + + + case css::rdf::URIs::PKG_HASPART: + ns = s_nsPkg; + ln = "hasPart"; + break; + + case css::rdf::URIs::PKG_MIMETYPE: + ns = s_nsPkg; + ln = "mimeType"; + break; + + case css::rdf::URIs::PKG_PACKAGE: + ns = s_nsPkg; + ln = "Package"; + break; + + case css::rdf::URIs::PKG_ELEMENT: + ns = s_nsPkg; + ln = "Element"; + break; + + case css::rdf::URIs::PKG_FILE: + ns = s_nsPkg; + ln = "File"; + break; + + case css::rdf::URIs::PKG_METADATAFILE: + ns = s_nsPkg; + ln = "MetadataFile"; + break; + + case css::rdf::URIs::PKG_DOCUMENT: + ns = s_nsPkg; + ln = "Document"; + break; + + case css::rdf::URIs::ODF_PREFIX: + ns = s_nsODF; + ln = "prefix"; + break; + + case css::rdf::URIs::ODF_SUFFIX: + ns = s_nsODF; + ln = "suffix"; + break; + + case css::rdf::URIs::ODF_ELEMENT: + ns = s_nsODF; + ln = "Element"; + break; + + case css::rdf::URIs::ODF_CONTENTFILE: + ns = s_nsODF; + ln = "ContentFile"; + break; + + case css::rdf::URIs::ODF_STYLESFILE: + ns = s_nsODF; + ln = "StylesFile"; + break; + + case css::rdf::URIs::LO_EXT_SHADING: + ns = s_nsLO_EXT; + ln = "shading"; + break; + + default: + throw css::lang::IllegalArgumentException( + "CURI::initialize: invalid URIs constant argument", *this, 0); + } + m_Namespace = OUString::createFromAscii(ns).intern(); + m_LocalName = OUString::createFromAscii(ln).intern(); +} + +// css::lang::XInitialization: +void SAL_CALL CURI::initialize(const css::uno::Sequence< css::uno::Any > & aArguments) +{ + sal_Int32 len = aArguments.getLength(); + if ((len < 1) || (len > 2)) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: must give 1 or 2 argument(s)", *this, 2); + } + + sal_Int16 arg(0); + OUString arg0; + OUString arg1; + if (aArguments[0] >>= arg) { + // integer argument: constant from rdf::URIs + if (len != 1) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: must give 1 int argument", *this, 1); + } + initFromConstant(arg); + return; + } + if (!(aArguments[0] >>= arg0)) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: argument must be string or short", *this, 0); + } + if (len > 1) { + if (!(aArguments[1] >>= arg1)) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: argument must be string", *this, 1); + } + // just append the parameters and then split them again; seems simplest + arg0 = arg0 + arg1; + arg1.clear(); + } + + // split parameter + sal_Int32 idx = arg0.indexOf('#'); + if (idx < 0) + idx = arg0.lastIndexOf('/'); + if (idx < 0) + idx = arg0.lastIndexOf(':'); + if (idx < 0) + { + throw css::lang::IllegalArgumentException( + "CURI::initialize: argument not splittable: no separator [#/:]", *this, 0); + } + if (idx < arg0.getLength() - 1) { + arg1 = arg0.copy(idx+1); + arg0 = arg0.copy(0, idx+1); + } + + //FIXME: what is legal? + if (arg0.isEmpty()) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: argument is not valid namespace", *this, 0); + } + m_Namespace = arg0; + + //FIXME: what is legal? + if ((false)) { + throw css::lang::IllegalArgumentException( + "CURI::initialize: argument is not valid local name", *this, 1); + } + m_LocalName = arg1; +} + +// css::rdf::XNode: +OUString SAL_CALL CURI::getStringValue() +{ + return m_Namespace + m_LocalName; +} + +// css::rdf::XURI: +OUString SAL_CALL CURI::getNamespace() +{ + return m_Namespace; +} + +OUString SAL_CALL CURI::getLocalName() +{ + return m_LocalName; +} + +} // closing anonymous implementation namespace + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CURI_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new CURI()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/rdf/librdf_repository.cxx b/unoxml/source/rdf/librdf_repository.cxx new file mode 100644 index 000000000..51c8317f3 --- /dev/null +++ b/unoxml/source/rdf/librdf_repository.cxx @@ -0,0 +1,2465 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <string.h> + +#include <map> +#include <memory> +#include <mutex> +#include <set> +#include <string_view> +#include <iterator> +#include <algorithm> +#include <atomic> + +#include <optional> + +#include <libxslt/security.h> + +#include <redland.h> + +#include <com/sun/star/container/ElementExistException.hpp> +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/rdf/ParseException.hpp> +#include <com/sun/star/rdf/QueryException.hpp> +#include <com/sun/star/rdf/RepositoryException.hpp> +#include <com/sun/star/rdf/XDocumentRepository.hpp> +#include <com/sun/star/rdf/XLiteral.hpp> +#include <com/sun/star/rdf/FileFormat.hpp> +#include <com/sun/star/rdf/BlankNode.hpp> +#include <com/sun/star/rdf/URI.hpp> +#include <com/sun/star/rdf/Literal.hpp> + +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weakref.hxx> + +#include <comphelper/sequence.hxx> +#include <comphelper/xmltools.hxx> + +#include <com/sun/star/embed/XEncryptionProtectedSource2.hpp> + +/** + Implementation of the service com.sun.star.rdf.Repository. + + This implementation uses the Redland RDF library (librdf). + + There are several classes involved: + librdf_TypeConverter: helper class to convert data types redland <-> uno + librdf_Repository: the main repository, does almost all the work + librdf_NamedGraph: the XNamedGraph, forwards everything to repository + librdf_GraphResult: an XEnumeration<Statement> + librdf_QuerySelectResult: an XEnumeration<sequence<XNode>> + + */ + +/// anonymous implementation namespace +namespace { + +class librdf_NamedGraph; +class librdf_Repository; + +using namespace ::com::sun::star; + +typedef std::map< OUString, ::rtl::Reference<librdf_NamedGraph> > + NamedGraphMap_t; + +const char s_sparql [] = "sparql"; +const char s_nsOOo [] = "http://openoffice.org/2004/office/rdfa/"; + + +//FIXME: this approach is not ideal. can we use blank nodes instead? +bool isInternalContext(librdf_node *i_pNode) noexcept +{ + OSL_ENSURE(i_pNode, "isInternalContext: context null"); + OSL_ENSURE(librdf_node_is_resource(i_pNode), + "isInternalContext: context not resource"); + if (i_pNode) { + librdf_uri *pURI(librdf_node_get_uri(i_pNode)); + OSL_ENSURE(pURI, "isInternalContext: URI null"); + if (pURI) { + unsigned char *pContextURI(librdf_uri_as_string(pURI)); + assert(pContextURI && "isInternalContext: URI string null"); + // if prefix matches reserved uri, it is RDFa context + if (!strncmp(reinterpret_cast<char *>(pContextURI), + s_nsOOo, sizeof(s_nsOOo)-1)) { + return true; + } + } + return false; + } + return true; +} + + +// n.b.: librdf destructor functions dereference null pointers! +// so they need to be wrapped to be usable with std::shared_ptr. +void safe_librdf_free_world(librdf_world *const world) +{ + if (world) { librdf_free_world(world); } +} +void safe_librdf_free_model(librdf_model *const model) +{ + if (model) { librdf_free_model(model); } +} +void safe_librdf_free_node(librdf_node* node) +{ + if (node) { librdf_free_node(node); } +} +void safe_librdf_free_parser(librdf_parser *const parser) +{ + if (parser) { librdf_free_parser(parser); } +} +void safe_librdf_free_query(librdf_query *const query) +{ + if (query) { librdf_free_query(query); } +} +void +safe_librdf_free_query_results(librdf_query_results *const query_results) +{ + if (query_results) { librdf_free_query_results(query_results); } +} +void safe_librdf_free_serializer(librdf_serializer *const serializer) +{ + if (serializer) { librdf_free_serializer(serializer); } +} +void safe_librdf_free_statement(librdf_statement *const statement) +{ + if (statement) { librdf_free_statement(statement); } +} +void safe_librdf_free_storage(librdf_storage *const storage) +{ + if (storage) { librdf_free_storage(storage); } +} +void safe_librdf_free_stream(librdf_stream *const stream) +{ + if (stream) { librdf_free_stream(stream); } +} +void safe_librdf_free_uri(librdf_uri *const uri) +{ + if (uri) { librdf_free_uri(uri); } +} + + +/** converts between librdf types and UNO API types. + */ +class librdf_TypeConverter +{ +public: + + // some wrapper classes to temporarily hold values of UNO XNodes + struct Node + { + virtual ~Node() {} + }; + struct Resource : public Node { }; + struct URI : public Resource + { + OString const value; + explicit URI(OString const& i_rValue) + : value(i_rValue) + { } + }; + struct BlankNode : public Resource + { + OString const value; + explicit BlankNode(OString const& i_rValue) + : value(i_rValue) + { } + }; + struct Literal : public Node + { + OString const value; + OString const language; + ::std::optional<OString> const type; + Literal(OString const& i_rValue, OString const& i_rLanguage, + ::std::optional<OString> const& i_rType) + : value(i_rValue) + , language(i_rLanguage) + , type(i_rType) + { } + }; + struct Statement + { + std::shared_ptr<Resource> const pSubject; + std::shared_ptr<URI> const pPredicate; + std::shared_ptr<Node> const pObject; + Statement(std::shared_ptr<Resource> const& i_pSubject, + std::shared_ptr<URI> const& i_pPredicate, + std::shared_ptr<Node> const& i_pObject) + : pSubject(i_pSubject) + , pPredicate(i_pPredicate) + , pObject(i_pObject) + { } + }; + + librdf_TypeConverter( + uno::Reference< uno::XComponentContext > const & i_xContext, + librdf_Repository &i_rRep) + : m_xContext(i_xContext) + , m_rRep(i_rRep) + { }; + + librdf_world *createWorld_Lock() const; + librdf_storage *createStorage_Lock(librdf_world *i_pWorld) const; + librdf_model *createModel_Lock(librdf_world *i_pWorld, + librdf_storage * i_pStorage) const; + static librdf_uri* mkURI_Lock(librdf_world* i_pWorld, + const OString & i_rURI); + static librdf_node* mkResource_Lock(librdf_world* i_pWorld, + const Resource * i_pResource); + static librdf_node* mkNode_Lock(librdf_world* i_pWorld, + const Node * i_pNode); + static librdf_statement* mkStatement_Lock(librdf_world* i_pWorld, + Statement const& i_rStatement); + static std::shared_ptr<Resource> extractResource_NoLock( + const uno::Reference< rdf::XResource > & i_xResource); + static void extractResourceToCacheKey_NoLock( + const uno::Reference< rdf::XResource > & i_xResource, + OUStringBuffer& rBuf); + static std::shared_ptr<Node> extractNode_NoLock( + const uno::Reference< rdf::XNode > & i_xNode); + static void extractNodeToCacheKey_NoLock( + const uno::Reference< rdf::XNode > & i_xNode, + OUStringBuffer& rBuffer); + static Statement extractStatement_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject); + uno::Reference<rdf::XURI> convertToXURI(librdf_uri* i_pURI) const; + uno::Reference<rdf::XURI> convertToXURI(librdf_node* i_pURI) const; + uno::Reference<rdf::XResource> + convertToXResource(librdf_node* i_pNode) const; + uno::Reference<rdf::XNode> convertToXNode(librdf_node* i_pNode) const; + rdf::Statement + convertToStatement(librdf_statement* i_pStmt, librdf_node* i_pContext) + const; + +private: + uno::Reference< uno::XComponentContext > const m_xContext; + librdf_Repository & m_rRep; +}; + + +/** implements the repository service. + */ +class librdf_Repository: +// private ::cppu::BaseMutex, + public ::cppu::WeakImplHelper< + lang::XServiceInfo, + rdf::XDocumentRepository, + lang::XInitialization> +{ +public: + + explicit librdf_Repository( + uno::Reference< uno::XComponentContext > const & i_xContext); + virtual ~librdf_Repository() override; + + // css::lang::XServiceInfo: + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( + const OUString & ServiceName) override; + virtual uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // css::rdf::XRepository: + virtual uno::Reference< rdf::XBlankNode > SAL_CALL createBlankNode() override; + virtual uno::Reference<rdf::XNamedGraph> SAL_CALL importGraph( + ::sal_Int16 i_Format, + const uno::Reference< io::XInputStream > & i_xInStream, + const uno::Reference< rdf::XURI > & i_xGraphName, + const uno::Reference< rdf::XURI > & i_xBaseURI) override; + virtual void SAL_CALL exportGraph(::sal_Int16 i_Format, + const uno::Reference< io::XOutputStream > & i_xOutStream, + const uno::Reference< rdf::XURI > & i_xGraphName, + const uno::Reference< rdf::XURI > & i_xBaseURI) override; + virtual uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL + getGraphNames() override; + virtual uno::Reference< rdf::XNamedGraph > SAL_CALL getGraph( + const uno::Reference< rdf::XURI > & i_xGraphName) override; + virtual uno::Reference< rdf::XNamedGraph > SAL_CALL createGraph( + const uno::Reference< rdf::XURI > & i_xGraphName) override; + virtual void SAL_CALL destroyGraph( + const uno::Reference< rdf::XURI > & i_xGraphName) override; + virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) override; + virtual uno::Reference< rdf::XQuerySelectResult > SAL_CALL + querySelect(const OUString & i_rQuery) override; + virtual uno::Reference< container::XEnumeration > SAL_CALL + queryConstruct(const OUString & i_rQuery) override; + virtual sal_Bool SAL_CALL queryAsk(const OUString & i_rQuery) override; + + // css::rdf::XDocumentRepository: + virtual void SAL_CALL setStatementRDFa( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates, + const uno::Reference< rdf::XMetadatable > & i_xObject, + const OUString & i_rRDFaContent, + const uno::Reference< rdf::XURI > & i_xRDFaDatatype) override; + virtual void SAL_CALL removeStatementRDFa( + const uno::Reference< rdf::XMetadatable > & i_xElement) override; + virtual beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool > SAL_CALL + getStatementRDFa(uno::Reference< rdf::XMetadatable > const& i_xElement) override; + virtual uno::Reference< container::XEnumeration > SAL_CALL + getStatementsRDFa( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) override; + + // css::lang::XInitialization: + virtual void SAL_CALL initialize( + const uno::Sequence< css::uno::Any > & i_rArguments) override; + + // XNamedGraph forwards --------------------------------------------- + NamedGraphMap_t::iterator clearGraph_NoLock( + const OUString & i_rGraphName, + bool i_Internal = false ); + NamedGraphMap_t::iterator clearGraph_Lock( + const OUString & i_rGraphName, + bool i_Internal); + void addStatementGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xName ); +// throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException); + void addStatementGraph_Lock( + librdf_TypeConverter::Statement const& i_rStatement, + OUString const& i_rGraphName, + bool i_Internal); + void removeStatementsGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xName ); +// throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException); + std::vector<rdf::Statement> getStatementsGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xName, + bool i_Internal = false ); +// throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException); + + const librdf_TypeConverter& getTypeConverter() const { return m_TypeConverter; }; + +private: + + librdf_Repository(librdf_Repository const&) = delete; + librdf_Repository& operator=(librdf_Repository const&) = delete; + + /// this is const, no need to lock m_aMutex to access it + uno::Reference< uno::XComponentContext > const m_xContext; + + /// librdf global data + /** N.B.: The redland documentation gives the impression that you can have + as many librdf_worlds as you like. This is true in the same sense + that you can physically be in as many places as you like. + Well, you can, just not at the same time. + The ugly truth is that destroying a librdf_world kills a bunch + of static variables; other librdf_worlds become very unhappy + when they access these. + And of course this is not documented anywhere that I could find. + So we allocate a single world, and refcount that. + */ + static std::shared_ptr<librdf_world> m_pWorld; + /// refcount + static sal_uInt32 m_NumInstances; + /// mutex for m_pWorld - redland is not as threadsafe as is often claimed + static std::mutex m_aMutex; + + // NB: sequence of the shared pointers is important! + /// librdf repository storage + std::shared_ptr<librdf_storage> m_pStorage; + /// librdf repository model + std::shared_ptr<librdf_model> m_pModel; + + /// all named graphs + NamedGraphMap_t m_NamedGraphs; + + /// type conversion helper - stateless + librdf_TypeConverter m_TypeConverter; + + /// set of xml:ids of elements with xhtml:content + ::std::set< OUString > m_RDFaXHTMLContentSet; +}; + + +/** result of operations that return a graph, i.e., + an XEnumeration of statements. + */ +class librdf_GraphResult: + public ::cppu::WeakImplHelper< + container::XEnumeration> +{ +public: + + librdf_GraphResult(librdf_Repository *i_pRepository, + std::mutex & i_rMutex, + std::shared_ptr<librdf_stream> const& i_pStream, + std::shared_ptr<librdf_node> const& i_pContext, + std::shared_ptr<librdf_query> const& i_pQuery = + std::shared_ptr<librdf_query>() ) + : m_xRep(i_pRepository) + , m_rMutex(i_rMutex) + , m_pQuery(i_pQuery) + , m_pContext(i_pContext) + , m_pStream(i_pStream) + { }; + + virtual ~librdf_GraphResult() override + { + std::scoped_lock g(m_rMutex); // lock mutex when destroying members + const_cast<std::shared_ptr<librdf_stream>& >(m_pStream).reset(); + const_cast<std::shared_ptr<librdf_node>& >(m_pContext).reset(); + const_cast<std::shared_ptr<librdf_query>& >(m_pQuery).reset(); + } + + // css::container::XEnumeration: + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual uno::Any SAL_CALL nextElement() override; + +private: + + librdf_GraphResult(librdf_GraphResult const&) = delete; + librdf_GraphResult& operator=(librdf_GraphResult const&) = delete; + + // NB: this is not a weak pointer: streams _must_ be deleted before the + // storage they point into, so we keep the repository alive here + // also, sequence is important: the stream must be destroyed first. + ::rtl::Reference< librdf_Repository > m_xRep; + // needed for synchronizing access to librdf (it doesn't do win32 threading) + std::mutex & m_rMutex; + // the query (in case this is a result of a graph query) + // not that the redland documentation spells this out explicitly, but + // queries must be freed only after all the results are completely read + std::shared_ptr<librdf_query> const m_pQuery; + std::shared_ptr<librdf_node> const m_pContext; + std::shared_ptr<librdf_stream> const m_pStream; + + librdf_node* getContext_Lock() const; +}; + + +// css::container::XEnumeration: +sal_Bool SAL_CALL +librdf_GraphResult::hasMoreElements() +{ + std::scoped_lock g(m_rMutex); + return m_pStream && !librdf_stream_end(m_pStream.get()); +} + +librdf_node* librdf_GraphResult::getContext_Lock() const +{ + if (!m_pStream || librdf_stream_end(m_pStream.get())) + return nullptr; + librdf_node *pCtxt( +#if LIBRDF_VERSION >= 10012 + librdf_stream_get_context2(m_pStream.get()) ); +#else + static_cast<librdf_node *>(librdf_stream_get_context(m_pStream.get())) ); +#endif + if (pCtxt) + return pCtxt; + return m_pContext.get(); +} + +css::uno::Any SAL_CALL +librdf_GraphResult::nextElement() +{ + std::scoped_lock g(m_rMutex); + if (m_pStream && librdf_stream_end(m_pStream.get())) { + throw container::NoSuchElementException(); + } + librdf_node * pCtxt = getContext_Lock(); + + librdf_statement *pStmt( librdf_stream_get_object(m_pStream.get()) ); + if (!pStmt) { + rdf::QueryException e( + "librdf_GraphResult::nextElement: " + "librdf_stream_get_object failed", *this); + throw lang::WrappedTargetException( + "librdf_GraphResult::nextElement: " + "librdf_stream_get_object failed", *this, + uno::Any(e)); + } + // NB: pCtxt may be null here if this is result of a graph query + if (pCtxt && isInternalContext(pCtxt)) { + pCtxt = nullptr; // XML ID context is implementation detail! + } + rdf::Statement Stmt( + m_xRep->getTypeConverter().convertToStatement(pStmt, pCtxt) ); + // NB: this will invalidate current item. + librdf_stream_next(m_pStream.get()); + return uno::Any(Stmt); +} + + +/** result of operations that return a graph, i.e., + an XEnumeration of statements. + */ +class librdf_GraphResult2: + public ::cppu::WeakImplHelper< + container::XEnumeration> +{ +public: + + librdf_GraphResult2(std::vector<rdf::Statement> statements) + : m_vStatements(std::move(statements)) + { }; + + // css::container::XEnumeration: + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual uno::Any SAL_CALL nextElement() override; + +private: + + std::vector<rdf::Statement> m_vStatements; + std::atomic<std::size_t> m_nIndex = 0; +}; + + +// css::container::XEnumeration: +sal_Bool SAL_CALL +librdf_GraphResult2::hasMoreElements() +{ + return m_nIndex < m_vStatements.size(); +} + +css::uno::Any SAL_CALL +librdf_GraphResult2::nextElement() +{ + std::size_t const n = m_nIndex++; + if (m_vStatements.size() <= n) + { + m_nIndex = m_vStatements.size(); // avoid overflow + throw container::NoSuchElementException(); + } + return uno::Any(m_vStatements[n]); +} + +/** result of tuple queries ("SELECT"). + */ +class librdf_QuerySelectResult: + public ::cppu::WeakImplHelper< + rdf::XQuerySelectResult> +{ +public: + + librdf_QuerySelectResult(librdf_Repository *i_pRepository, + std::mutex & i_rMutex, + std::shared_ptr<librdf_query> const& i_pQuery, + std::shared_ptr<librdf_query_results> const& i_pQueryResult, + uno::Sequence< OUString > const& i_rBindingNames ) + : m_xRep(i_pRepository) + , m_rMutex(i_rMutex) + , m_pQuery(i_pQuery) + , m_pQueryResult(i_pQueryResult) + , m_BindingNames(i_rBindingNames) + { }; + + virtual ~librdf_QuerySelectResult() override + { + std::scoped_lock g(m_rMutex); // lock mutex when destroying members + const_cast<std::shared_ptr<librdf_query_results>& >(m_pQueryResult) + .reset(); + const_cast<std::shared_ptr<librdf_query>& >(m_pQuery).reset(); + } + + // css::container::XEnumeration: + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual uno::Any SAL_CALL nextElement() override; + + // css::rdf::XQuerySelectResult: + virtual uno::Sequence< OUString > SAL_CALL getBindingNames() override; + +private: + + librdf_QuerySelectResult(librdf_QuerySelectResult const&) = delete; + librdf_QuerySelectResult& operator=(librdf_QuerySelectResult const&) = delete; + + // NB: this is not a weak pointer: streams _must_ be deleted before the + // storage they point into, so we keep the repository alive here + // also, sequence is important: the stream must be destroyed first. + ::rtl::Reference< librdf_Repository > m_xRep; + // needed for synchronizing access to librdf (it doesn't do win32 threading) + std::mutex & m_rMutex; + // not that the redland documentation spells this out explicitly, but + // queries must be freed only after all the results are completely read + std::shared_ptr<librdf_query> const m_pQuery; + std::shared_ptr<librdf_query_results> const m_pQueryResult; + uno::Sequence< OUString > const m_BindingNames; +}; + + +// css::container::XEnumeration: +sal_Bool SAL_CALL +librdf_QuerySelectResult::hasMoreElements() +{ + std::scoped_lock g(m_rMutex); + return !librdf_query_results_finished(m_pQueryResult.get()); +} + +class NodeArray : private std::vector<librdf_node*> +{ +public: + NodeArray(int cnt) : std::vector<librdf_node*>(cnt) {} + + ~NodeArray() noexcept + { + std::for_each(begin(), end(), safe_librdf_free_node); + } + + using std::vector<librdf_node*>::data; + using std::vector<librdf_node*>::operator[]; +}; + +css::uno::Any SAL_CALL +librdf_QuerySelectResult::nextElement() +{ + std::scoped_lock g(m_rMutex); + if (librdf_query_results_finished(m_pQueryResult.get())) { + throw container::NoSuchElementException(); + } + sal_Int32 count(m_BindingNames.getLength()); + OSL_ENSURE(count >= 0, "negative length?"); + NodeArray aNodes(count); + if (librdf_query_results_get_bindings(m_pQueryResult.get(), nullptr, + aNodes.data())) + { + rdf::QueryException e( + "librdf_QuerySelectResult::nextElement: " + "librdf_query_results_get_bindings failed", *this); + throw lang::WrappedTargetException( + "librdf_QuerySelectResult::nextElement: " + "librdf_query_results_get_bindings failed", *this, + uno::Any(e)); + } + uno::Sequence< uno::Reference< rdf::XNode > > ret(count); + auto retRange = asNonConstRange(ret); + for (int i = 0; i < count; ++i) { + retRange[i] = m_xRep->getTypeConverter().convertToXNode(aNodes[i]); + } + // NB: this will invalidate current item. + librdf_query_results_next(m_pQueryResult.get()); + return uno::Any(ret); +} + +// css::rdf::XQuerySelectResult: +uno::Sequence< OUString > SAL_CALL +librdf_QuerySelectResult::getBindingNames() +{ + // const - no lock needed + return m_BindingNames; +} + + +/** represents a named graph, and forwards all the work to repository. + */ +class librdf_NamedGraph: + public ::cppu::WeakImplHelper< + rdf::XNamedGraph> +{ +public: + librdf_NamedGraph(librdf_Repository * i_pRep, + uno::Reference<rdf::XURI> const & i_xName) + : m_wRep(i_pRep) + , m_pRep(i_pRep) + , m_xName(i_xName) + { }; + + // css::rdf::XNode: + virtual OUString SAL_CALL getStringValue() override; + + // css::rdf::XURI: + virtual OUString SAL_CALL getNamespace() override; + virtual OUString SAL_CALL getLocalName() override; + + // css::rdf::XNamedGraph: + virtual uno::Reference<rdf::XURI> SAL_CALL getName() override; + virtual void SAL_CALL clear() override; + virtual void SAL_CALL addStatement( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) override; + virtual void SAL_CALL removeStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) override; + virtual uno::Reference< container::XEnumeration > SAL_CALL getStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) override; + +private: + + librdf_NamedGraph(librdf_NamedGraph const&) = delete; + librdf_NamedGraph& operator=(librdf_NamedGraph const&) = delete; + + static OUString createCacheKey_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject); + + /// weak reference: this is needed to check if m_pRep is valid + uno::WeakReference< rdf::XRepository > const m_wRep; + librdf_Repository *const m_pRep; + uno::Reference< rdf::XURI > const m_xName; + + /// Querying is rather slow, so cache the results. + std::map<OUString, std::vector<rdf::Statement>> m_aStatementsCache; + std::mutex m_CacheMutex; +}; + + +// css::rdf::XNode: +OUString SAL_CALL librdf_NamedGraph::getStringValue() +{ + return m_xName->getStringValue(); +} + +// css::rdf::XURI: +OUString SAL_CALL librdf_NamedGraph::getNamespace() +{ + return m_xName->getNamespace(); +} + +OUString SAL_CALL librdf_NamedGraph::getLocalName() +{ + return m_xName->getLocalName(); +} + +// css::rdf::XNamedGraph: +uno::Reference< rdf::XURI > SAL_CALL librdf_NamedGraph::getName() +{ + return m_xName; +} + +void SAL_CALL librdf_NamedGraph::clear() +{ + uno::Reference< rdf::XRepository > xRep( m_wRep ); + if (!xRep.is()) { + throw rdf::RepositoryException( + "librdf_NamedGraph::clear: repository is gone", *this); + } + const OUString contextU( m_xName->getStringValue() ); + try { + m_pRep->clearGraph_NoLock(contextU); + } catch (lang::IllegalArgumentException & ex) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( ex.Message, + *this, anyEx ); + } + std::unique_lock g(m_CacheMutex); + m_aStatementsCache.clear(); +} + +void SAL_CALL librdf_NamedGraph::addStatement( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + uno::Reference< rdf::XRepository > xRep( m_wRep ); + if (!xRep.is()) { + throw rdf::RepositoryException( + "librdf_NamedGraph::addStatement: repository is gone", *this); + } + { + std::unique_lock g(m_CacheMutex); + m_aStatementsCache.clear(); + } + m_pRep->addStatementGraph_NoLock( + i_xSubject, i_xPredicate, i_xObject, m_xName); +} + +void SAL_CALL librdf_NamedGraph::removeStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + uno::Reference< rdf::XRepository > xRep( m_wRep ); + if (!xRep.is()) { + throw rdf::RepositoryException( + "librdf_NamedGraph::removeStatements: repository is gone", *this); + } + { + std::unique_lock g(m_CacheMutex); + m_aStatementsCache.clear(); + } + m_pRep->removeStatementsGraph_NoLock( + i_xSubject, i_xPredicate, i_xObject, m_xName); +} + +OUString librdf_NamedGraph::createCacheKey_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + OUStringBuffer cacheKey(256); + librdf_TypeConverter::extractResourceToCacheKey_NoLock(i_xSubject, cacheKey); + cacheKey.append("\t"); + librdf_TypeConverter::extractResourceToCacheKey_NoLock(i_xPredicate, cacheKey); + cacheKey.append("\t"); + librdf_TypeConverter::extractNodeToCacheKey_NoLock(i_xObject, cacheKey); + return cacheKey.makeStringAndClear(); +} + +uno::Reference< container::XEnumeration > SAL_CALL +librdf_NamedGraph::getStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + OUString cacheKey = createCacheKey_NoLock(i_xSubject, i_xPredicate, i_xObject); + { + std::unique_lock g(m_CacheMutex); + auto it = m_aStatementsCache.find(cacheKey); + if (it != m_aStatementsCache.end()) { + return new librdf_GraphResult2(it->second); + } + } + + uno::Reference< rdf::XRepository > xRep( m_wRep ); + if (!xRep.is()) { + throw rdf::RepositoryException( + "librdf_NamedGraph::getStatements: repository is gone", *this); + } + std::vector<rdf::Statement> vStatements = m_pRep->getStatementsGraph_NoLock( + i_xSubject, i_xPredicate, i_xObject, m_xName); + + { + std::unique_lock g(m_CacheMutex); + m_aStatementsCache.emplace(cacheKey, vStatements); + } + return new librdf_GraphResult2(std::move(vStatements)); +} + + +std::shared_ptr<librdf_world> librdf_Repository::m_pWorld; +sal_uInt32 librdf_Repository::m_NumInstances = 0; +std::mutex librdf_Repository::m_aMutex; + +librdf_Repository::librdf_Repository( + uno::Reference< uno::XComponentContext > const & i_xContext) + : /*BaseMutex(),*/ m_xContext(i_xContext) +// m_pWorld (static_cast<librdf_world *>(0), safe_librdf_free_world ), + , m_pStorage(static_cast<librdf_storage*>(nullptr), safe_librdf_free_storage) + , m_pModel (static_cast<librdf_model *>(nullptr), safe_librdf_free_model ) + , m_TypeConverter(i_xContext, *this) +{ + OSL_ENSURE(i_xContext.is(), "librdf_Repository: null context"); + + std::scoped_lock g(m_aMutex); + if (!m_NumInstances++) { + m_pWorld.reset(m_TypeConverter.createWorld_Lock(), + safe_librdf_free_world); + } +} + +librdf_Repository::~librdf_Repository() +{ + std::scoped_lock g(m_aMutex); + + // must destroy these before world! + m_pModel.reset(); + m_pStorage.reset(); + + // FIXME: so it turns out that calling librdf_free_world will + // (via raptor_sax2_finish) call xmlCleanupParser, which will + // free libxml2's globals! ARRRGH!!! => never call librdf_free_world +#if 0 + if (!--m_NumInstances) { + m_pWorld.reset(); + } +#endif +} + +// com.sun.star.uno.XServiceInfo: +OUString SAL_CALL librdf_Repository::getImplementationName() +{ + return "librdf_Repository"; +} + +sal_Bool SAL_CALL librdf_Repository::supportsService( + OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +uno::Sequence< OUString > SAL_CALL +librdf_Repository::getSupportedServiceNames() +{ + return { "com.sun.star.rdf.Repository" }; +} + +// css::rdf::XRepository: +uno::Reference< rdf::XBlankNode > SAL_CALL librdf_Repository::createBlankNode() +{ + std::scoped_lock g(m_aMutex); + const std::shared_ptr<librdf_node> pNode( + librdf_new_node_from_blank_identifier(m_pWorld.get(), nullptr), + safe_librdf_free_node); + if (!pNode) { + throw uno::RuntimeException( + "librdf_Repository::createBlankNode: " + "librdf_new_node_from_blank_identifier failed", *this); + } + const unsigned char * id (librdf_node_get_blank_identifier(pNode.get())); + if (!id) { + throw uno::RuntimeException( + "librdf_Repository::createBlankNode: " + "librdf_node_get_blank_identifier failed", *this); + } + const OUString nodeID(OUString::createFromAscii( + reinterpret_cast<const char *>(id))); + try { + return rdf::BlankNode::create(m_xContext, nodeID); + } catch (const lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_Repository::createBlankNode: " + "illegal blank node label", *this, anyEx); + } +} + +//void SAL_CALL +uno::Reference<rdf::XNamedGraph> SAL_CALL +librdf_Repository::importGraph(::sal_Int16 i_Format, + const uno::Reference< io::XInputStream > & i_xInStream, + const uno::Reference< rdf::XURI > & i_xGraphName, + const uno::Reference< rdf::XURI > & i_xBaseURI) +{ + if (!i_xInStream.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::importGraph: stream is null", *this, 1); + } + //FIXME: other formats + if (i_Format != rdf::FileFormat::RDF_XML) { + throw datatransfer::UnsupportedFlavorException( + "librdf_Repository::importGraph: file format not supported", *this); + } + if (!i_xGraphName.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::importGraph: graph name is null", *this, 2); + } + if (i_xGraphName->getStringValue().startsWith(s_nsOOo)) + { + throw lang::IllegalArgumentException( + "librdf_Repository::importGraph: URI is reserved", *this, 0); + } + if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI? + throw lang::IllegalArgumentException( + "librdf_Repository::importGraph: base URI is null", *this, 3); + } + OSL_ENSURE(i_xBaseURI.is(), "no base uri"); + const OUString baseURIU( i_xBaseURI->getStringValue() ); + if (baseURIU.indexOf('#') >= 0) { + throw lang::IllegalArgumentException( + "librdf_Repository::importGraph: base URI is not absolute", *this, 3); + } + + const OUString contextU( i_xGraphName->getStringValue() ); + + uno::Sequence<sal_Int8> buf; + uno::Reference<io::XSeekable> xSeekable(i_xInStream, uno::UNO_QUERY); + // UGLY: if only redland could read streams... + const sal_Int64 sz( xSeekable.is() ? xSeekable->getLength() : 1 << 20 ); + // exceptions are propagated + i_xInStream->readBytes( buf, static_cast<sal_Int32>( sz ) ); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) { + throw container::ElementExistException( + "librdf_Repository::importGraph: graph with given URI exists", *this); + } + const OString context( + OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::importGraph: librdf_new_node_from_uri_string failed", *this); + } + + const OString baseURI( + OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) ); + const std::shared_ptr<librdf_uri> pBaseURI( + librdf_new_uri(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (baseURI.getStr())), + safe_librdf_free_uri); + if (!pBaseURI) { + throw uno::RuntimeException( "librdf_Repository::importGraph: librdf_new_uri failed", *this); + } + + const std::shared_ptr<librdf_parser> pParser( + librdf_new_parser(m_pWorld.get(), "rdfxml", nullptr, nullptr), + safe_librdf_free_parser); + if (!pParser) { + throw uno::RuntimeException( + "librdf_Repository::importGraph: " + "librdf_new_parser failed", *this); + } + + const std::shared_ptr<librdf_stream> pStream( + librdf_parser_parse_counted_string_as_stream(pParser.get(), + reinterpret_cast<const unsigned char*>(buf.getConstArray()), + buf.getLength(), pBaseURI.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::ParseException( + "librdf_Repository::importGraph: " + "librdf_parser_parse_counted_string_as_stream failed", *this); + } + rtl::Reference<librdf_NamedGraph> const pGraph( + new librdf_NamedGraph(this, i_xGraphName)); + m_NamedGraphs.insert(std::make_pair(contextU, pGraph)); + if (librdf_model_context_add_statements(m_pModel.get(), + pContext.get(), pStream.get())) { + throw rdf::RepositoryException( + "librdf_Repository::importGraph: " + "librdf_model_context_add_statements failed", *this); + } + + return pGraph; +} + +void addChaffWhenEncryptedStorage(const uno::Reference< io::XOutputStream > &rStream, unsigned char* pBuffer, size_t length) +{ + if (!length) + return; + + uno::Reference< embed::XEncryptionProtectedSource2 > xEncr(rStream, + uno::UNO_QUERY); + + bool bAddChaff = xEncr.is() && xEncr->hasEncryptionData(); + + // exceptions are propagated + if (!bAddChaff) + { + const uno::Sequence<sal_Int8> buf( + reinterpret_cast<sal_Int8*>(pBuffer), length); + rStream->writeBytes(buf); + } + else + { + unsigned char *postcomment = + reinterpret_cast<unsigned char*>(strchr(reinterpret_cast<char*>(pBuffer), '\n')); + if (postcomment != nullptr) + { + ++postcomment; + + size_t preamblelen = postcomment - pBuffer; + + uno::Sequence<sal_Int8> buf( + reinterpret_cast<sal_Int8*>(pBuffer), preamblelen); + rStream->writeBytes(buf); + + OString aComment = + "<!--" + + comphelper::xml::makeXMLChaff() + + "-->"; + + buf = uno::Sequence<sal_Int8>( + reinterpret_cast<const sal_Int8*>(aComment.getStr()), aComment.getLength()); + rStream->writeBytes(buf); + + buf = uno::Sequence<sal_Int8>( + reinterpret_cast<sal_Int8*>(postcomment), length-preamblelen); + rStream->writeBytes(buf); + } + } +} + +void SAL_CALL +librdf_Repository::exportGraph(::sal_Int16 i_Format, + const uno::Reference< io::XOutputStream > & i_xOutStream, + const uno::Reference< rdf::XURI > & i_xGraphName, + const uno::Reference< rdf::XURI > & i_xBaseURI) +{ + if (!i_xOutStream.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::exportGraph: stream is null", *this, 1); + } + // FIXME: other formats + if (i_Format != rdf::FileFormat::RDF_XML) { + throw datatransfer::UnsupportedFlavorException( + "librdf_Repository::exportGraph: " + "file format not supported", *this); + } + if (!i_xGraphName.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::exportGraph: " + "graph name is null", *this, 2); + } + if (!i_xBaseURI.is()) { //FIXME: any i_Format that don't need a base URI? + throw lang::IllegalArgumentException( + "librdf_Repository::exportGraph: " + "base URI is null", *this, 3); + } + OSL_ENSURE(i_xBaseURI.is(), "no base uri"); + const OUString baseURIU( i_xBaseURI->getStringValue() ); + if (baseURIU.indexOf('#') >= 0) { + throw lang::IllegalArgumentException( + "librdf_Repository::exportGraph: " + "base URI is not absolute", *this, 3); + } + + const OUString contextU( i_xGraphName->getStringValue() ); + + std::unique_lock g(m_aMutex); // don't call i_x* with mutex locked + + if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) { + throw container::NoSuchElementException( + "librdf_Repository::exportGraph: " + "no graph with given URI exists", *this); + } + const OString context( + OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_new_node_from_uri_string failed", *this); + } + const OString baseURI( + OUStringToOString(baseURIU, RTL_TEXTENCODING_UTF8) ); + const std::shared_ptr<librdf_uri> pBaseURI( + librdf_new_uri(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (baseURI.getStr())), + safe_librdf_free_uri); + if (!pBaseURI) { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_new_uri failed", *this); + } + + const std::shared_ptr<librdf_stream> pStream( + librdf_model_context_as_stream(m_pModel.get(), pContext.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::RepositoryException( + "librdf_Repository::exportGraph: " + "librdf_model_context_as_stream failed", *this); + } + const char * const format("rdfxml"); + // #i116443#: abbrev breaks when certain URIs are used as data types +// const char *format("rdfxml-abbrev"); + const std::shared_ptr<librdf_serializer> pSerializer( + librdf_new_serializer(m_pWorld.get(), format, nullptr, nullptr), + safe_librdf_free_serializer); + if (!pSerializer) { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_new_serializer failed", *this); + } + + const std::shared_ptr<librdf_uri> pRelativeURI( + librdf_new_uri(m_pWorld.get(), reinterpret_cast<const unsigned char*> + ("http://feature.librdf.org/raptor-relativeURIs")), + safe_librdf_free_uri); + const std::shared_ptr<librdf_uri> pWriteBaseURI( + librdf_new_uri(m_pWorld.get(), reinterpret_cast<const unsigned char*> + ("http://feature.librdf.org/raptor-writeBaseURI")), + safe_librdf_free_uri); + const std::shared_ptr<librdf_node> p0( + librdf_new_node_from_literal(m_pWorld.get(), + reinterpret_cast<const unsigned char*> ("0"), nullptr, 0), + safe_librdf_free_node); + const std::shared_ptr<librdf_node> p1( + librdf_new_node_from_literal(m_pWorld.get(), + reinterpret_cast<const unsigned char*> ("1"), nullptr, 0), + safe_librdf_free_node); + if (!pWriteBaseURI || !pRelativeURI || !p0 || !p1) { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_new_uri or librdf_new_node_from_literal failed", *this); + } + + // make URIs relative to base URI + if (librdf_serializer_set_feature(pSerializer.get(), + pRelativeURI.get(), p1.get())) + { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_serializer_set_feature relativeURIs failed", *this); + } + // but do not write the base URI to the file! + if (librdf_serializer_set_feature(pSerializer.get(), + pWriteBaseURI.get(), p0.get())) + { + throw uno::RuntimeException( + "librdf_Repository::exportGraph: " + "librdf_serializer_set_feature writeBaseURI failed", *this); + } + + size_t length; + const std::shared_ptr<unsigned char> pBuf( + librdf_serializer_serialize_stream_to_counted_string( + pSerializer.get(), pBaseURI.get(), pStream.get(), &length), free); + if (!pBuf) { + throw rdf::RepositoryException( + "librdf_Repository::exportGraph: " + "librdf_serializer_serialize_stream_to_counted_string failed", + *this); + } + + g.unlock(); // release Mutex before calling i_xOutStream methods + + addChaffWhenEncryptedStorage(i_xOutStream, pBuf.get(), length); +} + +uno::Sequence< uno::Reference< rdf::XURI > > SAL_CALL +librdf_Repository::getGraphNames() +{ + std::scoped_lock g(m_aMutex); + ::std::vector< uno::Reference<rdf::XURI> > ret; + std::transform(m_NamedGraphs.begin(), m_NamedGraphs.end(), + std::back_inserter(ret), + [](std::pair<OUString, ::rtl::Reference<librdf_NamedGraph>> const& it) + { return it.second->getName(); }); + return comphelper::containerToSequence(ret); +} + +uno::Reference< rdf::XNamedGraph > SAL_CALL +librdf_Repository::getGraph(const uno::Reference< rdf::XURI > & i_xGraphName) +{ + if (!i_xGraphName.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::getGraph: URI is null", *this, 0); + } + const OUString contextU( i_xGraphName->getStringValue() ); + + std::scoped_lock g(m_aMutex); + const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(contextU) ); + if (iter != m_NamedGraphs.end()) { + return iter->second; + } else { + return nullptr; + } +} + +uno::Reference< rdf::XNamedGraph > SAL_CALL +librdf_Repository::createGraph(const uno::Reference< rdf::XURI > & i_xGraphName) +{ + if (!i_xGraphName.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::createGraph: URI is null", *this, 0); + } + + const OUString contextU( i_xGraphName->getStringValue() ); + if (contextU.startsWith(s_nsOOo)) + { + throw lang::IllegalArgumentException( + "librdf_Repository::createGraph: URI is reserved", *this, 0); + } + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + // NB: librdf does not have a concept of graphs as such; + // a librdf named graph exists iff the model contains a statement with + // the graph name as context + + if (m_NamedGraphs.find(contextU) != m_NamedGraphs.end()) { + throw container::ElementExistException( + "librdf_Repository::createGraph: graph with given URI exists", *this); + } + m_NamedGraphs.insert(std::make_pair(contextU, + new librdf_NamedGraph(this, i_xGraphName))); + return m_NamedGraphs.find(contextU)->second; +} + +void SAL_CALL +librdf_Repository::destroyGraph( + const uno::Reference< rdf::XURI > & i_xGraphName) +{ + if (!i_xGraphName.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::destroyGraph: URI is null", *this, 0); + } + const OUString contextU( i_xGraphName->getStringValue() ); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + const NamedGraphMap_t::iterator iter( clearGraph_Lock(contextU, false) ); + m_NamedGraphs.erase(iter); +} + +bool isMetadatableWithoutMetadata( + uno::Reference<uno::XInterface> const & i_xNode) +{ + const uno::Reference<rdf::XMetadatable> xMeta( i_xNode, uno::UNO_QUERY ); + return (xMeta.is() && xMeta->getMetadataReference().Second.isEmpty()); +} + +uno::Reference< container::XEnumeration > SAL_CALL +librdf_Repository::getStatements( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + if (isMetadatableWithoutMetadata(i_xSubject) || + isMetadatableWithoutMetadata(i_xPredicate) || + isMetadatableWithoutMetadata(i_xObject)) + { + return new librdf_GraphResult(this, m_aMutex, + std::shared_ptr<librdf_stream>(), + std::shared_ptr<librdf_node>()); + } + + librdf_TypeConverter::Statement const stmt( + librdf_TypeConverter::extractStatement_NoLock( + i_xSubject, i_xPredicate, i_xObject)); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + const std::shared_ptr<librdf_statement> pStatement( + librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), + safe_librdf_free_statement); + OSL_ENSURE(pStatement, "mkStatement failed"); + + const std::shared_ptr<librdf_stream> pStream( + librdf_model_find_statements(m_pModel.get(), pStatement.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::RepositoryException( + "librdf_Repository::getStatements: " + "librdf_model_find_statements failed", *this); + } + + return new librdf_GraphResult(this, m_aMutex, pStream, + std::shared_ptr<librdf_node>()); +} + + +uno::Reference< rdf::XQuerySelectResult > SAL_CALL +librdf_Repository::querySelect(const OUString & i_rQuery) +{ + std::scoped_lock g(m_aMutex); + const OString query( + OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); + const std::shared_ptr<librdf_query> pQuery( + librdf_new_query(m_pWorld.get(), s_sparql, nullptr, + reinterpret_cast<const unsigned char*> (query.getStr()), nullptr), + safe_librdf_free_query); + if (!pQuery) { + throw rdf::QueryException( + "librdf_Repository::querySelect: " + "librdf_new_query failed", *this); + } + const std::shared_ptr<librdf_query_results> pResults( + librdf_model_query_execute(m_pModel.get(), pQuery.get()), + safe_librdf_free_query_results); + if (!pResults || !librdf_query_results_is_bindings(pResults.get())) { + throw rdf::QueryException( + "librdf_Repository::querySelect: " + "query result is null or not bindings", *this); + } + + const int count( librdf_query_results_get_bindings_count(pResults.get()) ); + if (count < 0) { + throw rdf::QueryException( + "librdf_Repository::querySelect: " + "librdf_query_results_get_bindings_count failed", *this); + } + uno::Sequence< OUString > names(count); + auto namesRange = asNonConstRange(names); + for (int i = 0; i < count; ++i) { + const char* name( librdf_query_results_get_binding_name( + pResults.get(), i) ); + if (!name) { + throw rdf::QueryException( + "librdf_Repository::querySelect: binding is null", *this); + } + + namesRange[i] = OUString::createFromAscii(name); + } + + return new librdf_QuerySelectResult(this, m_aMutex, + pQuery, pResults, names); +} + +uno::Reference< container::XEnumeration > SAL_CALL +librdf_Repository::queryConstruct(const OUString & i_rQuery) +{ + std::scoped_lock g(m_aMutex); + const OString query( + OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); + const std::shared_ptr<librdf_query> pQuery( + librdf_new_query(m_pWorld.get(), s_sparql, nullptr, + reinterpret_cast<const unsigned char*> (query.getStr()), nullptr), + safe_librdf_free_query); + if (!pQuery) { + throw rdf::QueryException( + "librdf_Repository::queryConstruct: " + "librdf_new_query failed", *this); + } + const std::shared_ptr<librdf_query_results> pResults( + librdf_model_query_execute(m_pModel.get(), pQuery.get()), + safe_librdf_free_query_results); + if (!pResults || !librdf_query_results_is_graph(pResults.get())) { + throw rdf::QueryException( + "librdf_Repository::queryConstruct: " + "query result is null or not graph", *this); + } + const std::shared_ptr<librdf_stream> pStream( + librdf_query_results_as_stream(pResults.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::QueryException( + "librdf_Repository::queryConstruct: " + "librdf_query_results_as_stream failed", *this); + } + + return new librdf_GraphResult(this, m_aMutex, pStream, + std::shared_ptr<librdf_node>(), pQuery); +} + +sal_Bool SAL_CALL +librdf_Repository::queryAsk(const OUString & i_rQuery) +{ + std::scoped_lock g(m_aMutex); + + const OString query( + OUStringToOString(i_rQuery, RTL_TEXTENCODING_UTF8) ); + const std::shared_ptr<librdf_query> pQuery( + librdf_new_query(m_pWorld.get(), s_sparql, nullptr, + reinterpret_cast<const unsigned char*> (query.getStr()), nullptr), + safe_librdf_free_query); + if (!pQuery) { + throw rdf::QueryException( + "librdf_Repository::queryAsk: " + "librdf_new_query failed", *this); + } + const std::shared_ptr<librdf_query_results> pResults( + librdf_model_query_execute(m_pModel.get(), pQuery.get()), + safe_librdf_free_query_results); + if (!pResults || !librdf_query_results_is_boolean(pResults.get())) { + throw rdf::QueryException( + "librdf_Repository::queryAsk: " + "query result is null or not boolean", *this); + } + return bool(librdf_query_results_get_boolean(pResults.get())); +} + +// css::rdf::XDocumentRepository: +void SAL_CALL librdf_Repository::setStatementRDFa( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Sequence< uno::Reference< rdf::XURI > > & i_rPredicates, + const uno::Reference< rdf::XMetadatable > & i_xObject, + const OUString & i_rRDFaContent, + const uno::Reference< rdf::XURI > & i_xRDFaDatatype) +{ + if (!i_xSubject.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::setStatementRDFa: Subject is null", *this, 0); + } + if (!i_rPredicates.hasElements()) { + throw lang::IllegalArgumentException( + "librdf_Repository::setStatementRDFa: no Predicates", + *this, 1); + } + if (std::any_of(i_rPredicates.begin(), i_rPredicates.end(), + [](const uno::Reference< rdf::XURI >& rPredicate) { return !rPredicate.is(); })) { + throw lang::IllegalArgumentException( + "librdf_Repository::setStatementRDFa: Predicate is null", *this, 1); + } + if (!i_xObject.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::setStatementRDFa: Object is null", *this, 2); + } + const uno::Reference<lang::XServiceInfo> xService(i_xObject, + uno::UNO_QUERY_THROW); + uno::Reference<text::XTextRange> xTextRange; + if (xService->supportsService("com.sun.star.table.Cell") || + xService->supportsService("com.sun.star.text.CellProperties") || // for writer + xService->supportsService("com.sun.star.text.Paragraph")) + { + xTextRange.set(i_xObject, uno::UNO_QUERY_THROW); + } + else if (xService->supportsService("com.sun.star.text.Bookmark") || + xService->supportsService("com.sun.star.text.InContentMetadata")) + { + const uno::Reference<text::XTextContent> xTextContent(i_xObject, + uno::UNO_QUERY_THROW); + xTextRange = xTextContent->getAnchor(); + } + if (!xTextRange.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::setStatementRDFa: " + "Object does not support RDFa", *this, 2); + } + // ensure that the metadatable has an XML ID + i_xObject->ensureMetadataReference(); + const beans::StringPair mdref( i_xObject->getMetadataReference() ); + if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { + throw uno::RuntimeException( + "librdf_Repository::setStatementRDFa: " + "ensureMetadataReference did not", *this); + } + OUString const sXmlId(mdref.First + "#" + mdref.Second); + OUString const sContext(s_nsOOo + sXmlId); + OUString const content( (i_rRDFaContent.isEmpty()) + ? xTextRange->getString() + : i_rRDFaContent ); + uno::Reference<rdf::XNode> xContent; + try { + if (i_xRDFaDatatype.is()) { + xContent.set(rdf::Literal::createWithType(m_xContext, + content, i_xRDFaDatatype), + uno::UNO_QUERY_THROW); + } else { + xContent.set(rdf::Literal::create(m_xContext, content), + uno::UNO_QUERY_THROW); + } + } catch (const lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_Repository::setStatementRDFa: " + "cannot create literal", *this, anyEx); + } + + std::shared_ptr<librdf_TypeConverter::Resource> const pSubject( + librdf_TypeConverter::extractResource_NoLock(i_xSubject)); + std::shared_ptr<librdf_TypeConverter::Node> const pContent( + librdf_TypeConverter::extractNode_NoLock(xContent)); + ::std::vector< std::shared_ptr<librdf_TypeConverter::Resource> > + predicates; + ::std::transform(i_rPredicates.begin(), i_rPredicates.end(), + ::std::back_inserter(predicates), + [](uno::Reference<rdf::XURI> const& xURI) + { return librdf_TypeConverter::extractResource_NoLock(xURI); }); + + removeStatementRDFa(i_xObject); // not atomic with insertion? + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + if (i_rRDFaContent.isEmpty()) { + m_RDFaXHTMLContentSet.erase(sXmlId); + } else { + m_RDFaXHTMLContentSet.insert(sXmlId); + } + try + { + for (const auto& rPredicatePtr : predicates) + { + addStatementGraph_Lock( + librdf_TypeConverter::Statement(pSubject, + std::dynamic_pointer_cast<librdf_TypeConverter::URI>(rPredicatePtr), + pContent), + sContext, true); + } + } + catch (const container::NoSuchElementException&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_Repository::setStatementRDFa: " + "cannot addStatementGraph", *this, anyEx); + } +} + +void SAL_CALL librdf_Repository::removeStatementRDFa( + const uno::Reference< rdf::XMetadatable > & i_xElement) +{ + if (!i_xElement.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::removeStatementRDFa: Element is null", + *this, 0); + } + + const beans::StringPair mdref( i_xElement->getMetadataReference() ); + if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { + return; // nothing to do... + } + + OUString const sXmlId(s_nsOOo + mdref.First + "#" + mdref.Second); + + clearGraph_NoLock(sXmlId, true); +} + +beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool > SAL_CALL +librdf_Repository::getStatementRDFa( + const uno::Reference< rdf::XMetadatable > & i_xElement) +{ + if (!i_xElement.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::getStatementRDFa: Element is null", *this, 0); + } + const beans::StringPair mdref( i_xElement->getMetadataReference() ); + if ((mdref.First.isEmpty()) || (mdref.Second.isEmpty())) { + return beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool >(); + } + OUString const sXmlId(mdref.First + "#" + mdref.Second); + uno::Reference<rdf::XURI> xXmlId; + try { + xXmlId.set( rdf::URI::create(m_xContext, s_nsOOo + sXmlId), + uno::UNO_SET_THROW); + } catch (const lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_Repository::getStatementRDFa: " + "cannot create URI for XML ID", *this, anyEx); + } + + ::std::vector< rdf::Statement > ret; + try + { + ret = getStatementsGraph_NoLock(nullptr, nullptr, nullptr, xXmlId, true); + } + catch (const container::NoSuchElementException&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_Repository::getStatementRDFa: " + "cannot getStatementsGraph", *this, anyEx); + } + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + return beans::Pair< uno::Sequence<rdf::Statement>, sal_Bool >( + comphelper::containerToSequence(ret), 0 != m_RDFaXHTMLContentSet.count(sXmlId)); +} + +extern "C" +librdf_statement *rdfa_context_stream_map_handler( + librdf_stream *i_pStream, void *, librdf_statement *i_pStatement) +{ + OSL_ENSURE(i_pStream, "rdfa_context_stream_map_handler: stream null"); + if (i_pStream) { + librdf_node *pCtxt( +#if LIBRDF_VERSION >= 10012 + librdf_stream_get_context2(i_pStream) ); +#else + static_cast<librdf_node *>(librdf_stream_get_context(i_pStream)) ); +#endif + OSL_ENSURE(pCtxt, "rdfa_context_stream_map_handler: context null"); + if (pCtxt && isInternalContext(pCtxt)) { + return i_pStatement; + } + } + return nullptr; +}; + +uno::Reference< container::XEnumeration > SAL_CALL +librdf_Repository::getStatementsRDFa( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + if (isMetadatableWithoutMetadata(i_xSubject) || + isMetadatableWithoutMetadata(i_xPredicate) || + isMetadatableWithoutMetadata(i_xObject)) + { + return new librdf_GraphResult(this, m_aMutex, + std::shared_ptr<librdf_stream>(), + std::shared_ptr<librdf_node>()); + } + + librdf_TypeConverter::Statement const stmt( + librdf_TypeConverter::extractStatement_NoLock( + i_xSubject, i_xPredicate, i_xObject)); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + const std::shared_ptr<librdf_statement> pStatement( + librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), + safe_librdf_free_statement); + OSL_ENSURE(pStatement, "mkStatement failed"); + + const std::shared_ptr<librdf_stream> pStream( + librdf_model_find_statements(m_pModel.get(), pStatement.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::RepositoryException( + "librdf_Repository::getStatementsRDFa: " + "librdf_model_find_statements failed", *this); + } + + if (librdf_stream_add_map(pStream.get(), rdfa_context_stream_map_handler, + nullptr, nullptr)) { + throw rdf::RepositoryException( + "librdf_Repository::getStatementsRDFa: " + "librdf_stream_add_map failed", *this); + } + + return new librdf_GraphResult(this, m_aMutex, pStream, + std::shared_ptr<librdf_node>()); +} + +// css::lang::XInitialization: +void SAL_CALL librdf_Repository::initialize( + const uno::Sequence< css::uno::Any > &) +{ + std::scoped_lock g(m_aMutex); + +// m_pWorld.reset(m_TypeConverter.createWorld(), safe_librdf_free_world); + m_pStorage.reset(m_TypeConverter.createStorage_Lock(m_pWorld.get()), + safe_librdf_free_storage); + m_pModel.reset(m_TypeConverter.createModel_Lock( + m_pWorld.get(), m_pStorage.get()), safe_librdf_free_model); +} + +NamedGraphMap_t::iterator librdf_Repository::clearGraph_NoLock( + OUString const& i_rGraphName, bool i_Internal) +// throw (uno::RuntimeException, container::NoSuchElementException, +// rdf::RepositoryException) +{ + std::scoped_lock g(m_aMutex); + + return clearGraph_Lock(i_rGraphName, i_Internal); +} + +NamedGraphMap_t::iterator librdf_Repository::clearGraph_Lock( + OUString const& i_rGraphName, bool i_Internal) +{ + // internal: must be called with mutex locked! + const NamedGraphMap_t::iterator iter( m_NamedGraphs.find(i_rGraphName) ); + if (!i_Internal && iter == m_NamedGraphs.end()) { + throw container::NoSuchElementException( + "librdf_Repository::clearGraph: " + "no graph with given URI exists", *this); + } + const OString context( + OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::clearGraph: " + "librdf_new_node_from_uri_string failed", *this); + } + if (librdf_model_context_remove_statements(m_pModel.get(), pContext.get())) + { + throw rdf::RepositoryException( + "librdf_Repository::clearGraph: " + "librdf_model_context_remove_statements failed", *this); + } + return iter; +} + +void librdf_Repository::addStatementGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xGraphName) +//throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException) +{ + if (!i_xSubject.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::addStatement: Subject is null", *this, 0); + } + if (!i_xPredicate.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::addStatement: Predicate is null", + *this, 1); + } + if (!i_xObject.is()) { + throw lang::IllegalArgumentException( + "librdf_Repository::addStatement: Object is null", *this, 2); + } + + librdf_TypeConverter::Statement const stmt( + librdf_TypeConverter::extractStatement_NoLock( + i_xSubject, i_xPredicate, i_xObject)); + + const OUString contextU( i_xGraphName->getStringValue() ); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + addStatementGraph_Lock(stmt, contextU, false/*i_Internal*/); +} + +void librdf_Repository::addStatementGraph_Lock( + librdf_TypeConverter::Statement const& i_rStatement, + OUString const& i_rGraphName, + bool i_Internal) +{ + if (!i_Internal + && (m_NamedGraphs.find(i_rGraphName) == m_NamedGraphs.end())) + { + throw container::NoSuchElementException( + "librdf_Repository::addStatement: " + "no graph with given URI exists", *this); + } + const OString context( + OUStringToOString(i_rGraphName, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::addStatement: " + "librdf_new_node_from_uri_string failed", *this); + } + const std::shared_ptr<librdf_statement> pStatement( + librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), i_rStatement), + safe_librdf_free_statement); + OSL_ENSURE(pStatement, "mkStatement failed"); + + // Test for duplicate statement + // librdf_model_add_statement disallows duplicates while + // librdf_model_context_add_statement allows duplicates + { + const std::shared_ptr<librdf_stream> pStream( + librdf_model_find_statements_in_context(m_pModel.get(), + pStatement.get(), pContext.get()), + safe_librdf_free_stream); + if (pStream && !librdf_stream_end(pStream.get())) + return; + } + + if (librdf_model_context_add_statement(m_pModel.get(), + pContext.get(), pStatement.get())) { + throw rdf::RepositoryException( + "librdf_Repository::addStatement: " + "librdf_model_context_add_statement failed", *this); + } +} + +void librdf_Repository::removeStatementsGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xGraphName) +//throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException) +{ + if (isMetadatableWithoutMetadata(i_xSubject) || + isMetadatableWithoutMetadata(i_xPredicate) || + isMetadatableWithoutMetadata(i_xObject)) + { + return; + } + + librdf_TypeConverter::Statement const stmt( + librdf_TypeConverter::extractStatement_NoLock( + i_xSubject, i_xPredicate, i_xObject)); + const OUString contextU( i_xGraphName->getStringValue() ); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + if (m_NamedGraphs.find(contextU) == m_NamedGraphs.end()) { + throw container::NoSuchElementException( + "librdf_Repository::removeStatements: " + "no graph with given URI exists", *this); + } + const OString context( + OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::removeStatements: " + "librdf_new_node_from_uri_string failed", *this); + } + const std::shared_ptr<librdf_statement> pStatement( + librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), + safe_librdf_free_statement); + OSL_ENSURE(pStatement, "mkStatement failed"); + + const std::shared_ptr<librdf_stream> pStream( + librdf_model_find_statements_in_context(m_pModel.get(), + pStatement.get(), pContext.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::RepositoryException( + "librdf_Repository::removeStatements: " + "librdf_model_find_statements_in_context failed", *this); + } + + if (librdf_stream_end(pStream.get())) + return; + + do { + librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) ); + if (!pStmt) { + throw rdf::RepositoryException( + "librdf_Repository::removeStatements: " + "librdf_stream_get_object failed", *this); + } + if (librdf_model_context_remove_statement(m_pModel.get(), + pContext.get(), pStmt)) { + throw rdf::RepositoryException( + "librdf_Repository::removeStatements: " + "librdf_model_context_remove_statement failed", *this); + } + } while (!librdf_stream_next(pStream.get())); +} + +std::vector<rdf::Statement> +librdf_Repository::getStatementsGraph_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject, + const uno::Reference< rdf::XURI > & i_xGraphName, + bool i_Internal) +//throw (uno::RuntimeException, lang::IllegalArgumentException, +// container::NoSuchElementException, rdf::RepositoryException) +{ + std::vector<rdf::Statement> ret; + + // N.B.: if any of subject, predicate, object is an XMetadatable, and + // has no metadata reference, then there cannot be any node in the graph + // representing it; in order to prevent side effect + // (ensureMetadataReference), check for this condition and return + if (isMetadatableWithoutMetadata(i_xSubject) || + isMetadatableWithoutMetadata(i_xPredicate) || + isMetadatableWithoutMetadata(i_xObject)) + { + return ret; + } + + librdf_TypeConverter::Statement const stmt( + librdf_TypeConverter::extractStatement_NoLock( + i_xSubject, i_xPredicate, i_xObject)); + const OUString contextU( i_xGraphName->getStringValue() ); + + std::scoped_lock g(m_aMutex); // don't call i_x* with mutex locked + + if (!i_Internal && (m_NamedGraphs.find(contextU) == m_NamedGraphs.end())) { + throw container::NoSuchElementException( + "librdf_Repository::getStatements: " + "no graph with given URI exists", *this); + } + const OString context( + OUStringToOString(contextU, RTL_TEXTENCODING_UTF8) ); + + const std::shared_ptr<librdf_node> pContext( + librdf_new_node_from_uri_string(m_pWorld.get(), + reinterpret_cast<const unsigned char*> (context.getStr())), + safe_librdf_free_node); + if (!pContext) { + throw uno::RuntimeException( + "librdf_Repository::getStatements: " + "librdf_new_node_from_uri_string failed", *this); + } + const std::shared_ptr<librdf_statement> pStatement( + librdf_TypeConverter::mkStatement_Lock(m_pWorld.get(), stmt), + safe_librdf_free_statement); + OSL_ENSURE(pStatement, "mkStatement failed"); + + const std::shared_ptr<librdf_stream> pStream( + librdf_model_find_statements_in_context(m_pModel.get(), + pStatement.get(), pContext.get()), + safe_librdf_free_stream); + if (!pStream) { + throw rdf::RepositoryException( + "librdf_Repository::getStatements: " + "librdf_model_find_statements_in_context failed", *this); + } + + librdf_node *pCtxt1( +#if LIBRDF_VERSION >= 10012 + librdf_stream_get_context2(pStream.get()) ); +#else + static_cast<librdf_node *>(librdf_stream_get_context(pStream.get())) ); +#endif + while (!librdf_stream_end(pStream.get())) + { + auto pCtxt = pCtxt1; + librdf_statement *pStmt( librdf_stream_get_object(pStream.get()) ); + if (!pStmt) { + rdf::QueryException e( + "librdf_GraphResult::nextElement: " + "librdf_stream_get_object failed", *this); + throw lang::WrappedTargetException( + "librdf_GraphResult::nextElement: " + "librdf_stream_get_object failed", *this, + uno::Any(e)); + } + // NB: pCtxt may be null here if this is result of a graph query + if (pCtxt && isInternalContext(pCtxt)) { + pCtxt = nullptr; // XML ID context is implementation detail! + } + + ret.emplace_back( + getTypeConverter().convertToStatement(pStmt, pCtxt) ); + + // NB: this will invalidate current item. + librdf_stream_next(pStream.get()); + } + + return ret; +} + +extern "C" +void librdf_raptor_init(void* /*user_data*/, raptor_world* pRaptorWorld) +{ + // fdo#64672 prevent raptor from setting global libxml2 error handlers + raptor_world_set_flag(pRaptorWorld, + RAPTOR_WORLD_FLAG_LIBXML_STRUCTURED_ERROR_SAVE, 0); + raptor_world_set_flag(pRaptorWorld, + RAPTOR_WORLD_FLAG_LIBXML_GENERIC_ERROR_SAVE, 0); +} + +librdf_world *librdf_TypeConverter::createWorld_Lock() const +{ + // create and initialize world + librdf_world *pWorld( librdf_new_world() ); + if (!pWorld) { + throw uno::RuntimeException( + "librdf_TypeConverter::createWorld: librdf_new_world failed", + m_rRep); + } + librdf_world_set_raptor_init_handler(pWorld, nullptr, &librdf_raptor_init); + //FIXME logger, digest, features? + xsltSecurityPrefsPtr origprefs = xsltGetDefaultSecurityPrefs(); + librdf_world_open(pWorld); + xsltSecurityPrefsPtr newprefs = xsltGetDefaultSecurityPrefs(); + if (newprefs != origprefs) { + // #i110523# restore libxslt global configuration + // (gratuitously overwritten by raptor_init_parser_grddl_common) + // (this is the only reason unordf is linked against libxslt) + xsltSetDefaultSecurityPrefs(origprefs); + } + return pWorld; +} + +librdf_storage * +librdf_TypeConverter::createStorage_Lock(librdf_world *i_pWorld) const +{ + librdf_storage *pStorage( +// librdf_new_storage(i_pWorld, "memory", NULL, "contexts='yes'") ); + librdf_new_storage(i_pWorld, "hashes", nullptr, + "contexts='yes',hash-type='memory'") ); + if (!pStorage) { + throw uno::RuntimeException( + "librdf_TypeConverter::createStorage: librdf_new_storage failed", + m_rRep); + } + return pStorage; +} + +librdf_model *librdf_TypeConverter::createModel_Lock( + librdf_world *i_pWorld, librdf_storage * i_pStorage) const +{ + librdf_model *pRepository( librdf_new_model(i_pWorld, i_pStorage, nullptr) ); + if (!pRepository) { + throw uno::RuntimeException( + "librdf_TypeConverter::createModel: librdf_new_model failed", + m_rRep); + } + //FIXME +#if 0 + { + librdf_uri * ctxt = librdf_new_uri(i_pWorld, reinterpret_cast<const unsigned char *>(LIBRDF_MODEL_FEATURE_CONTEXTS)); + librdf_node * contexts = librdf_model_get_feature(repository, ctxt); + if (!contexts) + throw; + std::cout << "value of contexts feature: "; + prtNode(contexts); + std::cout << std::endl; + // librdf_model_set_feature(repository, LIBRDF_FEATURE_CONTEXTS, ...); + safe_librdf_free_node(contexts); + safe_librdf_free_uri(ctxt); + } +#endif + return pRepository; +} + +// this does NOT create a node, only URI +librdf_uri* librdf_TypeConverter::mkURI_Lock( librdf_world* i_pWorld, + OString const& i_rURI) +{ + librdf_uri *pURI( librdf_new_uri(i_pWorld, + reinterpret_cast<const unsigned char *>(i_rURI.getStr()))); + if (!pURI) { + throw uno::RuntimeException( + "librdf_TypeConverter::mkURI: librdf_new_uri failed", nullptr); + } + return pURI; +} + +// extract blank or URI node - call without Mutex locked +std::shared_ptr<librdf_TypeConverter::Resource> +librdf_TypeConverter::extractResource_NoLock( + const uno::Reference< rdf::XResource > & i_xResource) +{ + if (!i_xResource.is()) { + return std::shared_ptr<Resource>(); + } + uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY); + if (xBlankNode.is()) { + const OString label( + OUStringToOString(xBlankNode->getStringValue(), + RTL_TEXTENCODING_UTF8) ); + return std::make_shared<BlankNode>(label); + } else { // assumption: everything else is URI + const OString uri( + OUStringToOString(i_xResource->getStringValue(), + RTL_TEXTENCODING_UTF8) ); + return std::make_shared<URI>(uri); + } +} + +void +librdf_TypeConverter::extractResourceToCacheKey_NoLock( + const uno::Reference< rdf::XResource > & i_xResource, OUStringBuffer& rBuffer) +{ + if (!i_xResource.is()) { + return; + } + uno::Reference< rdf::XBlankNode > xBlankNode(i_xResource, uno::UNO_QUERY); + if (xBlankNode.is()) { + rBuffer.append("BlankNode " + xBlankNode->getStringValue()); + } else { // assumption: everything else is URI + rBuffer.append("URI " + i_xResource->getStringValue()); + } +} + +// create blank or URI node +librdf_node* librdf_TypeConverter::mkResource_Lock( librdf_world* i_pWorld, + Resource const*const i_pResource) +{ + if (!i_pResource) return nullptr; + BlankNode const*const pBlankNode( + dynamic_cast<BlankNode const*>(i_pResource)); + if (pBlankNode) { + librdf_node *pNode( + librdf_new_node_from_blank_identifier(i_pWorld, + reinterpret_cast<const unsigned char*>( + pBlankNode->value.getStr()))); + if (!pNode) { + throw uno::RuntimeException( + "librdf_TypeConverter::mkResource: " + "librdf_new_node_from_blank_identifier failed", nullptr); + } + return pNode; + } else { // assumption: everything else is URI + URI const*const pURI(dynamic_cast<URI const*>(i_pResource)); + assert(pURI); + librdf_node *pNode( + librdf_new_node_from_uri_string(i_pWorld, + reinterpret_cast<const unsigned char*>(pURI->value.getStr()))); + if (!pNode) { + throw uno::RuntimeException( + "librdf_TypeConverter::mkResource: " + "librdf_new_node_from_uri_string failed", nullptr); + } + return pNode; + } +} + +// extract blank or URI or literal node - call without Mutex locked +std::shared_ptr<librdf_TypeConverter::Node> +librdf_TypeConverter::extractNode_NoLock( + const uno::Reference< rdf::XNode > & i_xNode) +{ + if (!i_xNode.is()) { + return std::shared_ptr<Node>(); + } + uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY); + if (xResource.is()) { + return extractResource_NoLock(xResource); + } + uno::Reference< rdf::XLiteral> xLiteral(i_xNode, uno::UNO_QUERY); + OSL_ENSURE(xLiteral.is(), + "mkNode: someone invented a new rdf.XNode and did not tell me"); + if (!xLiteral.is()) { + return std::shared_ptr<Node>(); + } + const OString val( + OUStringToOString(xLiteral->getValue(), + RTL_TEXTENCODING_UTF8) ); + const OString lang( + OUStringToOString(xLiteral->getLanguage(), + RTL_TEXTENCODING_UTF8) ); + const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype()); + std::optional<OString> type; + if (xType.is()) + { + type = + OUStringToOString(xType->getStringValue(), RTL_TEXTENCODING_UTF8); + } + return std::make_shared<Literal>(val, lang, type); +} + +// extract blank or URI or literal node - call without Mutex locked +void +librdf_TypeConverter::extractNodeToCacheKey_NoLock( + const uno::Reference< rdf::XNode > & i_xNode, + OUStringBuffer& rBuffer) +{ + if (!i_xNode.is()) { + return; + } + uno::Reference< rdf::XResource > xResource(i_xNode, uno::UNO_QUERY); + if (xResource.is()) { + return extractResourceToCacheKey_NoLock(xResource, rBuffer); + } + uno::Reference< rdf::XLiteral> xLiteral(i_xNode, uno::UNO_QUERY); + OSL_ENSURE(xLiteral.is(), + "mkNode: someone invented a new rdf.XNode and did not tell me"); + if (!xLiteral.is()) { + return; + } + rBuffer.append("Literal " + xLiteral->getValue() + "\t" + xLiteral->getLanguage()); + const uno::Reference< rdf::XURI > xType(xLiteral->getDatatype()); + if (xType.is()) + rBuffer.append("\t" + xType->getStringValue()); +} + +// create blank or URI or literal node +librdf_node* librdf_TypeConverter::mkNode_Lock( librdf_world* i_pWorld, + Node const*const i_pNode) +{ + if (!i_pNode) return nullptr; + Resource const*const pResource(dynamic_cast<Resource const*>(i_pNode)); + if (pResource) { + return mkResource_Lock(i_pWorld, pResource); + } + + Literal const*const pLiteral(dynamic_cast<Literal const*>(i_pNode)); + assert(pLiteral); + librdf_node * ret(nullptr); + if (pLiteral->language.isEmpty()) { + if (!pLiteral->type) { + ret = librdf_new_node_from_literal(i_pWorld, + reinterpret_cast<const unsigned char*>(pLiteral->value.getStr()) + , nullptr, 0); + } else { + const std::shared_ptr<librdf_uri> pDatatype( + mkURI_Lock(i_pWorld, *pLiteral->type), + safe_librdf_free_uri); + ret = librdf_new_node_from_typed_literal(i_pWorld, + reinterpret_cast<const unsigned char*>(pLiteral->value.getStr()) + , nullptr, pDatatype.get()); + } + } else { + if (!pLiteral->type) { + ret = librdf_new_node_from_literal(i_pWorld, + reinterpret_cast<const unsigned char*>(pLiteral->value.getStr()) + , pLiteral->language.getStr(), 0); + } else { + OSL_FAIL("mkNode: invalid literal"); + return nullptr; + } + } + if (!ret) { + throw uno::RuntimeException( + "librdf_TypeConverter::mkNode: librdf_new_node_from_literal failed", nullptr); + } + return ret; +} + +// extract statement - call without Mutex locked +librdf_TypeConverter::Statement librdf_TypeConverter::extractStatement_NoLock( + const uno::Reference< rdf::XResource > & i_xSubject, + const uno::Reference< rdf::XURI > & i_xPredicate, + const uno::Reference< rdf::XNode > & i_xObject) +{ + std::shared_ptr<Resource> const pSubject( + extractResource_NoLock(i_xSubject)); + std::shared_ptr<URI> const pPredicate( + std::dynamic_pointer_cast<URI>(extractResource_NoLock(i_xPredicate))); + std::shared_ptr<Node> const pObject(extractNode_NoLock(i_xObject)); + return Statement(pSubject, pPredicate, pObject); +} + +librdf_statement* librdf_TypeConverter::mkStatement_Lock(librdf_world* i_pWorld, + Statement const& i_rStatement) +{ + librdf_node *const pSubject( + mkResource_Lock(i_pWorld, i_rStatement.pSubject.get()) ); + librdf_node* pPredicate(nullptr); + librdf_node* pObject(nullptr); + try { + pPredicate = mkResource_Lock(i_pWorld, i_rStatement.pPredicate.get()); + try { + pObject = mkNode_Lock(i_pWorld, i_rStatement.pObject.get()); + } catch (...) { + safe_librdf_free_node(pPredicate); + throw; + } + } catch (...) { + safe_librdf_free_node(pSubject); + throw; + } + // NB: this takes ownership of the nodes! (which is really ugly) + librdf_statement* pStatement( librdf_new_statement_from_nodes(i_pWorld, + pSubject, pPredicate, pObject) ); + if (!pStatement) { + throw uno::RuntimeException( + "librdf_TypeConverter::mkStatement: " + "librdf_new_statement_from_nodes failed", nullptr); + } + return pStatement; +} + +uno::Reference<rdf::XURI> +librdf_TypeConverter::convertToXURI(librdf_uri* i_pURI) const +{ + if (!i_pURI) return nullptr; + const unsigned char* uri( librdf_uri_as_string(i_pURI) ); + if (!uri) { + throw uno::RuntimeException( + "librdf_TypeConverter::convertToXURI: " + "librdf_uri_as_string failed", m_rRep); + } + OUString uriU( OStringToOUString( + std::string_view(reinterpret_cast<const char*>(uri)), + RTL_TEXTENCODING_UTF8) ); + try { + return rdf::URI::create(m_xContext, uriU); + } catch (const lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_TypeConverter::convertToXURI: " + "illegal uri", m_rRep, anyEx); + } +} + +uno::Reference<rdf::XURI> +librdf_TypeConverter::convertToXURI(librdf_node* i_pNode) const +{ + if (!i_pNode) return nullptr; + if (librdf_node_is_resource(i_pNode)) { + librdf_uri* pURI( librdf_node_get_uri(i_pNode) ); + if (!pURI) { + throw uno::RuntimeException( + "librdf_TypeConverter::convertToXURI: " + "resource has no uri", m_rRep); + } + return convertToXURI(pURI); + } else { + OSL_FAIL("convertToXURI: unknown librdf_node"); + return nullptr; + } +} + +uno::Reference<rdf::XResource> +librdf_TypeConverter::convertToXResource(librdf_node* i_pNode) const +{ + if (!i_pNode) return nullptr; + if (librdf_node_is_blank(i_pNode)) { + const unsigned char* label( librdf_node_get_blank_identifier(i_pNode) ); + if (!label) { + throw uno::RuntimeException( + "librdf_TypeConverter::convertToXResource: " + "blank node has no label", m_rRep); + } + OUString labelU( OStringToOUString( + std::string_view(reinterpret_cast<const char*>(label)), + RTL_TEXTENCODING_UTF8) ); + try { + return rdf::BlankNode::create(m_xContext, labelU); + } catch (const lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException( + "librdf_TypeConverter::convertToXResource: " + "illegal blank node label", m_rRep, anyEx); + } + } else { + return convertToXURI(i_pNode); + } +} + +uno::Reference<rdf::XNode> +librdf_TypeConverter::convertToXNode(librdf_node* i_pNode) const +{ + if (!i_pNode) return nullptr; + if (!librdf_node_is_literal(i_pNode)) { + return convertToXResource(i_pNode); + } + const unsigned char* value( librdf_node_get_literal_value(i_pNode) ); + if (!value) { + throw uno::RuntimeException( + "librdf_TypeConverter::convertToXNode: " + "literal has no value", m_rRep); + } + const char * lang( librdf_node_get_literal_value_language(i_pNode) ); + librdf_uri* pType( + librdf_node_get_literal_value_datatype_uri(i_pNode) ); + OSL_ENSURE(!lang || !pType, "convertToXNode: invalid literal"); + const OUString valueU( OStringToOUString( + std::string_view(reinterpret_cast<const char*>(value)), + RTL_TEXTENCODING_UTF8) ); + if (lang) { + const OUString langU( OStringToOUString( + std::string_view(lang), + RTL_TEXTENCODING_UTF8) ); + return rdf::Literal::createWithLanguage(m_xContext, valueU, langU); + } else if (pType) { + uno::Reference<rdf::XURI> xType(convertToXURI(pType)); + OSL_ENSURE(xType.is(), "convertToXNode: null uri"); + return rdf::Literal::createWithType(m_xContext, valueU, xType); + } else { + return rdf::Literal::create(m_xContext, valueU); + } +} + +rdf::Statement +librdf_TypeConverter::convertToStatement(librdf_statement* i_pStmt, + librdf_node* i_pContext) const +{ + if (!i_pStmt) { + throw uno::RuntimeException(); + } + return rdf::Statement( + convertToXResource(librdf_statement_get_subject(i_pStmt)), + convertToXURI(librdf_statement_get_predicate(i_pStmt)), + convertToXNode(librdf_statement_get_object(i_pStmt)), + convertToXURI(i_pContext)); +} + +} // closing anonymous implementation namespace + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_rdfRepository_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new librdf_Repository(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/rdf/unordf.component b/unoxml/source/rdf/unordf.component new file mode 100644 index 000000000..1b99b5786 --- /dev/null +++ b/unoxml/source/rdf/unordf.component @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="CBlankNode" constructor="unoxml_CBlankNode_get_implementation"> + <service name="com.sun.star.rdf.BlankNode"/> + </implementation> + <implementation name="CLiteral" constructor="unoxml_CLiteral_get_implementation"> + <service name="com.sun.star.rdf.Literal"/> + </implementation> + <implementation name="CURI" constructor="unoxml_CURI_get_implementation"> + <service name="com.sun.star.rdf.URI"/> + </implementation> + <implementation name="librdf_Repository" + constructor="unoxml_rdfRepository_get_implementation"> + <service name="com.sun.star.rdf.Repository"/> + </implementation> +</component> diff --git a/unoxml/source/service/unoxml.component b/unoxml/source/service/unoxml.component new file mode 100644 index 000000000..6591bde6a --- /dev/null +++ b/unoxml/source/service/unoxml.component @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.xml.dom.DocumentBuilder" + constructor="unoxml_CDocumentBuilder_get_implementation"> + <service name="com.sun.star.xml.dom.DocumentBuilder"/> + </implementation> + <implementation name="com.sun.star.comp.xml.dom.SAXDocumentBuilder" + constructor="unoxml_CSAXDocumentBuilder_get_implementation"> + <service name="com.sun.star.xml.dom.SAXDocumentBuilder"/> + </implementation> + <implementation name="com.sun.star.comp.xml.xpath.XPathAPI" + constructor="unoxml_CXPathAPI_get_implementation"> + <service name="com.sun.star.xml.xpath.XPathAPI"/> + </implementation> +</component> diff --git a/unoxml/source/xpath/nodelist.cxx b/unoxml/source/xpath/nodelist.cxx new file mode 100644 index 000000000..1b306aabc --- /dev/null +++ b/unoxml/source/xpath/nodelist.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "nodelist.hxx" + +#include "../dom/document.hxx" + +using namespace css::uno; +using namespace css::xml::dom; + +namespace XPath +{ + CNodeList::CNodeList( + ::rtl::Reference<DOM::CDocument> const& pDocument, + ::osl::Mutex & rMutex, + std::shared_ptr<xmlXPathObject> const& rxpathObj) + : m_pDocument(pDocument) + , m_rMutex(rMutex) + , m_pNodeSet(nullptr) + { + if (rxpathObj != nullptr && rxpathObj->type == XPATH_NODESET) + { + m_pNodeSet = rxpathObj->nodesetval; + m_pXPathObj = rxpathObj; + } + } + + /** + The number of nodes in the list. + */ + sal_Int32 SAL_CALL CNodeList::getLength() + { + ::osl::MutexGuard const g(m_rMutex); + + sal_Int32 value = 0; + if (m_pNodeSet != nullptr) + value = xmlXPathNodeSetGetLength(m_pNodeSet); + return value; + } + + /** + Returns the indexth item in the collection. + */ + Reference< XNode > SAL_CALL CNodeList::item(sal_Int32 index) + { + ::osl::MutexGuard const g(m_rMutex); + + if (nullptr == m_pNodeSet) { + return nullptr; + } + xmlNodePtr const pNode = xmlXPathNodeSetItem(m_pNodeSet, index); + return m_pDocument->GetCNode(pNode); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/xpath/nodelist.hxx b/unoxml/source/xpath/nodelist.hxx new file mode 100644 index 000000000..689510dea --- /dev/null +++ b/unoxml/source/xpath/nodelist.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <sal/types.h> +#include <rtl/ref.hxx> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> + +#include <libxml/xpath.h> + +#include "../dom/document.hxx" + +#include <memory> + +namespace DOM { + class CDocument; +} + +namespace XPath +{ + + class CNodeList : public cppu::WeakImplHelper< css::xml::dom::XNodeList > + { + private: + /// #i115995# keep document alive + ::rtl::Reference< DOM::CDocument > const m_pDocument; + ::osl::Mutex & m_rMutex; + /// retain the result set in case the CXPathObject is released + std::shared_ptr<xmlXPathObject> m_pXPathObj; + xmlNodeSetPtr m_pNodeSet; + + public: + CNodeList( + ::rtl::Reference<DOM::CDocument> const& pDocument, + ::osl::Mutex & rMutex, + std::shared_ptr<xmlXPathObject> const& rxpathObj); + /** + The number of nodes in the list. + */ + virtual sal_Int32 SAL_CALL getLength() override; + /** + Returns the indexth item in the collection. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL item(sal_Int32 index) override; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/xpath/xpathapi.cxx b/unoxml/source/xpath/xpathapi.cxx new file mode 100644 index 000000000..f1968c208 --- /dev/null +++ b/unoxml/source/xpath/xpathapi.cxx @@ -0,0 +1,398 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "xpathapi.hxx" + +#include <stdarg.h> +#include <string.h> + +#include <libxml/tree.h> +#include <libxml/xmlerror.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> + +#include <com/sun/star/xml/xpath/XPathException.hpp> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include "xpathobject.hxx" + +#include <node.hxx> +#include "../dom/document.hxx" + +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> + +using namespace css::io; +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::xpath; + +namespace XPath +{ + // ctor + CXPathAPI::CXPathAPI(const Reference< XComponentContext >& rxContext) + : m_xContext(rxContext) + { + } + + Sequence< OUString > SAL_CALL CXPathAPI::getSupportedServiceNames() + { + return { "com.sun.star.xml.xpath.XPathAPI" }; + } + + OUString SAL_CALL CXPathAPI::getImplementationName() + { + return "com.sun.star.comp.xml.xpath.XPathAPI"; + } + + sal_Bool SAL_CALL CXPathAPI::supportsService(const OUString& aServiceName) + { + return cppu::supportsService(this, aServiceName); + } + + void SAL_CALL CXPathAPI::registerNS( + const OUString& aPrefix, + const OUString& aURI) + { + std::scoped_lock const g(m_Mutex); + + m_nsmap.emplace(aPrefix, aURI); + } + + void SAL_CALL CXPathAPI::unregisterNS( + const OUString& aPrefix, + const OUString& aURI) + { + std::scoped_lock const g(m_Mutex); + + if ((m_nsmap.find(aPrefix))->second == aURI) { + m_nsmap.erase(aPrefix); + } + } + + // register all namespaces stored in the namespace list for this object + // with the current xpath evaluation context + static void lcl_registerNamespaces( + xmlXPathContextPtr ctx, + const nsmap_t& nsmap) + { + OString oprefix, ouri; + for (const auto& rEntry : nsmap) + { + oprefix = OUStringToOString(rEntry.first, RTL_TEXTENCODING_UTF8); + ouri = OUStringToOString(rEntry.second, RTL_TEXTENCODING_UTF8); + xmlChar const *p = reinterpret_cast<xmlChar const *>(oprefix.getStr()); + xmlChar const *u = reinterpret_cast<xmlChar const *>(ouri.getStr()); + (void)xmlXPathRegisterNs(ctx, p, u); + } + } + + // get all ns decls on a node (and parent nodes, if any) + static void lcl_collectNamespaces( + nsmap_t & rNamespaces, Reference< XNode > const& xNamespaceNode) + { + DOM::CNode *const pCNode(comphelper::getFromUnoTunnel<DOM::CNode>(xNamespaceNode)); + if (!pCNode) { throw RuntimeException(); } + + ::osl::MutexGuard const g(pCNode->GetOwnerDocument().GetMutex()); + + xmlNodePtr pNode = pCNode->GetNodePtr(); + while (pNode != nullptr) { + xmlNsPtr curDef = pNode->nsDef; + while (curDef != nullptr) { + const xmlChar* pHref = curDef->href; + OUString aURI(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8); + const xmlChar* pPre = curDef->prefix; + OUString aPrefix(reinterpret_cast<char const *>(pPre), strlen(reinterpret_cast<char const *>(pPre)), RTL_TEXTENCODING_UTF8); + // we could already have this prefix from a child node + rNamespaces.emplace(aPrefix, aURI); + curDef = curDef->next; + } + pNode = pNode->parent; + } + } + + static void lcl_collectRegisterNamespaces( + CXPathAPI & rAPI, Reference< XNode > const& xNamespaceNode) + { + nsmap_t namespaces; + lcl_collectNamespaces(namespaces, xNamespaceNode); + for (const auto& rEntry : namespaces) + { + rAPI.registerNS(rEntry.first, rEntry.second); + } + } + + // register function and variable lookup functions with the current + // xpath evaluation context + static void lcl_registerExtensions( + xmlXPathContextPtr ctx, + const extensions_t& extensions) + { + for (const auto& rExtensionRef : extensions) + { + Libxml2ExtensionHandle aHandle = rExtensionRef->getLibxml2ExtensionHandle(); + if ( aHandle.functionLookupFunction != 0 ) + { + xmlXPathRegisterFuncLookup(ctx, + reinterpret_cast<xmlXPathFuncLookupFunc>( + sal::static_int_cast<sal_IntPtr>(aHandle.functionLookupFunction)), + reinterpret_cast<void*>( + sal::static_int_cast<sal_IntPtr>(aHandle.functionData))); + } + if ( aHandle.variableLookupFunction != 0 ) + { + xmlXPathRegisterVariableLookup(ctx, + reinterpret_cast<xmlXPathVariableLookupFunc>( + sal::static_int_cast<sal_IntPtr>(aHandle.variableLookupFunction)), + reinterpret_cast<void*>( + sal::static_int_cast<sal_IntPtr>(aHandle.variableData))); + } + } + } + + /** + * Use an XPath string to select a nodelist. + */ + Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeList( + const Reference< XNode >& contextNode, + const OUString& expr) + { + Reference< XXPathObject > xobj = eval(contextNode, expr); + return xobj->getNodeList(); + } + + /** + * same as selectNodeList but registers all name space declarations found on namespaceNode + */ + Reference< XNodeList > SAL_CALL CXPathAPI::selectNodeListNS( + const Reference< XNode >& contextNode, + const OUString& expr, + const Reference< XNode >& namespaceNode) + { + lcl_collectRegisterNamespaces(*this, namespaceNode); + return selectNodeList(contextNode, expr); + } + + /** + * Same as selectNodeList but returns the first node (if any) + */ + Reference< XNode > SAL_CALL CXPathAPI::selectSingleNode( + const Reference< XNode >& contextNode, + const OUString& expr) + { + Reference< XNodeList > aList = selectNodeList(contextNode, expr); + Reference< XNode > aNode = aList->item(0); + return aNode; + } + + /** + * Same as selectSingleNode but registers all namespaces declared on + * namespaceNode + */ + Reference< XNode > SAL_CALL CXPathAPI::selectSingleNodeNS( + const Reference< XNode >& contextNode, + const OUString& expr, + const Reference< XNode >& namespaceNode ) + { + lcl_collectRegisterNamespaces(*this, namespaceNode); + return selectSingleNode(contextNode, expr); + } + + static OUString make_error_message(xmlErrorPtr pError) + { + OUStringBuffer buf; + if (pError) { + if (pError->message) { + buf.appendAscii(pError->message); + } + int line = pError->line; + if (line) { + buf.append("Line: "); + buf.append(static_cast<sal_Int32>(line)); + buf.append("\n"); + } + int column = pError->int2; + if (column) { + buf.append("Column: "); + buf.append(static_cast<sal_Int32>(column)); + buf.append("\n"); + } + } else { + buf.append("no error argument!"); + } + OUString msg = buf.makeStringAndClear(); + return msg; + } + + extern "C" { + +#if defined __GNUC__ + __attribute__ ((format (printf, 2, 3))) +#endif + static void generic_error_func(void *, const char *format, ...) + { + char str[1000]; + va_list args; + + va_start(args, format); +#ifdef _WIN32 +#define vsnprintf _vsnprintf +#endif + vsnprintf(str, sizeof(str), format, args); + va_end(args); + + SAL_WARN("unoxml", "libxml2 error: " << str); + } + + static void structured_error_func(void *, xmlErrorPtr error) + { + SAL_WARN("unoxml", "libxml2 error: " << make_error_message(error)); + } + + } // extern "C" + + /** + * evaluates an XPath string. relative XPath expressions are evaluated relative to + * the context Node + */ + Reference< XXPathObject > SAL_CALL CXPathAPI::eval( + Reference< XNode > const& xContextNode, + const OUString& expr) + { + if (!xContextNode.is()) { throw RuntimeException(); } + + nsmap_t nsmap; + extensions_t extensions; + + { + std::scoped_lock const g(m_Mutex); + nsmap = m_nsmap; + extensions = m_extensions; + } + + // get the node and document + ::rtl::Reference<DOM::CDocument> const pCDoc( + dynamic_cast<DOM::CDocument*>( comphelper::getFromUnoTunnel<DOM::CNode>( + xContextNode->getOwnerDocument()))); + if (!pCDoc.is()) { throw RuntimeException(); } + + DOM::CNode *const pCNode = comphelper::getFromUnoTunnel<DOM::CNode>(xContextNode); + if (!pCNode) { throw RuntimeException(); } + + ::osl::MutexGuard const g(pCDoc->GetMutex()); // lock the document! + + xmlNodePtr const pNode = pCNode->GetNodePtr(); + if (!pNode) { throw RuntimeException(); } + xmlDocPtr pDoc = pNode->doc; + + /* NB: workaround for #i87252#: + libxml < 2.6.17 considers it an error if the context + node is the empty document (i.e. its xpathCtx->doc has no + children). libxml 2.6.17 does not consider it an error. + Unfortunately, old libxml prints an error message to stderr, + which (afaik) cannot be turned off in this case, so we handle it. + */ + if (!pDoc->children) { + throw XPathException(); + } + + /* Create xpath evaluation context */ + std::shared_ptr<xmlXPathContext> const xpathCtx( + xmlXPathNewContext(pDoc), xmlXPathFreeContext); + if (xpathCtx == nullptr) { throw XPathException(); } + + // set context node + xpathCtx->node = pNode; + // error handling + xpathCtx->error = structured_error_func; + xmlSetGenericErrorFunc(nullptr, generic_error_func); + + // register namespaces and extension + lcl_registerNamespaces(xpathCtx.get(), nsmap); + lcl_registerExtensions(xpathCtx.get(), extensions); + + /* run the query */ + OString o1 = OUStringToOString(expr, RTL_TEXTENCODING_UTF8); + xmlChar const *pStr = reinterpret_cast<xmlChar const *>(o1.getStr()); + std::shared_ptr<xmlXPathObject> const xpathObj( + xmlXPathEval(pStr, xpathCtx.get()), xmlXPathFreeObject); + xmlSetGenericErrorFunc(nullptr, nullptr); + if (nullptr == xpathObj) { + // OSL_ENSURE(xpathCtx->lastError == NULL, xpathCtx->lastError->message); + throw XPathException(); + } + Reference<XXPathObject> const xObj( + new CXPathObject(pCDoc, pCDoc->GetMutex(), xpathObj)); + return xObj; + } + + /** + * same as eval but registers all namespace declarations found on namespaceNode + */ + Reference< XXPathObject > SAL_CALL CXPathAPI::evalNS( + const Reference< XNode >& contextNode, + const OUString& expr, + const Reference< XNode >& namespaceNode) + { + lcl_collectRegisterNamespaces(*this, namespaceNode); + return eval(contextNode, expr); + } + + /** + * uses the service manager to create an instance of the service denoted by aName. + * If the returned object implements the XXPathExtension interface, it is added to the list + * of extensions that are used when evaluating XPath strings with this XPathAPI instance + */ + void SAL_CALL CXPathAPI::registerExtension( + const OUString& aName) + { + std::scoped_lock const g(m_Mutex); + + // get extension from service manager + Reference< XXPathExtension > const xExtension( + m_xContext->getServiceManager()->createInstanceWithContext(aName, m_xContext), UNO_QUERY_THROW); + m_extensions.push_back(xExtension); + } + + /** + * registers the given extension instance to be used by XPath evaluations performed through this + * XPathAPI instance + */ + void SAL_CALL CXPathAPI::registerExtensionInstance( + Reference< XXPathExtension> const& xExtension) + { + if (!xExtension.is()) { + throw RuntimeException(); + } + std::scoped_lock const g(m_Mutex); + m_extensions.push_back( xExtension ); + } +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +unoxml_CXPathAPI_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new XPath::CXPathAPI(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/xpath/xpathapi.hxx b/unoxml/source/xpath/xpathapi.hxx new file mode 100644 index 000000000..bb37922c8 --- /dev/null +++ b/unoxml/source/xpath/xpathapi.hxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <map> +#include <mutex> +#include <vector> + +#include <sal/types.h> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/uno/Sequence.h> + +#include <com/sun/star/xml/xpath/XXPathAPI.hpp> +#include <com/sun/star/xml/dom/XNode.hpp> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/xpath/XXPathObject.hpp> +#include <com/sun/star/xml/xpath/XXPathExtension.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace XPath +{ + typedef std::map<OUString, OUString> nsmap_t; + typedef std::vector< css::uno::Reference<css::xml::xpath::XXPathExtension> > extensions_t; + + typedef ::cppu::WeakImplHelper + < css::xml::xpath::XXPathAPI + , css::lang::XServiceInfo + > CXPathAPI_Base; + + class CXPathAPI + : public CXPathAPI_Base + { + + private: + std::mutex m_Mutex; + nsmap_t m_nsmap; + const css::uno::Reference< css::uno::XComponentContext > m_xContext; + extensions_t m_extensions; + + public: + // ctor + explicit CXPathAPI( const css::uno::Reference< css::uno::XComponentContext >& ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames () override; + + + // --- XXPathAPI --- + + virtual void SAL_CALL registerNS(const OUString& aPrefix, const OUString& aURI) override; + + virtual void SAL_CALL unregisterNS(const OUString& aPrefix, const OUString& aURI) override; + + /** + Use an XPath string to select a nodelist. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL selectNodeList(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str) override; + + /** + Use an XPath string to select a nodelist. + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL selectNodeListNS(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str, const css::uno::Reference< css::xml::dom::XNode >& namespaceNode) override; + + /** + Use an XPath string to select a single node. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL selectSingleNode(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str) override; + + /** + Use an XPath string to select a single node. + */ + virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL selectSingleNodeNS(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str, const css::uno::Reference< css::xml::dom::XNode >& namespaceNode) override; + + virtual css::uno::Reference< css::xml::xpath::XXPathObject > SAL_CALL eval(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str) override; + + virtual css::uno::Reference< css::xml::xpath::XXPathObject > SAL_CALL evalNS(const css::uno::Reference< css::xml::dom::XNode >& contextNode, const OUString& str, const css::uno::Reference< css::xml::dom::XNode >& namespaceNode) override; + + virtual void SAL_CALL registerExtension(const OUString& aName) override; + virtual void SAL_CALL registerExtensionInstance(const css::uno::Reference< css::xml::xpath::XXPathExtension>& aExtension) override; + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/xpath/xpathobject.cxx b/unoxml/source/xpath/xpathobject.cxx new file mode 100644 index 000000000..1184129a0 --- /dev/null +++ b/unoxml/source/xpath/xpathobject.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include "xpathobject.hxx" + +#include <string.h> + +#include "../dom/document.hxx" +#include "nodelist.hxx" + +using namespace css::uno; +using namespace css::xml::dom; +using namespace css::xml::xpath; + +namespace XPath +{ + static XPathObjectType lcl_GetType(xmlXPathObjectPtr const pXPathObj) + { + switch (pXPathObj->type) + { + case XPATH_UNDEFINED: + return XPathObjectType_XPATH_UNDEFINED; + case XPATH_NODESET: + return XPathObjectType_XPATH_NODESET; + case XPATH_BOOLEAN: + return XPathObjectType_XPATH_BOOLEAN; + case XPATH_NUMBER: + return XPathObjectType_XPATH_NUMBER; + case XPATH_STRING: + return XPathObjectType_XPATH_STRING; +#if LIBXML_VERSION < 21000 || defined(LIBXML_XPTR_LOCS_ENABLED) + case XPATH_POINT: + return XPathObjectType_XPATH_POINT; + case XPATH_RANGE: + return XPathObjectType_XPATH_RANGE; + case XPATH_LOCATIONSET: + return XPathObjectType_XPATH_LOCATIONSET; +#endif + case XPATH_USERS: + return XPathObjectType_XPATH_USERS; + case XPATH_XSLT_TREE: + return XPathObjectType_XPATH_XSLT_TREE; + default: + return XPathObjectType_XPATH_UNDEFINED; + } + } + + CXPathObject::CXPathObject( + ::rtl::Reference<DOM::CDocument> const& pDocument, + ::osl::Mutex & rMutex, + std::shared_ptr<xmlXPathObject> const& pXPathObj) + : m_pDocument(pDocument) + , m_rMutex(rMutex) + , m_pXPathObj(pXPathObj) + , m_XPathObjectType(lcl_GetType(pXPathObj.get())) + { + } + + /** + get object type + */ + XPathObjectType CXPathObject::getObjectType() + { + return m_XPathObjectType; + } + + /** + get the nodes from a nodelist type object + */ + Reference< XNodeList > SAL_CALL + CXPathObject::getNodeList() + { + ::osl::MutexGuard const g(m_rMutex); + + Reference< XNodeList > const xRet( + new CNodeList(m_pDocument, m_rMutex, m_pXPathObj)); + return xRet; + } + + /** + get value of a boolean object + */ + sal_Bool SAL_CALL CXPathObject::getBoolean() + { + ::osl::MutexGuard const g(m_rMutex); + + return xmlXPathCastToBoolean(m_pXPathObj.get()) != 0; + } + + /** + get number as byte + */ + sal_Int8 SAL_CALL CXPathObject::getByte() + { + ::osl::MutexGuard const g(m_rMutex); + + return static_cast<sal_Int8>(xmlXPathCastToNumber(m_pXPathObj.get())); + } + + /** + get number as short + */ + sal_Int16 SAL_CALL CXPathObject::getShort() + { + ::osl::MutexGuard const g(m_rMutex); + + return static_cast<sal_Int16>(xmlXPathCastToNumber(m_pXPathObj.get())); + } + + /** + get number as long + */ + sal_Int32 SAL_CALL CXPathObject::getLong() + { + ::osl::MutexGuard const g(m_rMutex); + + return static_cast<sal_Int32>(xmlXPathCastToNumber(m_pXPathObj.get())); + } + + /** + get number as hyper + */ + sal_Int64 SAL_CALL CXPathObject::getHyper() + { + ::osl::MutexGuard const g(m_rMutex); + + return static_cast<sal_Int64>(xmlXPathCastToNumber(m_pXPathObj.get())); + } + + /** + get number as float + */ + float SAL_CALL CXPathObject::getFloat() + { + ::osl::MutexGuard const g(m_rMutex); + + return static_cast<float>(xmlXPathCastToNumber(m_pXPathObj.get())); + } + + /** + get number as double + */ + double SAL_CALL CXPathObject::getDouble() + { + ::osl::MutexGuard const g(m_rMutex); + + return xmlXPathCastToNumber(m_pXPathObj.get()); + } + + /** + get string value + */ + OUString SAL_CALL CXPathObject::getString() + { + ::osl::MutexGuard const g(m_rMutex); + + std::shared_ptr<xmlChar const> str( + xmlXPathCastToString(m_pXPathObj.get()), xmlFree); + char const*const pS(reinterpret_cast<char const*>(str.get())); + return OUString(pS, strlen(pS), RTL_TEXTENCODING_UTF8); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/unoxml/source/xpath/xpathobject.hxx b/unoxml/source/xpath/xpathobject.hxx new file mode 100644 index 000000000..41a8e45a0 --- /dev/null +++ b/unoxml/source/xpath/xpathobject.hxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <memory> + +#include <libxml/xpath.h> + +#include <sal/types.h> +#include <rtl/ref.hxx> + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/xml/dom/XNodeList.hpp> +#include <com/sun/star/xml/xpath/XXPathObject.hpp> + +#include "../dom/document.hxx" + +namespace DOM { + class CDocument; +} + +namespace XPath +{ + class CXPathObject : public cppu::WeakImplHelper< css::xml::xpath::XXPathObject > + { + private: + ::rtl::Reference< DOM::CDocument > const m_pDocument; + ::osl::Mutex & m_rMutex; + std::shared_ptr<xmlXPathObject> const m_pXPathObj; + css::xml::xpath::XPathObjectType const m_XPathObjectType; + + public: + CXPathObject( ::rtl::Reference<DOM::CDocument> const& pDocument, + ::osl::Mutex & rMutex, + std::shared_ptr<xmlXPathObject> const& pXPathObj); + + /** + get object type + */ + virtual css::xml::xpath::XPathObjectType SAL_CALL getObjectType() override; + + /** + get the nodes from a nodelist type object + */ + virtual css::uno::Reference< css::xml::dom::XNodeList > SAL_CALL getNodeList() override; + + /** + get value of a boolean object + */ + virtual sal_Bool SAL_CALL getBoolean() override; + + /** + get number as byte + */ + virtual sal_Int8 SAL_CALL getByte() override; + + /** + get number as short + */ + virtual sal_Int16 SAL_CALL getShort() override; + + /** + get number as long + */ + virtual sal_Int32 SAL_CALL getLong() override; + + /** + get number as hyper + */ + virtual sal_Int64 SAL_CALL getHyper() override; + + /** + get number as float + */ + virtual float SAL_CALL getFloat() override; + + /** + get number as double + */ + virtual double SAL_CALL getDouble() override; + + /** + get string value + */ + virtual OUString SAL_CALL getString() override; + + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |