1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: documentbuilder.cxx,v $
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"
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>
54 using ::rtl::OUStringBuffer
;
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
;
65 //char *strdup(const char *s);
67 static char* strdupfunc(const char* s)
70 while (s[len] != '\0') len++;
71 char *newStr = (char*)rtl_allocateMemory(len+1);
73 rtl_copyMemory(newStr, s, len+1);
80 class CDefaultEntityResolver
: public cppu::WeakImplHelper1
< XEntityResolver
>
83 virtual InputSource SAL_CALL
resolveEntity( const OUString
& sPublicId
, const OUString
& sSystemId
)
84 throw (::com::sun::star::uno::RuntimeException
)
87 is
.sPublicId
= sPublicId
;
88 is
.sSystemId
= sSystemId
;
89 is
.sEncoding
= OUString();
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
>();
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.
117 Reference
< XInterface
> CDocumentBuilder::_getInstance(const Reference
< XMultiServiceFactory
>& rSMgr
)
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",
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
]));
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
;
166 Reference
< XDOMImplementation
> SAL_CALL
CDocumentBuilder::getDOMImplementation()
167 throw (RuntimeException
)
170 return Reference
< XDOMImplementation
>();
173 sal_Bool SAL_CALL
CDocumentBuilder::isNamespaceAware()
174 throw (RuntimeException
)
179 sal_Bool SAL_CALL
CDocumentBuilder::isValidating()
180 throw (RuntimeException
)
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
)
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();
205 // -- callbacks and context struct for parsing from stream
206 // -- c-linkage, so the callbacks can be used by libxml
209 // context struct passed to IO functions
210 typedef struct context
{
211 CDocumentBuilder
*pBuilder
;
212 Reference
< XInputStream
> rInputStream
;
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())
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
);
231 } catch (com::sun::star::uno::Exception
& ex
) {
233 OSL_ENSURE(sal_False
, OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
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())
247 pctx
->rInputStream
->closeInput();
248 if (pctx
->freeOnClose
)
251 } catch (com::sun::star::uno::Exception
& ex
) {
253 OSL_ENSURE(sal_False
, OUStringToOString(ex
.Message
, RTL_TEXTENCODING_UTF8
).getStr());
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();
268 sysid
= OUString((sal_Char
*)systemId
, strlen((char*)systemId
), RTL_TEXTENCODING_UTF8
);
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
;
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
);
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());
319 void throwEx(xmlParserCtxtPtr ctxt
) {
320 OUString msg
= make_error_message(ctxt
);
321 xmlFreeParserCtxt(ctxt
);
322 com::sun::star::xml::sax::SAXParseException saxex
;
324 saxex
.LineNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.line
);
325 saxex
.ColumnNumber
= static_cast<sal_Int32
>(ctxt
->lastError
.int2
);
329 Reference
< XDocument
> SAL_CALL
CDocumentBuilder::parse(const Reference
< XInputStream
>& is
)
330 throw (RuntimeException
, SAXParseException
, IOException
)
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
343 ctxt
->_private
= this;
344 ctxt
->sax
->error
= error_func
;
345 ctxt
->sax
->warning
= warning_func
;
346 ctxt
->sax
->resolveEntity
= resolve_func
;
352 // we did not open the stream, thus we do not close it.
354 c
.freeOnClose
= false;
355 xmlDocPtr pDoc
= xmlCtxtReadIO(ctxt
, xmlIO_read_func
, xmlIO_close_func
, &c
,
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
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
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);
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
;