bump product version to 4.1.6.2
[LibreOffice.git] / unoxml / source / dom / documentbuilder.cxx
blob8d434e74bf1930f1b52e1427cd81e0b9153aa4ca
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 <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>
44 #include <node.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;
54 namespace DOM
57 class CDefaultEntityResolver : public cppu::WeakImplHelper1< XEntityResolver >
59 public:
60 virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId )
61 throw (::com::sun::star::uno::RuntimeException)
63 InputSource is;
64 is.sPublicId = sPublicId;
65 is.sSystemId = sSystemId;
66 is.sEncoding = OUString();
68 try {
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 >();
79 return is;
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.
92 xmlInitParser();
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",
103 NULL
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]));
117 return aSequence;
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;
140 return sal_False;
143 Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation()
144 throw (RuntimeException)
147 return Reference< XDOMImplementation >();
150 sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware()
151 throw (RuntimeException)
153 return sal_True;
156 sal_Bool SAL_CALL CDocumentBuilder::isValidating()
157 throw (RuntimeException)
159 return sal_False;
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());
171 return xRet;
174 static OUString make_error_message(xmlParserCtxtPtr ctxt)
176 OUStringBuffer buf;
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();
183 return msg;
186 // -- callbacks and context struct for parsing from stream
187 // -- c-linkage, so the callbacks can be used by libxml
188 extern "C" {
190 // context struct passed to IO functions
191 typedef struct context {
192 CDocumentBuilder *pBuilder;
193 Reference< XInputStream > rInputStream;
194 bool close;
195 bool freeOnClose;
196 } context_t;
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())
203 return -1;
204 try {
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);
211 return nread;
212 } catch (const com::sun::star::uno::Exception& ex) {
213 (void) ex;
214 OSL_FAIL(OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
215 return -1;
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())
224 return 0;
227 if (pctx->close)
228 pctx->rInputStream->closeInput();
229 if (pctx->freeOnClose)
230 delete pctx;
231 return 0;
232 } catch (const com::sun::star::uno::Exception& ex) {
233 (void) ex;
234 OSL_FAIL(OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
235 return -1;
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();
247 OUString sysid;
248 if (systemId != 0)
249 sysid = OUString((sal_Char*)systemId, strlen((char*)systemId), RTL_TEXTENCODING_UTF8);
250 OUString pubid;
251 if (publicId != 0)
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;
263 c->close = true;
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);
271 return pInput;
274 #if 0
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);
280 #endif
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());
300 } // extern "C"
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);
308 throw saxex;
311 Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
312 throw (RuntimeException, SAXParseException, IOException)
314 if (!is.is()) {
315 throw RuntimeException();
318 ::osl::MutexGuard const g(m_Mutex);
320 // encoding...
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
330 // on the console
331 pContext->_private = this;
332 pContext->sax->error = error_func;
333 pContext->sax->warning = warning_func;
334 pContext->sax->resolveEntity = resolve_func;
336 // IO context struct
337 context_t c;
338 c.pBuilder = this;
339 c.rInputStream = is;
340 // we did not open the stream, thus we do not close it.
341 c.close = false;
342 c.freeOnClose = false;
343 xmlDocPtr const pDoc = xmlCtxtReadIO(pContext.get(),
344 xmlIO_read_func, xmlIO_close_func, &c, 0, 0, 0);
346 if (pDoc == 0) {
347 throwEx(pContext.get());
349 Reference< XDocument > const xRet(
350 CDocument::CreateCDocument(pDoc).get());
351 return xRet;
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);
369 if (pDoc == 0) {
370 throwEx(pContext.get());
372 Reference< XDocument > const xRet(
373 CDocument::CreateCDocument(pDoc).get());
374 return xRet;
377 void SAL_CALL
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;
394 void SAL_CALL
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: */