xref: /AOO41X/main/unoxml/source/dom/documentbuilder.cxx (revision e9cbe144f2ea8c6fdc1a6527ef692f8296608908)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include <documentbuilder.hxx>
25 
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 
30 #include <libxml/xmlerror.h>
31 #include <libxml/tree.h>
32 
33 #include <boost/shared_ptr.hpp>
34 
35 #include <rtl/alloc.h>
36 #include <rtl/memory.h>
37 #include <rtl/ustrbuf.hxx>
38 
39 #include <cppuhelper/implbase1.hxx>
40 
41 #include <com/sun/star/xml/sax/SAXParseException.hpp>
42 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
43 #include <com/sun/star/task/XInteractionHandler.hpp>
44 
45 #include <ucbhelper/content.hxx>
46 #include <ucbhelper/commandenvironment.hxx>
47 
48 #include <node.hxx>
49 #include <document.hxx>
50 
51 
52 using ::rtl::OUStringBuffer;
53 using ::rtl::OString;
54 using ::com::sun::star::xml::sax::InputSource;
55 using namespace ucbhelper;
56 using namespace ::com::sun::star::ucb;
57 using ::com::sun::star::task::XInteractionHandler;
58 
59 
60 namespace DOM
61 {
62 
63     class CDefaultEntityResolver : public cppu::WeakImplHelper1< XEntityResolver >
64     {
65     public:
resolveEntity(const OUString & sPublicId,const OUString & sSystemId)66         virtual InputSource SAL_CALL resolveEntity( const OUString& sPublicId, const OUString& sSystemId )
67             throw (::com::sun::star::uno::RuntimeException)
68         {
69             InputSource is;
70             is.sPublicId = sPublicId;
71             is.sSystemId = sSystemId;
72             is.sEncoding = OUString();
73 
74             try {
75                 Reference< XCommandEnvironment > aEnvironment(
76                     new CommandEnvironment(Reference< XInteractionHandler >(),
77                                            Reference< XProgressHandler >() ));
78                 Content aContent(sSystemId, aEnvironment);
79 
80                 is.aInputStream = aContent.openStream();
81             } catch (com::sun::star::uno::Exception) {
82                 OSL_ENSURE(sal_False, "exception in default entity resolver");
83                 is.aInputStream = Reference< XInputStream >();
84             }
85             return is;
86         }
87 
88     };
89 
CDocumentBuilder(Reference<XMultiServiceFactory> const & xFactory)90     CDocumentBuilder::CDocumentBuilder(
91             Reference< XMultiServiceFactory > const& xFactory)
92         : m_xFactory(xFactory)
93         , m_xEntityResolver(new CDefaultEntityResolver())
94     {
95         // init libxml. libxml will protect itself against multiple
96         // initializations so there is no problem here if this gets
97         // called multiple times.
98         xmlInitParser();
99     }
100 
_getInstance(const Reference<XMultiServiceFactory> & rSMgr)101     Reference< XInterface > CDocumentBuilder::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
102     {
103         return static_cast< XDocumentBuilder* >(new CDocumentBuilder(rSMgr));
104     }
105 
106     const char* CDocumentBuilder::aImplementationName = "com.sun.star.comp.xml.dom.DocumentBuilder";
107     const char* CDocumentBuilder::aSupportedServiceNames[] = {
108         "com.sun.star.xml.dom.DocumentBuilder",
109         NULL
110     };
111 
_getImplementationName()112     OUString CDocumentBuilder::_getImplementationName()
113     {
114         return OUString::createFromAscii(aImplementationName);
115     }
_getSupportedServiceNames()116     Sequence<OUString> CDocumentBuilder::_getSupportedServiceNames()
117     {
118         Sequence<OUString> aSequence;
119         for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
120             aSequence.realloc(i+1);
121             aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
122         }
123         return aSequence;
124     }
125 
getSupportedServiceNames()126     Sequence< OUString > SAL_CALL CDocumentBuilder::getSupportedServiceNames()
127         throw (RuntimeException)
128     {
129         return CDocumentBuilder::_getSupportedServiceNames();
130     }
131 
getImplementationName()132     OUString SAL_CALL CDocumentBuilder::getImplementationName()
133         throw (RuntimeException)
134     {
135         return CDocumentBuilder::_getImplementationName();
136     }
137 
supportsService(const OUString & aServiceName)138     sal_Bool SAL_CALL CDocumentBuilder::supportsService(const OUString& aServiceName)
139         throw (RuntimeException)
140     {
141         Sequence< OUString > supported = CDocumentBuilder::_getSupportedServiceNames();
142         for (sal_Int32 i=0; i<supported.getLength(); i++)
143         {
144             if (supported[i] == aServiceName) return sal_True;
145         }
146         return sal_False;
147     }
148 
getDOMImplementation()149     Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation()
150         throw (RuntimeException)
151     {
152 
153         return Reference< XDOMImplementation >();
154     }
155 
isNamespaceAware()156     sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware()
157         throw (RuntimeException)
158     {
159         return sal_True;
160     }
161 
isValidating()162     sal_Bool SAL_CALL CDocumentBuilder::isValidating()
163         throw (RuntimeException)
164     {
165         return sal_False;
166     }
167 
newDocument()168     Reference< XDocument > SAL_CALL CDocumentBuilder::newDocument()
169         throw (RuntimeException)
170     {
171         ::osl::MutexGuard const g(m_Mutex);
172 
173         // create a new document
174         xmlDocPtr pDocument = xmlNewDoc((const xmlChar*)"1.0");
175         Reference< XDocument > const xRet(
176                 CDocument::CreateCDocument(pDocument).get());
177         return xRet;
178     }
179 
make_error_message(xmlParserCtxtPtr ctxt)180     static OUString make_error_message(xmlParserCtxtPtr ctxt)
181     {
182         OUStringBuffer buf;
183         buf.appendAscii(ctxt->lastError.message);
184         buf.appendAscii("Line: ");
185         buf.append(static_cast<sal_Int32>(ctxt->lastError.line));
186         buf.appendAscii("\nColumn: ");
187         buf.append(static_cast<sal_Int32>(ctxt->lastError.int2));
188         OUString msg = buf.makeStringAndClear();
189         return msg;
190     }
191 
192     // -- callbacks and context struct for parsing from stream
193     // -- c-linkage, so the callbacks can be used by libxml
194     extern "C" {
195 
196     // context struct passed to IO functions
197     typedef struct context {
198         CDocumentBuilder *pBuilder;
199         Reference< XInputStream > rInputStream;
200         bool close;
201         bool freeOnClose;
202     } context_t;
203 
xmlIO_read_func(void * context,char * buffer,int len)204     static int xmlIO_read_func( void *context, char *buffer, int len)
205     {
206         // get the context...
207         context_t *pctx = static_cast<context_t*>(context);
208         if (!pctx->rInputStream.is())
209             return -1;
210         try {
211             // try to read the requested number of bytes
212             Sequence< sal_Int8 > chunk(len);
213             int nread = pctx->rInputStream->readBytes(chunk, len);
214 
215             // copy bytes to the provided buffer
216             rtl_copyMemory(buffer, chunk.getConstArray(), nread);
217             return nread;
218         } catch (com::sun::star::uno::Exception& ex) {
219             (void) ex;
220             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
221             return -1;
222         }
223     }
224 
xmlIO_close_func(void * context)225     static int xmlIO_close_func(void* context)
226     {
227         // get the context...
228         context_t *pctx = static_cast<context_t*>(context);
229         if (!pctx->rInputStream.is())
230             return 0;
231         try
232         {
233             if (pctx->close)
234                 pctx->rInputStream->closeInput();
235             if (pctx->freeOnClose)
236                 delete pctx;
237             return 0;
238         } catch (com::sun::star::uno::Exception& ex) {
239             (void) ex;
240             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
241             return -1;
242         }
243     }
244 
resolve_func(void * ctx,const xmlChar * publicId,const xmlChar * systemId)245     static xmlParserInputPtr resolve_func(void *ctx,
246                                 const xmlChar *publicId,
247                                 const xmlChar *systemId)
248     {
249         // get the CDocumentBuilder object
250         xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
251         CDocumentBuilder *builder = static_cast< CDocumentBuilder* >(ctxt->_private);
252         Reference< XEntityResolver > resolver = builder->getEntityResolver();
253         OUString sysid;
254         if (systemId != 0)
255             sysid = OUString((sal_Char*)systemId, strlen((char*)systemId), RTL_TEXTENCODING_UTF8);
256         OUString pubid;
257         if (publicId != 0)
258             pubid = OUString((sal_Char*)publicId, strlen((char*)publicId), RTL_TEXTENCODING_UTF8);
259 
260         // resolve the entity
261         InputSource src = resolver->resolveEntity(pubid, sysid);
262 
263         // create IO context on heap because this call will no longer be on the stack
264         // when IO is actually performed through the callbacks. The close function must
265         // free the memory which is indicated by the freeOnClose field in the context struct
266         context_t *c = new context_t;
267         c->pBuilder = builder;
268         c->rInputStream = src.aInputStream;
269         c->close = true;
270         c->freeOnClose = true;
271 
272         // set up the inputBuffer and inputPtr for libxml
273         xmlParserInputBufferPtr pBuffer =
274             xmlParserInputBufferCreateIO(xmlIO_read_func, xmlIO_close_func, c, XML_CHAR_ENCODING_NONE);
275         xmlParserInputPtr pInput =
276                     xmlNewIOInputStream(ctxt, pBuffer, XML_CHAR_ENCODING_NONE);
277         return pInput;
278     }
279 
280 #if 0
281     static xmlParserInputPtr external_entity_loader(const char *URL, const char * /*ID*/, xmlParserCtxtPtr ctxt)
282     {
283         // just call our resolver function using the URL as systemId
284         return resolve_func(ctxt, 0, (const xmlChar*)URL);
285     }
286 #endif
287 
288     // default warning handler triggers assertion
warning_func(void * ctx,const char *,...)289     static void warning_func(void * ctx, const char * /*msg*/, ...)
290     {
291         OUStringBuffer buf(OUString::createFromAscii("libxml2 warning\n"));
292         buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
293         OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
294         OSL_ENSURE(sal_False, msg.getStr());
295     }
296 
297     // default error handler triggers assertion
error_func(void * ctx,const char *,...)298     static void error_func(void * ctx, const char * /*msg*/, ...)
299     {
300         OUStringBuffer buf(OUString::createFromAscii("libxml2 error\n"));
301         buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
302         OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
303         OSL_ENSURE(sal_False, msg.getStr());
304     }
305 
306     } // extern "C"
307 
throwEx(xmlParserCtxtPtr ctxt)308     void throwEx(xmlParserCtxtPtr ctxt) {
309         OUString msg = make_error_message(ctxt);
310         com::sun::star::xml::sax::SAXParseException saxex;
311         saxex.Message = msg;
312         saxex.LineNumber = static_cast<sal_Int32>(ctxt->lastError.line);
313         saxex.ColumnNumber = static_cast<sal_Int32>(ctxt->lastError.int2);
314         throw saxex;
315     }
316 
parse(const Reference<XInputStream> & is)317     Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
318         throw (RuntimeException, SAXParseException, IOException)
319     {
320         if (!is.is()) {
321             throw RuntimeException();
322         }
323 
324         ::osl::MutexGuard const g(m_Mutex);
325 
326         // encoding...
327         /*
328         xmlChar *encstr = (xmlChar*) OUStringToOString(src.sEncoding, RTL_TEXTENCODING_UTF8).getStr();
329         xmlCharEncoding enc = xmlParseCharEncoding(encstr);
330         */
331 
332         ::boost::shared_ptr<xmlParserCtxt> const pContext(
333                 xmlNewParserCtxt(), xmlFreeParserCtxt);
334 
335         // register error functions to prevent errors being printed
336         // on the console
337         pContext->_private = this;
338         pContext->sax->error = error_func;
339         pContext->sax->warning = warning_func;
340         pContext->sax->resolveEntity = resolve_func;
341 
342         // IO context struct
343         context_t c;
344         c.pBuilder = this;
345         c.rInputStream = is;
346         // we did not open the stream, thus we do not close it.
347         c.close = false;
348         c.freeOnClose = false;
349         xmlDocPtr const pDoc = xmlCtxtReadIO(pContext.get(),
350                 xmlIO_read_func, xmlIO_close_func, &c, 0, 0, 0);
351 
352         if (pDoc == 0) {
353             throwEx(pContext.get());
354         }
355         Reference< XDocument > const xRet(
356                 CDocument::CreateCDocument(pDoc).get());
357         return xRet;
358     }
359 
parseURI(const OUString & sUri)360     Reference< XDocument > SAL_CALL CDocumentBuilder::parseURI(const OUString& sUri)
361         throw (RuntimeException, SAXParseException, IOException)
362     {
363         ::osl::MutexGuard const g(m_Mutex);
364 
365         ::boost::shared_ptr<xmlParserCtxt> const pContext(
366                 xmlNewParserCtxt(), xmlFreeParserCtxt);
367         pContext->_private = this;
368         pContext->sax->error = error_func;
369         pContext->sax->warning = warning_func;
370         pContext->sax->resolveEntity = resolve_func;
371         // xmlSetExternalEntityLoader(external_entity_loader);
372         OString oUri = OUStringToOString(sUri, RTL_TEXTENCODING_UTF8);
373         char *uri = (char*) oUri.getStr();
374         xmlDocPtr pDoc = xmlCtxtReadFile(pContext.get(), uri, 0, 0);
375         if (pDoc == 0) {
376             throwEx(pContext.get());
377         }
378         Reference< XDocument > const xRet(
379                 CDocument::CreateCDocument(pDoc).get());
380         return xRet;
381     }
382 
383     void SAL_CALL
setEntityResolver(Reference<XEntityResolver> const & xER)384     CDocumentBuilder::setEntityResolver(Reference< XEntityResolver > const& xER)
385         throw (RuntimeException)
386     {
387         ::osl::MutexGuard const g(m_Mutex);
388 
389         m_xEntityResolver = xER;
390     }
391 
getEntityResolver()392     Reference< XEntityResolver > SAL_CALL CDocumentBuilder::getEntityResolver()
393         throw (RuntimeException)
394     {
395         ::osl::MutexGuard const g(m_Mutex);
396 
397         return m_xEntityResolver;
398     }
399 
400     void SAL_CALL
setErrorHandler(Reference<XErrorHandler> const & xEH)401     CDocumentBuilder::setErrorHandler(Reference< XErrorHandler > const& xEH)
402         throw (RuntimeException)
403     {
404         ::osl::MutexGuard const g(m_Mutex);
405 
406         m_xErrorHandler = xEH;
407     }
408 }
409