build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / unoxml / source / dom / documentbuilder.cxx
blobb460004687e0a56f26a2d3dfaf317ea85c49d5b1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdarg.h>
26 #include <libxml/xmlerror.h>
27 #include <libxml/tree.h>
29 #include <memory>
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>
46 #include <node.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;
60 namespace DOM
63 class CDefaultEntityResolver : public cppu::WeakImplHelper< XEntityResolver >
65 public:
66 virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId )
67 throw (css::uno::RuntimeException, std::exception) override
69 InputSource is;
70 is.sPublicId = sPublicId;
71 is.sSystemId = sSystemId;
72 is.sEncoding.clear();
74 try {
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();
85 return is;
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.
96 xmlInitParser();
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",
107 nullptr
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]));
121 return aSequence;
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)
152 return true;
155 sal_Bool SAL_CALL CDocumentBuilder::isValidating()
156 throw (RuntimeException, std::exception)
158 return false;
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());
170 return xRet;
173 static OUString make_error_message(xmlParserCtxtPtr ctxt)
175 OUStringBuffer buf;
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();
182 return msg;
185 // -- callbacks and context struct for parsing from stream
186 // -- c-linkage, so the callbacks can be used by libxml
187 extern "C" {
189 // context struct passed to IO functions
190 typedef struct context {
191 CDocumentBuilder *pBuilder;
192 Reference< XInputStream > rInputStream;
193 bool close;
194 bool freeOnClose;
195 } context_t;
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())
202 return -1;
203 try {
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);
210 return nread;
211 } catch (const css::uno::Exception& ex) {
212 (void) ex;
213 OSL_FAIL(OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
214 return -1;
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())
223 return 0;
226 if (pctx->close)
227 pctx->rInputStream->closeInput();
228 if (pctx->freeOnClose)
229 delete pctx;
230 return 0;
231 } catch (const css::uno::Exception& ex) {
232 (void) ex;
233 OSL_FAIL(OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
234 return -1;
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();
246 OUString sysid;
247 if (systemId != nullptr)
248 sysid = OUString(reinterpret_cast<char const *>(systemId), strlen(reinterpret_cast<char const *>(systemId)), RTL_TEXTENCODING_UTF8);
249 OUString pubid;
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;
262 c->close = true;
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);
270 return pInput;
273 #if 0
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);
279 #endif
281 // default warning handler does not trigger assertion
282 static void warning_func(void * ctx, const char * /*msg*/, ...)
284 SAL_INFO(
285 "unoxml",
286 "libxml2 warning: "
287 << make_error_message(static_cast<xmlParserCtxtPtr>(ctx)));
290 // default error handler triggers assertion
291 static void error_func(void * ctx, const char * /*msg*/, ...)
293 SAL_WARN(
294 "unoxml",
295 "libxml2 error: "
296 << make_error_message(static_cast<xmlParserCtxtPtr>(ctx)));
299 } // extern "C"
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);
307 throw saxex;
310 Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
311 throw (RuntimeException, SAXParseException, IOException, std::exception)
313 if (!is.is()) {
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
323 // on the console
324 pContext->_private = this;
325 pContext->sax->error = error_func;
326 pContext->sax->warning = warning_func;
327 pContext->sax->resolveEntity = resolve_func;
329 // IO context struct
330 context_t c;
331 c.pBuilder = this;
332 c.rInputStream = is;
333 // we did not open the stream, thus we do not close it.
334 c.close = false;
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());
344 return xRet;
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());
367 return xRet;
370 void SAL_CALL
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;
387 void SAL_CALL
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: */