/* -*- Mode: c; c-basic-offset: 2 -*-
 *
 * raptor_serialize.c - Serializers
 *
 * $Id: raptor_serialize.c,v 1.35 2005/06/07 22:34:32 cmdjb Exp $
 *
 * Copyright (C) 2004-2005, David Beckett http://purl.org/net/dajobe/
 * Institute for Learning and Research Technology http://www.ilrt.bristol.ac.uk/
 * University of Bristol, UK http://www.bristol.ac.uk/
 * 
 * This package is Free Software and part of Redland http://librdf.org/
 * 
 * It is licensed under the following three licenses as alternatives:
 *   1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
 *   2. GNU General Public License (GPL) V2 or any newer version
 *   3. Apache License, V2.0 or any newer version
 * 
 * You may not use this file except in compliance with at least one of
 * the above three licenses.
 * 
 * See LICENSE.html or LICENSE.txt at the top of this package for the
 * complete terms and further detail along with the license texts for
 * the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
 * 
 */


#ifdef HAVE_CONFIG_H
#include <raptor_config.h>
#else
#ifdef WIN32
#include <win32_raptor_config.h>
#endif
#endif


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

/* Raptor includes */
#include "raptor.h"
#include "raptor_internal.h"


/* prototypes for helper functions */
static raptor_serializer_factory* raptor_get_serializer_factory(const char *name);


/* statics */

/* list of serializer factories */
static raptor_serializer_factory* serializers=NULL;


/* helper functions */


/*
 * raptor_delete_serializer_factories - helper function to delete all the registered serializer factories
 */
void
raptor_delete_serializer_factories(void)
{
  raptor_serializer_factory *factory, *next;
  
  for(factory=serializers; factory; factory=next) {
    next=factory->next;

    if(factory->finish_factory)
      factory->finish_factory(factory);

    RAPTOR_FREE(raptor_serializer_factory, (void*)factory->name);
    RAPTOR_FREE(raptor_serializer_factory, (void*)factory->label);
    if(factory->alias)
      RAPTOR_FREE(raptor_serializer_factory, (void*)factory->alias);
    if(factory->mime_type)
      RAPTOR_FREE(raptor_serializer_factory, (void*)factory->mime_type);
    if(factory->uri_string)
      RAPTOR_FREE(raptor_serializer_factory, (void*)factory->uri_string);

    RAPTOR_FREE(raptor_serializer_factory, factory);
  }
  serializers=NULL;
}


/* class methods */

/*
 * raptor_serializer_register_factory - Register a syntax that can be generated by a serializer factory
 * @name: the short syntax name
 * @label: readable label for syntax
 * @mime_type: MIME type of the syntax generated by the serializer (or NULL)
 * @uri_string: URI string of the syntax (or NULL)
 * @factory: pointer to function to call to register the factory
 * 
 * INTERNAL
 *
 **/
void
raptor_serializer_register_factory(const char *name, const char *label,
                                   const char *mime_type,
                                   const char *alias,
                                   const unsigned char *uri_string,
                                   void (*factory) (raptor_serializer_factory*)) 
{
  raptor_serializer_factory *serializer, *h;
  char *name_copy, *label_copy, *mime_type_copy, *alias_copy;
  unsigned char *uri_string_copy;
  
#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1
  RAPTOR_DEBUG4("Received registration for syntax serializer %s '%s' with alias '%s'\n", 
                name, label, (alias ? alias : "none"));
  RAPTOR_DEBUG4(raptor_serializer_register_factory,
                "MIME type %s, URI %s\n", 
                (mime_type ? mime_type : "none"),
                (uri_string ? uri_string : "none"));
#endif
  
  serializer=(raptor_serializer_factory*)RAPTOR_CALLOC(raptor_serializer_factory, 1,
                                               sizeof(raptor_serializer_factory));
  if(!serializer)
    RAPTOR_FATAL1("Out of memory\n");

  for(h = serializers; h; h = h->next ) {
    if(!strcmp(h->name, name) ||
       (alias && !strcmp(h->name, alias))) {
      RAPTOR_FATAL2("serializer %s already registered\n", h->name);
    }
  }
  
  name_copy=(char*)RAPTOR_CALLOC(cstring, strlen(name)+1, 1);
  if(!name_copy) {
    RAPTOR_FREE(raptor_serializer, serializer);
    RAPTOR_FATAL1("Out of memory\n");
  }
  strcpy(name_copy, name);
  serializer->name=name_copy;
        
  label_copy=(char*)RAPTOR_CALLOC(cstring, strlen(label)+1, 1);
  if(!label_copy) {
    RAPTOR_FREE(raptor_serializer, serializer);
    RAPTOR_FATAL1("Out of memory\n");
  }
  strcpy(label_copy, label);
  serializer->label=label_copy;

  if(mime_type) {
    mime_type_copy=(char*)RAPTOR_CALLOC(cstring, strlen(mime_type)+1, 1);
    if(!mime_type_copy) {
      RAPTOR_FREE(raptor_serializer, serializer);
      RAPTOR_FATAL1("Out of memory\n");
    }
    strcpy(mime_type_copy, mime_type);
    serializer->mime_type=mime_type_copy;
  }

  if(uri_string) {
    uri_string_copy=(unsigned char*)RAPTOR_CALLOC(cstring, strlen((const char*)uri_string)+1, 1);
    if(!uri_string_copy) {
    RAPTOR_FREE(raptor_serializer, serializer);
    RAPTOR_FATAL1("Out of memory\n");
    }
    strcpy((char*)uri_string_copy, (const char*)uri_string);
    serializer->uri_string=uri_string_copy;
  }
        
  if(alias) {
    alias_copy=(char*)RAPTOR_CALLOC(cstring, strlen(alias)+1, 1);
    if(!alias_copy) {
      RAPTOR_FREE(raptor_serializer, serializer);
      RAPTOR_FATAL1("Out of memory\n");
    }
    strcpy(alias_copy, alias);
    serializer->alias=alias_copy;
  }

  /* Call the serializer registration function on the new object */
  (*factory)(serializer);
  
#if defined(RAPTOR_DEBUG) && RAPTOR_DEBUG > 1
  RAPTOR_DEBUG3("%s has context size %d\n", name, serializer->context_length);
#endif
  
  serializer->next = serializers;
  serializers = serializer;
}


/**
 * raptor_get_serializer_factory - Get a serializer factory by name
 * @name: the factory name or NULL for the default factory
 * 
 * Return value: the factory object or NULL if there is no such factory
 **/
static raptor_serializer_factory*
raptor_get_serializer_factory (const char *name) 
{
  raptor_serializer_factory *factory;

  /* return 1st serializer if no particular one wanted - why? */
  if(!name) {
    factory=serializers;
    if(!factory) {
      RAPTOR_DEBUG1("No (default) serializers registered\n");
      return NULL;
    }
  } else {
    for(factory=serializers; factory; factory=factory->next) {
      if(!strcmp(factory->name, name) ||
         (factory->alias && !strcmp(factory->alias, name)))
        break;
    }
    /* else FACTORY name not found */
    if(!factory) {
      RAPTOR_DEBUG2("No serializer with name %s found\n", name);
      return NULL;
    }
  }
        
  return factory;
}


/**
 * raptor_serializers_enumerate - Get information on syntax serializers
 * @counter: index into the list of syntaxes
 * @name: pointer to store the name of the syntax (or NULL)
 * @label: pointer to store syntax readable label (or NULL)
 * @mime_type: pointer to store syntax MIME Type (or NULL)
 * @uri_string: pointer to store syntax URI string (or NULL)
 * 
 * Return value: non 0 on failure of if counter is out of range
 **/
int
raptor_serializers_enumerate(const unsigned int counter,
                             const char **name, const char **label,
                             const char **mime_type,
                             const unsigned char **uri_string)
{
  unsigned int i;
  raptor_serializer_factory *factory=serializers;

  if(!factory || counter < 0)
    return 1;

  for(i=0; factory && i<=counter ; i++, factory=factory->next) {
    if(i == counter) {
      if(name)
        *name=factory->name;
      if(label)
        *label=factory->label;
      if(mime_type)
        *mime_type=factory->mime_type;
      if(uri_string)
        *uri_string=factory->uri_string;
      return 0;
    }
  }
        
  return 1;
}

/**
 * raptor_serializer_syntax_name_check -  Check name of a serializer
 * @name: the syntax name
 *
 * Return value: non 0 if name is a known syntax name
 */
int
raptor_serializer_syntax_name_check(const char *name) {
  return (raptor_get_serializer_factory(name) != NULL);
}


/**
 * raptor_new_serializer - Constructor - create a new raptor_serializer object
 * @name: the serializer name
 *
 * Return value: a new &raptor_serializer object or NULL on failure
 */
raptor_serializer*
raptor_new_serializer(const char *name) {
  raptor_serializer_factory* factory;
  raptor_serializer* rdf_serializer;

  factory=raptor_get_serializer_factory(name);
  if(!factory)
    return NULL;

  rdf_serializer=(raptor_serializer*)RAPTOR_CALLOC(raptor_serializer, 1,
                                           sizeof(raptor_serializer));
  if(!rdf_serializer)
    return NULL;
  
  rdf_serializer->context=(char*)RAPTOR_CALLOC(raptor_serializer_context, 1,
                                               factory->context_length);
  if(!rdf_serializer->context) {
    raptor_free_serializer(rdf_serializer);
    return NULL;
  }
  
  rdf_serializer->factory=factory;

  /* Default features */
  /* Emit relative URIs where possible */
  rdf_serializer->feature_relative_uris=1;

  if(factory->init(rdf_serializer, name)) {
    raptor_free_serializer(rdf_serializer);
    return NULL;
  }
  
  return rdf_serializer;
}


/**
 * raptor_serialize_start: Start serialization with given base URI
 * @rdf_serializer:  the &raptor_serializer
 * @uri: base URI or NULL if no base URI is required
 * @iostream: &raptor_iostream to write serialization to
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_start(raptor_serializer *rdf_serializer, raptor_uri *uri,
                       raptor_iostream *iostream) 
{
  if(rdf_serializer->base_uri)
    raptor_free_uri(rdf_serializer->base_uri);

  if(!iostream)
    return 1;
  
  if(uri)
    uri=raptor_uri_copy(uri);
  
  rdf_serializer->base_uri=uri;
  rdf_serializer->locator.uri=uri;
  rdf_serializer->locator.line=rdf_serializer->locator.column = 0;

  rdf_serializer->iostream=iostream;

  if(rdf_serializer->factory->serialize_start)
    return rdf_serializer->factory->serialize_start(rdf_serializer);
  return 0;
}


/**
 * raptor_serialize_start_to_filename: Start serializing to a filename
 * @rdf_serializer:  the &raptor_serializer
 * @filename:  filename to serialize to
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_start_to_filename(raptor_serializer *rdf_serializer,
                                   const char *filename)
{
  unsigned char *uri_string=raptor_uri_filename_to_uri_string(filename);
  if(!uri_string)
    return 1;

  if(rdf_serializer->base_uri)
    raptor_free_uri(rdf_serializer->base_uri);

  rdf_serializer->base_uri=raptor_new_uri(uri_string);
  rdf_serializer->locator.uri=rdf_serializer->base_uri;
  rdf_serializer->locator.line=rdf_serializer->locator.column = 0;

  RAPTOR_FREE(cstring, uri_string);

  rdf_serializer->iostream=raptor_new_iostream_to_filename(filename);
  if(!rdf_serializer->iostream)
    return 1;

  if(rdf_serializer->factory->serialize_start)
    return rdf_serializer->factory->serialize_start(rdf_serializer);
  return 0;
}



/**
 * raptor_serialize_start_to_string: Start serializing to a string
 * @rdf_serializer:  the &raptor_serializer
 * @uri: base URI or NULL if no base URI is required
 * @string_p: pointer to location to hold string
 * @length_p: pointer to location to hold length of string (or NULL)
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_start_to_string(raptor_serializer *rdf_serializer,
                                 raptor_uri *uri,
                                 void **string_p, size_t *length_p) 
{
  if(rdf_serializer->base_uri)
    raptor_free_uri(rdf_serializer->base_uri);

  if(uri)
    rdf_serializer->base_uri=raptor_uri_copy(uri);
  else
    rdf_serializer->base_uri=NULL;
  rdf_serializer->locator.uri=rdf_serializer->base_uri;
  rdf_serializer->locator.line=rdf_serializer->locator.column = 0;


  rdf_serializer->iostream=raptor_new_iostream_to_string(string_p, length_p, 
                                                         NULL);
  if(!rdf_serializer->iostream)
    return 1;

  if(rdf_serializer->factory->serialize_start)
    return rdf_serializer->factory->serialize_start(rdf_serializer);
  return 0;
}


/**
 * raptor_serialize_start_to_file_handle: Start serializing to a FILE*
 * @rdf_serializer:  the &raptor_serializer
 * @uri: base URI or NULL if no base URI is required
 * @fh:  FILE* to serialize to
 * 
 * NOTE: This does not fclose the handle when it is finished.
8
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_start_to_file_handle(raptor_serializer *rdf_serializer,
                                      raptor_uri *uri, FILE *fh) 
{
  if(rdf_serializer->base_uri)
    raptor_free_uri(rdf_serializer->base_uri);

  if(uri)
    rdf_serializer->base_uri=raptor_uri_copy(uri);
  else
    rdf_serializer->base_uri=NULL;
  rdf_serializer->locator.uri=rdf_serializer->base_uri;
  rdf_serializer->locator.line=rdf_serializer->locator.column = 0;

  rdf_serializer->iostream=raptor_new_iostream_to_file_handle(fh);
  if(!rdf_serializer->iostream)
    return 1;

  if(rdf_serializer->factory->serialize_start)
    return rdf_serializer->factory->serialize_start(rdf_serializer);
  return 0;
}


/**
 * raptor_serialize_set_namespace: Set a namespace URI/prefix mapping
 * @rdf_serializer: the &raptor_serializer
 * @uri: &raptor_uri of namespace
 * @prefix: prefix to use
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_set_namespace(raptor_serializer* rdf_serializer,
                               raptor_uri *uri, const unsigned char *prefix) 
{
  if(rdf_serializer->factory->declare_namespace)
    return rdf_serializer->factory->declare_namespace(rdf_serializer, 
                                                      uri, prefix);

  return 1;
}


/**
 * raptor_start_serialize: Serialize a statement
 * @rdf_serializer: the &raptor_serializer
 * @statement: &raptor_statement to serialize to a syntax
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_statement(raptor_serializer* rdf_serializer,
                           const raptor_statement *statement) {
  if(!rdf_serializer->iostream)
    return 1;
  return rdf_serializer->factory->serialize_statement(rdf_serializer, statement);
}


/**
 * raptor_serialize_end: End a serialization
 * @rdf_serializer:  the &raptor_serializer
 * 
 * Return value: non-0 on failure.
 **/
int
raptor_serialize_end(raptor_serializer *rdf_serializer) 
{
  int rc;
  
  if(!rdf_serializer->iostream)
    return 1;

  if(rdf_serializer->factory->serialize_end)
    rc=rdf_serializer->factory->serialize_end(rdf_serializer);
  else
    rc=0;

  if(rdf_serializer->iostream) {
    raptor_free_iostream(rdf_serializer->iostream);
    rdf_serializer->iostream=NULL;
  }
  return rc;
}



/**
 * raptor_free_serializer - Destructor - destroy a raptor_serializer object
 * @serializer: &raptor_serializer object
 * 
 **/
void
raptor_free_serializer(raptor_serializer* rdf_serializer) 
{
  if(rdf_serializer->factory)
    rdf_serializer->factory->terminate(rdf_serializer);

  if(rdf_serializer->context)
    RAPTOR_FREE(raptor_serializer_context, rdf_serializer->context);

  if(rdf_serializer->base_uri)
    raptor_free_uri(rdf_serializer->base_uri);

  if(rdf_serializer->feature_start_uri)
    raptor_free_uri(rdf_serializer->feature_start_uri);

  RAPTOR_FREE(raptor_serializer, rdf_serializer);
}


/**
 * raptor_serializer_get_iostream - Get the current serializer iostream
 * @serializer: &raptor_serializer object
 *
 * Return value: the serializer's current iostream or NULL if 
 **/
raptor_iostream*
raptor_serializer_get_iostream(raptor_serializer *serializer)
{
  return serializer->iostream;
}



/**
 * raptor_serializer_features_enumerate - Get list of serializer features
 * @counter: feature enumeration (0+)
 * @name: pointer to store feature short name (or NULL)
 * @uri: pointer to store feature URI (or NULL)
 * @label: pointer to feature label (or NULL)
 * 
 * If uri is not NULL, a pointer toa new raptor_uri is returned
 * that must be freed by the caller with raptor_free_uri().
 *
 * Return value: 0 on success, <0 on failure, >0 if feature is unknown
 **/
int
raptor_serializer_features_enumerate(const raptor_feature feature,
                                     const char **name, 
                                     raptor_uri **uri, const char **label)
{
  return raptor_features_enumerate_common(feature, name, uri, label, 2);
}


/**
 * raptor_set_serializer_feature - Set serializer features with integer values
 * @serializer: &raptor_serializer serializer object
 * @feature: feature to set from enumerated &raptor_feature values
 * @value: integer feature value (0 or larger)
 * 
 * The allowed features are available via raptor_features_enumerate().
 *
 * Return value: non 0 on failure or if the feature is unknown
 **/
int
raptor_serializer_set_feature(raptor_serializer *serializer, 
                              raptor_feature feature, int value)
{
  if(value < 0)
    return -1;
  
  switch(feature) {
    case RAPTOR_FEATURE_RELATIVE_URIS:
      serializer->feature_relative_uris=value;
      break;

    case RAPTOR_FEATURE_START_URI:
      return -1;
      break;

    /* parser features */
    case RAPTOR_FEATURE_SCANNING:
    case RAPTOR_FEATURE_ASSUME_IS_RDF:
    case RAPTOR_FEATURE_ALLOW_NON_NS_ATTRIBUTES:
    case RAPTOR_FEATURE_ALLOW_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_ALLOW_BAGID:
    case RAPTOR_FEATURE_ALLOW_RDF_TYPE_RDF_LIST:
    case RAPTOR_FEATURE_NORMALIZE_LANGUAGE:
    case RAPTOR_FEATURE_NON_NFC_FATAL:
    case RAPTOR_FEATURE_WARN_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_CHECK_RDF_ID:

    /* XML writer features */
    case RAPTOR_FEATURE_WRITER_AUTO_INDENT:
    case RAPTOR_FEATURE_WRITER_AUTO_EMPTY:
    case RAPTOR_FEATURE_WRITER_INDENT_WIDTH:

    default:
      return -1;
      break;
  }

  return 0;
}


/**
 * raptor_serializer_set_feature_string - Set serializer features with string values
 * @serializer: &raptor_serializer serializer object
 * @feature: feature to set from enumerated &raptor_feature values
 * @value: feature value
 * 
 * The allowed features are available via raptor_serializer_features_enumerate().
 * If the feature type is integer, the value is interpreted as an integer.
 *
 * Return value: non 0 on failure or if the feature is unknown
 **/
int
raptor_serializer_set_feature_string(raptor_serializer *serializer, 
                                     raptor_feature feature, 
                                     const unsigned char *value)
{
  int value_is_string=(raptor_feature_value_type(feature) == 1);
  if(!value_is_string)
    return raptor_serializer_set_feature(serializer, feature, 
                                         atoi((const char*)value));

  switch(feature) {
    case RAPTOR_FEATURE_START_URI:
      if(value)
        serializer->feature_start_uri=raptor_new_uri(value);
      else
        return -1;
      break;

    case RAPTOR_FEATURE_RELATIVE_URIS:
      /* actually handled above because value_is_string is false */
      return -1;
      break;

    /* parser features */
    case RAPTOR_FEATURE_SCANNING:
    case RAPTOR_FEATURE_ASSUME_IS_RDF:
    case RAPTOR_FEATURE_ALLOW_NON_NS_ATTRIBUTES:
    case RAPTOR_FEATURE_ALLOW_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_ALLOW_BAGID:
    case RAPTOR_FEATURE_ALLOW_RDF_TYPE_RDF_LIST:
    case RAPTOR_FEATURE_NORMALIZE_LANGUAGE:
    case RAPTOR_FEATURE_NON_NFC_FATAL:
    case RAPTOR_FEATURE_WARN_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_CHECK_RDF_ID:

    /* XML writer features */
    case RAPTOR_FEATURE_WRITER_AUTO_INDENT:
    case RAPTOR_FEATURE_WRITER_AUTO_EMPTY:
    case RAPTOR_FEATURE_WRITER_INDENT_WIDTH:

    default:
      return -1;
      break;
  }

  return 0;
}


/**
 * raptor_serializer_get_feature - Get various serializer features
 * @serializer: &raptor_serializer serializer object
 * @feature: feature to get value
 * 
 * The allowed features are available via raptor_features_enumerate().
 *
 * Note: no feature value is negative
 *
 * Return value: feature value or < 0 for an illegal feature
 **/
int
raptor_serializer_get_feature(raptor_serializer *serializer, 
                              raptor_feature feature)
{
  int result= -1;
  
  switch(feature) {
    case RAPTOR_FEATURE_RELATIVE_URIS:
      result=(serializer->feature_relative_uris != 0);
      break;

    case RAPTOR_FEATURE_START_URI:
      result= -1;
      break;

    /* parser features */
    case RAPTOR_FEATURE_SCANNING:
    case RAPTOR_FEATURE_ASSUME_IS_RDF:
    case RAPTOR_FEATURE_ALLOW_NON_NS_ATTRIBUTES:
    case RAPTOR_FEATURE_ALLOW_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_ALLOW_BAGID:
    case RAPTOR_FEATURE_ALLOW_RDF_TYPE_RDF_LIST:
    case RAPTOR_FEATURE_NORMALIZE_LANGUAGE:
    case RAPTOR_FEATURE_NON_NFC_FATAL:
    case RAPTOR_FEATURE_WARN_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_CHECK_RDF_ID:

    /* XML writer features */
    case RAPTOR_FEATURE_WRITER_AUTO_INDENT:
    case RAPTOR_FEATURE_WRITER_AUTO_EMPTY:
    case RAPTOR_FEATURE_WRITER_INDENT_WIDTH:

    default:
      break;
  }
  
  return result;
}


/**
 * raptor_serializer_get_feature_string - Get serializer features with string values
 * @serializer: &raptor_serializer serializer object
 * @feature: feature to get value
 * 
 * The allowed features are available via raptor_features_enumerate().
 *
 * Return value: feature value or NULL for an illegal feature or no value
 **/
const unsigned char *
raptor_serializer_get_feature_string(raptor_serializer *serializer, 
                                     raptor_feature feature)
{
  int value_is_string=(raptor_feature_value_type(feature) == 1);
  if(!value_is_string)
    return NULL;
  
  switch(feature) {
    case RAPTOR_FEATURE_START_URI:
      if(serializer->feature_start_uri)
        return raptor_uri_to_string(serializer->feature_start_uri);
      break;

    case RAPTOR_FEATURE_RELATIVE_URIS:
      /* actually handled above because value_is_string is false */
      return NULL;
      break;
      
    /* parser features */
    case RAPTOR_FEATURE_SCANNING:
    case RAPTOR_FEATURE_ASSUME_IS_RDF:
    case RAPTOR_FEATURE_ALLOW_NON_NS_ATTRIBUTES:
    case RAPTOR_FEATURE_ALLOW_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_ALLOW_BAGID:
    case RAPTOR_FEATURE_ALLOW_RDF_TYPE_RDF_LIST:
    case RAPTOR_FEATURE_NORMALIZE_LANGUAGE:
    case RAPTOR_FEATURE_NON_NFC_FATAL:
    case RAPTOR_FEATURE_WARN_OTHER_PARSETYPES:
    case RAPTOR_FEATURE_CHECK_RDF_ID:

    /* XML writer features */
    case RAPTOR_FEATURE_WRITER_AUTO_INDENT:
    case RAPTOR_FEATURE_WRITER_AUTO_EMPTY:
    case RAPTOR_FEATURE_WRITER_INDENT_WIDTH:

    default:
      return NULL;
      break;
  }

  return NULL;
}


/*
 * raptor_serializer_error - Error from a serializer - Internal
 */
void
raptor_serializer_error(raptor_serializer* serializer, const char *message, ...)
{
  va_list arguments;

  va_start(arguments, message);

  raptor_serializer_error_varargs(serializer, message, arguments);
  
  va_end(arguments);
}


/*
 * raptor_serializer_simple_error - Error from a serializer - Internal
 *
 * Matches the raptor_simple_message_handler API but same as
 * raptor_serializer_error 
 */
void
raptor_serializer_simple_error(void* serializer, const char *message, ...)
{
  va_list arguments;

  va_start(arguments, message);

  raptor_serializer_error_varargs((raptor_serializer*)serializer, message, arguments);
  
  va_end(arguments);
}


/*
 * raptor_serializer_error_varargs - Error from a serializer - Internal
 */
void
raptor_serializer_error_varargs(raptor_serializer* serializer, 
                                const char *message, 
                                va_list arguments)
{
  if(serializer->error_handler) {
    char *buffer=raptor_vsnprintf(message, arguments);
    size_t length;
    if(!buffer) {
      fprintf(stderr, "raptor_serializer_error_varargs: Out of memory\n");
      return;
    }
    length=strlen(buffer);
    if(buffer[length-1]=='\n')
      buffer[length-1]='\0';
    serializer->error_handler(serializer->error_user_data, 
                              &serializer->locator, buffer);
    RAPTOR_FREE(cstring, buffer);
    return;
  }

  raptor_print_locator(stderr, &serializer->locator);
  fprintf(stderr, " raptor error - ");
  vfprintf(stderr, message, arguments);
  fputc('\n', stderr);
}


/*
 * raptor_serializer_warning - Warning from a serializer - Internal
 */
void
raptor_serializer_warning(raptor_serializer* serializer, const char *message, ...)
{
  va_list arguments;

  va_start(arguments, message);

  raptor_serializer_warning_varargs(serializer, message, arguments);

  va_end(arguments);
}


/*
 * raptor_serializer_warning - Warning from a serializer - Internal
 */
void
raptor_serializer_warning_varargs(raptor_serializer* serializer, const char *message, 
                                  va_list arguments)
{

  if(serializer->warning_handler) {
    char *buffer=raptor_vsnprintf(message, arguments);
    size_t length;
    if(!buffer) {
      fprintf(stderr, "raptor_serializer_warning_varargs: Out of memory\n");
      return;
    }
    length=strlen(buffer);
    if(buffer[length-1]=='\n')
      buffer[length-1]='\0';
    serializer->warning_handler(serializer->warning_user_data,
                                &serializer->locator, buffer);
    RAPTOR_FREE(cstring, buffer);
    return;
  }

  raptor_print_locator(stderr, &serializer->locator);
  fprintf(stderr, " raptor warning - ");
  vfprintf(stderr, message, arguments);
  fputc('\n', stderr);
}


/**
 * raptor_serializer_set_error_handler - Set the serializer error handling function
 * @serializer: the serializer
 * @user_data: user data to pass to function
 * @handler: pointer to the function
 * 
 * The function will receive callbacks when the serializer fails.
 * 
 **/
void
raptor_serializer_set_error_handler(raptor_serializer* serializer, 
                                    void *user_data,
                                    raptor_message_handler handler)
{
  serializer->error_user_data=user_data;
  serializer->error_handler=handler;
}


/**
 * raptor_serializer_set_warning_handler - Set the serializer warning handling function
 * @serializer: the serializer
 * @user_data: user data to pass to function
 * @handler: pointer to the function
 * 
 * The function will receive callbacks when the serializer fails.
 * 
 **/
void
raptor_serializer_set_warning_handler(raptor_serializer* serializer, 
                                      void *user_data,
                                      raptor_message_handler handler)
{
  serializer->warning_user_data=user_data;
  serializer->warning_handler=handler;
}


/**
 * raptor_serializer_get_locator: Get the serializer raptor locator object
 * @rdf_serializer: raptor serializer
 * 
 * Return value: raptor locator
 **/
raptor_locator*
raptor_serializer_get_locator(raptor_serializer *rdf_serializer)
{
  return &rdf_serializer->locator;
}





/*
 * Raptor RDF/XML serializer object
 */
typedef struct {
  /* Namespace stack */
  raptor_namespace_stack *nstack;

  /* the rdf: namespace - this is destroyed when nstack above is deleted */
  raptor_namespace *rdf_nspace;

  /* the rdf:RDF element */
  raptor_xml_element* rdf_RDF_element;

  /* where the xml is being written */
  raptor_xml_writer *xml_writer;

  /* User declared namespaces */
  raptor_sequence *namespaces;

  /* URI of rdf:XMLLiteral */
  raptor_uri* rdf_xml_literal_uri;
} raptor_rdfxml_serializer_context;


/* local prototypes */

/* create a new serializer */
static int
raptor_rdfxml_serialize_init(raptor_serializer* serializer, const char *name)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;
  raptor_uri_handler *uri_handler;
  void *uri_context;
  
  raptor_uri_get_handler(&uri_handler, &uri_context);
  context->nstack=raptor_new_namespaces(uri_handler, uri_context,
                                        raptor_serializer_simple_error,
                                        serializer,
                                        1);
  context->rdf_nspace=raptor_new_namespace(context->nstack,
                                           (const unsigned char*)"rdf",
                                           (const unsigned char*)raptor_rdf_namespace_uri,
                                           0);

  context->namespaces=raptor_new_sequence(NULL, NULL);

  context->rdf_xml_literal_uri=raptor_new_uri(raptor_xml_literal_datatype_uri_string);
  
  return 0;
}
  

/* destroy a serializer */
static void
raptor_rdfxml_serialize_terminate(raptor_serializer* serializer)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;

  if(context->xml_writer)
    raptor_free_xml_writer(context->xml_writer);

  if(context->rdf_RDF_element)
    raptor_free_xml_element(context->rdf_RDF_element);

  if(context->rdf_nspace)
    raptor_free_namespace(context->rdf_nspace);

  if(context->rdf_xml_literal_uri)
    raptor_free_uri(context->rdf_xml_literal_uri);

  if(context->namespaces) {
    int i;
    
    for(i=0; i< raptor_sequence_size(context->namespaces); i++) {
      raptor_namespace* ns=(raptor_namespace*)raptor_sequence_get_at(context->namespaces, i);
      if(ns)
        raptor_free_namespace(ns);
    }
    raptor_free_sequence(context->namespaces);
  }

  if(context->nstack)
    raptor_free_namespaces(context->nstack);
}
  

/* add a namespace */
static int
raptor_rdfxml_serialize_declare_namespace(raptor_serializer* serializer, 
                                          raptor_uri *uri,
                                          const unsigned char *prefix)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;
  raptor_namespace *ns;
  
  ns=raptor_new_namespace_from_uri(context->nstack, prefix, uri, 0);
  if(!ns)
    return 1;
  
  raptor_sequence_push(context->namespaces, ns);
  return 0;
}


/* start a serialize */
static int
raptor_rdfxml_serialize_start(raptor_serializer* serializer)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;
  raptor_xml_writer* xml_writer;
  raptor_uri *base_uri=serializer->base_uri;
  int i;
  raptor_uri_handler *uri_handler;
  void *uri_context;
  raptor_xml_element *element;
  raptor_qname *qname;

  raptor_uri_get_handler(&uri_handler, &uri_context);

  xml_writer=raptor_new_xml_writer(context->nstack,
                                   uri_handler, uri_context,
                                   serializer->iostream,
                                   raptor_serializer_simple_error,
                                   serializer,
                                   1);
  context->xml_writer=xml_writer;
  
  qname=raptor_new_qname_from_namespace_local_name(context->rdf_nspace,
                                                   (const unsigned char*)"RDF",  NULL);
  if(base_uri)
    base_uri=raptor_uri_copy(base_uri);
  element=raptor_new_xml_element(qname, NULL, base_uri);
  context->rdf_RDF_element=element;
  for(i=0; i< raptor_sequence_size(context->namespaces); i++) {
    raptor_namespace* ns=(raptor_namespace*)raptor_sequence_get_at(context->namespaces, i);
    raptor_xml_element_declare_namespace(context->rdf_RDF_element, ns);
  }
  

  raptor_xml_writer_raw(xml_writer,
                        (const unsigned char*)"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");

  raptor_xml_writer_start_element(xml_writer, context->rdf_RDF_element);
  raptor_xml_writer_raw_counted(xml_writer, (const unsigned char*)"\n", 1);

  return 0;
}


/* serialize a statement */
static int
raptor_rdfxml_serialize_statement(raptor_serializer* serializer, 
                                  const raptor_statement *statement)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;
  raptor_xml_writer* xml_writer=context->xml_writer;
  unsigned char* uri_string=NULL; /* predicate URI */
  unsigned char ordinal_name[20];
  unsigned char* name=NULL;  /* where to split predicate name */
  unsigned char* subject_uri_string=NULL;
  unsigned char* object_uri_string=NULL;
  const unsigned char* nsprefix=(const unsigned char*)"ns0";
  int rc;
  size_t len;
  raptor_qname* rdf_Description_qname;
  raptor_xml_element* rdf_Description_element;
  raptor_uri* predicate_ns_uri=NULL;
  raptor_namespace* predicate_ns=NULL;
  int free_predicate_ns=0;
  raptor_qname* predicate_qname=NULL;
  raptor_xml_element* predicate_element=NULL;
  raptor_qname **attrs;
  int attrs_count=0;
  raptor_uri* base_uri=NULL;
  raptor_identifier_type object_type;
  
  if(statement->predicate_type == RAPTOR_IDENTIFIER_TYPE_ORDINAL) {
    predicate_ns=context->rdf_nspace;
    sprintf((char*)ordinal_name, "_%d", *((int*)statement->predicate));
    name=ordinal_name;
  } else {
    unsigned char *p;
    size_t uri_len;
    size_t name_len=1;
    unsigned char c;
    
    uri_string=raptor_uri_as_counted_string((raptor_uri*)statement->predicate,
                                            &uri_len);

    p= uri_string;
    name_len=uri_len;
    while(name_len >0) {
      if(raptor_xml_name_check(p, name_len, 10)) {
        name=p;
        break;
      }
      p++; name_len--;
    }
      
    if(!name || (name == uri_string)) {
      raptor_serializer_error(serializer, "Cannot split predicate URI %s into an XML qname - skipping statement", uri_string);
      return 1;
    }

    c=*name; *name='\0';
    predicate_ns_uri=raptor_new_uri(uri_string);
    *name=c;
    
    predicate_ns=raptor_namespaces_find_namespace_by_uri(context->nstack,
                                                         predicate_ns_uri);
    if(!predicate_ns) {
      predicate_ns=raptor_new_namespace_from_uri(context->nstack,
                                                 nsprefix,
                                                 predicate_ns_uri, 0);
      free_predicate_ns=1;
    }
    raptor_free_uri(predicate_ns_uri);
  }

  
  rdf_Description_qname=raptor_new_qname_from_namespace_local_name(context->rdf_nspace,
                                                                   (unsigned const char*)"Description",  NULL);
  if(serializer->base_uri)
    base_uri=raptor_uri_copy(serializer->base_uri);
  rdf_Description_element=raptor_new_xml_element(rdf_Description_qname, NULL,
                                                 base_uri);

  attrs=(raptor_qname **)RAPTOR_CALLOC(qnamearray, 3, sizeof(raptor_qname*));
  attrs_count=0;

  /* subject */
  rc=0;
  switch(statement->subject_type) {
    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"nodeID",  (unsigned char*)statement->subject);
      break;

    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      if(statement->subject_type == RAPTOR_IDENTIFIER_TYPE_ORDINAL) {
        subject_uri_string=(unsigned char*)RAPTOR_MALLOC(cstring, raptor_rdf_namespace_uri_len+13);
        sprintf((char*)subject_uri_string, "%s_%d", 
                raptor_rdf_namespace_uri, *((int*)statement->subject));
      } else {
        if(serializer->feature_relative_uris)
          subject_uri_string=raptor_uri_to_relative_uri_string(serializer->base_uri,
                                                              (raptor_uri*)statement->subject);
        else
          subject_uri_string=raptor_uri_as_string((raptor_uri*)statement->subject);
      }
      
      attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"about",  subject_uri_string);
      RAPTOR_FREE(cstring, subject_uri_string);
      
      break;
      
    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:

    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    default:
      raptor_serializer_error(serializer, "Do not know how to serialize node type %d\n", statement->subject_type);
  }

  if(attrs_count)
    raptor_xml_element_set_attributes(rdf_Description_element, attrs, attrs_count);

  raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"  ", 2);
  raptor_xml_writer_start_element(xml_writer, rdf_Description_element);
  raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"\n", 1);


  /* predicate */
  predicate_qname=raptor_new_qname_from_namespace_local_name(predicate_ns,
                                                             name,  NULL);
  if(serializer->base_uri)
    base_uri=raptor_uri_copy(serializer->base_uri);
  predicate_element=raptor_new_xml_element(predicate_qname, NULL, base_uri);


  /* object */
  attrs=(raptor_qname **)RAPTOR_CALLOC(qnamearray, 3, sizeof(raptor_qname*));
  attrs_count=0;

  object_type=statement->object_type;
  switch(object_type) {
    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
      if(statement->object_literal_datatype &&
         raptor_uri_equals(statement->object_literal_datatype, 
                           context->rdf_xml_literal_uri))
        object_type = RAPTOR_IDENTIFIER_TYPE_XML_LITERAL;
      
      /* FALLTHROUGH */
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:

      if(statement->object_literal_language)
        attrs[attrs_count++]=raptor_new_qname(context->nstack,
                                              (unsigned char*)"xml:lang",
                                              (unsigned char*)statement->object_literal_language,
                                              raptor_serializer_simple_error,
                                              serializer);

      len=strlen((const char*)statement->object);

      if(object_type == RAPTOR_IDENTIFIER_TYPE_XML_LITERAL) {
        attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"parseType", (const unsigned char*)"Literal");
        raptor_xml_element_set_attributes(predicate_element, attrs, attrs_count);

        raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"    ", 4);
        raptor_xml_writer_start_element(xml_writer, predicate_element);

        /* Print without escaping XML */
        if(len)
          raptor_xml_writer_raw_counted(xml_writer,
                                        (const unsigned char*)statement->object,
                                        len);
      } else {
        if(statement->object_literal_datatype)
          attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"datatype", (unsigned char*)raptor_uri_as_string((raptor_uri*)statement->object_literal_datatype));
        
        raptor_xml_element_set_attributes(predicate_element, attrs, attrs_count);

        raptor_xml_writer_cdata_counted(xml_writer, 
                                        (const unsigned char*)"    ", 4);
        raptor_xml_writer_start_element(xml_writer, predicate_element);

        if(len)
          raptor_xml_writer_cdata_counted(xml_writer,
                                          (const unsigned char*)statement->object, len);
      }

      raptor_xml_writer_end_element(xml_writer, predicate_element);
      raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"\n", 1);

      break;

    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"nodeID", (unsigned char*)statement->object);

      raptor_xml_element_set_attributes(predicate_element, attrs, attrs_count);

      raptor_xml_writer_cdata_counted(xml_writer,
                                      (const unsigned char*)"    ", 4);
      raptor_xml_writer_empty_element(xml_writer, predicate_element);
      raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"\n", 1);
      break;

    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:

      if(object_type == RAPTOR_IDENTIFIER_TYPE_ORDINAL) {
        object_uri_string=(unsigned char*)RAPTOR_MALLOC(cstring, raptor_rdf_namespace_uri_len+13);
        sprintf((char*)object_uri_string, "%s_%d",
                raptor_rdf_namespace_uri, *((int*)statement->object));
      } else {
        /* must be URI */
        if(serializer->feature_relative_uris)
          object_uri_string=raptor_uri_to_relative_uri_string(serializer->base_uri,
                                                              (raptor_uri*)statement->object);
        else
          object_uri_string=raptor_uri_as_string((raptor_uri*)statement->object);
      }
      
      attrs[attrs_count++]=raptor_new_qname_from_namespace_local_name(context->rdf_nspace, (const unsigned char*)"resource", object_uri_string);
      RAPTOR_FREE(cstring, object_uri_string);

      raptor_xml_element_set_attributes(predicate_element, attrs, 1);

      raptor_xml_writer_cdata_counted(xml_writer,
                                      (const unsigned char*)"    ", 4);
      raptor_xml_writer_empty_element(xml_writer, predicate_element);
      raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"\n", 1);
      break;

    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    default:
      raptor_serializer_error(serializer, "Do not know how to serialize node type %d\n", object_type);
  }

  raptor_free_xml_element(predicate_element);
  if(free_predicate_ns) {
    raptor_free_namespace(predicate_ns);
  }


  raptor_xml_writer_cdata_counted(xml_writer, 
                                  (const unsigned char*)"  ", 2);
  raptor_xml_writer_end_element(xml_writer, rdf_Description_element);
  raptor_xml_writer_cdata_counted(xml_writer, (const unsigned char*)"\n", 1);

  raptor_free_xml_element(rdf_Description_element);

  return 0;
}


/* end a serialize */
static int
raptor_rdfxml_serialize_end(raptor_serializer* serializer)
{
  raptor_rdfxml_serializer_context* context=(raptor_rdfxml_serializer_context*)serializer->context;
  raptor_xml_writer* xml_writer=context->xml_writer;

  raptor_xml_writer_end_element(xml_writer, context->rdf_RDF_element);
  raptor_xml_writer_raw_counted(xml_writer, (const unsigned char*)"\n", 1);

  raptor_free_xml_element(context->rdf_RDF_element);
  context->rdf_RDF_element=NULL;
  
  return 0;
}


/* finish the serializer factory */
static void
raptor_rdfxml_serialize_finish_factory(raptor_serializer_factory* factory)
{

}

static void
raptor_rdfxml_serializer_register_factory(raptor_serializer_factory *factory)
{
  factory->context_length     = sizeof(raptor_rdfxml_serializer_context);
  
  factory->init                = raptor_rdfxml_serialize_init;
  factory->terminate           = raptor_rdfxml_serialize_terminate;
  factory->declare_namespace   = raptor_rdfxml_serialize_declare_namespace;
  factory->serialize_start     = raptor_rdfxml_serialize_start;
  factory->serialize_statement = raptor_rdfxml_serialize_statement;
  factory->serialize_end       = raptor_rdfxml_serialize_end;
  factory->finish_factory      = raptor_rdfxml_serialize_finish_factory;
}



void
raptor_init_serializer_rdfxml(void) {
  raptor_serializer_register_factory("rdfxml", "RDF/XML", 
                                     "application/rdf+xml",
                                     NULL,
                                     (const unsigned char*)"http://www.w3.org/TR/rdf-syntax-grammar",
                                     &raptor_rdfxml_serializer_register_factory);
}


/*
 * Raptor N-Triples serializer object
 */
typedef struct {
  int dummy;
} raptor_ntriples_serializer_context;



/* create a new serializer */
static int
raptor_ntriples_serialize_init(raptor_serializer* serializer, const char *name)
{
  return 0;
}
  

/* destroy a serializer */
static void
raptor_ntriples_serialize_terminate(raptor_serializer* serializer)
{

}
  

/* add a namespace */
static int
raptor_ntriples_serialize_declare_namespace(raptor_serializer* serializer, 
                                            raptor_uri *uri,
                                            const unsigned char *prefix)
{
  /* NOP */
  return 0;
}


#if 0
/* start a serialize */
static int
raptor_ntriples_serialize_start(raptor_serializer* serializer)
{
  return 0;
}
#endif



/**
 * raptor_iostream_write_string_ntriples - Write an UTF-8 string using N-Triples escapes to an iostream
 * @iostr: &raptor_iostream to write to
 * @string: UTF-8 string to write
 * @len: length of UTF-8 string
 * @delim: Terminating delimiter character for string (such as " or >)
 * or \0 for no escaping.
 * 
 * Return value: non-0 on failure such as bad UTF-8 encoding.
 **/
int
raptor_iostream_write_string_ntriples(raptor_iostream *iostr,
                                      const unsigned char *string,
                                      size_t len,
                                      const char delim)
{
  unsigned char c;
  int unichar_len;
  unsigned long unichar;
  
  for(; (c=*string); string++, len--) {
    if((delim && c == delim && (delim=='\'' || delim == '"')) ||
       c == '\\') {
      raptor_iostream_write_byte(iostr, '\\');
      raptor_iostream_write_byte(iostr, c);
      continue;
    }
    if(delim && c == delim) {
      raptor_iostream_write_counted_string(iostr, "\\u", 2);
      raptor_iostream_format_hexadecimal(iostr, c, 4);
      continue;
    }
    
    /* Note: NTriples is ASCII */
    if(c == 0x09) {
      raptor_iostream_write_counted_string(iostr, "\\t", 2);
      continue;
    } else if(c == 0x0a) {
      raptor_iostream_write_counted_string(iostr, "\\n", 2);
      continue;
    } else if(c == 0x0d) {
      raptor_iostream_write_counted_string(iostr, "\\r", 2);
      continue;
    } else if(c < 0x20|| c == 0x7f) {
      raptor_iostream_write_counted_string(iostr, "\\u", 2);
      raptor_iostream_format_hexadecimal(iostr, c, 4);
      continue;
    } else if(c < 0x80) {
      raptor_iostream_write_byte(iostr, c);
      continue;
    }
    
    /* It is unicode */
    
    unichar_len=raptor_utf8_to_unicode_char(NULL, string, len);
    if(unichar_len < 0 || unichar_len > (int)len)
      /* UTF-8 encoding had an error or ended in the middle of a string */
      return 1;

    unichar_len=raptor_utf8_to_unicode_char(&unichar, string, len);
    
    if(unichar < 0x10000) {
      raptor_iostream_write_counted_string(iostr, "\\u", 2);
      raptor_iostream_format_hexadecimal(iostr, unichar, 4);
    } else {
      raptor_iostream_write_counted_string(iostr, "\\U", 2);
      raptor_iostream_format_hexadecimal(iostr, unichar, 8);
    }
    
    unichar_len--; /* since loop does len-- */
    string += unichar_len; len -= unichar_len;

  }

  return 0;
}


static void
raptor_iostream_write_statement_part_ntriples(raptor_iostream* iostr,
                                               const void *term, 
                                               raptor_identifier_type type,
                                               raptor_uri* literal_datatype,
                                               const unsigned char *literal_language) 
{
  size_t len;
  
  switch(type) {
    case RAPTOR_IDENTIFIER_TYPE_LITERAL:
    case RAPTOR_IDENTIFIER_TYPE_XML_LITERAL:
      raptor_iostream_write_byte(iostr, '"');
      raptor_iostream_write_string_ntriples(iostr, (const unsigned char*)term, strlen((const char*)term), '"');
      raptor_iostream_write_byte(iostr, '"');
      if(literal_language && type == RAPTOR_IDENTIFIER_TYPE_LITERAL) {
        raptor_iostream_write_byte(iostr, '@');
        raptor_iostream_write_string(iostr, literal_language);
      }
      if(type == RAPTOR_IDENTIFIER_TYPE_XML_LITERAL) {
        raptor_iostream_write_counted_string(iostr, "^^<", 3);
        raptor_iostream_write_string(iostr, raptor_xml_literal_datatype_uri_string);
        raptor_iostream_write_byte(iostr, '>');
      } else if(literal_datatype) {
        raptor_iostream_write_counted_string(iostr, "^^<", 3);
        raptor_iostream_write_string(iostr, raptor_uri_as_string((raptor_uri*)literal_datatype));
        raptor_iostream_write_byte(iostr, '>');
      }

      break;
      
    case RAPTOR_IDENTIFIER_TYPE_ANONYMOUS:
      raptor_iostream_write_counted_string(iostr, "_:", 2);
      raptor_iostream_write_string(iostr, term);
      break;
      
    case RAPTOR_IDENTIFIER_TYPE_ORDINAL:
      raptor_iostream_write_counted_string(iostr, "<_", 1);
      raptor_iostream_write_counted_string(iostr, raptor_rdf_namespace_uri,
                                           raptor_rdf_namespace_uri_len);
      raptor_iostream_write_counted_string(iostr, "_", 1);
      raptor_iostream_write_decimal(iostr, *((int*)term));
      raptor_iostream_write_byte(iostr, '>');
      break;
  
    case RAPTOR_IDENTIFIER_TYPE_RESOURCE:
    case RAPTOR_IDENTIFIER_TYPE_PREDICATE:
      raptor_iostream_write_byte(iostr, '<');
      term=raptor_uri_as_counted_string((raptor_uri*)term, &len);
      raptor_iostream_write_string_ntriples(iostr, (const unsigned char*)term, len, '>');
      raptor_iostream_write_byte(iostr, '>');
      break;
      
    case RAPTOR_IDENTIFIER_TYPE_UNKNOWN:
    default:
      RAPTOR_FATAL2("Unknown type %d", type);
  }
}


void
raptor_iostream_write_statement_ntriples(raptor_iostream* iostr,
                                         const raptor_statement *statement)
{
  raptor_iostream_write_statement_part_ntriples(iostr,
                                                statement->subject,
                                                statement->subject_type,
                                                NULL, NULL);
  raptor_iostream_write_byte(iostr, ' ');
  raptor_iostream_write_statement_part_ntriples(iostr,
                                                statement->predicate,
                                                statement->predicate_type,
                                                NULL, NULL);
  raptor_iostream_write_byte(iostr, ' ');
  raptor_iostream_write_statement_part_ntriples(iostr,
                                                statement->object,
                                                statement->object_type,
                                                statement->object_literal_datatype,
                                                statement->object_literal_language);
  raptor_iostream_write_counted_string(iostr, " .\n", 3);
}


/* serialize a statement */
static int
raptor_ntriples_serialize_statement(raptor_serializer* serializer, 
                                    const raptor_statement *statement)
{
  raptor_iostream_write_statement_ntriples(serializer->iostream, statement);
  return 0;
}


#if 0
/* end a serialize */
static int
raptor_ntriples_serialize_end(raptor_serializer* serializer)
{
  return 0;
}
#endif
  
/* finish the serializer factory */
static void
raptor_ntriples_serialize_finish_factory(raptor_serializer_factory* factory)
{

}


static void
raptor_ntriples_serializer_register_factory(raptor_serializer_factory *factory)
{
  factory->context_length     = sizeof(raptor_ntriples_serializer_context);
  
  factory->init                = raptor_ntriples_serialize_init;
  factory->terminate           = raptor_ntriples_serialize_terminate;
  factory->declare_namespace   = raptor_ntriples_serialize_declare_namespace;
  factory->serialize_start     = NULL;
  factory->serialize_statement = raptor_ntriples_serialize_statement;
  factory->serialize_end       = NULL;
  factory->finish_factory      = raptor_ntriples_serialize_finish_factory;
}


void
raptor_init_serializer_ntriples (void) {
  raptor_serializer_register_factory("ntriples",  "N-Triples", 
                                     "text/plain",
                                     NULL,
                                     (const unsigned char*)"http://www.w3.org/TR/rdf-testcases/#ntriples",
                                     &raptor_ntriples_serializer_register_factory);
}


/*
 * Raptor 'Simple' serializer object
 */
typedef struct {
  int dummy;
} raptor_simple_serializer_context;



/* create a new serializer */
static int
raptor_simple_serialize_init(raptor_serializer* serializer, const char *name)
{
  return 0;
}
  

/* destroy a serializer */
static void
raptor_simple_serialize_terminate(raptor_serializer* serializer)
{

}
  

/* serialize a statement */
static int
raptor_simple_serialize_statement(raptor_serializer* serializer, 
                                  const raptor_statement *statement)
{
  raptor_iostream *iostr=serializer->iostream;
  
  /* was: fprintf(stdout, "%s: Statement: ", program); */
  raptor_iostream_write_string(iostr, "Statement: ");

  /* from raptor_print_statement */
  raptor_iostream_write_byte(iostr, '[');

  if(statement->subject_type == RAPTOR_IDENTIFIER_TYPE_ANONYMOUS) {
    raptor_iostream_write_string(iostr, statement->subject);
  } else {
#ifdef RAPTOR_DEBUG
    if(!statement->subject)
      RAPTOR_FATAL1("Statement has NULL subject URI\n");
#endif
    raptor_iostream_write_string(iostr, raptor_uri_as_string((raptor_uri*)statement->subject));
  }

  raptor_iostream_write_counted_string(iostr, ", ", 2);

  if(statement->predicate_type == RAPTOR_IDENTIFIER_TYPE_ORDINAL) {
    raptor_iostream_write_counted_string(iostr, "[rdf:_", 6);
    raptor_iostream_write_decimal(iostr, *((int*)statement->predicate));
    raptor_iostream_write_byte(iostr, ']');
  } else {
#ifdef RAPTOR_DEBUG
    if(!statement->predicate)
      RAPTOR_FATAL1("Statement has NULL predicate URI\n");
#endif
    raptor_iostream_write_string(iostr, raptor_uri_as_string((raptor_uri*)statement->predicate));
  }

  raptor_iostream_write_counted_string(iostr, ", ", 2);

  if(statement->object_type == RAPTOR_IDENTIFIER_TYPE_LITERAL || 
     statement->object_type == RAPTOR_IDENTIFIER_TYPE_XML_LITERAL) {
    if(statement->object_type == RAPTOR_IDENTIFIER_TYPE_XML_LITERAL) {
      raptor_iostream_write_byte(iostr, '<');
      raptor_iostream_write_string(iostr, raptor_xml_literal_datatype_uri_string);
      raptor_iostream_write_byte(iostr, '>');
    } else if(statement->object_literal_datatype) {
      raptor_iostream_write_byte(iostr, '<');
      raptor_iostream_write_string(iostr, raptor_uri_as_string((raptor_uri*)statement->object_literal_datatype));
      raptor_iostream_write_byte(iostr, '>');
    }
    raptor_iostream_write_byte(iostr, '"');
    raptor_iostream_write_string(iostr, statement->object);
    raptor_iostream_write_byte(iostr, '"');
  } else if(statement->object_type == RAPTOR_IDENTIFIER_TYPE_ANONYMOUS)
    raptor_iostream_write_string(iostr, statement->object);
  else if(statement->object_type == RAPTOR_IDENTIFIER_TYPE_ORDINAL) {
    raptor_iostream_write_counted_string(iostr, "[rdf:_", 6);
    raptor_iostream_write_decimal(iostr, *((int*)statement->object));
    raptor_iostream_write_byte(iostr, ']');
  } else {
#ifdef RAPTOR_DEBUG
    if(!statement->object)
      RAPTOR_FATAL1("Statement has NULL object URI\n");
#endif
    raptor_iostream_write_string(iostr, raptor_uri_as_string((raptor_uri*)statement->object));
  }

  raptor_iostream_write_counted_string(iostr, "]\n", 2);

  return 0;
}


  
/* finish the serializer factory */
static void
raptor_simple_serialize_finish_factory(raptor_serializer_factory* factory)
{

}


static void
raptor_simple_serializer_register_factory(raptor_serializer_factory *factory)
{
  factory->context_length     = sizeof(raptor_simple_serializer_context);
  
  factory->init                = raptor_simple_serialize_init;
  factory->terminate           = raptor_simple_serialize_terminate;
  factory->declare_namespace   = NULL;
  factory->serialize_start     = NULL;
  factory->serialize_statement = raptor_simple_serialize_statement;
  factory->serialize_end       = NULL;
  factory->finish_factory      = raptor_simple_serialize_finish_factory;
}


void
raptor_init_serializer_simple (void) {
  raptor_serializer_register_factory("simple",  "A simple format", 
                                     NULL,
                                     NULL,
                                     NULL,
                                     &raptor_simple_serializer_register_factory);
}
