update dev300-m58
[ooovba.git] / unoxml / source / dom / documentbuilder.cxx
blob4e28db74c63a8ad4803f5b8b2ed37b788cddac39
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: documentbuilder.cxx,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "documentbuilder.hxx"
32 #include "node.hxx"
33 #include "document.hxx"
35 #include <rtl/alloc.h>
36 #include <rtl/memory.h>
37 #include <rtl/ustrbuf.hxx>
39 #include <cppuhelper/implbase1.hxx>
41 #include <libxml/xmlerror.h>
43 #include <com/sun/star/xml/sax/SAXParseException.hpp>
44 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
45 #include <com/sun/star/task/XInteractionHandler.hpp>
46 #include <ucbhelper/content.hxx>
47 #include <ucbhelper/commandenvironment.hxx>
49 #include <string.h>
50 #include <stdio.h>
51 #include <stdarg.h>
54 using ::rtl::OUStringBuffer;
55 using ::rtl::OString;
56 using ::com::sun::star::xml::sax::InputSource;
57 using namespace ucbhelper;
58 using namespace ::com::sun::star::ucb;
59 using ::com::sun::star::task::XInteractionHandler;
62 namespace DOM
64 extern "C" {
65 //char *strdup(const char *s);
67 static char* strdupfunc(const char* s)
69 sal_Int32 len = 0;
70 while (s[len] != '\0') len++;
71 char *newStr = (char*)rtl_allocateMemory(len+1);
72 if (newStr != NULL)
73 rtl_copyMemory(newStr, s, len+1);
74 return newStr;
80 class CDefaultEntityResolver : public cppu::WeakImplHelper1< XEntityResolver >
82 public:
83 virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId )
84 throw (::com::sun::star::uno::RuntimeException)
86 InputSource is;
87 is.sPublicId = sPublicId;
88 is.sSystemId = sSystemId;
89 is.sEncoding = OUString();
91 try {
92 Reference< XCommandEnvironment > aEnvironment(
93 new CommandEnvironment(Reference< XInteractionHandler >(),
94 Reference< XProgressHandler >() ));
95 Content aContent(sSystemId, aEnvironment);
97 is.aInputStream = aContent.openStream();
98 } catch (com::sun::star::uno::Exception) {
99 OSL_ENSURE(sal_False, "exception in default entity resolver");
100 is.aInputStream = Reference< XInputStream >();
102 return is;
107 CDocumentBuilder::CDocumentBuilder(const Reference< XMultiServiceFactory >& xFactory)
108 : m_aFactory(xFactory)
109 , m_aEntityResolver(Reference< XEntityResolver > (new CDefaultEntityResolver()))
111 // init libxml. libxml will protect itself against multiple
112 // initializations so there is no problem here if this gets
113 // called multiple times.
114 xmlInitParser();
117 Reference< XInterface > CDocumentBuilder::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
119 // XXX
120 return static_cast< XDocumentBuilder* >(new CDocumentBuilder(rSMgr));
123 const char* CDocumentBuilder::aImplementationName = "com.sun.star.comp.xml.dom.DocumentBuilder";
124 const char* CDocumentBuilder::aSupportedServiceNames[] = {
125 "com.sun.star.xml.dom.DocumentBuilder",
126 NULL
129 OUString CDocumentBuilder::_getImplementationName()
131 return OUString::createFromAscii(aImplementationName);
133 Sequence<OUString> CDocumentBuilder::_getSupportedServiceNames()
135 Sequence<OUString> aSequence;
136 for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
137 aSequence.realloc(i+1);
138 aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
140 return aSequence;
143 Sequence< OUString > SAL_CALL CDocumentBuilder::getSupportedServiceNames()
144 throw (RuntimeException)
146 return CDocumentBuilder::_getSupportedServiceNames();
149 OUString SAL_CALL CDocumentBuilder::getImplementationName()
150 throw (RuntimeException)
152 return CDocumentBuilder::_getImplementationName();
155 sal_Bool SAL_CALL CDocumentBuilder::supportsService(const OUString& aServiceName)
156 throw (RuntimeException)
158 Sequence< OUString > supported = CDocumentBuilder::_getSupportedServiceNames();
159 for (sal_Int32 i=0; i<supported.getLength(); i++)
161 if (supported[i] == aServiceName) return sal_True;
163 return sal_False;
166 Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation()
167 throw (RuntimeException)
170 return Reference< XDOMImplementation >();
173 sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware()
174 throw (RuntimeException)
176 return sal_True;
179 sal_Bool SAL_CALL CDocumentBuilder::isValidating()
180 throw (RuntimeException)
182 return sal_False;
185 Reference< XDocument > SAL_CALL CDocumentBuilder::newDocument()
186 throw (RuntimeException)
188 // create a new document
189 xmlDocPtr pDocument = xmlNewDoc((const xmlChar*)"1.0");
190 return Reference< XDocument >(static_cast< CDocument* >(CNode::get((xmlNodePtr)pDocument)));
193 static OUString make_error_message(xmlParserCtxtPtr ctxt)
195 OUStringBuffer buf;
196 buf.appendAscii(ctxt->lastError.message);
197 buf.appendAscii("Line: ");
198 buf.append(static_cast<sal_Int32>(ctxt->lastError.line));
199 buf.appendAscii("\nColumn: ");
200 buf.append(static_cast<sal_Int32>(ctxt->lastError.int2));
201 OUString msg = buf.makeStringAndClear();
202 return msg;
205 // -- callbacks and context struct for parsing from stream
206 // -- c-linkage, so the callbacks can be used by libxml
207 extern "C" {
209 // context struct passed to IO functions
210 typedef struct context {
211 CDocumentBuilder *pBuilder;
212 Reference< XInputStream > rInputStream;
213 bool close;
214 bool freeOnClose;
215 } context_t;
217 static int xmlIO_read_func( void *context, char *buffer, int len)
219 // get the context...
220 context_t *pctx = static_cast<context_t*>(context);
221 if (!pctx->rInputStream.is())
222 return -1;
223 try {
224 // try to read the requested number of bytes
225 Sequence< sal_Int8 > chunk(len);
226 int nread = pctx->rInputStream->readBytes(chunk, len);
228 // copy bytes to the provided buffer
229 rtl_copyMemory(buffer, chunk.getConstArray(), nread);
230 return nread;
231 } catch (com::sun::star::uno::Exception& ex) {
232 (void) ex;
233 OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
234 return -1;
238 static int xmlIO_close_func(void* context)
240 // get the context...
241 context_t *pctx = static_cast<context_t*>(context);
242 if (!pctx->rInputStream.is())
243 return 0;
246 if (pctx->close)
247 pctx->rInputStream->closeInput();
248 if (pctx->freeOnClose)
249 delete pctx;
250 return 0;
251 } catch (com::sun::star::uno::Exception& ex) {
252 (void) ex;
253 OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
254 return -1;
258 static xmlParserInputPtr resolve_func(void *ctx,
259 const xmlChar *publicId,
260 const xmlChar *systemId)
262 // get the CDocumentBuilder object
263 xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
264 CDocumentBuilder *builder = static_cast< CDocumentBuilder* >(ctxt->_private);
265 Reference< XEntityResolver > resolver = builder->getEntityResolver();
266 OUString sysid;
267 if (systemId != 0)
268 sysid = OUString((sal_Char*)systemId, strlen((char*)systemId), RTL_TEXTENCODING_UTF8);
269 OUString pubid;
270 if (publicId != 0)
271 pubid = OUString((sal_Char*)publicId, strlen((char*)publicId), RTL_TEXTENCODING_UTF8);
273 // resolve the entity
274 InputSource src = resolver->resolveEntity(pubid, sysid);
276 // create IO context on heap because this call will no longer be on the stack
277 // when IO is actually performed through the callbacks. The close function must
278 // free the memory which is indicated by the freeOnClose field in the context struct
279 context_t *c = new context_t;
280 c->pBuilder = builder;
281 c->rInputStream = src.aInputStream;
282 c->close = true;
283 c->freeOnClose = true;
285 // set up the inputBuffer and inputPtr for libxml
286 xmlParserInputBufferPtr pBuffer =
287 xmlParserInputBufferCreateIO(xmlIO_read_func, xmlIO_close_func, c, XML_CHAR_ENCODING_NONE);
288 xmlParserInputPtr pInput =
289 xmlNewIOInputStream(ctxt, pBuffer, XML_CHAR_ENCODING_NONE);
290 return pInput;
293 static xmlParserInputPtr external_entity_loader(const char *URL, const char * /*ID*/, xmlParserCtxtPtr ctxt)
295 // just call our resolver function using the URL as systemId
296 return resolve_func(ctxt, 0, (const xmlChar*)URL);
299 // default warning handler triggers assertion
300 static void warning_func(void * ctx, const char * /*msg*/, ...)
302 OUStringBuffer buf(OUString::createFromAscii("libxml2 warning\n"));
303 buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
304 OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
305 OSL_ENSURE(sal_False, msg.getStr());
308 // default error handler triggers assertion
309 static void error_func(void * ctx, const char * /*msg*/, ...)
311 OUStringBuffer buf(OUString::createFromAscii("libxml2 error\n"));
312 buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
313 OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
314 OSL_ENSURE(sal_False, msg.getStr());
317 } // extern "C"
319 void throwEx(xmlParserCtxtPtr ctxt) {
320 OUString msg = make_error_message(ctxt);
321 xmlFreeParserCtxt(ctxt);
322 com::sun::star::xml::sax::SAXParseException saxex;
323 saxex.Message = msg;
324 saxex.LineNumber = static_cast<sal_Int32>(ctxt->lastError.line);
325 saxex.ColumnNumber = static_cast<sal_Int32>(ctxt->lastError.int2);
326 throw saxex;
329 Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
330 throw (RuntimeException, SAXParseException, IOException)
333 // encoding...
335 xmlChar *encstr = (xmlChar*) OUStringToOString(src.sEncoding, RTL_TEXTENCODING_UTF8).getStr();
336 xmlCharEncoding enc = xmlParseCharEncoding(encstr);
339 xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
341 // register error functions to prevent errors being printed
342 // on the console
343 ctxt->_private = this;
344 ctxt->sax->error = error_func;
345 ctxt->sax->warning = warning_func;
346 ctxt->sax->resolveEntity = resolve_func;
348 // IO context struct
349 context_t c;
350 c.pBuilder = this;
351 c.rInputStream = is;
352 // we did not open the stream, thus we do not close it.
353 c.close = false;
354 c.freeOnClose = false;
355 xmlDocPtr pDoc = xmlCtxtReadIO(ctxt, xmlIO_read_func, xmlIO_close_func, &c,
356 0, 0, 0);
358 if (pDoc == 0) {
359 throwEx(ctxt);
361 xmlFreeParserCtxt(ctxt);
362 return Reference< XDocument >(static_cast< CDocument* >(CNode::get((xmlNodePtr)pDoc)));
365 Reference< XDocument > SAL_CALL CDocumentBuilder::parseSource(const InputSource& is)
366 throw (RuntimeException, SAXParseException, IOException)
368 // if there is an encoding specified in the input source, use it
369 xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
370 if (is.sEncoding.getLength() > 0) {
371 OString oEncstr = OUStringToOString(is.sEncoding, RTL_TEXTENCODING_UTF8);
372 char *encstr = (char*) oEncstr.getStr();
373 enc = xmlParseCharEncoding(encstr);
376 // set up parser context
377 xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
378 // register error functions to prevent errors being printed
379 // on the console
380 ctxt->_private = this;
381 ctxt->sax->error = error_func;
382 ctxt->sax->warning = warning_func;
384 // setup entity resolver binding(s)
385 ctxt->sax->resolveEntity = resolve_func;
386 xmlSetExternalEntityLoader(external_entity_loader);
388 // if an input stream is provided, use it
390 // use the systemID
392 return Reference< XDocument >();
395 Reference< XDocument > SAL_CALL CDocumentBuilder::parseURI(const OUString& sUri)
396 throw (RuntimeException, SAXParseException, IOException)
398 xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
399 ctxt->_private = this;
400 ctxt->sax->error = error_func;
401 ctxt->sax->warning = warning_func;
402 ctxt->sax->resolveEntity = resolve_func;
403 // xmlSetExternalEntityLoader(external_entity_loader);
404 OString oUri = OUStringToOString(sUri, RTL_TEXTENCODING_UTF8);
405 char *uri = (char*) oUri.getStr();
406 xmlDocPtr pDoc = xmlCtxtReadFile(ctxt, uri, 0, 0);
407 if (pDoc == 0) {
408 throwEx(ctxt);
410 xmlFreeParserCtxt(ctxt);
411 return Reference< XDocument >(static_cast< CDocument* >(CNode::get((xmlNodePtr)pDoc)));
414 void SAL_CALL CDocumentBuilder::setEntityResolver(const Reference< XEntityResolver >& er)
415 throw (RuntimeException)
417 m_aEntityResolver = er;
420 Reference< XEntityResolver > SAL_CALL CDocumentBuilder::getEntityResolver()
421 throw (RuntimeException)
423 return m_aEntityResolver;
427 void SAL_CALL CDocumentBuilder::setErrorHandler(const Reference< XErrorHandler >& eh)
428 throw (RuntimeException)
430 m_aErrorHandler = eh;