xref: /trunk/main/unoxml/source/dom/documentbuilder.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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         {
68             InputSource is;
69             is.sPublicId = sPublicId;
70             is.sSystemId = sSystemId;
71             is.sEncoding = OUString();
72 
73             try {
74                 Reference< XCommandEnvironment > aEnvironment(
75                     new CommandEnvironment(Reference< XInteractionHandler >(),
76                                            Reference< XProgressHandler >() ));
77                 Content aContent(sSystemId, aEnvironment);
78 
79                 is.aInputStream = aContent.openStream();
80             } catch (com::sun::star::uno::Exception) {
81                 OSL_ENSURE(sal_False, "exception in default entity resolver");
82                 is.aInputStream = Reference< XInputStream >();
83             }
84             return is;
85         }
86 
87     };
88 
CDocumentBuilder(Reference<XMultiServiceFactory> const & xFactory)89     CDocumentBuilder::CDocumentBuilder(
90             Reference< XMultiServiceFactory > const& xFactory)
91         : m_xFactory(xFactory)
92         , m_xEntityResolver(new CDefaultEntityResolver())
93     {
94         // init libxml. libxml will protect itself against multiple
95         // initializations so there is no problem here if this gets
96         // called multiple times.
97         xmlInitParser();
98     }
99 
_getInstance(const Reference<XMultiServiceFactory> & rSMgr)100     Reference< XInterface > CDocumentBuilder::_getInstance(const Reference< XMultiServiceFactory >& rSMgr)
101     {
102         return static_cast< XDocumentBuilder* >(new CDocumentBuilder(rSMgr));
103     }
104 
105     const char* CDocumentBuilder::aImplementationName = "com.sun.star.comp.xml.dom.DocumentBuilder";
106     const char* CDocumentBuilder::aSupportedServiceNames[] = {
107         "com.sun.star.xml.dom.DocumentBuilder",
108         NULL
109     };
110 
_getImplementationName()111     OUString CDocumentBuilder::_getImplementationName()
112     {
113         return OUString::createFromAscii(aImplementationName);
114     }
_getSupportedServiceNames()115     Sequence<OUString> CDocumentBuilder::_getSupportedServiceNames()
116     {
117         Sequence<OUString> aSequence;
118         for (int i=0; aSupportedServiceNames[i]!=NULL; i++) {
119             aSequence.realloc(i+1);
120             aSequence[i]=(OUString::createFromAscii(aSupportedServiceNames[i]));
121         }
122         return aSequence;
123     }
124 
getSupportedServiceNames()125     Sequence< OUString > SAL_CALL CDocumentBuilder::getSupportedServiceNames()
126     {
127         return CDocumentBuilder::_getSupportedServiceNames();
128     }
129 
getImplementationName()130     OUString SAL_CALL CDocumentBuilder::getImplementationName()
131     {
132         return CDocumentBuilder::_getImplementationName();
133     }
134 
supportsService(const OUString & aServiceName)135     sal_Bool SAL_CALL CDocumentBuilder::supportsService(const OUString& aServiceName)
136     {
137         Sequence< OUString > supported = CDocumentBuilder::_getSupportedServiceNames();
138         for (sal_Int32 i=0; i<supported.getLength(); i++)
139         {
140             if (supported[i] == aServiceName) return sal_True;
141         }
142         return sal_False;
143     }
144 
getDOMImplementation()145     Reference< XDOMImplementation > SAL_CALL CDocumentBuilder::getDOMImplementation()
146     {
147 
148         return Reference< XDOMImplementation >();
149     }
150 
isNamespaceAware()151     sal_Bool SAL_CALL CDocumentBuilder::isNamespaceAware()
152     {
153         return sal_True;
154     }
155 
isValidating()156     sal_Bool SAL_CALL CDocumentBuilder::isValidating()
157     {
158         return sal_False;
159     }
160 
newDocument()161     Reference< XDocument > SAL_CALL CDocumentBuilder::newDocument()
162     {
163         ::osl::MutexGuard const g(m_Mutex);
164 
165         // create a new document
166         xmlDocPtr pDocument = xmlNewDoc((const xmlChar*)"1.0");
167         Reference< XDocument > const xRet(
168                 CDocument::CreateCDocument(pDocument).get());
169         return xRet;
170     }
171 
make_error_message(xmlParserCtxtPtr ctxt)172     static OUString make_error_message(xmlParserCtxtPtr ctxt)
173     {
174         OUStringBuffer buf;
175         buf.appendAscii(ctxt->lastError.message);
176         buf.appendAscii("Line: ");
177         buf.append(static_cast<sal_Int32>(ctxt->lastError.line));
178         buf.appendAscii("\nColumn: ");
179         buf.append(static_cast<sal_Int32>(ctxt->lastError.int2));
180         OUString msg = buf.makeStringAndClear();
181         return msg;
182     }
183 
184     // -- callbacks and context struct for parsing from stream
185     // -- c-linkage, so the callbacks can be used by libxml
186     extern "C" {
187 
188     // context struct passed to IO functions
189     typedef struct context {
190         CDocumentBuilder *pBuilder;
191         Reference< XInputStream > rInputStream;
192         bool close;
193         bool freeOnClose;
194     } context_t;
195 
xmlIO_read_func(void * context,char * buffer,int len)196     static int xmlIO_read_func( void *context, char *buffer, int len)
197     {
198         // get the context...
199         context_t *pctx = static_cast<context_t*>(context);
200         if (!pctx->rInputStream.is())
201             return -1;
202         try {
203             // try to read the requested number of bytes
204             Sequence< sal_Int8 > chunk(len);
205             int nread = pctx->rInputStream->readBytes(chunk, len);
206 
207             // copy bytes to the provided buffer
208             rtl_copyMemory(buffer, chunk.getConstArray(), nread);
209             return nread;
210         } catch (com::sun::star::uno::Exception& ex) {
211             (void) ex;
212             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
213             return -1;
214         }
215     }
216 
xmlIO_close_func(void * context)217     static int xmlIO_close_func(void* context)
218     {
219         // get the context...
220         context_t *pctx = static_cast<context_t*>(context);
221         if (!pctx->rInputStream.is())
222             return 0;
223         try
224         {
225             if (pctx->close)
226                 pctx->rInputStream->closeInput();
227             if (pctx->freeOnClose)
228                 delete pctx;
229             return 0;
230         } catch (com::sun::star::uno::Exception& ex) {
231             (void) ex;
232             OSL_ENSURE(sal_False, OUStringToOString(ex.Message, RTL_TEXTENCODING_UTF8).getStr());
233             return -1;
234         }
235     }
236 
resolve_func(void * ctx,const xmlChar * publicId,const xmlChar * systemId)237     static xmlParserInputPtr resolve_func(void *ctx,
238                                 const xmlChar *publicId,
239                                 const xmlChar *systemId)
240     {
241         // get the CDocumentBuilder object
242         xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr)ctx;
243         CDocumentBuilder *builder = static_cast< CDocumentBuilder* >(ctxt->_private);
244         Reference< XEntityResolver > resolver = builder->getEntityResolver();
245         OUString sysid;
246         if (systemId != 0)
247             sysid = OUString((sal_Char*)systemId, strlen((char*)systemId), RTL_TEXTENCODING_UTF8);
248         OUString pubid;
249         if (publicId != 0)
250             pubid = OUString((sal_Char*)publicId, strlen((char*)publicId), RTL_TEXTENCODING_UTF8);
251 
252         // resolve the entity
253         InputSource src = resolver->resolveEntity(pubid, sysid);
254 
255         // create IO context on heap because this call will no longer be on the stack
256         // when IO is actually performed through the callbacks. The close function must
257         // free the memory which is indicated by the freeOnClose field in the context struct
258         context_t *c = new context_t;
259         c->pBuilder = builder;
260         c->rInputStream = src.aInputStream;
261         c->close = true;
262         c->freeOnClose = true;
263 
264         // set up the inputBuffer and inputPtr for libxml
265         xmlParserInputBufferPtr pBuffer =
266             xmlParserInputBufferCreateIO(xmlIO_read_func, xmlIO_close_func, c, XML_CHAR_ENCODING_NONE);
267         xmlParserInputPtr pInput =
268                     xmlNewIOInputStream(ctxt, pBuffer, XML_CHAR_ENCODING_NONE);
269         return pInput;
270     }
271 
272 #if 0
273     static xmlParserInputPtr external_entity_loader(const char *URL, const char * /*ID*/, xmlParserCtxtPtr ctxt)
274     {
275         // just call our resolver function using the URL as systemId
276         return resolve_func(ctxt, 0, (const xmlChar*)URL);
277     }
278 #endif
279 
280     // default warning handler triggers assertion
warning_func(void * ctx,const char *,...)281     static void warning_func(void * ctx, const char * /*msg*/, ...)
282     {
283         OUStringBuffer buf(OUString::createFromAscii("libxml2 warning\n"));
284         buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
285         OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
286         OSL_ENSURE(sal_False, msg.getStr());
287     }
288 
289     // default error handler triggers assertion
error_func(void * ctx,const char *,...)290     static void error_func(void * ctx, const char * /*msg*/, ...)
291     {
292         OUStringBuffer buf(OUString::createFromAscii("libxml2 error\n"));
293         buf.append(make_error_message(static_cast< xmlParserCtxtPtr >(ctx)));
294         OString msg = OUStringToOString(buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US);
295         OSL_ENSURE(sal_False, msg.getStr());
296     }
297 
298     } // extern "C"
299 
throwEx(xmlParserCtxtPtr ctxt)300     void throwEx(xmlParserCtxtPtr ctxt) {
301         OUString msg = make_error_message(ctxt);
302         com::sun::star::xml::sax::SAXParseException saxex;
303         saxex.Message = msg;
304         saxex.LineNumber = static_cast<sal_Int32>(ctxt->lastError.line);
305         saxex.ColumnNumber = static_cast<sal_Int32>(ctxt->lastError.int2);
306         throw saxex;
307     }
308 
parse(const Reference<XInputStream> & is)309     Reference< XDocument > SAL_CALL CDocumentBuilder::parse(const Reference< XInputStream >& is)
310     {
311         if (!is.is()) {
312             throw RuntimeException();
313         }
314 
315         ::osl::MutexGuard const g(m_Mutex);
316 
317         // encoding...
318         /*
319         xmlChar *encstr = (xmlChar*) OUStringToOString(src.sEncoding, RTL_TEXTENCODING_UTF8).getStr();
320         xmlCharEncoding enc = xmlParseCharEncoding(encstr);
321         */
322 
323         ::boost::shared_ptr<xmlParserCtxt> const pContext(
324                 xmlNewParserCtxt(), xmlFreeParserCtxt);
325 
326         // register error functions to prevent errors being printed
327         // on the console
328         pContext->_private = this;
329         pContext->sax->error = error_func;
330         pContext->sax->warning = warning_func;
331         pContext->sax->resolveEntity = resolve_func;
332 
333         // IO context struct
334         context_t c;
335         c.pBuilder = this;
336         c.rInputStream = is;
337         // we did not open the stream, thus we do not close it.
338         c.close = false;
339         c.freeOnClose = false;
340         xmlDocPtr const pDoc = xmlCtxtReadIO(pContext.get(),
341                 xmlIO_read_func, xmlIO_close_func, &c, 0, 0, 0);
342 
343         if (pDoc == 0) {
344             throwEx(pContext.get());
345         }
346         Reference< XDocument > const xRet(
347                 CDocument::CreateCDocument(pDoc).get());
348         return xRet;
349     }
350 
parseURI(const OUString & sUri)351     Reference< XDocument > SAL_CALL CDocumentBuilder::parseURI(const OUString& sUri)
352     {
353         ::osl::MutexGuard const g(m_Mutex);
354 
355         ::boost::shared_ptr<xmlParserCtxt> const pContext(
356                 xmlNewParserCtxt(), xmlFreeParserCtxt);
357         pContext->_private = this;
358         pContext->sax->error = error_func;
359         pContext->sax->warning = warning_func;
360         pContext->sax->resolveEntity = resolve_func;
361         // xmlSetExternalEntityLoader(external_entity_loader);
362         OString oUri = OUStringToOString(sUri, RTL_TEXTENCODING_UTF8);
363         char *uri = (char*) oUri.getStr();
364         xmlDocPtr pDoc = xmlCtxtReadFile(pContext.get(), uri, 0, 0);
365         if (pDoc == 0) {
366             throwEx(pContext.get());
367         }
368         Reference< XDocument > const xRet(
369                 CDocument::CreateCDocument(pDoc).get());
370         return xRet;
371     }
372 
373     void SAL_CALL
setEntityResolver(Reference<XEntityResolver> const & xER)374     CDocumentBuilder::setEntityResolver(Reference< XEntityResolver > const& xER)
375     {
376         ::osl::MutexGuard const g(m_Mutex);
377 
378         m_xEntityResolver = xER;
379     }
380 
getEntityResolver()381     Reference< XEntityResolver > SAL_CALL CDocumentBuilder::getEntityResolver()
382     {
383         ::osl::MutexGuard const g(m_Mutex);
384 
385         return m_xEntityResolver;
386     }
387 
388     void SAL_CALL
setErrorHandler(Reference<XErrorHandler> const & xEH)389     CDocumentBuilder::setErrorHandler(Reference< XErrorHandler > const& xEH)
390     {
391         ::osl::MutexGuard const g(m_Mutex);
392 
393         m_xErrorHandler = xEH;
394     }
395 }
396