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>
29 #include <boost/shared_ptr.hpp>
31 #include <rtl/alloc.h>
32 #include <rtl/ustrbuf.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <cppuhelper/implbase1.hxx>
37 #include <com/sun/star/xml/sax/SAXParseException.hpp>
38 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
39 #include <com/sun/star/task/XInteractionHandler.hpp>
41 #include <ucbhelper/content.hxx>
42 #include <ucbhelper/commandenvironment.hxx>
45 #include <document.hxx>
48 using ::com::sun::star::xml::sax::InputSource
;
49 using namespace ucbhelper
;
50 using namespace ::com::sun::star::ucb
;
51 using ::com::sun::star::task::XInteractionHandler
;
57 class CDefaultEntityResolver
: public cppu::WeakImplHelper1
< XEntityResolver
>
60 virtual InputSource SAL_CALL
resolveEntity( const OUString
& sPublicId
, const OUString
& sSystemId
)
61 throw (::com::sun::star::uno::RuntimeException
)
64 is
.sPublicId
= sPublicId
;
65 is
.sSystemId
= sSystemId
;
66 is
.sEncoding
= OUString();
69 Reference
< XCommandEnvironment
> aEnvironment(
70 new CommandEnvironment(Reference
< XInteractionHandler
>(),
71 Reference
< XProgressHandler
>() ));
72 Content
aContent(sSystemId
, aEnvironment
, comphelper::getProcessComponentContext());
74 is
.aInputStream
= aContent
.openStream();
75 } catch (const com::sun::star::uno::Exception
&) {
76 OSL_FAIL("exception in default entity resolver");
77 is
.aInputStream
= Reference
< XInputStream
>();
84 CDocumentBuilder::CDocumentBuilder(
85 Reference
< XMultiServiceFactory
> const& xFactory
)
86 : m_xFactory(xFactory
)
87 , m_xEntityResolver(new CDefaultEntityResolver())
89 // init libxml. libxml will protect itself against multiple
90 // initializations so there is no problem here if this gets
91 // called multiple times.
95 Reference
< XInterface
> CDocumentBuilder::_getInstance(const Reference
< XMultiServiceFactory
>& rSMgr
)
97 return static_cast< XDocumentBuilder
* >(new CDocumentBuilder(rSMgr
));
100 const char* CDocumentBuilder::aImplementationName
= "com.sun.star.comp.xml.dom.DocumentBuilder";
101 const char* CDocumentBuilder::aSupportedServiceNames
[] = {
102 "com.sun.star.xml.dom.DocumentBuilder",
106 OUString
CDocumentBuilder::_getImplementationName()
108 return OUString::createFromAscii(aImplementationName
);
110 Sequence
<OUString
> CDocumentBuilder::_getSupportedServiceNames()
112 Sequence
<OUString
> aSequence
;
113 for (int i
=0; aSupportedServiceNames
[i
]!=NULL
; i
++) {
114 aSequence
.realloc(i
+1);
115 aSequence
[i
]=(OUString::createFromAscii(aSupportedServiceNames
[i
]));
120 Sequence
< OUString
> SAL_CALL
CDocumentBuilder::getSupportedServiceNames()
121 throw (RuntimeException
)
123 return CDocumentBuilder::_getSupportedServiceNames();
126 OUString SAL_CALL
CDocumentBuilder::getImplementationName()
127 throw (RuntimeException
)
129 return CDocumentBuilder::_getImplementationName();
132 sal_Bool SAL_CALL
CDocumentBuilder::supportsService(const OUString
& aServiceName
)
133 throw (RuntimeException
)
135 Sequence
< OUString
> supported
= CDocumentBuilder::_getSupportedServiceNames();
136 for (sal_Int32 i
=0; i
<supported
.getLength(); i
++)
138 if (supported
[i
] == aServiceName
) return sal_True
;
143 Reference
< XDOMImplementation
> SAL_CALL
CDocumentBuilder::getDOMImplementation()
144 throw (RuntimeException
)
147 return Reference
< XDOMImplementation
>();
150 sal_Bool SAL_CALL
CDocumentBuilder::isNamespaceAware()
151 throw (RuntimeException
)
156 sal_Bool SAL_CALL
CDocumentBuilder::isValidating()
157 throw (RuntimeException
)
162 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::newDocument()
163 throw (RuntimeException
)
165 ::osl::MutexGuard
const g(m_Mutex
);
167 // create a new document
168 xmlDocPtr pDocument
= xmlNewDoc((const xmlChar
*)"1.0");
169 Reference
< XDocument
> const xRet(
170 CDocument::CreateCDocument(pDocument
).get());
174 static OUString
make_error_message(xmlParserCtxtPtr ctxt
)
177 buf
.appendAscii(ctxt
->lastError
.message
);
178 buf
.appendAscii("Line: ");
179 buf
.append(static_cast<sal_Int32
>(ctxt
->lastError
.line
));
180 buf
.appendAscii("\nColumn: ");
181 buf
.append(static_cast<sal_Int32
>(ctxt
->lastError
.int2
));
182 OUString msg
= buf
.makeStringAndClear();
186 // -- callbacks and context struct for parsing from stream
187 // -- c-linkage, so the callbacks can be used by libxml
190 // context struct passed to IO functions
191 typedef struct context
{
192 CDocumentBuilder
*pBuilder
;
193 Reference
< XInputStream
> rInputStream
;
198 static int xmlIO_read_func( void *context
, char *buffer
, int len
)
200 // get the context...
201 context_t
*pctx
= static_cast<context_t
*>(context
);
202 if (!pctx
->rInputStream
.is())
205 // try to read the requested number of bytes
206 Sequence
< sal_Int8
> chunk(len
);
207 int nread
= pctx
->rInputStream
->readBytes(chunk
, len
);
209 // copy bytes to the provided buffer
210 memcpy(buffer
, chunk
.getConstArray(), nread
);
212 } catch (const com::sun::star::uno::Exception
& ex
) {
214 OSL_FAIL(OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
219 static int xmlIO_close_func(void* context
)
221 // get the context...
222 context_t
*pctx
= static_cast<context_t
*>(context
);
223 if (!pctx
->rInputStream
.is())
228 pctx
->rInputStream
->closeInput();
229 if (pctx
->freeOnClose
)
232 } catch (const com::sun::star::uno::Exception
& ex
) {
234 OSL_FAIL(OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
239 static xmlParserInputPtr
resolve_func(void *ctx
,
240 const xmlChar
*publicId
,
241 const xmlChar
*systemId
)
243 // get the CDocumentBuilder object
244 xmlParserCtxtPtr ctxt
= (xmlParserCtxtPtr
)ctx
;
245 CDocumentBuilder
*builder
= static_cast< CDocumentBuilder
* >(ctxt
->_private
);
246 Reference
< XEntityResolver
> resolver
= builder
->getEntityResolver();
249 sysid
= OUString((sal_Char
*)systemId
, strlen((char*)systemId
), RTL_TEXTENCODING_UTF8
);
252 pubid
= OUString((sal_Char
*)publicId
, strlen((char*)publicId
), RTL_TEXTENCODING_UTF8
);
254 // resolve the entity
255 InputSource src
= resolver
->resolveEntity(pubid
, sysid
);
257 // create IO context on heap because this call will no longer be on the stack
258 // when IO is actually performed through the callbacks. The close function must
259 // free the memory which is indicated by the freeOnClose field in the context struct
260 context_t
*c
= new context_t
;
261 c
->pBuilder
= builder
;
262 c
->rInputStream
= src
.aInputStream
;
264 c
->freeOnClose
= true;
266 // set up the inputBuffer and inputPtr for libxml
267 xmlParserInputBufferPtr pBuffer
=
268 xmlParserInputBufferCreateIO(xmlIO_read_func
, xmlIO_close_func
, c
, XML_CHAR_ENCODING_NONE
);
269 xmlParserInputPtr pInput
=
270 xmlNewIOInputStream(ctxt
, pBuffer
, XML_CHAR_ENCODING_NONE
);
275 static xmlParserInputPtr
external_entity_loader(const char *URL
, const char * /*ID*/, xmlParserCtxtPtr ctxt
)
277 // just call our resolver function using the URL as systemId
278 return resolve_func(ctxt
, 0, (const xmlChar
*)URL
);
282 // default warning handler does not trigger assertion
283 static void warning_func(void * ctx
, const char * /*msg*/, ...)
285 OUStringBuffer
buf("libxml2 warning\n");
286 buf
.append(make_error_message(static_cast< xmlParserCtxtPtr
>(ctx
)));
287 OString msg
= OUStringToOString(buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
);
288 OSL_TRACE(msg
.getStr());
291 // default error handler triggers assertion
292 static void error_func(void * ctx
, const char * /*msg*/, ...)
294 OUStringBuffer
buf("libxml2 error\n");
295 buf
.append(make_error_message(static_cast< xmlParserCtxtPtr
>(ctx
)));
296 OString msg
= OUStringToOString(buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
);
297 OSL_FAIL(msg
.getStr());
302 void throwEx(xmlParserCtxtPtr ctxt
)
304 com::sun::star::xml::sax::SAXParseException saxex
;
305 saxex
.Message
= make_error_message(ctxt
);
306 saxex
.LineNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.line
);
307 saxex
.ColumnNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.int2
);
311 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::parse(const Reference
< XInputStream
>& is
)
312 throw (RuntimeException
, SAXParseException
, IOException
)
315 throw RuntimeException();
318 ::osl::MutexGuard
const g(m_Mutex
);
322 xmlChar *encstr = (xmlChar*) OUStringToOString(src.sEncoding, RTL_TEXTENCODING_UTF8).getStr();
323 xmlCharEncoding enc = xmlParseCharEncoding(encstr);
326 ::boost::shared_ptr
<xmlParserCtxt
> const pContext(
327 xmlNewParserCtxt(), xmlFreeParserCtxt
);
329 // register error functions to prevent errors being printed
331 pContext
->_private
= this;
332 pContext
->sax
->error
= error_func
;
333 pContext
->sax
->warning
= warning_func
;
334 pContext
->sax
->resolveEntity
= resolve_func
;
340 // we did not open the stream, thus we do not close it.
342 c
.freeOnClose
= false;
343 xmlDocPtr
const pDoc
= xmlCtxtReadIO(pContext
.get(),
344 xmlIO_read_func
, xmlIO_close_func
, &c
, 0, 0, 0);
347 throwEx(pContext
.get());
349 Reference
< XDocument
> const xRet(
350 CDocument::CreateCDocument(pDoc
).get());
354 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::parseURI(const OUString
& sUri
)
355 throw (RuntimeException
, SAXParseException
, IOException
)
357 ::osl::MutexGuard
const g(m_Mutex
);
359 ::boost::shared_ptr
<xmlParserCtxt
> const pContext(
360 xmlNewParserCtxt(), xmlFreeParserCtxt
);
361 pContext
->_private
= this;
362 pContext
->sax
->error
= error_func
;
363 pContext
->sax
->warning
= warning_func
;
364 pContext
->sax
->resolveEntity
= resolve_func
;
365 // xmlSetExternalEntityLoader(external_entity_loader);
366 OString oUri
= OUStringToOString(sUri
, RTL_TEXTENCODING_UTF8
);
367 char *uri
= (char*) oUri
.getStr();
368 xmlDocPtr pDoc
= xmlCtxtReadFile(pContext
.get(), uri
, 0, 0);
370 throwEx(pContext
.get());
372 Reference
< XDocument
> const xRet(
373 CDocument::CreateCDocument(pDoc
).get());
378 CDocumentBuilder::setEntityResolver(Reference
< XEntityResolver
> const& xER
)
379 throw (RuntimeException
)
381 ::osl::MutexGuard
const g(m_Mutex
);
383 m_xEntityResolver
= xER
;
386 Reference
< XEntityResolver
> SAL_CALL
CDocumentBuilder::getEntityResolver()
387 throw (RuntimeException
)
389 ::osl::MutexGuard
const g(m_Mutex
);
391 return m_xEntityResolver
;
395 CDocumentBuilder::setErrorHandler(Reference
< XErrorHandler
> const& xEH
)
396 throw (RuntimeException
)
398 ::osl::MutexGuard
const g(m_Mutex
);
400 m_xErrorHandler
= xEH
;
404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */