Merge from mainline.
[official-gcc.git] / libjava / classpath / native / jni / xmlj / xmlj_io.c
blobaa2964dc312dc9ec58182143a8cb070e2947a222
1 /* xmlj_io.c -
2 Copyright (C) 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 #include "xmlj_io.h"
39 #include "xmlj_error.h"
40 #include "xmlj_node.h"
41 #include "xmlj_sax.h"
42 #include "xmlj_util.h"
44 #include <math.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <stdarg.h>
49 #include <libxml/xmlIO.h>
50 #include <libxml/parserInternals.h>
52 #include <pthread.h>
54 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
55 #define UNSIGN(a) (((a) < 0) ? ((a) + 0x100) : (a))
57 #define DETECT_BUFFER_SIZE 50
59 typedef struct _OutputStreamContext
62 JNIEnv *env;
63 jobject outputStream;
64 jmethodID outputStreamWriteFunc;
65 jmethodID outputStreamCloseFunc;
68 OutputStreamContext;
70 typedef struct _InputStreamContext
73 JNIEnv *env;
74 jobject inputStream;
75 jmethodID inputStreamReadFunc;
76 jmethodID inputStreamCloseFunc;
77 jobject bufferByteArray;
78 jint bufferLength;
81 InputStreamContext;
83 InputStreamContext *xmljNewInputStreamContext (JNIEnv * env,
84 jobject inputStream);
86 void xmljFreeInputStreamContext (InputStreamContext * inContext);
88 int xmljInputReadCallback (void *context, char *buffer, int len);
90 int xmljInputCloseCallback (void *context);
92 int xmljOutputWriteCallback (void *context, const char *buffer, int len);
94 int xmljOutputCloseCallback (void *context);
96 OutputStreamContext *xmljNewOutputStreamContext (JNIEnv * env,
97 jobject outputStream);
99 void
100 xmljFreeOutputStreamContext (OutputStreamContext * outContext);
102 xmlCharEncoding
103 xmljDetectCharEncoding (JNIEnv * env, jbyteArray buffer);
106 xmljOutputWriteCallback (void *context, const char *buffer, int len)
108 OutputStreamContext *outContext;
109 JNIEnv *env;
110 jbyteArray byteArray;
112 outContext = (OutputStreamContext *) context;
113 env = outContext->env;
114 byteArray = (*env)->NewByteArray (env, len);
116 if (0 != byteArray)
118 (*env)->SetByteArrayRegion (env, byteArray, 0, len, (jbyte *) buffer);
120 (*env)->CallVoidMethod (env,
121 outContext->outputStream,
122 outContext->outputStreamWriteFunc, byteArray);
124 (*env)->DeleteLocalRef (env, byteArray);
126 return (*env)->ExceptionOccurred (env) ? -1 : len;
128 else
130 /* Out of memory, signal error */
131 return -1;
136 xmljOutputCloseCallback (void *context)
138 OutputStreamContext *outContext;
139 JNIEnv *env;
141 outContext = (OutputStreamContext *) context;
142 env = outContext->env;
143 (*env)->CallVoidMethod (env,
144 outContext->outputStream,
145 outContext->outputStreamCloseFunc);
147 return (*env)->ExceptionOccurred (env) ? -1 : 0;
151 xmljInputReadCallback (void *context, char *buffer, int len)
153 InputStreamContext *inContext;
154 JNIEnv *env;
155 jint nread;
156 int offset;
158 inContext = (InputStreamContext *) context;
159 env = inContext->env;
160 nread = 0;
162 for (offset = 0; offset < len && nread >= 0;)
164 nread = (*env)->CallIntMethod (env,
165 inContext->inputStream,
166 inContext->inputStreamReadFunc,
167 inContext->bufferByteArray,
168 0, MIN (len - offset,
169 inContext->bufferLength));
171 if (nread > 0)
173 (*env)->GetByteArrayRegion (env,
174 inContext->bufferByteArray,
175 0, nread, ((jbyte *) buffer) + offset);
177 offset += nread;
181 return (*env)->ExceptionOccurred (env) ? -1 : offset;
185 xmljInputCloseCallback (void *context)
187 InputStreamContext *inContext;
188 JNIEnv *env;
190 inContext = (InputStreamContext *) context;
191 env = inContext->env;
192 (*env)->CallVoidMethod (env, inContext->inputStream,
193 inContext->inputStreamCloseFunc);
195 return (*env)->ExceptionOccurred (env) ? -1 : 0;
198 InputStreamContext *
199 xmljNewInputStreamContext (JNIEnv * env, jobject inputStream)
201 jclass inputStreamClass;
202 InputStreamContext *result;
204 inputStreamClass = (*env)->FindClass (env, "java/io/InputStream");
205 if (inputStreamClass == NULL)
207 return NULL;
209 result = (InputStreamContext *) malloc (sizeof (InputStreamContext));
210 if (result == NULL)
212 return NULL;
215 result->env = env;
216 result->inputStream = inputStream;
217 result->inputStreamReadFunc =
218 (*env)->GetMethodID (env, inputStreamClass, "read", "([BII)I");
219 result->inputStreamCloseFunc =
220 (*env)->GetMethodID (env, inputStreamClass, "close", "()V");
221 result->bufferLength = 4096;
222 result->bufferByteArray = (*env)->NewByteArray (env, result->bufferLength);
223 return result;
226 void
227 xmljFreeInputStreamContext (InputStreamContext * inContext)
229 JNIEnv *env;
231 env = inContext->env;
232 (*env)->DeleteLocalRef (env, inContext->bufferByteArray);
233 free (inContext);
236 OutputStreamContext *
237 xmljNewOutputStreamContext (JNIEnv * env, jobject outputStream)
239 jclass outputStreamClass;
240 OutputStreamContext *result;
242 outputStreamClass = (*env)->FindClass (env, "java/io/OutputStream");
243 if (outputStreamClass == NULL)
245 return NULL;
247 result = (OutputStreamContext *) malloc (sizeof (OutputStreamContext));
248 if (result == NULL)
250 return NULL;
253 result->env = env;
254 result->outputStream = outputStream;
255 result->outputStreamWriteFunc =
256 (*env)->GetMethodID (env, outputStreamClass, "write", "([B)V");
257 result->outputStreamCloseFunc =
258 (*env)->GetMethodID (env, outputStreamClass, "close", "()V");
259 return result;
263 void
264 xmljFreeOutputStreamContext (OutputStreamContext * outContext)
266 free (outContext);
269 SAXParseContext *
270 xmljNewSAXParseContext (JNIEnv * env, jobject obj, xmlParserCtxtPtr ctx,
271 jstring publicId, jstring systemId)
273 SAXParseContext *ret;
275 ret = (SAXParseContext *) malloc (sizeof (SAXParseContext));
276 ret->env = env;
277 ret->obj = obj;
278 ret->ctx = ctx;
279 ret->sax = ctx->sax;
280 ret->loc = NULL;
281 ret->publicId = publicId;
282 ret->systemId = systemId;
284 ret->startDTD = NULL;
285 ret->externalEntityDecl = NULL;
286 ret->internalEntityDecl = NULL;
287 ret->resolveEntity = NULL;
288 ret->notationDecl = NULL;
289 ret->attributeDecl = NULL;
290 ret->elementDecl = NULL;
291 ret->unparsedEntityDecl = NULL;
292 ret->setDocumentLocator = NULL;
293 ret->startDocument = NULL;
294 ret->endDocument = NULL;
295 ret->startElement = NULL;
296 ret->endElement = NULL;
297 ret->characters = NULL;
298 ret->ignorableWhitespace = NULL;
299 ret->processingInstruction = NULL;
300 ret->comment = NULL;
301 ret->cdataBlock = NULL;
302 ret->warning = NULL;
303 ret->error = NULL;
304 ret->fatalError = NULL;
306 ret->resolveURIAndOpen = NULL;
307 ret->stringClass = NULL;
308 return ret;
311 void
312 xmljFreeSAXParseContext (SAXParseContext * saxCtx)
314 free (saxCtx);
317 xmlCharEncoding
318 xmljDetectCharEncoding (JNIEnv * env, jbyteArray buffer)
320 xmlCharEncoding ret;
321 jint nread;
323 if (buffer == NULL)
325 return XML_CHAR_ENCODING_ERROR;
327 nread = (*env)->GetArrayLength (env, buffer);
328 if (nread >= 5)
330 jbyte nativeBuffer[DETECT_BUFFER_SIZE + 1];
331 unsigned char converted[DETECT_BUFFER_SIZE + 1];
332 int i;
334 memset (nativeBuffer, 0, DETECT_BUFFER_SIZE + 1);
335 (*env)->GetByteArrayRegion (env, buffer, 0, nread, nativeBuffer);
336 /* Convert from signed to unsigned */
337 for (i = 0; i < DETECT_BUFFER_SIZE + 1; i++)
339 converted[i] = UNSIGN (nativeBuffer[i]);
341 ret = xmlDetectCharEncoding (converted, nread);
343 else
345 ret = XML_CHAR_ENCODING_NONE;
347 return ret;
350 xmlParserCtxtPtr
351 xmljNewParserContext (JNIEnv * env,
352 jobject inputStream,
353 jbyteArray detectBuffer,
354 jstring publicId,
355 jstring systemId,
356 jstring base,
357 jboolean validate,
358 jboolean coalesce,
359 jboolean expandEntities,
360 jboolean loadEntities)
362 InputStreamContext *inputContext;
363 xmlCharEncoding encoding;
364 xmlParserCtxtPtr ctx;
365 int options;
367 encoding = xmljDetectCharEncoding (env, detectBuffer);
368 if (encoding != XML_CHAR_ENCODING_ERROR)
370 inputContext = xmljNewInputStreamContext (env, inputStream);
371 if (NULL != inputContext)
373 /* NOTE: userdata must be NULL for DOM to work */
374 ctx = xmlCreateIOParserCtxt (NULL,
375 NULL,
376 xmljInputReadCallback,
377 xmljInputCloseCallback,
378 inputContext,
379 encoding);
380 if (NULL != ctx)
382 ctx->userData = ctx;
384 /* Set parsing options */
385 options = 0;
386 if (validate)
388 options |= XML_PARSE_DTDVALID;
390 if (coalesce)
392 options |= XML_PARSE_NOCDATA;
394 if (expandEntities)
396 options |= XML_PARSE_NOENT;
398 if (loadEntities)
400 options |= XML_PARSE_DTDLOAD;
402 if (xmlCtxtUseOptions (ctx, options))
404 xmljThrowException (env,
405 "java/lang/RuntimeException",
406 "Unable to set xmlParserCtxtPtr options");
408 if (base != NULL)
410 ctx->input->directory =
411 (*env)->GetStringUTFChars (env, base, 0);
413 return ctx;
415 xmljFreeInputStreamContext (inputContext);
418 return NULL;
421 void
422 xmljFreeParserContext (xmlParserCtxtPtr ctx)
424 InputStreamContext *inputStreamContext = NULL;
426 if (ctx->input != NULL && ctx->input->buf != NULL)
428 inputStreamContext
429 = (InputStreamContext *) ctx->input->buf->context;
432 xmlFreeParserCtxt (ctx);
433 if (inputStreamContext != NULL)
435 xmljFreeInputStreamContext (inputStreamContext);
439 xmlDocPtr
440 xmljParseDocument (JNIEnv * env,
441 jobject self,
442 jobject in,
443 jbyteArray detectBuffer,
444 jstring publicId,
445 jstring systemId,
446 jstring base,
447 jboolean validate,
448 jboolean coalesce,
449 jboolean expandEntities,
450 jboolean contentHandler,
451 jboolean dtdHandler,
452 jboolean entityResolver,
453 jboolean errorHandler,
454 jboolean declarationHandler,
455 jboolean lexicalHandler,
456 int mode)
458 xmlParserCtxtPtr ctx;
459 SAXParseContext *saxCtx;
460 xmlSAXHandlerPtr sax;
462 ctx = xmljNewParserContext (env, in, detectBuffer, publicId, systemId, base,
463 validate, coalesce, expandEntities,
464 entityResolver);
465 if (ctx != NULL)
467 saxCtx = xmljNewSAXParseContext (env, self, ctx, publicId, systemId);
468 if (saxCtx != NULL)
470 sax = xmljNewSAXHandler (contentHandler,
471 dtdHandler,
472 entityResolver,
473 errorHandler,
474 declarationHandler,
475 lexicalHandler);
476 if (sax != NULL)
478 return xmljParseDocument2 (env,
479 ctx,
480 saxCtx,
481 sax,
482 mode);
484 xmljFreeSAXParseContext (saxCtx);
486 xmljFreeParserContext (ctx);
488 if (!(*env)->ExceptionOccurred (env))
490 xmljThrowException (env, "java/io/IOException",
491 "Unable to create parser context");
493 return NULL;
496 xmlDocPtr
497 xmljParseDocument2 (JNIEnv * env,
498 xmlParserCtxtPtr ctx,
499 SAXParseContext *saxCtx,
500 xmlSAXHandlerPtr sax,
501 int mode)
503 xmlSAXHandlerPtr orig;
504 xmlDocPtr doc;
505 int ret;
507 ctx->_private = saxCtx;
508 ctx->userData = ctx;
509 orig = ctx->sax;
510 ctx->sax = sax;
512 xmljSetThreadContext (saxCtx);
514 ret = xmlParseDocument (ctx);
515 doc = ctx->myDoc;
516 if (ret || !doc)
518 const char *msg = ctx->lastError.message;
519 switch (mode)
521 case 0:
522 xmljSAXFatalError (ctx, msg);
523 break;
524 case 1:
525 xmljThrowDOMException (env, ret, msg);
526 break;
527 case 2:
528 xmljThrowException (env,
529 "javax/xml/transform/TransformerException",
530 msg);
534 xmljClearThreadContext ();
536 ctx->sax = orig;
537 free(sax);
538 xmljFreeSAXParseContext (saxCtx);
539 xmljFreeParserContext (ctx);
540 xmljClearStringCache ();
541 return doc;
544 xmlParserInputPtr
545 xmljNewParserInput (JNIEnv * env,
546 jobject inputStream,
547 jbyteArray detectBuffer,
548 xmlParserCtxtPtr parserContext)
550 xmlParserInputPtr ret;
551 xmlParserInputBufferPtr input;
552 xmlCharEncoding encoding;
554 encoding = xmljDetectCharEncoding (env, detectBuffer);
555 if (encoding != XML_CHAR_ENCODING_ERROR)
557 input = xmljNewParserInputBuffer (env, inputStream, encoding);
558 if (input != NULL)
560 ret = xmlNewIOInputStream (parserContext, input, encoding);
561 return ret;
563 xmlFreeParserInputBuffer (input);
565 return NULL;
568 xmlParserInputBufferPtr
569 xmljNewParserInputBuffer (JNIEnv * env,
570 jobject inputStream, xmlCharEncoding encoding)
572 xmlParserInputBufferPtr ret;
573 InputStreamContext *inputContext;
575 inputContext = xmljNewInputStreamContext (env, inputStream);
576 if (NULL != inputContext)
578 ret = xmlParserInputBufferCreateIO (&xmljInputReadCallback,
579 &xmljInputCloseCallback,
580 inputContext, encoding);
581 if (ret != NULL)
582 return ret;
583 xmljFreeInputStreamContext (inputContext);
585 return NULL;
588 void
589 xmljSaveFileToJavaOutputStream (JNIEnv * env, jobject outputStream,
590 xmlDocPtr tree,
591 const char *outputEncodingName)
593 OutputStreamContext *outputContext =
594 xmljNewOutputStreamContext (env, outputStream);
596 xmlCharEncoding outputEncoding = xmlParseCharEncoding (outputEncodingName);
598 xmlOutputBufferPtr outputBuffer =
599 xmlOutputBufferCreateIO (xmljOutputWriteCallback,
600 xmljOutputCloseCallback,
601 outputContext,
602 xmlGetCharEncodingHandler (outputEncoding));
604 /* Write result to output stream */
606 xmlSaveFileTo (outputBuffer, tree, outputEncodingName);
608 xmljFreeOutputStreamContext (outputContext);
612 jobject
613 xmljResolveURI (SaxErrorContext * saxErrorContext,
614 const char *URL, const char *ID)
616 JNIEnv *env = saxErrorContext->env;
618 jstring hrefString = (*env)->NewStringUTF (env, URL);
619 jstring baseString = saxErrorContext->systemId;
621 jobject sourceWrapper = (*env)->CallObjectMethod (env,
622 saxErrorContext->
623 saxErrorAdapter,
624 saxErrorContext->
625 resolveURIMethodID,
626 hrefString,
627 baseString);
628 (*env)->DeleteLocalRef (env, hrefString);
630 if (NULL == sourceWrapper)
632 return NULL;
634 else
636 jobject sourceInputStream = (*env)->CallObjectMethod (env,
637 sourceWrapper,
638 saxErrorContext->
639 getInputStreamMethodID);
641 (*env)->DeleteLocalRef (env, sourceWrapper);
643 if ((*env)->ExceptionOccurred (env))
645 -* Report to ErrorAdapter here? *-
646 return NULL;
649 return sourceInputStream;
653 xmlDocPtr
654 xmljResolveURIAndOpen (SAXParseContext *saxContext,
655 const char *URL,
656 const char *ID)
658 jobject libxmlDocument;
659 xmlDocPtr doc;
660 JNIEnv *env = saxContext->env;
662 jstring hrefString = (*env)->NewStringUTF (env, URL);
663 jstring baseString = saxContext->systemId;
665 if (saxContext->resolveURIAndOpen == NULL)
667 jclass cls = (*env)->GetObjectClass (env, saxContext->obj);
668 saxContext->resolveURIAndOpen =
669 (*env)->GetMethodID (env, cls, "resolveURIAndOpen",
670 "Ljava/lang/String;Ljava/lang/String)Lgnu/xml/libxmlj/transform/LibxmlDocument;");
672 libxmlDocument =
673 (*env)->CallObjectMethod (env,
674 saxContext->obj,
675 saxContext->resolveURIAndOpen,
676 hrefString,
677 baseString);
679 doc = (xmlDocPtr) xmljGetNodeID (env, libxmlDocument);
681 (*env)->DeleteLocalRef (env, libxmlDocument);
683 if ((*env)->ExceptionOccurred (env))
685 /* Report to ErrorAdapter here? */
686 return NULL;
688 else
690 return doc;
694 /*xmlParserInputPtr
695 xmljLoadExternalEntity (const char *URL, const char *ID,
696 xmlParserCtxtPtr ctxt)
698 SaxErrorContext *saxErrorContext = xmljGetThreadContext ();
700 JNIEnv *env = saxErrorContext->env;
702 jstring hrefString = (*env)->NewStringUTF (env, URL);
703 jstring baseString = saxErrorContext->systemId;
705 jobject sourceWrapper = (*env)->CallObjectMethod (env,
706 saxErrorContext->
707 saxErrorAdapter,
708 saxErrorContext->
709 resolveURIMethodID,
710 hrefString,
711 baseString);
713 (*env)->DeleteLocalRef (env, hrefString);
715 if (NULL == sourceWrapper)
717 return NULL;
719 else
721 InputStreamContext *inputContext;
722 xmlParserInputBufferPtr inputBuffer;
723 xmlParserInputPtr inputStream;
725 jobject sourceInputStream = (*env)->CallObjectMethod (env,
726 sourceWrapper,
727 saxErrorContext->
728 getInputStreamMethodID);
730 (*env)->DeleteLocalRef (env, sourceWrapper);
732 if ((*env)->ExceptionOccurred (env))
734 -* Report to ErrorAdapter *-
735 return NULL;
738 inputContext = xmljNewInputStreamContext (env, sourceInputStream);
740 inputBuffer
741 = xmlParserInputBufferCreateIO (xmljInputReadCallback,
742 xmljInputCloseCallback,
743 inputContext, XML_CHAR_ENCODING_NONE);
745 inputStream = xmlNewInputStream (ctxt);
746 if (inputStream == NULL)
748 return (NULL);
751 inputStream->filename = NULL;
752 inputStream->directory = NULL;
753 inputStream->buf = inputBuffer;
755 inputStream->base = inputStream->buf->buffer->content;
756 inputStream->cur = inputStream->buf->buffer->content;
757 inputStream->end = &inputStream->base[inputStream->buf->buffer->use];
758 if ((ctxt->directory == NULL) && (inputStream->directory != NULL))
759 ctxt->directory =
760 (char *) xmlStrdup ((const xmlChar *) inputStream->directory);
761 return (inputStream);
765 /* Key for the thread-specific buffer */
766 static pthread_key_t thread_context_key;
768 /* Once-only initialisation of the key */
769 static pthread_once_t thread_context_once = PTHREAD_ONCE_INIT;
771 static void
772 thread_context_key_alloc (void);
774 /* Allocate the key */
775 static void
776 thread_context_key_alloc ()
778 pthread_key_create (&thread_context_key, NULL);
781 void
782 xmljSetThreadContext (SAXParseContext * context)
784 pthread_once (&thread_context_once, thread_context_key_alloc);
785 pthread_setspecific (thread_context_key, context);
788 void
789 xmljClearThreadContext (void)
791 pthread_setspecific (thread_context_key, NULL);
794 /* Return the thread-specific buffer */
795 SAXParseContext *
796 xmljGetThreadContext (void)
798 return (SAXParseContext *) pthread_getspecific (thread_context_key);