/* 
 * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Classpathx/jaxp.
 * 
 * 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;

  /*fprintf(stderr,"xmljDocLoader:1\n");*/
  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, jclass clazz)
{
  xsltCleanupGlobals ();
  xmlCleanupParser ();
}

