1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <documentbuilder.hxx>
26 #include <libxml/xmlerror.h>
27 #include <libxml/tree.h>
31 #include <rtl/alloc.h>
32 #include <rtl/ustrbuf.hxx>
33 #include <osl/diagnose.h>
35 #include <comphelper/processfactory.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <cppuhelper/supportsservice.hxx>
39 #include <com/sun/star/xml/sax/SAXParseException.hpp>
40 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
41 #include <com/sun/star/task/XInteractionHandler.hpp>
43 #include <ucbhelper/content.hxx>
44 #include <ucbhelper/commandenvironment.hxx>
47 #include <document.hxx>
49 using namespace css::io
;
50 using namespace css::lang
;
51 using namespace css::ucb
;
52 using namespace css::uno
;
53 using namespace css::xml::dom
;
54 using namespace css::xml::sax
;
55 using namespace ucbhelper
;
56 using css::task::XInteractionHandler
;
57 using css::xml::sax::InputSource
;
63 class CDefaultEntityResolver
: public cppu::WeakImplHelper
< XEntityResolver
>
66 virtual InputSource SAL_CALL
resolveEntity( const OUString
& sPublicId
, const OUString
& sSystemId
)
67 throw (css::uno::RuntimeException
, std::exception
) override
70 is
.sPublicId
= sPublicId
;
71 is
.sSystemId
= sSystemId
;
75 Reference
< XCommandEnvironment
> aEnvironment(
76 new CommandEnvironment(Reference
< XInteractionHandler
>(),
77 Reference
< XProgressHandler
>() ));
78 Content
aContent(sSystemId
, aEnvironment
, comphelper::getProcessComponentContext());
80 is
.aInputStream
= aContent
.openStream();
81 } catch (const css::uno::Exception
&) {
82 OSL_FAIL("exception in default entity resolver");
83 is
.aInputStream
.clear();
90 CDocumentBuilder::CDocumentBuilder()
91 : m_xEntityResolver(new CDefaultEntityResolver())
93 // init libxml. libxml will protect itself against multiple
94 // initializations so there is no problem here if this gets
95 // called multiple times.
99 Reference
< XInterface
> CDocumentBuilder::_getInstance(const Reference
< XMultiServiceFactory
>& )
101 return static_cast< XDocumentBuilder
* >(new CDocumentBuilder
);
104 const char* CDocumentBuilder::aImplementationName
= "com.sun.star.comp.xml.dom.DocumentBuilder";
105 const char* CDocumentBuilder::aSupportedServiceNames
[] = {
106 "com.sun.star.xml.dom.DocumentBuilder",
110 OUString
CDocumentBuilder::_getImplementationName()
112 return OUString::createFromAscii(aImplementationName
);
114 Sequence
<OUString
> CDocumentBuilder::_getSupportedServiceNames()
116 Sequence
<OUString
> aSequence
;
117 for (int i
=0; aSupportedServiceNames
[i
]!=nullptr; i
++) {
118 aSequence
.realloc(i
+1);
119 aSequence
[i
]=(OUString::createFromAscii(aSupportedServiceNames
[i
]));
124 Sequence
< OUString
> SAL_CALL
CDocumentBuilder::getSupportedServiceNames()
125 throw (RuntimeException
, std::exception
)
127 return CDocumentBuilder::_getSupportedServiceNames();
130 OUString SAL_CALL
CDocumentBuilder::getImplementationName()
131 throw (RuntimeException
, std::exception
)
133 return CDocumentBuilder::_getImplementationName();
136 sal_Bool SAL_CALL
CDocumentBuilder::supportsService(const OUString
& aServiceName
)
137 throw (RuntimeException
, std::exception
)
139 return cppu::supportsService(this, aServiceName
);
142 Reference
< XDOMImplementation
> SAL_CALL
CDocumentBuilder::getDOMImplementation()
143 throw (RuntimeException
, std::exception
)
146 return Reference
< XDOMImplementation
>();
149 sal_Bool SAL_CALL
CDocumentBuilder::isNamespaceAware()
150 throw (RuntimeException
, std::exception
)
155 sal_Bool SAL_CALL
CDocumentBuilder::isValidating()
156 throw (RuntimeException
, std::exception
)
161 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::newDocument()
162 throw (RuntimeException
, std::exception
)
164 ::osl::MutexGuard
const g(m_Mutex
);
166 // create a new document
167 xmlDocPtr pDocument
= xmlNewDoc(reinterpret_cast<const xmlChar
*>("1.0"));
168 Reference
< XDocument
> const xRet(
169 CDocument::CreateCDocument(pDocument
).get());
173 static OUString
make_error_message(xmlParserCtxtPtr ctxt
)
176 buf
.appendAscii(ctxt
->lastError
.message
);
177 buf
.append("Line: ");
178 buf
.append(static_cast<sal_Int32
>(ctxt
->lastError
.line
));
179 buf
.append("\nColumn: ");
180 buf
.append(static_cast<sal_Int32
>(ctxt
->lastError
.int2
));
181 OUString msg
= buf
.makeStringAndClear();
185 // -- callbacks and context struct for parsing from stream
186 // -- c-linkage, so the callbacks can be used by libxml
189 // context struct passed to IO functions
190 typedef struct context
{
191 CDocumentBuilder
*pBuilder
;
192 Reference
< XInputStream
> rInputStream
;
197 static int xmlIO_read_func( void *context
, char *buffer
, int len
)
199 // get the context...
200 context_t
*pctx
= static_cast<context_t
*>(context
);
201 if (!pctx
->rInputStream
.is())
204 // try to read the requested number of bytes
205 Sequence
< sal_Int8
> chunk(len
);
206 int nread
= pctx
->rInputStream
->readBytes(chunk
, len
);
208 // copy bytes to the provided buffer
209 memcpy(buffer
, chunk
.getConstArray(), nread
);
211 } catch (const css::uno::Exception
& ex
) {
213 OSL_FAIL(OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
218 static int xmlIO_close_func(void* context
)
220 // get the context...
221 context_t
*pctx
= static_cast<context_t
*>(context
);
222 if (!pctx
->rInputStream
.is())
227 pctx
->rInputStream
->closeInput();
228 if (pctx
->freeOnClose
)
231 } catch (const css::uno::Exception
& ex
) {
233 OSL_FAIL(OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
238 static xmlParserInputPtr
resolve_func(void *ctx
,
239 const xmlChar
*publicId
,
240 const xmlChar
*systemId
)
242 // get the CDocumentBuilder object
243 xmlParserCtxtPtr ctxt
= static_cast<xmlParserCtxtPtr
>(ctx
);
244 CDocumentBuilder
*builder
= static_cast< CDocumentBuilder
* >(ctxt
->_private
);
245 Reference
< XEntityResolver
> resolver
= builder
->getEntityResolver();
247 if (systemId
!= nullptr)
248 sysid
= OUString(reinterpret_cast<char const *>(systemId
), strlen(reinterpret_cast<char const *>(systemId
)), RTL_TEXTENCODING_UTF8
);
250 if (publicId
!= nullptr)
251 pubid
= OUString(reinterpret_cast<char const *>(publicId
), strlen(reinterpret_cast<char const *>(publicId
)), RTL_TEXTENCODING_UTF8
);
253 // resolve the entity
254 InputSource src
= resolver
->resolveEntity(pubid
, sysid
);
256 // create IO context on heap because this call will no longer be on the stack
257 // when IO is actually performed through the callbacks. The close function must
258 // free the memory which is indicated by the freeOnClose field in the context struct
259 context_t
*c
= new context_t
;
260 c
->pBuilder
= builder
;
261 c
->rInputStream
= src
.aInputStream
;
263 c
->freeOnClose
= true;
265 // set up the inputBuffer and inputPtr for libxml
266 xmlParserInputBufferPtr pBuffer
=
267 xmlParserInputBufferCreateIO(xmlIO_read_func
, xmlIO_close_func
, c
, XML_CHAR_ENCODING_NONE
);
268 xmlParserInputPtr pInput
=
269 xmlNewIOInputStream(ctxt
, pBuffer
, XML_CHAR_ENCODING_NONE
);
274 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char * /*ID*/, xmlParserCtxtPtr ctxt
)
276 // just call our resolver function using the URL as systemId
277 return resolve_func(ctxt
, 0, (const xmlChar
*)URL
);
281 // default warning handler does not trigger assertion
282 static void warning_func(void * ctx
, const char * /*msg*/, ...)
287 << make_error_message(static_cast<xmlParserCtxtPtr
>(ctx
)));
290 // default error handler triggers assertion
291 static void error_func(void * ctx
, const char * /*msg*/, ...)
296 << make_error_message(static_cast<xmlParserCtxtPtr
>(ctx
)));
301 void throwEx(xmlParserCtxtPtr ctxt
)
303 css::xml::sax::SAXParseException saxex
;
304 saxex
.Message
= make_error_message(ctxt
);
305 saxex
.LineNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.line
);
306 saxex
.ColumnNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.int2
);
310 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::parse(const Reference
< XInputStream
>& is
)
311 throw (RuntimeException
, SAXParseException
, IOException
, std::exception
)
314 throw RuntimeException();
317 ::osl::MutexGuard
const g(m_Mutex
);
319 std::shared_ptr
<xmlParserCtxt
> const pContext(
320 xmlNewParserCtxt(), xmlFreeParserCtxt
);
322 // register error functions to prevent errors being printed
324 pContext
->_private
= this;
325 pContext
->sax
->error
= error_func
;
326 pContext
->sax
->warning
= warning_func
;
327 pContext
->sax
->resolveEntity
= resolve_func
;
333 // we did not open the stream, thus we do not close it.
335 c
.freeOnClose
= false;
336 xmlDocPtr
const pDoc
= xmlCtxtReadIO(pContext
.get(),
337 xmlIO_read_func
, xmlIO_close_func
, &c
, nullptr, nullptr, 0);
339 if (pDoc
== nullptr) {
340 throwEx(pContext
.get());
342 Reference
< XDocument
> const xRet(
343 CDocument::CreateCDocument(pDoc
).get());
347 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::parseURI(const OUString
& sUri
)
348 throw (RuntimeException
, SAXParseException
, IOException
, std::exception
)
350 ::osl::MutexGuard
const g(m_Mutex
);
352 std::shared_ptr
<xmlParserCtxt
> const pContext(
353 xmlNewParserCtxt(), xmlFreeParserCtxt
);
354 pContext
->_private
= this;
355 pContext
->sax
->error
= error_func
;
356 pContext
->sax
->warning
= warning_func
;
357 pContext
->sax
->resolveEntity
= resolve_func
;
358 // xmlSetExternalEntityLoader(external_entity_loader);
359 OString oUri
= OUStringToOString(sUri
, RTL_TEXTENCODING_UTF8
);
360 char *uri
= const_cast<char*>(oUri
.getStr());
361 xmlDocPtr pDoc
= xmlCtxtReadFile(pContext
.get(), uri
, nullptr, 0);
362 if (pDoc
== nullptr) {
363 throwEx(pContext
.get());
365 Reference
< XDocument
> const xRet(
366 CDocument::CreateCDocument(pDoc
).get());
371 CDocumentBuilder::setEntityResolver(Reference
< XEntityResolver
> const& xER
)
372 throw (RuntimeException
, std::exception
)
374 ::osl::MutexGuard
const g(m_Mutex
);
376 m_xEntityResolver
= xER
;
379 Reference
< XEntityResolver
> SAL_CALL
CDocumentBuilder::getEntityResolver()
380 throw (RuntimeException
)
382 ::osl::MutexGuard
const g(m_Mutex
);
384 return m_xEntityResolver
;
388 CDocumentBuilder::setErrorHandler(Reference
< XErrorHandler
> const& xEH
)
389 throw (RuntimeException
, std::exception
)
391 ::osl::MutexGuard
const g(m_Mutex
);
393 m_xErrorHandler
= xEH
;
397 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */