/* xmlj_transform.c -
   Copyright (C) 2003, 2004 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */

#include "gnu_xml_libxmlj_transform_GnomeTransformerFactory.h"
#include "gnu_xml_libxmlj_transform_GnomeTransformer.h"

#include "xmlj_dom.h"
#include "xmlj_io.h"
#include "xmlj_error.h"
#include "xmlj_node.h"
#include "xmlj_sax.h"
#include "xmlj_util.h"

#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include <libxml/xmlmemory.h>
#include <libxml/debugXML.h>
#include <libxml/xmlIO.h>
#include <libxml/xinclude.h>
#include <libxml/parser.h>
#include <libxml/catalog.h>
#include <libxslt/keys.h>
#include <libxslt/xslt.h>
#include <libxslt/xsltInternals.h>
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include <libxslt/functions.h>
#include <libxslt/extensions.h>
#include <libxslt/documents.h>

/* Local function prototypes */

void
xmljDocumentFunction (xmlXPathParserContextPtr ctxt, int nargs);

xsltStylesheetPtr
xmljGetStylesheetID (JNIEnv * env, jobject transformer);

jobject
xmljGetTransformerProperties (JNIEnv *env, jobject transformer);

const xmlChar *
xmljBooleanToString (int value);

void
xmljSetOutputProperties (JNIEnv *env, jobject transformer,
                         xsltStylesheetPtr stylesheet);

jobjectArray
xmljGetParameterArray (JNIEnv *env, jobject transformer);

const char **
xmljGetParameters (JNIEnv *env, jobjectArray pa);

void
xmljFreeParameters (JNIEnv *env, jobjectArray pa, const char **parameters);

xmlDocPtr
xmljTransform (JNIEnv *env, jobject transformer, xmlDocPtr source);

void
xmljTransformToSAX (JNIEnv *env, jobject transformer, xmlDocPtr source,
                    jobject callback);

xmlDocPtr
xmljDocLoader (const xmlChar *uri, xmlDictPtr dict, int options,
               void *ctxt, xsltLoadType type);

/* HACK: store stylesheet URL as context for resolving URIs in xmljDocLoader */
static jstring stylesheetURL = NULL;

/*
 * --------------------------------------------------------------------------
 * 
 * Native implementation for class
 * gnu.xml.libxmlj.transform.GnomeTransformer follows.
 */

static void
xmljSetProperty (JNIEnv * env, jobject outputProperties,
		 jmethodID setPropertyMethodID, const char *name,
		 const xmlChar * value)
{
  if (NULL != value)
    {
      jstring nameString = (*env)->NewStringUTF (env, name);
      jstring valueString = (*env)->NewStringUTF (env, (const char *) value);

      jobject prevValue = (*env)->CallObjectMethod (env, outputProperties,
						    setPropertyMethodID,
						    nameString, valueString);
      if (NULL != prevValue)
	{
	  (*env)->DeleteLocalRef (env, prevValue);
	}

      (*env)->DeleteLocalRef (env, nameString);
      (*env)->DeleteLocalRef (env, valueString);
    }
}

typedef struct CdataSectionScannerInfo_
{
  JNIEnv *env;
  jobject stringBuffer;
  jmethodID appendMethodID;
  int isFirst;
} CdataSectionScannerInfo;

static void
cdataSectionScanner (void *payload, void *data, xmlChar * name)
{
  CdataSectionScannerInfo *info = (CdataSectionScannerInfo *) data;
  JNIEnv *env = info->env;
  jstring nameString = (*env)->NewStringUTF (env, (const char *) name);
  jstring blankString = (*env)->NewStringUTF (env, " ");
  jobject stringBuffer;
  if (!info->isFirst)
    {
      stringBuffer
	= (*env)->CallObjectMethod (env,
				    info->stringBuffer,
				    info->appendMethodID, blankString);
      (*env)->DeleteLocalRef (env, stringBuffer);
    }
  info->isFirst = 0;
  stringBuffer
    = (*env)->CallObjectMethod (env,
				info->stringBuffer,
				info->appendMethodID, nameString);
  (*env)->DeleteLocalRef (env, stringBuffer);
  (*env)->DeleteLocalRef (env, blankString);
  (*env)->DeleteLocalRef (env, nameString);
}

void
xmljDocumentFunction (xmlXPathParserContextPtr ctxt, int nargs)
{
  xmlXPathObjectPtr obj, obj2 = NULL;

  if ((nargs < 1) || (nargs > 2))
    {
      xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL, NULL,
			  "document() : invalid number of args %d\n", nargs);
      ctxt->error = XPATH_INVALID_ARITY;
      return;
    }
  if (ctxt->value == NULL)
    {
      xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL, NULL,
			  "document() : invalid arg value\n");
      ctxt->error = XPATH_INVALID_TYPE;
      return;
    }

  if (nargs == 2)
    {
      if (ctxt->value->type != XPATH_NODESET)
	{
	  xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL, NULL,
			      "document() : invalid arg expecting a nodeset\n");
	  ctxt->error = XPATH_INVALID_TYPE;
	  return;
	}

      obj2 = valuePop (ctxt);
    }

  if (ctxt->value->type == XPATH_NODESET)
    {
      int i;
      xmlXPathObjectPtr newobj, ret;

      obj = valuePop (ctxt);
      ret = xmlXPathNewNodeSet (NULL);

      if (obj->nodesetval)
	{
	  for (i = 0; i < obj->nodesetval->nodeNr; i++)
	    {
	      valuePush (ctxt,
			 xmlXPathNewNodeSet (obj->nodesetval->nodeTab[i]));
	      xmlXPathStringFunction (ctxt, 1);
	      if (nargs == 2)
		{
		  valuePush (ctxt, xmlXPathObjectCopy (obj2));
		}
	      else
		{
		  valuePush (ctxt,
			     xmlXPathNewNodeSet (obj->nodesetval->
						 nodeTab[i]));
		}
	      xsltDocumentFunction (ctxt, 2);
	      newobj = valuePop (ctxt);
	      ret->nodesetval = xmlXPathNodeSetMerge (ret->nodesetval,
						      newobj->nodesetval);
	      xmlXPathFreeObject (newobj);
	    }
	}

      xmlXPathFreeObject (obj);
      if (obj2 != NULL)
        {
          xmlXPathFreeObject (obj2);
        }
      valuePush (ctxt, ret);
      return;
    }
  /*
   * Make sure it's converted to a string
   */
  xmlXPathStringFunction (ctxt, 1);
  if (ctxt->value->type != XPATH_STRING)
    {
      xsltTransformError (xsltXPathGetTransformContext (ctxt), NULL, NULL,
			  "document() : invalid arg expecting a string\n");
      ctxt->error = XPATH_INVALID_TYPE;
      if (obj2 != NULL)
	xmlXPathFreeObject (obj2);
      return;
    }
  obj = valuePop (ctxt);
  if (obj->stringval == NULL)
    {
      valuePush (ctxt, xmlXPathNewNodeSet (NULL));
    }
  else
    {

      xsltTransformContextPtr tctxt;

      tctxt = xsltXPathGetTransformContext (ctxt);

      {
        SAXParseContext *saxContext =
          (SAXParseContext *) tctxt->style->_private;

        xmlDocPtr tree = xmljResolveURIAndOpen (saxContext,
                                                (const char*)obj->stringval, 
                                                NULL);

        xsltNewDocument (tctxt, tree);	/* FIXME - free at a later point */

        valuePush (ctxt, xmlXPathNewNodeSet ((xmlNodePtr) tree));
      }
    }
  xmlXPathFreeObject (obj);
  if (obj2 != NULL) {
    xmlXPathFreeObject (obj2);
  }
}

/*
 * Returns the stylesheet pointer for the given GnomeTransformer.
 */
xsltStylesheetPtr
xmljGetStylesheetID (JNIEnv * env, jobject transformer)
{
  jclass cls;
  jfieldID field;
  jobject id;
  xsltStylesheetPtr stylesheet;

  if (transformer == NULL)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "Transformer is null");
      return NULL;
    }
  cls = (*env)->GetObjectClass (env, transformer);
  if (cls == NULL)
    {
      return NULL;
    }
  field = (*env)->GetFieldID (env, cls, "stylesheet", "Ljava/lang/Object;");
  if (field == NULL)
    {
      return NULL;
    }
  id = (*env)->GetObjectField (env, transformer, field);
  stylesheet = (xsltStylesheetPtr) xmljAsPointer (env, id);
  if (stylesheet == NULL)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "Stylesheet is null");
      return NULL;
    }
  return stylesheet;
}

jobject
xmljGetTransformerProperties (JNIEnv *env, jobject transformer)
{
  jclass cls;
  jfieldID field;
  
  cls = (*env)->GetObjectClass (env, transformer);
  if (cls == NULL)
    {
      return NULL;
    }
  field = (*env)->GetFieldID (env, cls, "outputProperties",
                              "Ljava/util/Properties;");
  if (field == NULL)
    {
      return NULL;
    }
  return (*env)->GetObjectField (env, transformer, field);
}

const xmlChar *
xmljBooleanToString (int value)
{
  return value ? BAD_CAST "yes" : BAD_CAST "no";
}

/*
 * Sets the output properties for the given transformer,
 * based on its stylesheet.
 */
void
xmljSetOutputProperties (JNIEnv *env, jobject transformer,
                         xsltStylesheetPtr stylesheet)
{
  jobject outputProperties;
  jclass propertiesClass;
  jmethodID setPropertyMethod;

  outputProperties = xmljGetTransformerProperties (env, transformer);
  if (outputProperties == NULL)
    {
      return;
    }
  propertiesClass = (*env)->FindClass (env, "java/util/Properties");
  if (propertiesClass == NULL)
    {
      return;
    }
  setPropertyMethod =
    (*env)->GetMethodID (env, propertiesClass, "setProperty",
                         "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
  if (setPropertyMethod == NULL)
    {
      return;
    }
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "encoding", stylesheet->encoding);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "media-type", stylesheet->mediaType);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "doctype-public", stylesheet->doctypePublic);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "doctype-system", stylesheet->doctypeSystem);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "indent", xmljBooleanToString (stylesheet->indent));
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "method", stylesheet->method);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "standalone", xmljBooleanToString (stylesheet->standalone));
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "version", stylesheet->version);
  
  xmljSetProperty (env, outputProperties, setPropertyMethod,
                   "omit-xml-declaration",
                   xmljBooleanToString (stylesheet->omitXmlDeclaration));
  
    {
      CdataSectionScannerInfo info;
      jclass stringBufferClass
        =
        (*env)->FindClass (env,
                           "java/lang/StringBuffer");
      jmethodID stringBufferConstructorID =
        (*env)->GetMethodID (env, stringBufferClass,
                             "<init>", "()V");
      jmethodID toStringMethodID =
        (*env)->GetMethodID (env, stringBufferClass,
                             "toString",
                             "()Ljava/lang/String;");
      info.env = env;
      info.isFirst = 1;
      info.stringBuffer
        = (*env)->AllocObject (env, stringBufferClass);
      (*env)->CallVoidMethod (env, info.stringBuffer,
                              stringBufferConstructorID);
      info.appendMethodID =
        (*env)->GetMethodID (env, stringBufferClass,
                             "append",
                             "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
      
      xmlHashScan (stylesheet->cdataSection,
                   cdataSectionScanner, &info);
      
        {
          jstring result = (jstring)
            (*env)->CallObjectMethod (env,
                                      info.stringBuffer,
                                      toStringMethodID);
          
          jstring nameString =
            (*env)->NewStringUTF (env,
                                  "cdata-section-elements");
          
          jobject prevValue
            =
            (*env)->CallObjectMethod (env,
                                      outputProperties,
                                      setPropertyMethod,
                                      nameString, result);
          if (NULL != prevValue)
            {
              (*env)->DeleteLocalRef (env, prevValue);
            }
          (*env)->DeleteLocalRef (env, nameString);
        }
      
      (*env)->DeleteLocalRef (env, info.stringBuffer);
    }
}

/*
 * Returns the parameter array for the given GnomeTransformer.
 */
jobjectArray
xmljGetParameterArray (JNIEnv *env, jobject transformer)
{
  jclass cls;
  jmethodID method;

  cls = (*env)->GetObjectClass (env, transformer);
  if (cls == NULL)
    {
      return NULL;
    }
  method = (*env)->GetMethodID (env, cls, "getParameterArray",
                                "()[Ljava/lang/String;");
  if (method == NULL)
    {
      return NULL;
    }
  return (jobjectArray) (*env)->CallObjectMethod (env, transformer, method);
}

/* Convert parameter array to xmlChar ** */
const char **
xmljGetParameters (JNIEnv *env, jobjectArray pa)
{
  int i, len;
  const char **parameters;

  len = (*env)->GetArrayLength (env, pa);
  parameters = (const char **) malloc ((len + 2) * sizeof (const char *));
  if (parameters == NULL)
    {
      return NULL;
    }
  
  for (i = 0; i < len; i++)
    {
      jstring string = (jstring) (*env)->GetObjectArrayElement (env, pa, i);
      
      if (string != NULL)
        {
          parameters[i] = (*env)->GetStringUTFChars (env, string, NULL);
        }
      else
        {
          parameters[i] = NULL;
        }
    }
  
  parameters[len] = 0;
  parameters[len + 1] = 0;
  return parameters;
}
  
/* Release parameter strings */
void
xmljFreeParameters (JNIEnv *env, jobjectArray pa, const char **parameters)
{ 
  int i, len;
  
  len = (*env)->GetArrayLength (env, pa);
  for (i = 0; i < len; i++)
    {
      jstring string = (jstring) (*env)->GetObjectArrayElement (env, pa, i);
      if (string != NULL)
        {
          (*env)->ReleaseStringUTFChars (env, string, parameters[i]);
        }
    }
  
  free (parameters);
}

xmlDocPtr
xmljTransform (JNIEnv *env, jobject transformer, xmlDocPtr source)
{
  xsltStylesheetPtr stylesheet;
  xmlDocPtr result;
  jobjectArray pa;
  const char **parameters;

  stylesheet = xmljGetStylesheetID (env, transformer);
  pa = xmljGetParameterArray (env, transformer);
  parameters = xmljGetParameters (env, pa);
  if (parameters == NULL)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "Couldn't allocate memory for parameters");
      return NULL;
    }
  result = xsltApplyStylesheet (stylesheet, source, parameters);
  xmljFreeParameters (env, pa, parameters);
  if (result == NULL)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "XSLT transformation failed");
    }
  return result;
}

void
xmljTransformToSAX (JNIEnv *env, jobject transformer, xmlDocPtr source,
                    jobject callback)
{
  xsltStylesheetPtr stylesheet;
  int ret;
  jobjectArray pa;
  const char **parameters;
  xmlSAXHandlerPtr sax;

  stylesheet = xmljGetStylesheetID (env, transformer);
  pa = xmljGetParameterArray (env, transformer);
  parameters = xmljGetParameters (env, pa);
  if (parameters == NULL)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "Couldn't allocate memory for parameters");
      return;
    }
  sax = NULL; /* TODO link up sax and callback */
  ret = xsltRunStylesheet (stylesheet, source, parameters, NULL, sax, NULL);
  xmljFreeParameters (env, pa, parameters);
  if (ret == -1)
    {
      xmljThrowException (env, "javax/xml/transform/TransformerException",
                          "XSLT transformation failed");
    }
}

xmlDocPtr
xmljDocLoader (const xmlChar *uri, xmlDictPtr dict, int options,
               void *ctxt, xsltLoadType type)
{
  JNIEnv *env;
  jclass xmljClass;
  jclass inputStreamClass;
  jmethodID getInputStream;
  jmethodID getDetectBuffer;
  jstring systemId;
  jobject inputStream;
  jbyteArray detectBuffer;

  fflush(stdout);
  env = xmljGetJNIEnv ();
  if (!env)
    {
      return NULL;
    }
  xmljClass = (*env)->FindClass (env, "gnu/xml/libxmlj/util/XMLJ");
  if (!xmljClass)
    {
      return NULL;
    }
  getInputStream =
    (*env)->GetStaticMethodID (env, xmljClass, "xmljGetInputStream",
                               "(Ljava/lang/String;Ljava/lang/String;)Lgnu/xml/libxmlj/util/NamedInputStream;");
  if (!getInputStream)
    {
      return NULL;
    }
  systemId = xmljNewString (env, uri);
  inputStream = (*env)->CallStaticObjectMethod (env, xmljClass, getInputStream,
                                                stylesheetURL, systemId);
  if (!inputStream)
    {
      return NULL;
    }
  inputStreamClass = (*env)->GetObjectClass (env, inputStream);
  if (!inputStreamClass)
    {
      return NULL;
    }
  getDetectBuffer = (*env)->GetMethodID (env, inputStreamClass,
                                         "getDetectBuffer", "()[B");
  if (!getDetectBuffer)
    {
      return NULL;
    }
  detectBuffer = (*env)->CallObjectMethod (env, inputStream, getDetectBuffer);
  if (!detectBuffer)
    {
      return NULL;
    }
  return xmljParseDocument (env, NULL, inputStream, detectBuffer,
                            NULL, systemId, stylesheetURL,
                            0, 0, 0, 0, 0, 0, 0, 0, 0, 2);
}

/* GnomeTransformer.newStylesheet */
JNIEXPORT jobject JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_newStylesheet (JNIEnv *env,
                                                               jobject self)
{
  xsltStylesheetPtr stylesheet;
  jobject ret;

  stylesheetURL = NULL;
  xsltSetLoaderFunc (xmljDocLoader);
  stylesheet = xsltNewStylesheet ();
  xmljSetOutputProperties (env, self, stylesheet);
  ret = xmljAsField (env, stylesheet);
  if (ret == NULL)
    {
      xmljThrowException (env,
                          "javax/xml/transform/TransformerConfigurationException",
                          "Can't create Java object for stylesheet");
    }
  return ret;
}

/* GnomeTransformer.newStylesheetFromStream */
JNIEXPORT jobject JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_newStylesheetFromStream
(JNIEnv *env, jobject self, jobject in, jbyteArray detectBuffer,
 jstring publicId, jstring systemId, jstring base,
 jboolean entityResolver, jboolean errorHandler)
{
  xmlDocPtr doc;
  xsltStylesheetPtr stylesheet;
  jobject ret;

  doc = xmljParseDocument (env, self, in, detectBuffer, publicId, systemId,
                           base, 0, 0, 0, 0, 0, 
                           entityResolver, errorHandler, 0, 0, 2);
  if (doc == NULL)
    {
      return NULL;
    }
  stylesheetURL = systemId;
  xsltSetLoaderFunc (xmljDocLoader);
  stylesheet = xsltParseStylesheetDoc (doc);
  if (stylesheet == NULL)
    {
      xmljThrowException (env,
                          "javax/xml/transform/TransformerConfigurationException",
                          "Error parsing XSLT stylesheet");
      return NULL;
    }
  xmljSetOutputProperties (env, self, stylesheet);
  ret =  xmljAsField (env, stylesheet);
  if (ret == NULL)
    {
      xmljThrowException (env,
                          "javax/xml/transform/TransformerConfigurationException",
                          "Can't create Java object for stylesheet");
    }
  return ret;
}

/* GnomeTransformer.newStylesheetFromDoc */
JNIEXPORT jobject JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_newStylesheetFromDoc
(JNIEnv *env, jobject self, jobject in)
{
  xmlDocPtr doc;
  xsltStylesheetPtr stylesheet;
  jobject ret;

  doc = (xmlDocPtr) xmljGetNodeID (env, in);
  if (doc == NULL)
    {
      return NULL;
    }
  stylesheetURL = xmljNewString (env, doc->URL);
  xsltSetLoaderFunc (xmljDocLoader);
  stylesheet = xsltParseStylesheetDoc (doc);
  if (stylesheet == NULL)
    {
      xmljThrowException (env,
                          "javax/xml/transform/TransformerConfigurationException",
                          "Error parsing XSLT stylesheet");
    }
  xmljSetOutputProperties (env, self, stylesheet);
  ret =  xmljAsField (env, stylesheet);
  if (ret == NULL)
    {
      xmljThrowException (env,
                          "javax/xml/transform/TransformerConfigurationException",
                          "Can't create Java object for stylesheet");
    }
  return ret;
}

/* GnomeTransformer.transformStreamToStream */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformStreamToStream
(JNIEnv *env, jobject self, jobject in, jbyteArray detectBuffer,
 jstring publicId, jstring systemId, jstring base,
 jboolean entityResolver, jboolean errorHandler, jobject out)
{
  xmlDocPtr source;
  xmlDocPtr result;

  source = xmljParseDocument (env, self, in, detectBuffer, publicId, systemId,
                              base, 0, 0, 0, 0, 0, 
                              entityResolver, errorHandler, 0, 0, 2);
  result = xmljTransform (env, self, source);
  xmljSaveFileToJavaOutputStream (env, out, result,
                                  (const char*) result->encoding);
  xmlFreeDoc (result);
}

/* GnomeTransformer.transformStreamToDoc */
JNIEXPORT jobject JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformStreamToDoc
(JNIEnv *env, jobject self, jobject in, jbyteArray detectBuffer,
 jstring publicId, jstring systemId, jstring base,
 jboolean entityResolver, jboolean errorHandler)
{
  xmlDocPtr source;
  xmlDocPtr result;

  source = xmljParseDocument (env, self, in, detectBuffer, publicId, systemId,
                              base, 0, 0, 0, 0, 0, 
                              entityResolver, errorHandler, 0, 0, 2);
  result = xmljTransform (env, self, source);
  return xmljGetNodeInstance (env, (xmlNodePtr) result);
}

/* GnomeTransformer.transformStreamToSAX */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformStreamToSAX
(JNIEnv *env, jobject self, jobject in, jbyteArray detectBuffer,
 jstring publicId, jstring systemId, jstring base,
 jboolean entityResolver, jboolean errorHandler, jobject callback)
{
  xmlDocPtr source;

  source = xmljParseDocument (env, self, in, detectBuffer, publicId, systemId,
                              base, 0, 0, 0, 0, 0, 
                              entityResolver, errorHandler, 0, 0, 2);
  xmljTransformToSAX (env, self, source, callback);
}

/* GnomeTransformer.transformDocToStream */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformDocToStream
(JNIEnv *env, jobject self, jobject doc, jobject out)
{
  xmlDocPtr source;
  xmlDocPtr result;

  source = (xmlDocPtr) xmljGetNodeID (env, doc);
  result = xmljTransform (env, self, source);
  xmljSaveFileToJavaOutputStream (env, out, result,
                                  (const char*) result->encoding);
  xmlFreeDoc (result);
}

/* GnomeTransformer.transformDocToDoc */
JNIEXPORT jobject JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformDocToDoc
(JNIEnv *env, jobject self, jobject doc)
{
  xmlDocPtr source;
  xmlDocPtr result;

  source = (xmlDocPtr) xmljGetNodeID (env, doc);
  result = xmljTransform (env, self, source);
  return xmljGetNodeInstance (env, (xmlNodePtr) result);
}

/* GnomeTransformer.transformDocToSAX */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_transformDocToSAX
(JNIEnv *env, jobject self, jobject doc, jobject callback)
{
  xmlDocPtr source;

  source = (xmlDocPtr) xmljGetNodeID (env, doc);
  xmljTransformToSAX (env, self, source, callback);
}

/* GnomeTransformer.free */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformer_free (JNIEnv *env,
                                                      jobject self)
{
  xsltStylesheetPtr stylesheet;

  stylesheet = xmljGetStylesheetID (env, self);
  xsltFreeStylesheet (stylesheet);
}

/*
 * --------------------------------------------------------------------------
 * Native implementation for class
 * gnu.xml.libxmlj.transform.GnomeTransformerFactory follows.
 */

/* GnomeTransformerFactory.freeLibxsltGlobal */
JNIEXPORT void JNICALL
Java_gnu_xml_libxmlj_transform_GnomeTransformerFactory_freeLibxsltGlobal (
  JNIEnv *env __attribute__((__unused__)),
  jclass clazz __attribute__((__unused__)))
{
  xsltCleanupGlobals ();
  xmlCleanupParser ();
}

