Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
acedbServer.c 126.76 KiB
/*  File: acedbServer.c
 *  Author: Ed Griffiths (edgrif@sanger.ac.uk)
 *  Copyright (c) 2006: Genome Research Ltd.
 *-------------------------------------------------------------------
 * ZMap 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
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
 *-------------------------------------------------------------------
 * This file is part of the ZMap genome database package
 * and was written by
 * 	Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk,
 *      Roy Storey (Sanger Institute, UK) rds@sanger.ac.uk
 *
 * Description: Implements the method functions for a zmap server
 *              by making the appropriate calls to the acedb server.
 *              
 * Exported functions: See zmapServer.h
 * HISTORY:
 * Last edited: May 22 10:03 2009 (rds)
 * Created: Wed Aug  6 15:46:38 2003 (edgrif)
 * CVS info:   $Id: acedbServer.c,v 1.136 2009-05-22 09:05:07 rds Exp $
 *-------------------------------------------------------------------
 */

#include <string.h>
#include <stdio.h>
#include <AceConn.h>
#include <glib.h>
#include <ZMap/zmapUtils.h>
#include <ZMap/zmapGLibUtils.h>
#include <ZMap/zmapConfigIni.h>
#include <ZMap/zmapConfigStrings.h>
#include <ZMap/zmapGFF.h>
#include <ZMap/zmapStyle.h>
#include <zmapServerPrototype.h>
#include <acedbServer_P.h>




typedef struct
{
  gboolean first_method ;
  gboolean find_string ;
  gboolean name_list ;
  GString *str ;
} ZMapTypesStringStruct, *ZMapTypesString ;


typedef struct
{
  GList *methods ;
  GHashTable *method_2_data ;
  GData *styles ;
} LoadableStruct, *Loadable ;


typedef struct
{
  AcedbServer server ;
  GList *feature_set_methods ;
  GList *feature_methods ;
  GList *required_styles ;
  GHashTable *set_2_styles ;
  gboolean error ;
} GetMethodsStylesStruct, *GetMethodsStyles ;


typedef struct
{
  GQuark feature_set_id ;
  char *remark ;
  GHashTable *method_2_feature_set ;
} HashFeatureSetStruct, *HashFeatureSet ;



typedef struct
{
  GQuark feature_set ;
} ZMapColGroupDataStruct, *ZMapColGroupData ;


typedef struct
{
  ZMapServerResponseType result ;
  AcedbServer server ;
  GData *styles ;
  GHFunc eachBlock ;
} DoAllAlignBlocksStruct, *DoAllAlignBlocks ;


typedef struct
{
  char *name ;
  char *spec ;
} AcedbColourSpecStruct, *AcedbColourSpec ;


typedef struct
{
  GHashTable *method_2_data ;
  GData *styles;
  GList *fetch_methods ;
} MethodFetchStruct, *MethodFetch ;


typedef struct
{
  char *draw ;
  char *fill ;
  char *border ;
} StyleColourStruct, *StyleColour ;

typedef struct
{
  StyleColourStruct normal ;
  StyleColourStruct selected ;
} StyleFeatureColoursStruct, *StyleFeatureColours ;


typedef struct
{
  GList *names_list ;

  GQuark feature_set ;

  GList *required_styles ;
} ColGroupNamesStruct, *ColGroupNames ;


typedef ZMapFeatureTypeStyle (*ParseMethodFunc)(char *method_str_in,
						char **end_pos, ZMapColGroupData *col_group_data) ;

typedef gboolean (*ParseMethodNamesFunc)(AcedbServer server, char *method_str_in,
					 char **end_pos, gpointer user_data) ;


/* These provide the interface functions for an acedb server implementation, i.e. you
 * shouldn't change these prototypes without changing all the other server prototypes..... */
static gboolean globalInit(void) ;
static gboolean createConnection(void **server_out,
				 ZMapURL url, char *format, 
                                 char *version_str, int timeout) ;
static ZMapServerResponseType openConnection(void *server) ;
static ZMapServerResponseType getInfo(void *server, char **database_path) ;
static ZMapServerResponseType getFeatureSetNames(void *server,
						 GList **feature_sets_out,
						 GList **required_styles,
						 GHashTable **featureset_2_stylelist_inout) ;
static ZMapServerResponseType getStyles(void *server, GData **styles_out) ;
static ZMapServerResponseType haveModes(void *server, gboolean *have_mode) ;
static ZMapServerResponseType getSequences(void *server_in, GList *sequences_inout) ;
static ZMapServerResponseType setContext(void *server, ZMapFeatureContext feature_context) ;
static ZMapServerResponseType getFeatures(void *server_in, GData *styles, ZMapFeatureContext feature_context_out) ;
static ZMapServerResponseType getContextSequence(void *server_in, GData *styles, ZMapFeatureContext feature_context_out) ;
static char *lastErrorMsg(void *server) ;
static ZMapServerResponseType closeConnection(void *server_in) ;
static ZMapServerResponseType destroyConnection(void *server) ;


/* general internal routines. */
static ZMapServerResponseType findColStyleTags(AcedbServer server,
					       GList **feature_set_methods_inout,
					       GList **feature_methods_out,
					       GList **required_styles_out,
					       GHashTable **featureset_2_stylelist_inout) ;
static GList *getMethodsLoadable(GList *all_methods, GHashTable *method_2_data, GData *styles) ;
static void loadableCB(gpointer data, gpointer user_data) ;
static char *getMethodString(GList *styles_or_style_names,
			     gboolean style_name_list, gboolean find_string, gboolean find_methods) ;
static void addTypeName(gpointer data, gpointer user_data) ;
static gboolean sequenceRequest(AcedbServer server, GData *styles, ZMapFeatureBlock feature_block) ;
static gboolean blockDNARequest(AcedbServer server, GData *styles, ZMapFeatureBlock feature_block) ;
static gboolean getDNARequest(AcedbServer server, char *sequence_name, int start, int end,
			      int *dna_length_out, char **dna_sequence_out) ;
static gboolean getSequenceMapping(AcedbServer server, ZMapFeatureContext feature_context) ;
static gboolean getSMapping(AcedbServer server, char *class,
			    char *sequence, int start, int end,
			    char **parent_class_out, char **parent_name_out,
			    ZMapMapBlock child_to_parent_out) ;
static gboolean getSMapLength(AcedbServer server, char *obj_class, char *obj_name,
			      int *obj_length_out) ;
static gboolean checkServerVersion(AcedbServer server) ;
static gboolean findSequence(AcedbServer server, char *sequence_name) ;
static gboolean setQuietMode(AcedbServer server) ;

static gboolean parseTypes(AcedbServer server, GData **styles_out,
			   ParseMethodNamesFunc parse_func_in, gpointer user_data) ;
static ZMapServerResponseType findMethods(AcedbServer server, char *search_str, int *num_found) ;
static ZMapServerResponseType getObjNames(AcedbServer server, GList **style_names_out) ;
static ZMapFeatureTypeStyle parseMethod(char *method_str_in,
					char **end_pos, ZMapColGroupData *col_group_data) ;
static gboolean parseMethodColGroupNames(AcedbServer server, char *method_str_in,
					 char **end_pos, gpointer user_data) ;
static void addMethodCB(gpointer data, gpointer user_data) ;
gint resortStyles(gconstpointer a, gconstpointer b, gpointer user_data) ;
int getFoundObj(char *text) ;
static void eachAlignment(gpointer key, gpointer data, gpointer user_data) ;
static void eachBlockSequenceRequest(gpointer key, gpointer data, gpointer user_data) ;
static void eachBlockDNARequest(gpointer key, gpointer data, gpointer user_data);

static char *getAcedbColourSpec(char *acedb_colour_name) ;

static gboolean parseMethodStyleNames(AcedbServer server, char *method_str_in,
				      char **end_pos, gpointer user_data) ;

#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
static void printCB(gpointer data, gpointer user_data) ;
static void stylePrintCB(gpointer data, gpointer user_data) ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */

static ZMapFeatureTypeStyle parseStyle(char *method_str_in,
				       char **end_pos, ZMapColGroupData *col_group_data) ;
static gboolean getStyleColour(StyleFeatureColours style_colours, char **line_pos) ;
static ZMapServerResponseType doGetSequences(AcedbServer server, GList *sequences_inout) ;
static gboolean getServerInfo(AcedbServer server, char **database_path_out) ;

static int equaliseLists(AcedbServer server, GList **feature_sets_inout, GList *method_names,
			 char *query_name, char *reference_name) ;
static gint quarkCaseCmp(gconstpointer a, gconstpointer b) ;
static void setErrMsg(AcedbServer server, char *new_msg) ;
static void resetErr(AcedbServer server) ;

static char *get_url_query_value(char *full_query, char *key) ;
static gboolean get_url_query_boolean(char *full_query, char *key) ;

static void freeDataCB(gpointer data) ;
static void freeSetCB(gpointer data) ;

/* 
 *             Server interface functions. 
 */



/* Compulsory routine, without this we can't do anything....returns a list of functions
 * that can be used to contact an acedb server. The functions are only visible through
 * this struct. */
void acedbGetServerFuncs(ZMapServerFuncs acedb_funcs)
{
  acedb_funcs->global_init = globalInit ;
  acedb_funcs->create = createConnection ;
  acedb_funcs->open = openConnection ;
  acedb_funcs->get_info = getInfo ;
  acedb_funcs->feature_set_names = getFeatureSetNames ;
  acedb_funcs->get_styles = getStyles ;
  acedb_funcs->have_modes = haveModes ;
  acedb_funcs->get_sequence = getSequences ;
  acedb_funcs->set_context = setContext ;
  acedb_funcs->get_features = getFeatures ;
  acedb_funcs->get_context_sequences = getContextSequence ;
  acedb_funcs->errmsg = lastErrorMsg ;
  acedb_funcs->close = closeConnection;
  acedb_funcs->destroy = destroyConnection ;

  return ;
}



/* 
 *    Although these routines are static they form the external interface to the acedb server.
 */


/* For stuff that just needs to be done once at the beginning..... */
static gboolean globalInit(void)
{
  gboolean result = TRUE ;

  return result ;
}

static gboolean createConnection(void **server_out,
				 ZMapURL url, char *format, 
                                 char *version_str, int timeout)
{
  gboolean result = FALSE ;
  gboolean use_methods = FALSE;
  AcedbServer server ;

  /* Always return a server struct as it contains error message stuff. */
  server = (AcedbServer)g_new0(AcedbServerStruct, 1) ;

  resetErr(server) ;

  server->host = g_strdup(url->host) ;
  server->port = url->port ;

  /* We need a minimum server version but user can specify a higher one in the config file. */
  if (version_str)
    {
      if (zMapCompareVersionStings(ACEDB_SERVER_MIN_VERSION, version_str))
	server->version_str = g_strdup(version_str) ;
      else
	{
	  ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
			 "Requested server version was %s but minimum supported is %s.",
			 version_str, ACEDB_SERVER_MIN_VERSION) ;
	  server->version_str = g_strdup(ACEDB_SERVER_MIN_VERSION) ;
	}
    }
  else
    server->version_str = g_strdup(ACEDB_SERVER_MIN_VERSION) ;

  server->has_new_tags = TRUE ;

  use_methods = get_url_query_boolean(url->query, "use_methods");

  server->has_new_tags = !use_methods;

  if (server->has_new_tags)
    {
      server->method_2_data = g_hash_table_new_full(NULL, NULL, NULL, freeDataCB) ;
      server->method_2_feature_set = g_hash_table_new_full(NULL, NULL, NULL, freeSetCB) ;
    }

  *server_out = (void *)server ;

  if ((server->last_err_status =
       AceConnCreate(&(server->connection), server->host, url->port, url->user, url->passwd, timeout)) == ACECONN_OK)
    result = TRUE ;

  return result ;
}


/* When we open the connection we not only check the acedb version of the server but also
 * set "quiet" mode on so that we can get dna, gff and other stuff back unadulterated by
 * extraneous information. */
static ZMapServerResponseType openConnection(void *server_in)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;
  if ((server->last_err_status = AceConnConnect(server->connection)) == ACECONN_OK)
    {
      if (checkServerVersion(server) && setQuietMode(server))
	result = ZMAP_SERVERRESPONSE_OK ;
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
			 "Could open connection because: %s", server->last_err_msg) ;
	}
    }

  return result ;
}



static ZMapServerResponseType getInfo(void *server_in, char **database_path)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;

  if (getServerInfo(server, database_path))
    {
      result = ZMAP_SERVERRESPONSE_OK ;
    }
  else
    {
      result = ZMAP_SERVERRESPONSE_REQFAIL ;
      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
		     "Could not get server info because: %s", server->last_err_msg) ;
    }

  return result ;
}


/* Feature Set names passed to the acedb server _MUST_ be the names of Method objects in the
 * database.
 * If we are using the new method/style tags then the Method objects may contain Column_group
 * tags which are used to combine several feature sets into one and each method that contains
 * features must have a zmap style.
 * 
 * This function must do a lot of checking and it is vital this is done well otherwise we end up
 * with styles/methods we don't need or much worse we don't load all the styles that the 
 * feature sets require.
 *
 * This function takes a list of names, checks that it can find the corresponding Method objects
 * and then retrieves those methods. It looks in the methods for Column_group tags and uses them
 * to construct a new list of all the feature sets that need to be retrieved from the server.
 * 
 *  */
static ZMapServerResponseType getFeatureSetNames(void *server_in,
						 GList **feature_sets_inout,
						 GList **required_styles_out,
						 GHashTable **featureset_2_stylelist_inout)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;
  char *method_string = NULL ;
  int num_methods = 0, num_feature_sets ;
  GList *feature_sets, *feature_set_methods, *feature_methods, *col_group_methods,  *method_names ;
  GList *required_styles ;
  GHashTable *featureset_2_stylelist ;

  resetErr(server) ;

  if (server->all_methods)
    {
      g_list_free(server->all_methods) ;
      server->all_methods = NULL ;
    }

  if (server->has_new_tags)
    {
      g_hash_table_destroy(server->method_2_data) ;
      server->method_2_data = g_hash_table_new_full(NULL, NULL, NULL, freeDataCB) ;

      g_hash_table_destroy(server->method_2_feature_set) ;
      server->method_2_feature_set = g_hash_table_new_full(NULL, NULL, NULL, freeSetCB) ;
    }


  /* Here we need to find methods for all the given feature set names and then look
   * for column groups and styles if the new tag sets are being used.
   *
   * The list needs to be stored inside the server struct for later use in getting styles..*/

  feature_sets = *feature_sets_inout ;
  feature_set_methods = col_group_methods = required_styles = method_names = NULL ;
  num_feature_sets = g_list_length(feature_sets) ;


  /* 1) find methods to match feature set names, puts methods into acedb's active keyset.
   * (method_string is an acedb query string to find all those methods). */
  method_string = getMethodString(feature_sets, TRUE, TRUE, TRUE) ;

  if ((result = findMethods(server_in, method_string, &num_methods)) != ZMAP_SERVERRESPONSE_OK)
    {
      result = ZMAP_SERVERRESPONSE_REQFAIL ;
      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
		     "Could not find feature set methods in server because: %s", server->last_err_msg) ;
    }
  else if (num_feature_sets != num_methods)
    {
      if (num_feature_sets > num_methods)
	{
	  ZMAPSERVER_LOG(Warning,  ACEDB_PROTOCOL_STR, server->host,
			 "%s", "Some featuresets could not be found.") ;
	}
      else
	{
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "%s", "Too many featuresets found ! Ace Server Query probably incorrect !") ;
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	}
    }

  g_free(method_string) ;



  /* 2) Check feature set names against method names, remove any feature sets for which there is
   * no method, we fail if no methods are found. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      if ((result = getObjNames(server, &feature_set_methods)) == ZMAP_SERVERRESPONSE_OK)
	{


#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
	  zMap_g_list_quark_print(feature_set_methods, "feature_set_methods", FALSE) ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */



	  if (num_feature_sets != num_methods)
	    {
	      if (!equaliseLists(server, &(feature_sets), feature_set_methods, "Feature Sets", "Methods"))
		{
		  result = ZMAP_SERVERRESPONSE_REQFAIL ;
		}
	    }
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "Could not get list of feature set methods from server because: %s",
			 server->last_err_msg) ;
	}
    }


  /* If we are using styles/column groups then extra processing is required, otherwise we just
   * return what methods we have and if possible the list of required styles. The all_methods
   * list is used by acedb in retrieving just those features in a seqget/seqfeatures call. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL && server->has_new_tags)
    {
      featureset_2_stylelist = *featureset_2_stylelist_inout ;

      if ((result = findColStyleTags(server,
				     &feature_sets, &feature_methods,
				     &required_styles, &featureset_2_stylelist))
	  != ZMAP_SERVERRESPONSE_REQFAIL)
	{
	  server->all_methods = feature_methods ;
	  
	  *feature_sets_inout = feature_sets ;
      
	  *required_styles_out = required_styles ;

	  *featureset_2_stylelist_inout = featureset_2_stylelist ;
	}
    }
  else if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      server->all_methods = feature_set_methods ;

      *feature_sets_inout = feature_sets ;

      *required_styles_out = g_list_copy(feature_set_methods) ;

      /* We need to create the one-to-one relationship */
      if((feature_set_methods = g_list_first(feature_set_methods)) &&
	 featureset_2_stylelist_inout)
	{
	  do
	    {
	      GQuark feature_set_id, feature_set_name_id;;
	      /* We _must_ canonicalise here. */
	      feature_set_name_id = GPOINTER_TO_UINT(feature_set_methods->data);

	      feature_set_id = zMapStyleCreateID((char *)g_quark_to_string(feature_set_name_id));

	      zMap_g_hashlist_insert(*featureset_2_stylelist_inout, 
				     feature_set_id,
				     GUINT_TO_POINTER(feature_set_id));
	    }
	  while((feature_set_methods = g_list_next(feature_set_methods)));
	}

    }

  return result ;
}



static ZMapServerResponseType getStyles(void *server_in, GData **styles_out)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;

  if (!findMethods(server, NULL, NULL) == ZMAP_SERVERRESPONSE_OK)
    {
      result = ZMAP_SERVERRESPONSE_REQFAIL ;
      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
		     "Could not find types on server because: %s", server->last_err_msg) ;
    }
  else
    {
      if (parseTypes(server, styles_out, NULL, NULL))
	{
	  result = ZMAP_SERVERRESPONSE_OK ;
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
			 "Could not get types from server because: %s", server->last_err_msg) ;
	}
    }

  return result ;
}


/* acedb Method objects do not usually have any mode information (e.g. transcript etc),
 * Zmap_Style objects do.
 * 
 * We can't test for this until the config file is read which happens when we make
 * the connection. */
static ZMapServerResponseType haveModes(void *server_in, gboolean *have_mode)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;

  if (server->connection)
    {
      if (server->has_new_tags)
	*have_mode = TRUE ;
      else
	*have_mode = FALSE ;

      result = ZMAP_SERVERRESPONSE_OK ;
    }

  return result ;
}


static ZMapServerResponseType getSequences(void *server_in, GList *sequences_inout)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;

  if (!sequences_inout)
    {
      setErrMsg(server, g_strdup("getSequences request made but no sequence names specified.")) ;
      server->last_err_status = ACECONN_BADARGS ;
    }      
  else
    {
      /* For acedb the default is to use the style names as the feature sets. */
      if ((result = doGetSequences(server_in, sequences_inout)) == ZMAP_SERVERRESPONSE_OK)
	{
	  /* Nothing to do I think.... */
	  ;
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
			 "Could not get sequences from server because: %s", server->last_err_msg) ;
	}
    }

  return result ;
}



/* the struct/param handling will not work in these routines now and needs sorting out.... */

static ZMapServerResponseType setContext(void *server_in, ZMapFeatureContext feature_context)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_OK ;
  AcedbServer server = (AcedbServer)server_in ;
  gboolean status ;

  resetErr(server) ;

  server->req_context = feature_context ;


  /*  HERE WE NEED TO ACCEPT A PASSED IN FEATURE CONTEXT AND THEN COPY IT AND FILL IT IN,
      THE COPY CONTEXT NEEDS TO HAPPEN HERE.....*/
#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  feature_context = zMapFeatureContextCreate(server->sequence) ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */


  if (!(status = getSequenceMapping(server, feature_context)))
    {
      result = ZMAP_SERVERRESPONSE_REQFAIL ;
      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
		     "Could not map %s because: %s",
		     g_quark_to_string(server->req_context->sequence_name), server->last_err_msg) ;
    }
  else
    server->current_context = feature_context ;

  return result ;
}



/* Get features sequence. */
static ZMapServerResponseType getFeatures(void *server_in, GData *styles, ZMapFeatureContext feature_context)
{
  AcedbServer server = (AcedbServer)server_in ;
  DoAllAlignBlocksStruct get_features ;

  resetErr(server) ;

  server->current_context = feature_context ;

  get_features.result = ZMAP_SERVERRESPONSE_OK ;
  get_features.server = (AcedbServer)server_in ;
  get_features.server->last_err_status = ACECONN_OK ;
  get_features.styles = styles ;
  get_features.eachBlock = eachBlockSequenceRequest;



  zMapPrintTimer(NULL, "In thread, getting features") ;

  /* Fetch all the alignment blocks for all the sequences. */
  g_hash_table_foreach(feature_context->alignments, eachAlignment, (gpointer)&get_features) ;

  zMapPrintTimer(NULL, "In thread, got features") ;


#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  {
    GError *error = NULL ;

    zMapFeatureDumpStdOutFeatures(feature_context, styles, &error) ;

  }
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */


  return get_features.result ;
}


/* Get features and/or sequence. */
static ZMapServerResponseType getContextSequence(void *server_in, GData *styles, ZMapFeatureContext feature_context)
{
  AcedbServer server = (AcedbServer)server_in ;
  DoAllAlignBlocksStruct get_sequence ;

  resetErr(server) ;

  get_sequence.result = ZMAP_SERVERRESPONSE_OK;
  get_sequence.server = server ;
  get_sequence.styles = styles ;
  get_sequence.server->last_err_status = ACECONN_OK;
  get_sequence.eachBlock = eachBlockDNARequest;

  g_hash_table_foreach(feature_context->alignments, eachAlignment, (gpointer)&get_sequence) ;
  
  return get_sequence.result;
}


char *lastErrorMsg(void *server_in)
{
  char *err_msg = NULL ;
  AcedbServer server = (AcedbServer)server_in ;

  zMapAssert(server_in) ;

  if (server->last_err_msg)
    err_msg = server->last_err_msg ;
  else if (server->last_err_status != ACECONN_OK)
    server->last_err_msg = err_msg = 
      g_strdup(AceConnGetErrorMsg(server->connection, server->last_err_status)) ;

  return err_msg ;
}


static ZMapServerResponseType closeConnection(void *server_in)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  AcedbServer server = (AcedbServer)server_in ;
  resetErr(server) ;

  if ((server->last_err_status = AceConnConnectionOpen(server->connection)) == ACECONN_OK
      && (server->last_err_status = AceConnDisconnect(server->connection)) == ACECONN_OK)
    result = ZMAP_SERVERRESPONSE_OK ;

  return result ;
}

static ZMapServerResponseType destroyConnection(void *server_in)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_OK ;
  AcedbServer server = (AcedbServer)server_in ;

  resetErr(server) ;

  AceConnDestroy(server->connection) ;			    /* Does not fail. */
  server->connection = NULL ;				    /* Prevents accidental reuse. */

  g_free(server->host) ;

  if (server->last_err_msg)
    g_free(server->last_err_msg) ;

  if (server->version_str)
    g_free(server->version_str) ;

  g_free(server) ;

  return result ;
}




/* 
 *                       Internal routines
 */


/* Assumes that server has a keyset of methods to be searched for Column_XXX and Style tags.
 * The methods are the "columns" and each method may directly represent features itself or
 * may group together other methods that represent features via the Column tags.
 * 
 * This all takes a few stages as below.
 *  */
static ZMapServerResponseType findColStyleTags(AcedbServer server,
					       GList **feature_sets_inout,
					       GList **feature_methods_out,
					       GList **required_styles_out,
					       GHashTable **featureset_2_stylelist_inout)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_OK ;
  GList *feature_sets, *feature_set_methods = NULL, *feature_methods = NULL, *required_styles = NULL ;
  GHashTable *featureset_2_stylelist ;
  char *method_string ;
  int num_orig, num_curr ;


  feature_sets = *feature_sets_inout ;
  featureset_2_stylelist = *featureset_2_stylelist_inout ;

  /*
   * Check methods/styles for columns.
   */

  /* Check that all of the methods either have a column_child tag and no style
   * or have a style and no column_child tag, any that don't will be excluded (n.b. we don't
   * validate the style it may come from another server.) This step also gets all the
   * methods that are required to load features. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      GetMethodsStylesStruct get_sets = {NULL} ;

      num_orig = g_list_length(feature_sets) ;

      if (parseTypes(server, NULL, parseMethodColGroupNames, &(get_sets)))
	{
	  result = ZMAP_SERVERRESPONSE_OK ;

	  feature_set_methods = get_sets.feature_set_methods ;
	  feature_methods = get_sets.feature_methods ;



#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
	  printf("\n=======================\n") ;
	  zMap_g_list_quark_print(feature_set_methods, "Column feature_sets", FALSE) ;
	  printf("\n=======================\n") ;
	  zMap_g_list_quark_print(feature_methods, "Child methods", FALSE) ;
	  printf("\n=======================\n") ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */



	  num_curr = g_list_length(feature_set_methods) ;

	  /* Check the list of feature sets against valid methods, remove any feature sets
	   * without a valid method, we fail if no methods are found. */
	  if (num_orig != num_curr)
	    {
	      if (!equaliseLists(server, &(feature_sets), feature_set_methods, "Feature Sets", "Valid Methods"))
		{
		  result = ZMAP_SERVERRESPONSE_REQFAIL ;
		}
	    }
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "Could not fetch methods to look for Column_child and Style tags: %s", server->last_err_msg) ;
	}
    }



  /* 
   * Check methods/styles for feature sets.
   */

  /* 2) Check that all feature methods have a style, any that don't will be excluded.
   * Also check whether any reference a parent column group. Update hash of both for 
   * reference in reading features. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      /* 2a) Get all methods into acedb's active keyset. */
      num_orig = g_list_length(feature_methods) ;

      method_string = getMethodString(feature_methods, TRUE, TRUE, TRUE) ;

      if ((result = findMethods(server, method_string, &num_curr)) != ZMAP_SERVERRESPONSE_OK)
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "Could not find feature set methods in server because: %s", server->last_err_msg) ;
	}
      else if (num_orig != num_curr)
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;

	  if (num_orig > num_curr)
	    ZMAPSERVER_LOG(Warning,  ACEDB_PROTOCOL_STR, server->host,
			   "%s", "Some featuresets could not be found.") ;
	  else
	    ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			   "%s", "Too many featuresets found ! Ace Server Query probably incorrect !") ;
	}

      g_free(method_string) ;
    }


  /* 2b) Check all methods were found, remove any methods that were not, we fail if no methods are found. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      GList *curr_methods = NULL ;

      if ((result = getObjNames(server, &curr_methods)) == ZMAP_SERVERRESPONSE_OK)
	{
	  if (num_orig != num_curr)
	    {
	      if (!equaliseLists(server, &(feature_methods), curr_methods, "Feature Methods", "Methods"))
		{
		  result = ZMAP_SERVERRESPONSE_REQFAIL ;
		}
	    }
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "Could not get list of feature set methods from server because: %s",
			 server->last_err_msg) ;
	}

      g_list_free(curr_methods) ;
    }


  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      GetMethodsStylesStruct get_sets = {NULL} ;

      num_orig = g_list_length(feature_methods) ;

      get_sets.server = server ;
      get_sets.set_2_styles = featureset_2_stylelist ;

      if (parseTypes(server, NULL, parseMethodStyleNames, &(get_sets)))
	{
	  result = ZMAP_SERVERRESPONSE_OK ;

	  required_styles = get_sets.required_styles ;
	  featureset_2_stylelist = get_sets.set_2_styles ;



#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
	  zMap_g_list_quark_print(required_styles, "feature_sets", FALSE) ;
	  printf("\n=======================\n") ;
	  zMap_g_hashlist_print(featureset_2_stylelist) ;
	  printf("\n=======================\n") ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */


	  num_curr = g_list_length(get_sets.feature_methods) ;

	  if (num_orig != num_curr)
	    {
	      if (!equaliseLists(server, &(feature_methods), get_sets.feature_methods, "Feature Methods", "Methods"))
		{
		  result = ZMAP_SERVERRESPONSE_REQFAIL ;
		}
	    }
	}
      else
	{
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			 "Could not fetch " COL_CHILD " methods to look for zmap_styles: %s", server->last_err_msg) ;
	}
    }

  /* Return results if all ok. */
  if (result != ZMAP_SERVERRESPONSE_REQFAIL)
    {
      *feature_sets_inout = feature_sets ;
      *feature_methods_out = feature_methods ;
      *required_styles_out = required_styles ;
      *featureset_2_stylelist_inout = featureset_2_stylelist ;
   }

  return result ;
}



static GList *getMethodsLoadable(GList *all_methods, GHashTable *method_2_data, GData *styles)
{
  GList *loadable_methods = NULL ;
  LoadableStruct loadable_data ;

  loadable_data.methods = NULL ;
  loadable_data.method_2_data = method_2_data ;
  loadable_data.styles = styles  ;

  g_list_foreach(all_methods, loadableCB, &loadable_data) ;

  loadable_methods = loadable_data.methods ;

  return loadable_methods ;
}


static void loadableCB(gpointer data, gpointer user_data)
{
  GQuark methods_id = GPOINTER_TO_UINT(data) ;		    /* Not needed. */
  Loadable loadable_data = ( Loadable)user_data ;
  ZMapGFFSource source_data ;

  methods_id = zMapStyleCreateID((char *)g_quark_to_string(methods_id)) ;

  if ((source_data = g_hash_table_lookup(loadable_data->method_2_data, GINT_TO_POINTER(methods_id))))
    {
      GQuark real_id ;
      ZMapFeatureTypeStyle style ;

      real_id = zMapStyleCreateID((char *)g_quark_to_string(source_data->style_id)) ;

      if ((style = zMapFindStyle(loadable_data->styles, real_id)))
	{
	  gboolean deferred = FALSE ;

	  g_object_get(style, ZMAPSTYLE_PROPERTY_DEFERRED, &deferred, NULL) ;

	  if (!deferred)
	    loadable_data->methods = g_list_append(loadable_data->methods, GUINT_TO_POINTER(data)) ;
	}
    }


  return ;
}


/* Make up a string that contains method names in the correct format for an acedb "Find" command
 * (find_string == TRUE) or to be part of an acedb "seqget" command.
 * We may be passed either a list of style names in GQuark form (style_name_list == TRUE)
 * or a list of the actual styles. */
static char *getMethodString(GList *styles_or_style_names,
			     gboolean style_name_list, gboolean find_string, gboolean find_methods)
{
  char *type_names = NULL ;
  ZMapTypesStringStruct types_data ;
  GString *str ;
  gboolean free_string = TRUE ;

  zMapAssert(styles_or_style_names) ;

  str = g_string_new("") ;

  if (find_string)
    {
      if (find_methods)
	str = g_string_append(str, "query find method ") ;
      else
	str = g_string_append(str, "query find zmap_style ") ;
    }
  else
    str = g_string_append(str, "+method ") ;

  types_data.first_method = TRUE ;
  types_data.find_string = find_string ;
  types_data.name_list = style_name_list ;
  types_data.str = str ;

  g_list_foreach(styles_or_style_names, addTypeName, (void *)&types_data) ;

  if (*(str->str))
    free_string = FALSE ;

  type_names = g_string_free(str, free_string) ;


  return type_names ;
}


/* GFunc() callback function, appends style names to a string, its called for lists
 * of either style name GQuarks or lists of style structs. */
static void addTypeName(gpointer data, gpointer user_data)
{
  char *type_name = NULL ;
  GQuark key_id ;
  ZMapTypesString types_data = (ZMapTypesString)user_data ;

  /* We might be passed either a list of style names (as quarks) or a list of the actual styles
   * from which we need to extract the style name. */
  if (types_data->name_list)
    key_id = GPOINTER_TO_UINT(data) ;
  else
    {
      ZMapFeatureTypeStyle style = (ZMapFeatureTypeStyle)data;
      key_id = zMapStyleGetID(style) ;
    }

  type_name = (char *)g_quark_to_string(key_id) ;

  if (!types_data->first_method)
    {
      if (types_data->find_string)
	types_data->str = g_string_append(types_data->str, " OR ") ;
      else
	types_data->str = g_string_append(types_data->str, "|") ;
    }
  else
    types_data->first_method = FALSE ;

  if (types_data->find_string)
    {
      g_string_append_printf(types_data->str, "\"%s\"", type_name) ;
    }
  else
    types_data->str = g_string_append(types_data->str, type_name) ;

  return ;
}





/* bit of an issue over returning error messages here.....sort this out as some errors many be
 * aceconn errors, others may be data processing errors, e.g. in GFF etc., e.g.
 * 
 * If we issue a command like this: "gif seqget obj ; seqfeatures" then we have a problem because
 * the server treats this as one command and returns any errors from the seqget() munged on the
 * front of the GFF output from the seqfeatures() like this:
 * 
 * // ERROR -  Sequence:"yk47h9.3" (5' match) and Sequence:"yk47h9.3" (3' match) are in wrong orientation.
 * // ERROR -  Sequence:"AW057380" (5' match) and Sequence:"AW057380" (3' match) are in wrong orientation.
 * // ERROR -  Sequence:"OSTR010G5_1" (5' match) and Sequence:"OSTR010G5_1" (3' match) are in wrong orientation.
 * ##gff-version 2
 * ##source-version sgifaceserver:ACEDB 4.9.27
 * ##date 2004-09-21
 * ##sequence-region F22D3 1 35712 
 * F22D3	Genomic_canonical	region	1	200	.	+	.	Sequence "B0252"
 * 
 * so what to do...agh....
 * 
 * I guess the best thing is to shove the errors out to the log and look for the gff start...
 * 
 */
static gboolean sequenceRequest(AcedbServer server, GData *styles, ZMapFeatureBlock feature_block)
{
  gboolean result = FALSE ;
  char *gene_finder_cmds = "seqactions -gf_features no_draw ;" ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;
  GList *loadable_methods = NULL ;
  char *methods = "" ;
  gboolean no_clip = TRUE ;



#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  /* Get any styles stored in the context. */
  styles = ((ZMapFeatureContext)(feature_block->parent->parent))->styles ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */



  /* Exclude any methods that have "deferred loading" set in their styles, if no styles then
   * include all methods. */
  if (server->has_new_tags)
    loadable_methods = getMethodsLoadable(server->all_methods, server->method_2_data, styles) ;
  else
    loadable_methods = g_list_copy(server->all_methods) ;
  
  methods = getMethodString(loadable_methods, TRUE, FALSE, FALSE) ;

  g_list_free(loadable_methods) ;
  loadable_methods = NULL ;


  /* Check for presence of genefinderfeatures method, if present we need to tell acedb to send
   * us the gene finder methods... */
  if ((zMap_g_ascii_strstrcasecmp(methods, ZMAP_FIXED_STYLE_GFF_NAME)))
    server->fetch_gene_finder_features = TRUE ;


  /* Here we can convert the GFF that comes back, in the end we should be doing a number of
   * calls to AceConnRequest() as the server slices...but that will need a change to my
   * AceConn package.....
   * We make the big assumption that what comes back is a C string for now, this is true
   * for most acedb requests, only images/postscript are not and we aren't asking for them. */
  /* -rawmethods makes sure that the server does _not_ use the GFF_source field in the method obj
   * to output the source field in the gff, we need to see the raw methods.
   * -refseq makes sure that the coords returned are relative to the reference sequence, _not_
   * to the object at the top of the smap tree .
   * 
   * Note that we specify the methods both for the seqget and the seqfeatures to try and exclude
   * the parent sequence if it is not required, this is actually quite fiddly to do in the acedb
   * code in a way that won't break zmap so we do it here. */

  zMapPrintTimer(NULL, "In thread, about to ask for features") ;

  acedb_request =  g_strdup_printf("gif seqget %s -coords %d %d %s %s ; "
				   " %s "
				   "seqfeatures -refseq %s -rawmethods -zmap %s",
				   g_quark_to_string(feature_block->original_id),
				   feature_block->features_start,
				   feature_block->features_end,
				   no_clip ? "-noclip" : "",
				   methods,
				   (server->fetch_gene_finder_features ? gene_finder_cmds : ""),
				   g_quark_to_string(feature_block->original_id),
				   methods) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request, &reply, &reply_len))
      == ACECONN_OK)
    {
      char *next_line ;
      ZMapReadLine line_reader ;
      gboolean inplace = TRUE ;
      char *first_error = NULL ;

      zMapPrintTimer(NULL, "In thread, got features and about to parse into context") ;

      line_reader = zMapReadLineCreate((char *)reply, inplace) ;

      /* Look for "##gff-version" at start of line which signals start of GFF, as detailed above
       * there may be errors before the GFF output. */
      result = TRUE ;
      do
	{
	  if (!(result = zMapReadLineNext(line_reader, &next_line)))
	    {
	      /* If the readline fails it may be because of an error or because its reached the
	       * end, if next_line is empty then its reached the end. */
	      if (*next_line)
		{
		  setErrMsg(server, g_strdup_printf("Request from server contained incomplete line: %s",
						    next_line)) ;
		  if (!first_error)
		    first_error = next_line ;		    /* Remember first line for later error
							       message.*/
		}
	      else
		{
		  if (first_error)
		    setErrMsg(server,  g_strdup_printf("No GFF found in server reply,"
						       "but did find: \"%s\"", first_error)) ;
		  else
		    setErrMsg(server,  g_strdup_printf("%s", "No GFF found in server reply.")) ;
		}

	      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
			     "%s", server->last_err_msg) ;
	    }
	  else
	    {
	      /* The ace server first gives us all the errors from the seqget/seqfeatures as
	       * comments as then finally we get to the gff. */
	      if (g_str_has_prefix((char *)next_line, "##gff-version"))
		{
		  break ;
		}
	      else
		{
		  if (g_str_has_prefix((char *)next_line, "// ERROR"))
		    {
		      setErrMsg(server,  g_strdup_printf("Error fetching features: %s",
							 next_line)) ;
		      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
				     "%s", server->last_err_msg) ;
		    }
		  else if (g_str_has_prefix((char *)next_line, "//"))
		    {
		      setErrMsg(server,  g_strdup_printf("Information from server: %s",
							 next_line)) ;
		      ZMAPSERVER_LOG(Message, ACEDB_PROTOCOL_STR, server->host,
				     "%s", server->last_err_msg) ;
		    }
		  else
		    {
		      setErrMsg(server,  g_strdup_printf("Bad GFF line: %s",
							 next_line)) ;
		      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
				     "%s", server->last_err_msg) ;
		    }

		  if (!first_error)
		    first_error = next_line ;		    /* Remember first line for later error
							       message.*/
		}
	    }
	}
      while (result && *next_line) ;


      if (result)
	{
	  ZMapGFFParser parser ;
	  gboolean free_on_destroy ;
      

	  parser = zMapGFFCreateParser(styles, FALSE) ;


	  /* If we are doing cols/styles then set hash tables in parser to map the gff source
	   * name to the Feature Set (== Column) and a Style. */
	  if (server->has_new_tags)
	    {
	      zMapGFFParseSetSourceHash(parser, server->method_2_feature_set, server->method_2_data) ;
	    }

	  /* We probably won't have to deal with part lines here acedb should only return whole lines
	   * ....but should check for sure...bomb out for now.... */
	  result = TRUE ;
	  do
	    {
	      /* Note that we already have the first line from the loop above. */
	      if (!zMapGFFParseLine(parser, next_line))
		{
		  /* This is a hack, I would like to make the acedb code have a quiet mode but
		   * as usual this is not straight forward and will take a bit of fixing...
		   * The problem for us is that the gff output is terminated with with a couple
		   * of acedb comment lines which then screw up our parsing....so we ignore
		   * lines starting with "//" hoping this doesn't conflict with gff.... */
		  if (!g_str_has_prefix(next_line, "//"))
		    {
		      GError *error = zMapGFFGetError(parser) ;

		      if (!error)
			{
			  setErrMsg(server, 
				    g_strdup_printf("zMapGFFParseLine() failed with no GError for line %d: %s",
						    zMapGFFGetLineNumber(parser), next_line)) ;
			  ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
					 "%s", server->last_err_msg) ;

			  result = FALSE ;
			}
		      else
			{
			  /* If the error was serious we stop processing and return the error,
			   * otherwise we just log the error. */
			  if (zMapGFFTerminated(parser))
			    {
			      result = FALSE ;
			      setErrMsg(server,  g_strdup_printf("%s", error->message)) ;
			    }
			  else
			    {
			      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
					     "%s", error->message) ;
			    }
			}
		    }
		}


	      if (!(result = zMapReadLineNext(line_reader, &next_line)))
		{
		  /* If the readline fails it may be because of an error or because its reached the
		   * end, if next_line is empty then its reached the end. */
		  if (*next_line)
		    {
		      setErrMsg(server,  g_strdup_printf("Request from server contained incomplete line: %s",
							 next_line)) ;
		      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
				     "%s", server->last_err_msg) ;
		    }
		  else
		    result = TRUE ;
		}

	    }
	  while (result && *next_line) ;

	  free_on_destroy = TRUE ;
	  if (result)
	    {
	      if (zMapGFFGetFeatures(parser, feature_block))
		{
		  free_on_destroy = FALSE ;			    /* Make sure parser does _not_ free our
								       data. ! */
		}
	      else
		result = FALSE ;
	    }

	  zMapGFFSetFreeOnDestroy(parser, free_on_destroy) ;
	  zMapGFFDestroyParser(parser) ;
	}

      zMapReadLineDestroy(line_reader, FALSE) ;		    /* n.b. don't free string as it is the
							       same as reply which is freed later.*/

      g_free(reply) ;


      zMapPrintTimer(NULL, "In thread, finished parsing features") ;

    }

  if(acedb_request)
    g_free(acedb_request) ;

  if(methods)
    g_free(methods);

  return result ;
}



static void eachBlockDNARequest(gpointer key, gpointer data, gpointer user_data)
{
  ZMapFeatureBlock feature_block = (ZMapFeatureBlock)data ;
  DoAllAlignBlocks get_sequence = (DoAllAlignBlocks)user_data ;


  /* We should check that there is a sequence context here and report an error if there isn't... */


  /* We should be using the start/end info. in context for the below stuff... */
  if (!blockDNARequest(get_sequence->server, get_sequence->styles, feature_block))
    {
      /* If the call failed it may be that the connection failed or that the data coming
       * back had a problem. */
      if (get_sequence->server->last_err_status == ACECONN_OK)
	{
	  get_sequence->result = ZMAP_SERVERRESPONSE_REQFAIL ;
	}
      else if (get_sequence->server->last_err_status == ACECONN_TIMEDOUT)
	{
	  get_sequence->result = ZMAP_SERVERRESPONSE_TIMEDOUT ;
	}
      else
	{
	  /* Probably we will want to analyse the response more than this ! */
	  get_sequence->result = ZMAP_SERVERRESPONSE_SERVERDIED ;
	}

      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, get_sequence->server->host,
		     "Could not get DNA sequence for %s because: %s",
		     g_quark_to_string(get_sequence->server->req_context->sequence_name),
		     get_sequence->server->last_err_msg) ;
    }


  return ;
}


/* bit of an issue over returning error messages here.....sort this out as some errors many be
 * aceconn errors, others may be data processing errors, e.g. in GFF etc., e.g.
 * 
 * 
 */
static gboolean blockDNARequest(AcedbServer server, GData *styles, ZMapFeatureBlock feature_block)
{
  gboolean result = FALSE ;
  ZMapFeatureContext context = NULL ;
  int block_start, block_end, dna_length = 0 ;
  char *dna_sequence = NULL ;

  context = (ZMapFeatureContext)zMapFeatureGetParentGroup((ZMapFeatureAny)feature_block, 
							  ZMAPFEATURE_STRUCT_CONTEXT) ;


  block_start = feature_block->block_to_sequence.q1 ;
  block_end   = feature_block->block_to_sequence.q2 ;
  /* These block numbers appear correct, but I may have the wrong
   * end of the block_to_sequence stick! */


  /* Because the acedb "dna" command works on the current keyset, we have to find the sequence
   * first before we can get its dna. A bit poor really but otherwise
   * we will have to add a new code to acedb to do the dna for a named key. */
  if ((result = getDNARequest(server,
			      (char *)g_quark_to_string(server->req_context->sequence_name),
			      block_start, block_end,
			      &dna_length, &dna_sequence)))
    {
      ZMapFeature feature = NULL;
      ZMapFeatureSet feature_set = NULL;
      ZMapFeatureTypeStyle dna_style = NULL;

      if (zMapFeatureDNACreateFeatureSet(feature_block, &feature_set))
	{
	  ZMapFeatureTypeStyle temp_style = NULL;
	  
	  /* This temp style creation feels wrong, and probably is,
	   * but we don't have the merged in default styles in here,
	   * or so it seems... */

	  if (!(dna_style = zMapFindStyle(styles, zMapStyleCreateID(ZMAP_FIXED_STYLE_DNA_NAME))))
	    temp_style = dna_style = zMapStyleCreate(ZMAP_FIXED_STYLE_DNA_NAME, 
						     ZMAP_FIXED_STYLE_DNA_NAME_TEXT);
	  
	  feature = zMapFeatureDNACreateFeature(feature_block, dna_style,
						dna_sequence, dna_length);
	  
	  if(temp_style)
	    zMapStyleDestroy(temp_style);
	}

      /* I'm going to create the three frame translation up front! */
      if (zMap_g_list_find_quark(context->feature_set_names, zMapStyleCreateID(ZMAP_FIXED_STYLE_3FT_NAME)))
	{
	  if ((zMapFeature3FrameTranslationCreateSet(feature_block, &feature_set)))
	    {
	      ZMapFeatureTypeStyle frame_style = NULL;
	      gboolean style_absolutely_required = FALSE;

	      frame_style = zMapFindStyle(styles, zMapStyleCreateID(ZMAP_FIXED_STYLE_3FT_NAME));

	      if(style_absolutely_required && !frame_style)
		zMapLogWarning("Cowardly refusing to create features '%s' without style",
			       ZMAP_FIXED_STYLE_3FT_NAME);
	      else
		zMapFeature3FrameTranslationSetCreateFeatures(feature_set, frame_style);
	    }
	}
                
      /* everything should now be done, result is true */
      result = TRUE ;
    }

  return result ;
}




/* bit of an issue over returning error messages here.....sort this out as some errors many be
 * aceconn errors, others may be data processing errors, e.g. in GFF etc., e.g.
 * 
 * 
 */
static gboolean getDNARequest(AcedbServer server, char *sequence_name, int start, int end,
			      int *dna_length_out, char **dna_sequence_out)
{
  gboolean result = FALSE ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;


  /* Because the acedb "dna" command works on the current keyset, we have to find the sequence
   * first before we can get its dna. A bit poor really but otherwise
   * we will have to add a new code to acedb to do the dna for a named key. */
  if (findSequence(server, sequence_name))
    {
      int dna_length ;

      /* Here we get all the dna in one go, in the end we should be doing a number of
       * calls to AceConnRequest() as the server slices...but that will need a change to my
       * AceConn package....
       * -u says get the dna as a single line.*/
      acedb_request = g_strdup_printf("dna -u -x1 %d -x2 %d", start, end) ;

      server->last_err_status = AceConnRequest(server->connection, acedb_request, &reply, &reply_len) ;
      if (server->last_err_status == ACECONN_OK)
	{
	  if ((reply_len - 1) != ((dna_length = end - start + 1)))
	    {
	      setErrMsg(server,  g_strdup_printf("DNA request failed (\"%s\"),  "
						 "expected dna length: %d "
						 "returned length: %d",
						 acedb_request,
						 dna_length, (reply_len - 1))) ;
	      result = FALSE ;
	    }
	  else
	    {
	      *dna_length_out = dna_length ;
	      *dna_sequence_out = reply ;
                
	      /* everything should now be done, result is true */
	      result = TRUE ;
	    }
	}

      g_free(acedb_request) ;
    }

  return result ;
}





/* Tries to smap sequence into whatever its parent is, if the call fails then we set all the
 * mappings in feature_context to be something sensible...we hope....
 */
static gboolean getSequenceMapping(AcedbServer server, ZMapFeatureContext feature_context)
{
  gboolean result = FALSE ;
  char *parent_name = NULL, *parent_class = NULL ;
  ZMapMapBlockStruct sequence_to_parent = {0, 0, 0, 0}, parent_to_self = {0, 0, 0, 0} ;
  int parent_length = 0 ;


  /* We have a special case where the caller can specify  end == 0  meaning "get the sequence
   * up to the end", in this case we explicitly find out what the end is. */
  if (server->req_context->sequence_to_parent.c2 == 0)
    {
      int child_length ;

      /* NOTE that we hard code sequence in here...but what to do...gff does not give back the
       * class of the sequence object..... */
      if (getSMapLength(server, "sequence",
			(char *)g_quark_to_string(server->req_context->sequence_name),
			&child_length))
	server->req_context->sequence_to_parent.c2 =  child_length ;
    }

  if (getSMapping(server, NULL, (char *)g_quark_to_string(server->req_context->sequence_name),
		  server->req_context->sequence_to_parent.c1, server->req_context->sequence_to_parent.c2,
		  &parent_class, &parent_name, &sequence_to_parent)
      && ((server->req_context->sequence_to_parent.c2 - server->req_context->sequence_to_parent.c1 + 1) == (sequence_to_parent.c2 - sequence_to_parent.c1 + 1))
      && getSMapLength(server, parent_class, parent_name, &parent_length))
    {
      feature_context->length = sequence_to_parent.c2 - sequence_to_parent.c1 + 1 ;

      parent_to_self.p1 = parent_to_self.c1 = 1 ;
      parent_to_self.p2 = parent_to_self.c2 = parent_length ;

      feature_context->parent_name = g_quark_from_string(parent_name) ;
      g_free(parent_name) ;

      if (feature_context->sequence_to_parent.p1 < feature_context->sequence_to_parent.p2)
	{
	  feature_context->parent_span.x1 = parent_to_self.p1 ;
	  feature_context->parent_span.x2 = parent_to_self.p2 ;
	}
      else
	{
	  feature_context->parent_span.x1 = parent_to_self.p2 ;
	  feature_context->parent_span.x2 = parent_to_self.p1 ;
	}

      feature_context->sequence_to_parent.c1 = sequence_to_parent.c1 ;
      feature_context->sequence_to_parent.c2 = sequence_to_parent.c2 ;
      feature_context->sequence_to_parent.p1 = sequence_to_parent.p1 ;
      feature_context->sequence_to_parent.p2 = sequence_to_parent.p2 ;
     
      result = TRUE ;
    }

  return result ;
}



/* Gets the mapping of a sequence object into its ultimate SMap parent, uses the
 * smap and smaplength commands to do this. Note that we currently assume that the
 * object is of the sequence class. If the smap call succeeds then returns TRUE,
 * FALSE otherwise (server->last_err_msg is set to show the problem).
 * 
 * To do this we issue the smap request to the server:
 * 
 *        gif smap -from sequence:<seq_name>
 * 
 * and receive a reply in the form:
 * 
 *        SMAP Sequence:<parent_name> 10995378 11034593 1 39216 PERFECT_MAP
 *        // 0 Active Objects
 * 
 * on error we get something like:
 * 
 *        // gif smap error: invalid from object
 *        // 0 Active Objects
 * 
 */
static gboolean getSMapping(AcedbServer server, char *class,
			    char *sequence, int start, int end,
			    char **parent_class_out, char **parent_name_out,
			    ZMapMapBlock child_to_parent_out)
{
  gboolean result = FALSE ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  acedb_request = g_strdup_printf("gif smap -coords %d %d -from sequence:%s",
				  start, end, sequence) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request, &reply, &reply_len))
      == ACECONN_OK)
    {
      char *reply_text = (char *)reply ;

      /* this is a hack, none of the acedb commands return an error code so the only way you know
       * if anything has gone wrong is to examine the contents of the text that is returned...
       * in our case if the reply starts with acedb comment chars, this means trouble.. */
      if (g_str_has_prefix(reply_text, "//"))
	{
	  setErrMsg(server,  g_strdup_printf("SMap request failed, "
					     "request: \"%s\",  "
					     "reply: \"%s\"", acedb_request, reply_text)) ;
	  result = FALSE ;
	}
      else
	{
	  enum {FIELD_BUFFER_LEN = 257} ;
	  char parent_class[FIELD_BUFFER_LEN] = {'\0'}, parent_name[FIELD_BUFFER_LEN] = {'\0'} ;
	  char status[FIELD_BUFFER_LEN] = {'\0'} ;
	  ZMapMapBlockStruct child_to_parent = {0, 0, 0, 0} ;
	  enum {FIELD_NUM = 7} ;
	  char *format_str = "SMAP %256[^:]:%256s%d%d%d%d%256s" ;
	  int fields ;
	  
	  if ((fields = sscanf(reply_text, format_str, &parent_class[0], &parent_name[0],
			       &child_to_parent.p1, &child_to_parent.p2,
			       &child_to_parent.c1, &child_to_parent.c2,
			       &status[0])) != FIELD_NUM)
	    {
	      setErrMsg(server,  g_strdup_printf("Could not parse smap data, "
						 "request: \"%s\",  "
						 "reply: \"%s\"", acedb_request, reply_text)) ;
	      result = FALSE ;
	    }
	  else
	    {
	      /* Really we need to do more than this, we should check the status of the mapping. */

	      if (parent_class_out)
		*parent_class_out = g_strdup(&parent_class[0]) ;
	      if (parent_name_out)
		*parent_name_out = g_strdup(&parent_name[0]) ;

	      if (child_to_parent_out)
		*child_to_parent_out = child_to_parent ;    /* n.b. struct copy. */

	      result = TRUE ;
	    }
	}

      g_free(reply) ;
    }

  g_free(acedb_request) ;

  return result ;
}




/* Makes smaplength request to server for a given object. If the smap call succeeds
 * then returns TRUE, FALSE otherwise (server->last_err_msg is set to show the problem).
 * 
 * To do this we issue the smap request to the server:
 * 
 *        smaplength Sequence:CHROMOSOME_V
 * 
 * and receive a reply in the form:
 * 
 *        SMAPLENGTH Sequence:"CHROMOSOME_V" 20922231
 *        // 0 Active Objects
 * 
 * on error we get something like:
 * 
 *        // smaplength error: object "Sequence:CHROMOSOME_V" does not exist.
 *        // 0 Active Objects
 * 
 *  */
static gboolean getSMapLength(AcedbServer server, char *obj_class, char *obj_name,
			      int *obj_length_out)
{
  gboolean result = FALSE ;
  char *command = "smaplength" ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  acedb_request =  g_strdup_printf("%s %s:%s", command, obj_class, obj_name) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request, &reply, &reply_len))
      == ACECONN_OK)
    {
      char *reply_text = (char *)reply ;

      /* this is a hack, none of the acedb commands return an error code so the only way you know
       * if anything has gone wrong is to examine the contents of the text that is returned...
       * in our case if the reply starts with acedb comment chars, this means trouble.. */
      if (g_str_has_prefix(reply_text, "//"))
	{
	  setErrMsg(server,  g_strdup_printf("%s request failed, "
					     "request: \"%s\",  "
					     "reply: \"%s\"", command, acedb_request, reply_text)) ;
	  result = FALSE ;
	}
      else
	{
	  enum {FIELD_BUFFER_LEN = 257} ;
	  char obj_class_name[FIELD_BUFFER_LEN] = {'\0'} ;
	  int length = 0 ;
	  enum {FIELD_NUM = 2} ;
	  char *format_str = "SMAPLENGTH %256s%d" ;
	  int fields ;
	  
	  if ((fields = sscanf(reply_text, format_str, &obj_class_name[0], &length)) != FIELD_NUM)
	    {
	      setErrMsg(server,  g_strdup_printf("Could not parse smap data, "
						 "request: \"%s\",  "
						 "reply: \"%s\"", acedb_request, reply_text)) ;
	      result = FALSE ;
	    }
	  else
	    {
	      if (obj_length_out)
		*obj_length_out = length ;

	      result = TRUE ;
	    }
	}

      g_free(reply) ;
    }

  g_free(acedb_request) ;

  return result ;
}


/* Makes status request to get acedb server code version. Returns TRUE if server version is
 * same or more recent than ours, returns FALSE if version is older or it call fails
 * failure implies a serious error.
 *
 * Command and output to do this are like this:
 * 
 * acedb> status -code
 *  // ************************************************
 *  // AceDB status at 2004-11-09_15:17:03
 *  // 
 *  // - Code
 *  //             Program: giface
 *  //             Version: ACEDB 4.9.28
 *  //               Build: Nov  9 2004 13:34:42
 *  // 
 *  // ************************************************
 * 
 */
static gboolean checkServerVersion(AcedbServer server)
{
  gboolean result = FALSE ;
  char *command = "status -code" ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  if (server->version_str == NULL)
    result = TRUE ;
  else
    {
      acedb_request =  g_strdup_printf("%s", command) ;

      if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						    &reply, &reply_len)) == ACECONN_OK)
	{
	  char *reply_text = (char *)reply ;
	  char *scan_text = reply_text ;
	  char *next_line = NULL ;

	  /* Scan lines for "Version:" and then extract the version, release and update numbers. */
	  while ((next_line = strtok(scan_text, "\n")))
	    {
	      scan_text = NULL ;
	      
	      if (strstr(next_line, "Version:"))
		{
		  /* Parse this string: "//             Version: ACEDB 4.9.28" */
		  char *next ;
		  
		  next = strtok(next_line, ":") ;
		  next = strtok(NULL, " ") ;
		  next = strtok(NULL, " ") ;
		  
		  if (!(result = zMapCompareVersionStings(server->version_str, next)))
		    setErrMsg(server,  g_strdup_printf("Server version must be at least %s "
						       "but this server is %s.",
						       server->version_str, next)) ;
		  break ;
		}
	    }

	  g_free(reply) ;
	}

      g_free(acedb_request) ;
    }

  return result ;
}


/* Makes "find sequence x" request to get sequence into current keyset on server.
 * Returns TRUE if sequence found, returns FALSE otherwise.
 *
 * Command and output to do this are like this:
 * 
 * acedb> find sequence b0250
 * 
 * // Found 1 objects in this class
 * // 1 Active Objects
 * 
 * 
 */
static gboolean findSequence(AcedbServer server, char *sequence_name)
{
  gboolean result = FALSE ;
  char *command = "find sequence" ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  acedb_request = g_strdup_printf("%s %s", command, sequence_name) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      char *reply_text = (char *)reply ;
      char *scan_text = reply_text ;
      char *next_line = NULL ;

      /* Scan lines for "Found" and then extract the version, release and update numbers. */
      while ((next_line = strtok(scan_text, "\n")))
	{
	  scan_text = NULL ;
	  
	  if (strstr(next_line, "Found"))
	    {
	      /* Parse this string: "// Found 1 objects in this class" */
	      char *next ;
	      int num_obj ;
	      
	      next = strtok(next_line, " ") ;
	      next = strtok(NULL, " ") ;
	      next = strtok(NULL, " ") ;

	      num_obj = atoi(next) ;

	      if (num_obj == 1)
		result = TRUE ;
	      else
		setErrMsg(server,  g_strdup_printf("Expected to find \"1\" sequence object with name %s, "
						   "but found %d objects.",
						   sequence_name, num_obj)) ;
	      break ;
	    }
	}

      g_free(reply) ;
    }

  g_free(acedb_request) ;

  return result ;
}


/* Makes "quiet -on" request to stop acedb outputting all sorts of informational junk
 * for every single request. Returns TRUE if request ok, returns FALSE otherwise.
 *
 * (n.b. if the request was successful then there is no output from the command !)
 * 
 */
static gboolean setQuietMode(AcedbServer server)
{
  gboolean result = FALSE ;
  char *command = "quiet -on" ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  acedb_request =  g_strdup_printf("%s", command) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      result = TRUE ;

      if (reply_len != 0)
	{
	  zMapLogWarning("Replay to \"%s\" should have been NULL but was: \"%s\"",
			 command, (char *)reply) ;
	  g_free(reply) ;
	}
    }

  g_free(acedb_request) ;

  return result ;
}




/* Uses the "status" command to get database and code information, e.g.
 *
 * acedb> status -database
 *  // ************************************************
 *  // AceDB status at 2008-02-08_10:59:02
 *  // 
 *  // - Database
 *  //               Title: <undefined>
 *  //                Name: <undefined>
 *  //             Release: 4_0
 *  //           Directory: /nfs/team71/acedb/edgrif/acedb/databases/JAMES.NEWSTYLES/
 *  //             Session: 7
 *  //                User: edgrif
 *  //           Last Save: 2008-02-06_15:47:52
 *  //        Write Access: No
 *  //      Global Address: 3470
 *  // 
 *  // ************************************************
 * 
 * // 0 Active Objects
 * acedb> 
 * 
 *
 *  */
static gboolean getServerInfo(AcedbServer server, char **database_path_out)
{
  gboolean result = FALSE ;
  char *command ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  /* We could add "status -code" later..... */
  command = "status -database" ;

  acedb_request =  g_strdup_printf("%s", command) ;
  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      char *scan_text = (char *)reply ;
      char *next_line = NULL ;
      char *curr_pos = NULL ;

      while ((next_line = strtok_r(scan_text, "\n", &curr_pos)))
	{
	  scan_text = NULL ;

	  if (strstr(next_line, "Directory") != NULL)
	    {
	      char *target ;
	      char *tag_pos = NULL ;

	      target = strtok_r(next_line, ":", &tag_pos) ;
	      target = strtok_r(NULL, " ", &tag_pos) ;

	      if (target)
		{
		  result = TRUE ;
		  *database_path_out = g_strdup(target) ;
		}
	      else
		setErrMsg(server,  g_strdup("No directory name after \"Directory\" in acedb response.")) ;

	      break ;
	    }
	}

      g_free(reply) ;
      reply = NULL ;
    }

  g_free(acedb_request) ;

  return result ;
}



/* Makes requests "find method" and "show -a" to get all methods in a form we can
 * parse, e.g.
 * 
 * The "query find" command is used to find either a requested list of methods
 * or all methods:
 * 
 * acedb> query find method "coding" OR  "genepairs" OR "genefinder"
 *
 * // Found 3 objects
 * // 3 Active Objects
 * acedb>
 * 
 * 
 * Then the "show" command is used to display the methods themselves:
 * 
 * acedb> show -a
 * 
 * Method : "wublastx_briggsae"
 * Remark   "wublastx search of C. elegans genomic clones vs C. briggsae peptides"
 * Colour   LIGHTGREEN
 * Frame_sensitive
 * Show_up_strand
 * Score_by_width
 * Score_bounds     10.000000 100.000000
 * Right_priority   4.750000
 * Join_blocks
 * Blixem_X
 * GFF_source       "wublastx"
 * GFF_feature      "similarity"
 * 
 * <more methods>
 * 
 * // 7 objects dumped
 * // 7 Active Objects
 * 
 * 
 *
 *  */
static gboolean parseTypes(AcedbServer server, GData **types_out,
			   ParseMethodNamesFunc parse_func_in, gpointer user_data)
{
  gboolean result = FALSE ;
  char *command ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;
  GData *types = NULL ;

  /* Get all the methods and then filter them if there are requested types. */
  command = "show -a" ;
  acedb_request = g_strdup_printf("%s", command) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      ParseMethodFunc parse_func ;
      int num_types = 0 ;
      char *method_str = (char *)reply ;
      char *scan_text = method_str ;
      char *curr_pos = NULL ;
      char *next_line = NULL ;

      /* Set correct parser. */
      if (!parse_func_in)
	{
	  if (server->has_new_tags)
	    parse_func = parseStyle ;
	  else
	    parse_func = parseMethod ;
	}


      while ((next_line = strtok_r(scan_text, "\n", &curr_pos))
	     && !g_str_has_prefix(next_line, "// "))
	{
	  ZMapFeatureTypeStyle style = NULL ;
	  ZMapColGroupData col_group = NULL ;
	  
	  scan_text = NULL ;
	      
	  if (parse_func_in)
	    {
	      GQuark method_id ;

	      if ((method_id = (parse_func_in)(server, next_line, &curr_pos, user_data)))
		{
		  num_types++ ;
		}
	    }
	  else if ((style = (parse_func)(next_line, &curr_pos, &col_group)))
	    {
	      g_datalist_id_set_data(&types, zMapStyleGetUniqueID(style), style) ;
	      num_types++ ;
	    }

	}

      if (!num_types)
	{
	  result = FALSE ;

	  setErrMsg(server,  g_strdup("Styles found on server but they could not be parsed, check the log.")) ;
	}
      else
	{
	  result = TRUE ;

	  if (!parse_func_in)
	    {
	      *types_out = types ;

#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
	      g_list_foreach(types, stylePrintCB, NULL) ; /* debug */
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */
	    }
	}

      g_free(reply) ;
      reply = NULL ;
    }

  g_free(acedb_request) ;

  return result ;
}


/* Makes request "query find method" to get all methods into keyset on server.
 * 
 * The "query find" command is used to find all methods:
 * 
 * acedb> query find method
 *
 * // Found 3 objects
 * // 3 Active Objects
 * acedb>
 * 
 * Function returns TRUE if methods were found, FALSE otherwise.
 * 
 *  */
static ZMapServerResponseType findMethods(AcedbServer server, char *search_str, int *num_found_out)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  char *command ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;


  if (search_str)
    {
      command = search_str ;
    }
  else
    {
      /* Get all the methods or styles into the current keyset on the server. */
      if (server->has_new_tags)
	command = "query find zmap_style" ;
      else
	command = "query find method" ;
    }

  acedb_request =  g_strdup_printf("%s", command) ;
  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      /* reply is:
       *
       * <blank line>
       * // Found 132 objects in this class
       * // 132 Active Objects
       *
       */
      char *scan_text = (char *)reply ;
      char *next_line = NULL ;
      int num_methods ;

      while ((next_line = strtok(scan_text, "\n")))
	{
	  scan_text = NULL ;

	  if (g_str_has_prefix(next_line, "// "))
	    {
	      num_methods = getFoundObj(next_line) ;
	      if (num_methods > 0)
		{
		  if (num_found_out)
		    *num_found_out = num_methods ;

		  result = ZMAP_SERVERRESPONSE_OK ;
		}
	      else
		setErrMsg(server,  g_strdup_printf("Expected to find %s objects but found none.",
						   (server->has_new_tags ? "ZMap_style" : "Method"))) ;
	      break ;
	    }
	}

      g_free(reply) ;
      reply = NULL ;
    }

  g_free(acedb_request) ;

  return result ;
}



/* The method string should be of the form:
 *
 * Method : "wublastx_briggsae"
 * Remark	 "wublastx search of C. elegans genomic clones vs C. briggsae peptides"
 * Colour	 LIGHTGREEN
 * etc
 * Style
 * <white space only lines>
 * more methods....
 * 
 * Only called if we are using zmap_styles. This parses the method looking
 * for Style tags, returns FALSE if they are invalid and logs the error.
 * 
 * We also record the Remark if there is one.
 *
 * The function also returns a pointer to the blank line that ends the current
 * method. 
 *
 */
static gboolean parseMethodStyleNames(AcedbServer server, char *method_str_in,
				      char **end_pos, gpointer user_data)
{
  gboolean result = FALSE ;
  char *method_str = method_str_in ;
  char *next_line = method_str ;
  GetMethodsStyles get_sets = (GetMethodsStyles)user_data ;
  char *name = NULL, *zmap_style = NULL, *col_parent = NULL, *remark = NULL ;
  int obj_lines ;


  /* This should be in parse types really and then parsetypes should move on to next obj. */
  if (!g_str_has_prefix(method_str, "Method : "))
    return FALSE ;


  obj_lines = 0 ;				    /* Used to detect empty objects. */
  do
    {
      char *tag = NULL ;
      char *line_pos = NULL ;

      if (!(tag = strtok_r(next_line, "\t ", &line_pos)))
	break ;

      /* We don't formally test this but Method _MUST_ be the first line of the acedb output
       * representing an object. */
      if (g_ascii_strcasecmp(tag, "Method") == 0)
	{
	  /* Line format:    Method : "possibly long method name"  */
	  name = strtok_r(NULL, "\"", &line_pos) ;
	  name = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;

	  if (!name)
	    {
	      result = FALSE ;
	      break ;
	    }
	  else
	    {
	      result = TRUE ;
	    }
	}
      else if (server->has_new_tags && g_ascii_strcasecmp(tag, "Style") == 0)
	{
	  /* Format for this tag:   Style     "some_zmap_style" */
	  zmap_style = strtok_r(NULL, "\"", &line_pos) ; /* Skip '"' */
	  zmap_style = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;

	  if (!zmap_style)
	    {
	      result = FALSE ;
	      break ;
	    }
	  else
	    {
	      result = TRUE ;
	    }
	}
      else if (server->has_new_tags && g_ascii_strcasecmp(tag, COL_PARENT) == 0)
	{
	  /* Format for this tag:   Column_parent     "some_method" */
	  if ((col_parent = strtok_r(NULL, "\"", &line_pos))) /* Skip '"' */
	    col_parent = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (server->has_new_tags && g_ascii_strcasecmp(tag, "Remark") == 0)
	{
	  /* Line format:    Remark "possibly quite long bit of text"  */

	  remark = strtok_r(NULL, "\"", &line_pos) ;
	  remark = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}


    }
  while (++obj_lines && **end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;

  /* acedb can have empty objects which consist of a first line only. */
  if (obj_lines == 1)
    {
      result = FALSE ;
    }
  
  /* If we failed while processing a method we won't have reached the end of the current
   * method paragraph so we need to skip to the end so the next method can be processed. */
  if (!result)
    {
      while (**end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;
    }

  if (result)
    {
      GQuark method_id = 0, style_id = 0, text_id = 0, feature_set_id ;
      ZMapGFFSource source_data ;

      /* name/style are mandatory, remark is optional. */
      method_id = zMapStyleCreateID(name) ;
      style_id = zMapStyleCreateID(zmap_style) ;
      if (remark)
	text_id = g_quark_from_string(remark) ;

      get_sets->feature_methods = g_list_append(get_sets->feature_methods,
						GINT_TO_POINTER(method_id)) ;

      get_sets->required_styles = g_list_append(get_sets->required_styles,
						GINT_TO_POINTER(style_id)) ;

      if (col_parent)
	feature_set_id = zMapStyleCreateID(col_parent) ;
      else
	feature_set_id = zMapStyleCreateID(name) ;

      zMap_g_hashlist_insert(get_sets->set_2_styles, feature_set_id, GINT_TO_POINTER(style_id)) ;

      /* Record mappings we need later for parsing features. */
      source_data = g_new0(ZMapGFFSourceStruct, 1) ;
      source_data->source_id = method_id ;
      source_data->style_id = style_id ;
      source_data->source_text = text_id ;
      g_hash_table_insert(server->method_2_data, GINT_TO_POINTER(method_id), source_data) ;
    }
  else
    {
      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
		     "Feature Set \"%s\" : Method obj for feature set does not have a valid ZMap_style"
		     " so this feature set will be excluded.", name) ;
    }


  g_free(name) ;
  g_free(zmap_style) ;
  g_free(remark) ;

  return result ;
}



/* The method string should be of the form:
 *
 * Method : "wublastx_briggsae"
 * Remark	 "wublastx search of C. elegans genomic clones vs C. briggsae peptides"
 * Colour	 LIGHTGREEN
 * Frame_sensitive	
 * Show_up_strand	
 * etc
 * Column_parent | Column_child
 * Style
 * <white space only lines>
 * more methods....
 * 
 * Only called if we are using zmap_styles. Parses the method and:
 *
 * if Column_child and _no_ Style tag found then the name of the child method
 * is added to feature_methods_out and TRUE is returned.
 * 
 * or if _no_ Column_XXX tags and a Style tag is found then the name of _this_ method
 * is added to feature_methods_out and TRUE is returned.
 *
 * otherwise returns FALSE and logs the error.
 * 
 * The function also returns a pointer to the blank line that ends the current
 * method. 
 *
 */
static gboolean parseMethodColGroupNames(AcedbServer server, char *method_str_in,
					 char **end_pos, gpointer user_data)
{
  gboolean result = TRUE ;
  char *method_str = method_str_in ;
  char *next_line = method_str ;
  GetMethodsStyles get_sets = (GetMethodsStyles)user_data ;
  GList *feature_sets, *method_list, *child_list = NULL ;
  char *name = NULL, *column_parent = NULL, *column_child = NULL, *style = NULL, *remark = NULL ;
  int obj_lines = 0 ;					    /* Used to detect empty objects. */


  if (!g_str_has_prefix(method_str, "Method : "))
    {
      get_sets->error = TRUE ;
      return FALSE ;
    }

  feature_sets = get_sets->feature_set_methods ;
  method_list = get_sets->feature_methods ;

  do
    {
      char *tag = NULL ;
      char *line_pos = NULL ;

      if (!(tag = strtok_r(next_line, "\t ", &line_pos)))
	break ;

      /* We don't formally test this but Method _MUST_ be the first line of the acedb output
       * representing an object. */
      if (g_ascii_strcasecmp(tag, "Method") == 0)
	{
	  /* Line format:    Method : "possibly long method name"  */

	  name = strtok_r(NULL, "\"", &line_pos) ;
	  name = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, COL_PARENT) == 0)
	{
	  /* Format for this tag:   Column_parent     "some_method_object" */
	  column_parent = strtok_r(NULL, "\"", &line_pos) ; /* Skip '"' */
	  column_parent = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, COL_CHILD) == 0)
	{
	  /* Format for this tag:   Column_child     "some_method_object", keep a list of all
	   * children found. */
	  column_child = strtok_r(NULL, "\"", &line_pos) ; /* Skip '"' */
	  column_child = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;

	  child_list = g_list_append(child_list, GINT_TO_POINTER(zMapStyleCreateID(column_child))) ;

	  g_free(column_child) ;
	}
      else if (g_ascii_strcasecmp(tag, STYLE) == 0)
	{
	  /* Format for this tag:   Style     "some_style_object" */
	  style = strtok_r(NULL, "\"", &line_pos) ; /* Skip '"' */
	  style = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Remark") == 0)
	{
	  /* Line format:    Remark "possibly quite long bit of text"  */

	  remark = strtok_r(NULL, "\"", &line_pos) ;
	  remark = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
    }
  while (++obj_lines && **end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;


  /* acedb can have empty objects which consist of a first line only. */
  if (obj_lines == 1)
    {
      result = FALSE ;
      get_sets->error = TRUE ;
    }

  
  if (!result)
    {
      /* If we failed while processing a method we won't have reached the end of the current
       * method paragraph so we need to skip to the end so the next method can be processed. */

      while (**end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;
    }
  else
    {
      if (column_parent)
	{
	  /* None of these methods should be true "child" methods, they should either be parent
	   * methods or feature set methods in their own right. */
	  result = FALSE ;
	}
      else
	{
	  if ((child_list && !style) || (!child_list && style))
	    {
	      HashFeatureSetStruct hash_data = {0} ;
	      GQuark feature_set_id ;

	      feature_set_id = zMapStyleCreateID(name) ;

	      hash_data.feature_set_id = feature_set_id ;
	      hash_data.remark = remark ;
	      hash_data.method_2_feature_set = server->method_2_feature_set ;

	      /* Add this feature_set to valid list. */
	      feature_sets = g_list_append(feature_sets, GINT_TO_POINTER(feature_set_id)) ;

	      if (!child_list)
		{
		  addMethodCB(GINT_TO_POINTER(zMapStyleCreateID(name)), &hash_data) ;

		  /* This has no children but has it's own features so add it to the feature
		   * method list. */
		  method_list = g_list_append(method_list, GINT_TO_POINTER(g_quark_from_string(name))) ;
		}
	      else
		{
		  g_list_foreach(child_list, addMethodCB, &hash_data) ;

		  /* Add all the child methods to the feature method list. */
		  method_list = g_list_concat(method_list, child_list) ; /* Subsumes child list, no
									    need to free. */

		  child_list = NULL ;
		}

	      get_sets->feature_set_methods = feature_sets ;
	      get_sets->feature_methods = method_list ;	      
	    }
	  else
	    {
	      zMapLogWarning("Method \"%s\" ignored, Column Methods should either have " COL_CHILD
                             " or " STYLE " tags, not both.", name) ; 

	      result = FALSE ;
	    }
	}
    }

  if (child_list)
    g_list_free(child_list) ;
  g_free(name) ;
  g_free(column_parent) ;
  g_free(style) ;

  return result ;
}


/* GFunc() to add child entries to our hash of methods to feature_sets. */
static void addMethodCB(gpointer data, gpointer user_data)
{
  GQuark child_id = GPOINTER_TO_INT(data) ;
  HashFeatureSet hash_data = (HashFeatureSet)user_data ;
  ZMapGFFSet set_data ;


  set_data = g_new0(ZMapGFFSetStruct, 1) ;
  set_data->feature_set_id = hash_data->feature_set_id ;
  set_data->description = hash_data->remark ;

  g_hash_table_insert(hash_data->method_2_feature_set,
		      GINT_TO_POINTER(child_id),
		      set_data) ;

  return ;
}




/* The method string should be of the form:
 *
 * Method : "wublastx_briggsae"
 * Remark	 "wublastx search of C. elegans genomic clones vs C. briggsae peptides"
 * Colour	 LIGHTGREEN
 * Frame_sensitive	
 * Show_up_strand	
 * <white space only line>
 * more methods....
 * 
 * This parses the method using it to create a style struct which it returns.
 * The function also returns a pointer to the blank line that ends the current
 * method. 
 *
 * If the method name is not in the list of methods in requested_types
 * then NULL is returned. NOTE that this is not just dependent on comparing method
 * name to the requested list we have to look in column group as well.
 * 
 * Acedb had the concept of empty objects, these are objects whose name/class can
 * be looked up but which do not have an instance in the database. The code will NOT
 * produce styles for these objects.
 * 
 * Acedb methods can also contain a "No_display" tag which says "do not display this
 * object at all", if we find this tag we honour it. NOTE however that this can lead
 * to error messages during zmap display if the feature_set erroneously tries to
 * display features with this method.
 * 
 */
ZMapFeatureTypeStyle parseMethod(char *method_str_in,
				 char **end_pos, ZMapColGroupData *col_group_data_out)
{
  ZMapFeatureTypeStyle style = NULL ;
  char *method_str = method_str_in ;
  char *next_line = method_str ;
  char *name = NULL, *remark = NULL, *parent = NULL ;
  char *colour = NULL, *cds_colour = NULL, *outline = NULL, *foreground = NULL, *background = NULL ;
  char *gff_source = NULL, *gff_feature = NULL ;
  char *column_group = NULL, *orig_style = NULL ;
  ZMapStyleBumpMode default_bump_mode = ZMAPBUMP_OVERLAP, curr_bump_mode = ZMAPBUMP_UNBUMP ;
  double width = -999.0 ;				    /* this is going to cause problems.... */
  gboolean strand_specific = FALSE, show_up_strand = FALSE ;
  ZMapStyle3FrameMode frame_mode = ZMAPSTYLE_3_FRAME_INVALID ;
  ZMapStyleMode mode = ZMAPSTYLE_MODE_INVALID ;
  gboolean displayable = TRUE ;
  ZMapStyleColumnDisplayState col_state = ZMAPSTYLE_COLDISPLAY_INVALID ;
  double min_mag = 0.0, max_mag = 0.0 ;
  gboolean score_set = FALSE ;
  double min_score = 0.0, max_score = 0.0 ;
  gboolean score_by_histogram = FALSE ;
  double histogram_baseline = 0.0 ;
  gboolean status = TRUE, outline_flag = FALSE, directional_end = FALSE, gaps = FALSE, join_aligns = FALSE ;
  gboolean deferred_flag = FALSE;
  int obj_lines ;
  int within_align_error = 0, between_align_error = 0 ;


  if (!g_str_has_prefix(method_str, "Method : "))
    return style ;

  obj_lines = 0 ;				    /* Used to detect empty objects. */
  do
    {
      char *tag = NULL ;
      char *line_pos = NULL ;

      if (!(tag = strtok_r(next_line, "\t ", &line_pos)))
	break ;

      /* We don't formally test this but Method _MUST_ be the first line of the acedb output
       * representing an object. */
      if (g_ascii_strcasecmp(tag, "Method") == 0)
	{
	  /* Line format:    Method : "possibly long method name"  */

	  name = strtok_r(NULL, "\"", &line_pos) ;
	  name = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;

	}

      if (g_ascii_strcasecmp(tag, "Remark") == 0)
	{
	  /* Line format:    Remark "possibly quite long bit of text"  */

	  remark = strtok_r(NULL, "\"", &line_pos) ;
	  remark = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Colour") == 0)
	{
	  char *tmp_colour ;

	  tmp_colour = strtok_r(NULL, " ", &line_pos) ;

	  /* Is colour one of the standard acedb colours ? It's really an acedb bug if it
	   * isn't.... */
	  if (!(colour = getAcedbColourSpec(tmp_colour)))
	    colour = tmp_colour ;
	  
	  colour = g_strdup(colour) ;
	}
      else if (g_ascii_strcasecmp(tag, "ZMap_mode_text") == 0)
	{
	  mode = ZMAPSTYLE_MODE_TEXT ;
	}
      else if (g_ascii_strcasecmp(tag, "ZMap_mode_basic") == 0)
	{
	  mode = ZMAPSTYLE_MODE_BASIC ;
	}
      else if (g_ascii_strcasecmp(tag, "ZMap_mode_graph") == 0)
	{
	  mode = ZMAPSTYLE_MODE_GRAPH ;
	}
      else if (g_ascii_strcasecmp(tag, "Deferred") == 0)
	{
	  deferred_flag = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Immediate") == 0)
	{
	  deferred_flag = FALSE ;
	}
      else if (g_ascii_strcasecmp(tag, "Outline") == 0)
	{
	  outline_flag = TRUE;
	}
      else if (g_ascii_strcasecmp(tag, "CDS_colour") == 0)
	{
	  char *tmp_colour ;

	  tmp_colour = strtok_r(NULL, " ", &line_pos) ;

	  /* Is colour one of the standard acedb colours ? It's really an acedb bug if it
	   * isn't.... */
	  if (!(cds_colour = getAcedbColourSpec(tmp_colour)))
	    cds_colour = tmp_colour ;
	  
	  cds_colour = g_strdup(cds_colour) ;
	}

      /* The link between bump mode and what actually happens to the column is not straight
       * forward in acedb, really it should be partly dependant on feature type.... */
      else if (g_ascii_strcasecmp(tag, "Bump") == 0)
	{
	  default_bump_mode = ZMAPBUMP_OVERLAP ;
	  curr_bump_mode = ZMAPBUMP_UNBUMP ;
	}
      else if (g_ascii_strcasecmp(tag, "Bumpable") == 0
	       || g_ascii_strcasecmp(tag, "Cluster") == 0)
	{
	  default_bump_mode = curr_bump_mode = ZMAPBUMP_NAME_BEST_ENDS ; 
	}

      else if (g_ascii_strcasecmp(tag, "GFF_source") == 0)
	{
	  gff_source = g_strdup(strtok_r(NULL, " \"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "GFF_feature") == 0)
	{
	  gff_feature = g_strdup(strtok_r(NULL, " \"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Width") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &width)))
	    {
	      zMapLogWarning("No value for \"Width\" specified in method: %s", name) ;
	      break ;
	    }
	}

      else if (g_ascii_strcasecmp(tag, "Strand_sensitive") == 0)
	strand_specific = TRUE ;
      else if (g_ascii_strcasecmp(tag, "Show_up_strand") == 0)
	show_up_strand = TRUE ;
      else if (g_ascii_strcasecmp(tag, "Frame_sensitive") == 0)
	frame_mode = ZMAPSTYLE_3_FRAME_ALWAYS ;

      else if (g_ascii_strcasecmp(tag, "No_display") == 0)
	{
	  /* Objects that have the No_display tag set should not be shown at all. */
	  displayable = FALSE ;

	  /* If No_display is the first tag in the models file we end
	   * up breaking here and obj_lines == 1 forcing status =
	   * FALSE later on. */
#ifdef CANT_BREAK_HERE
	  break ;
#endif
	}
      else if (g_ascii_strcasecmp(tag, "init_hidden") == 0)
	{
	  col_state = ZMAPSTYLE_COLDISPLAY_HIDE ;
	}
      else if (g_ascii_strcasecmp(tag, "Directional_ends") == 0)
	{
	  directional_end = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Gapped") == 0)
	{
	  char *value ;

	  gaps = TRUE ;

	  if ((value = strtok_r(NULL, " ", &line_pos)))
	    {
	      if (!(status = zMapStr2Int(value, &within_align_error)))
		{
		  zMapLogWarning("Bad value for \"Gapped\" align error specified in method: %s", name) ;
		  
		  break ;
		}
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Join_aligns") == 0)
	{
	  char *value ;

	  join_aligns = TRUE ;

	  if ((value = strtok_r(NULL, " ", &line_pos)))
	    {
	      if (!(status = zMapStr2Int(value, &between_align_error)))
		{
		  zMapLogWarning("Bad value for \"Join_aligns\" align error specified in method: %s", name) ;
		  
		  break ;
		}
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Min_mag") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &min_mag)))
	    {
	      zMapLogWarning("Bad value for \"Min_mag\" specified in method: %s", name) ;
	      
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Max_mag") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &max_mag)))
	    {
	      zMapLogWarning("Bad value for \"Max_mag\" specified in method: %s", name) ;
	      
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Score_by_histogram") == 0)
	{
	  char *value ;

	  score_by_histogram = TRUE ;

	  if((value = strtok_r(NULL, " ", &line_pos)))
            {
              if (!(status = zMapStr2Double(value, &histogram_baseline)))
                {
                  zMapLogWarning("Bad value for \"Score_by_histogram\" specified in method: %s", name) ;
                  
                  break ;
                }
            }
	}
      else if (g_ascii_strcasecmp(tag, "Score_bounds") == 0)
	{
	  char *value ;

	  score_set = TRUE ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &min_score)))
	    {
	      zMapLogWarning("Bad value for \"Score_bounds\" specified in method: %s", name) ;
	      score_set = FALSE ;
	      break ;
	    }
	  else
	    {
	      value = strtok_r(NULL, " ", &line_pos) ;

	      if (!(status = zMapStr2Double(value, &max_score)))
		{
		  zMapLogWarning("Bad value for \"Score_bounds\" specified in method: %s", name) ;
		  score_set = FALSE ;
		  break ;
		}
	    }
	}
    }
  while (++obj_lines && **end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;

  /* acedb can have empty objects which consist of a first line only. */
  if (obj_lines == 1)
    {
      status = FALSE ;
    }

  
  /* If we failed while processing a method we won't have reached the end of the current
   * method paragraph so we need to skip to the end so the next method can be processed. */
  if (!status)
    {
      while (**end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;
    }

  /* Set some final method stuff and create the ZMap style. */
  if (status)
    {
      /* NOTE that style is created with the method name, NOT the column_group, column
       * names are independent of method names, they may or may not be the same.
       * Also, there is no way of deriving the mode from the acedb method object
       * currently, we have to set it later. */
      style = zMapStyleCreate(name, remark) ;

      if (mode != ZMAPSTYLE_MODE_INVALID)
	zMapStyleSetMode(style, mode) ;


      /* In acedb methods the colour is interpreted differently according to the type of the
       * feature which we have to intuit here from the GFF type. acedb also has colour names
       * that don't exist in X Windows. */
      if (colour || cds_colour)
	{
	  /* When it comes to colours acedb has some built in rules which we have to simulate,
	   * e.g. "Colour" when applied to transcripts means "outline", not "fill".
	   *  */
#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
	  /* THIS IS REALLY THE PLACE THAT WE SHOULD SET UP THE ACEDB SPECIFIC DISPLAY STUFF... */

	  /* this doesn't work because it messes up the rev. video.... */
	  if (gff_type && (g_ascii_strcasecmp(gff_type, "\"similarity\"") == 0
			   || g_ascii_strcasecmp(gff_type, "\"repeat\"")
			   || g_ascii_strcasecmp(gff_type, "\"experimental\"")))
	    background = colour ;
	  else
	    outline = colour ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */

	  /* Set default acedb colours. */
	  background = colour ;
	  outline = "black" ;

	  /* If there's a cds then it must be a transcript which are shown in outline. */
	  if (cds_colour
	      || g_ascii_strcasecmp(name, "coding_transcript") == 0)
	    {
	      outline = colour ;
	      background = NULL ;
	    }


	  if (colour)
	    zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_NORMAL, ZMAPSTYLE_COLOURTYPE_NORMAL,
				background, foreground, outline) ;

	  if (cds_colour)
	    {
	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_CDS, ZMAPSTYLE_COLOURTYPE_NORMAL,
				  NULL, NULL, cds_colour) ;
	      g_free(cds_colour);
	      cds_colour = NULL;
	    }
	}


      if (width != -999.0)
	{
	  /* acedb widths are wider on the screen than zmaps, so scale them up. */
	  width = width * ACEDB_MAG_FACTOR ;

	  zMapStyleSetWidth(style, width) ;
	}

      if (parent)
	zMapStyleSetParent(style, parent) ;

      if (min_mag || max_mag)
	zMapStyleSetMag(style, min_mag, max_mag) ;

      /* Note that we require bounds to be set for graphing.... */
      if (score_set)
	{
	  ZMapStyleGraphMode graph_mode = ZMAPSTYLE_GRAPH_HISTOGRAM ; /* Hard coded for now. */

	  if (score_by_histogram)
	    zMapStyleSetGraph(style, graph_mode, min_score, max_score, histogram_baseline) ;
	  else
	    zMapStyleSetScore(style, min_score, max_score) ;
	}

      if (strand_specific)
	zMapStyleSetStrandSpecific(style, strand_specific) ;

      if (show_up_strand)
	zMapStyleSetStrandShowReverse(style, show_up_strand) ;

      if (frame_mode)
	zMapStyleSetFrameMode(style, frame_mode) ;

      zMapStyleInitBumpMode(style, default_bump_mode, curr_bump_mode) ;

      if (gff_source || gff_feature)
	zMapStyleSetGFF(style, gff_source, gff_feature) ;

      zMapStyleSetDisplayable(style, displayable) ;

      zMapStyleSetDeferred(style, deferred_flag) ;
      zMapStyleSetLoaded(style, FALSE) ;

      if (col_state != ZMAPSTYLE_COLDISPLAY_INVALID)
	zMapStyleSetDisplay(style, col_state) ;

      if(directional_end)
        zMapStyleSetEndStyle(style, directional_end);

      /* Current setting is for gaps to be parsed but they will only
       * be displayed when the feature is bumped. */
      if (gaps)
	zMapStyleSetGappedAligns(style, TRUE, within_align_error) ;

      if (join_aligns)
	zMapStyleSetJoinAligns(style, between_align_error) ;
    }


  /* Clean up, note g_free() does nothing if given NULL. */
  g_free(name) ;
  g_free(remark) ;
  g_free(parent) ;
  g_free(colour) ;
  g_free(foreground) ;
  g_free(column_group) ;
  g_free(orig_style) ;
  g_free(gff_source) ;
  g_free(gff_feature) ;

  return style ;
}


/* The style string should be of the form:
 *
 * ZMap_style : "Allele"
 * Description   "Alleles in WormBase represent small sequence mutations...etc"
 * Colours	 Normal Fill "ORANGE"
 * Width	 1.100000
 * Strand_sensitive	
 * GFF	 Source "Allele"
 * GFF	 Feature "Allele"
 * <white space only line>
 * more styles....
 * 
 * This parses the style using it to create a style struct which it returns.
 * The function also returns a pointer to the blank line that ends the current
 * style. 
 *
 * If the style name is not in the list of styles in requested_types
 * then NULL is returned. NOTE that this is not just dependent on comparing style
 * name to the requested list we have to look in column group as well.
 * 
 * Acedb had the concept of empty objects, these are objects whose name/class can
 * be looked up but which do not have an instance in the database. The code will NOT
 * produce styles for these objects.
 * 
 * Acedb styles can also contain a "No_display" tag which says "do not display this
 * object at all", if we find this tag we honour it. NOTE however that this can lead
 * to error messages during zmap display if the feature_set erroneously tries to
 * display features with this style.
 * 
 */
ZMapFeatureTypeStyle parseStyle(char *style_str_in,
				char **end_pos, ZMapColGroupData *col_group_data_out)
{
  ZMapFeatureTypeStyle style = NULL ;
  gboolean status = TRUE ;
  int obj_lines ;
  char *style_str = style_str_in ;
  char *next_line = style_str ;
  char *name = NULL, *description = NULL, *parent = NULL,
    *colour = NULL, *foreground = NULL,
    *gff_source = NULL, *gff_feature = NULL,
    *column_group = NULL, *orig_style = NULL ;
  gboolean width_set = FALSE ;
  gboolean deferred = FALSE ;
  double width = 0.0 ;
  gboolean strand_specific = FALSE, show_up_strand = FALSE ;
  ZMapStyle3FrameMode frame_mode = ZMAPSTYLE_3_FRAME_INVALID ;
  ZMapStyleMode mode = ZMAPSTYLE_MODE_INVALID ;
  ZMapStyleGlyphMode glyph_mode = ZMAPSTYLE_GLYPH_INVALID ;
  gboolean displayable_set = TRUE, displayable = TRUE,
    show_when_empty_set = FALSE, show_when_empty = FALSE ;
  ZMapStyleColumnDisplayState col_state = ZMAPSTYLE_COLDISPLAY_INVALID ;
  double min_mag = 0.0, max_mag = 0.0 ;
  gboolean directional_end_set = FALSE, directional_end = FALSE ;
  gboolean internal = FALSE, external = FALSE, allow_misalign = FALSE ;
  int within_align_error = 0, between_align_error = 0 ;
  gboolean bump_mode_set = FALSE, bump_default_set = FALSE ;
  ZMapStyleBumpMode default_bump_mode = ZMAPBUMP_INVALID, curr_bump_mode = ZMAPBUMP_INVALID ;
  gboolean bump_spacing_set = FALSE ;
  double bump_spacing = 0.0 ;
  gboolean bump_fixed = FALSE ;
  gboolean some_colours = FALSE ;
  StyleFeatureColoursStruct style_colours = {{NULL}, {NULL}} ;
  gboolean some_frame0_colours = FALSE, some_frame1_colours = FALSE, some_frame2_colours = FALSE ;
  StyleFeatureColoursStruct frame0_style_colours = {{NULL}, {NULL}},
    frame1_style_colours = {{NULL}, {NULL}}, frame2_style_colours = {{NULL}, {NULL}} ;
  gboolean some_CDS_colours = FALSE ;
  StyleFeatureColoursStruct CDS_style_colours = {{NULL}, {NULL}} ;
  double min_score = 0.0, max_score = 0.0 ;
  gboolean score_by_width, score_is_percent ;
  gboolean histogram = FALSE ;
  double histogram_baseline = 0.0 ;
  gboolean pfetchable = FALSE ;
  ZMapStyleBlixemType blixem_type = ZMAPSTYLE_BLIXEM_INVALID ;


  if (g_ascii_strncasecmp(style_str, "ZMap_style : ", strlen("ZMap_style : ")) != 0)
    return style ;


  obj_lines = 0 ;				    /* Used to detect empty objects. */
  do
    {
      char *tag = NULL ;
      char *line_pos = NULL ;

      if (!(tag = strtok_r(next_line, "\t ", &line_pos)))
	break ;

      /* We don't formally test this but Style _MUST_ be the first line of the acedb output
       * representing an object. */
      if (g_ascii_strcasecmp(tag, "ZMap_style") == 0)
	{
	  /* Line format:    ZMap_style : "possibly long style name"  */

	  name = strtok_r(NULL, "\"", &line_pos) ;
	  name = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}


      if (g_ascii_strcasecmp(tag, "Description") == 0)
	{
	  /* Line format:    Description "possibly quite long bit of text"  */

	  description = strtok_r(NULL, "\"", &line_pos) ;
	  description = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Style_parent") == 0)
	{
	  parent = strtok_r(NULL, "\"", &line_pos) ;
	  parent = g_strdup(strtok_r(NULL, "\"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Deferred") == 0)
	{
	  deferred = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Immediate") == 0)
	{
	  deferred = FALSE ;
	}


      /* OK, THIS MODE STUFF IS NO GOOD, WE NEED TO USE THE STYLE CALL THAT WILL HAVE THE LATEST
       * ENUMS BUILT IN OTHERWISE EVERYTIME WE ADD A NEW TYPE ALL THIS FAILS.... */

      /* Grab the mode... */
      else if (g_ascii_strcasecmp(tag, "Basic") == 0)
	{
	  mode = ZMAPSTYLE_MODE_BASIC ;
	}
      else if (g_ascii_strcasecmp(tag, "Transcript") == 0)
	{
	  char *tmp_next_tag ;

	  mode = ZMAPSTYLE_MODE_TRANSCRIPT ;

	  if ((tmp_next_tag = strtok_r(NULL, " ", &line_pos)))
	    {
	      if (g_ascii_strcasecmp(tmp_next_tag, "CDS_colour") == 0)
		{
		  gboolean colour_parse ;
		  
		  if ((colour_parse = getStyleColour(&CDS_style_colours, &line_pos)))
		    some_CDS_colours = TRUE ;
		  else
		    zMapLogWarning("Style \"%s\": Bad CDS colour spec: %s", name, next_line) ;
		}
	      /* OTHER THINGS WILL NEED TO BE PARSED HERE AS WE EXPAND THIS.... */
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Alignment") == 0)
	{
	  char *align_type ;
	  char *value ;

	  mode = ZMAPSTYLE_MODE_ALIGNMENT ;

	  if ((align_type = strtok_r(NULL, " ", &line_pos)))
	    {
	      if (g_ascii_strcasecmp(align_type, "Internal") == 0)
		internal = TRUE ;
	      else if (g_ascii_strcasecmp(align_type, "External") == 0)
		external = TRUE ;
	      else if (g_ascii_strcasecmp(align_type, "Allow_misalign") == 0)
		allow_misalign = TRUE ;
	      else if (g_ascii_strcasecmp(align_type, "Pfetchable") == 0)
		pfetchable = TRUE ;
	      else if (g_ascii_strcasecmp(align_type, "Blixem_N") == 0)
		blixem_type = ZMAPSTYLE_BLIXEM_N ;
	      else if (g_ascii_strcasecmp(align_type, "Blixem_X") == 0)
		blixem_type = ZMAPSTYLE_BLIXEM_X ;
	      else
		zMapLogWarning("Style \"%s\": Unknown tag \"%s\" for \"Alignment\" specified in style: %s",
			       name, align_type, name) ;

	      if (internal || external)
		{
		  int *target ;

		  if (internal)
		    target = &within_align_error ;
		  else
		    target = &between_align_error ;

		  value = strtok_r(NULL, " ", &line_pos) ;

		  /* If no value is set then the error margin is set to 0 */
		  if (!value)
		    *target = 0 ;
		  else if (!(status = zMapStr2Int(value, target)))
		    {
		      zMapLogWarning("Style \"%s\": Bad error factor for \"Alignment   %s\" specified in style: %s",
				     name, (internal ? "Internal" : "External"), name) ;
		      break ;
		    }
		}
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Sequence") == 0)
	{
	  mode = ZMAPSTYLE_MODE_RAW_SEQUENCE ;
	}
      else if (g_ascii_strcasecmp(tag, "Peptide") == 0)
	{
	  mode = ZMAPSTYLE_MODE_PEP_SEQUENCE ;
	}
      else if (g_ascii_strcasecmp(tag, "Assembly_path") == 0)
	{
	  mode = ZMAPSTYLE_MODE_ASSEMBLY_PATH ;
	}
      else if (g_ascii_strcasecmp(tag, "Plain_text") == 0)
	{
	  mode = ZMAPSTYLE_MODE_TEXT ;
	}
      else if (g_ascii_strcasecmp(tag, "Graph") == 0)
	{
	  char *tmp_next_tag ;

	  mode = ZMAPSTYLE_MODE_GRAPH ;

	  tmp_next_tag = strtok_r(NULL, " ", &line_pos) ;

	  if (g_ascii_strcasecmp(tmp_next_tag, "Histogram") == 0)
	    {
	      histogram = TRUE ;
	    }
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Baseline") == 0)
	    {
	      char *value ;

	      value = strtok_r(NULL, " ", &line_pos) ;

	      if (!(status = zMapStr2Double(value, &histogram_baseline)))
		{
		  zMapLogWarning("Style \"%s\": No value for \"Baseline\".", name) ;
		  break ;
		}
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Glyph") == 0)
	{
	  char *tmp_next_tag ;

	  mode = ZMAPSTYLE_MODE_GLYPH ;

	  tmp_next_tag = strtok_r(NULL, " ", &line_pos) ;

	  if (tmp_next_tag && g_ascii_strcasecmp(tmp_next_tag, "Splice") == 0)
	    {
	      glyph_mode = ZMAPSTYLE_GLYPH_SPLICE ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Colours") == 0)
	{
	  gboolean colour_parse ;

	  if ((colour_parse = getStyleColour(&style_colours, &line_pos)))
	    some_colours = TRUE ;
	  else
	    zMapLogWarning("Style \"%s\": Bad colour spec: %s", name, next_line) ;
	}
      else if (g_ascii_strcasecmp(tag, "Frame_0") == 0
	       || g_ascii_strcasecmp(tag, "Frame_1") == 0
	       || g_ascii_strcasecmp(tag, "Frame_2") == 0)
	{
	  StyleFeatureColours colours = NULL ;
	  gboolean *set = NULL ;

	  if (g_ascii_strcasecmp(tag, "Frame_0") == 0)
	    {
	      colours = &frame0_style_colours ;
	      set = &some_frame0_colours ;
	    }
	  else if (g_ascii_strcasecmp(tag, "Frame_1") == 0)
	    {
	      colours = &frame1_style_colours ;
	      set = &some_frame1_colours ;
	    }
	  else if (g_ascii_strcasecmp(tag, "Frame_2") == 0)
	    {
	      colours = &frame2_style_colours ;
	      set = &some_frame2_colours ;
	    }

	  if (!colours)
	    {
	      zMapLogWarning("Style \"%s\": Bad Frame name: %s", name, next_line) ;
	    }
	  else
	    {
	      gboolean colour_parse ;
	      
	      if ((colour_parse = getStyleColour(colours, &line_pos)))
		*set = TRUE ;
	      else
		zMapLogWarning("Style \"%s\": Bad colour spec: %s", name, next_line) ;
	    }
	}

      /* Bumping types */
      else if (g_ascii_strcasecmp(tag, "Bump_initial") == 0 || g_ascii_strcasecmp(tag, "Bump_default") == 0)
	{
	  char *tmp_next_tag ;
	  ZMapStyleBumpMode *tmp_bump ;

	  if (g_ascii_strcasecmp(tag, "Bump_initial") == 0)
	    {
	      tmp_bump = &curr_bump_mode ;
	      bump_mode_set = TRUE ;
	    }
	  else
	    {
	      tmp_bump = &default_bump_mode ;
	      bump_default_set = TRUE ;
	    }

	  tmp_next_tag = strtok_r(NULL, " ", &line_pos) ;

	  if (g_ascii_strcasecmp(tmp_next_tag, "Unbump") == 0)
	    *tmp_bump = ZMAPBUMP_UNBUMP ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Overlap") == 0)
	    *tmp_bump = ZMAPBUMP_OVERLAP ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Navigator") == 0)
	    *tmp_bump = ZMAPBUMP_NAVIGATOR ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Start_position") == 0)
	    *tmp_bump = ZMAPBUMP_START_POSITION ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Alternating") == 0)
	    *tmp_bump = ZMAPBUMP_ALTERNATING ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "All") == 0)
	    *tmp_bump = ZMAPBUMP_ALL ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Name") == 0)
	    *tmp_bump = ZMAPBUMP_NAME ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Name_interleave") == 0)
	    *tmp_bump = ZMAPBUMP_NAME_INTERLEAVE ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Name_no_interleave") == 0)
	    *tmp_bump = ZMAPBUMP_NAME_NO_INTERLEAVE ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Name_colinear") == 0)
	    *tmp_bump = ZMAPBUMP_NAME_COLINEAR ;
	  else if (g_ascii_strcasecmp(tmp_next_tag, "Name_best_ends") == 0)
	    *tmp_bump = ZMAPBUMP_NAME_BEST_ENDS ;
	  else
	    zMapLogWarning("Style \"%s\": Bad bump spec: %d", name, *tmp_bump) ;
	}
      else if (g_ascii_strcasecmp(tag, "Bump_spacing") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if ((status = zMapStr2Double(value, &bump_spacing)))
	    {
	      bump_spacing_set = TRUE ;
	    }
	  else
	    {
	      zMapLogWarning("Style \"%s\": No value for \"Bump_spacing\".", name) ;
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Bump_fixed") == 0)
	{
	  bump_fixed = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "GFF") == 0)
	{
	  char *gff_type ;

	  gff_type = strtok_r(NULL, " ", &line_pos) ;

	  if (g_ascii_strcasecmp(tag, "Source") == 0)
	    gff_source = g_strdup(strtok_r(NULL, " \"", &line_pos)) ;
	  else if (g_ascii_strcasecmp(tag, "Feature") == 0)
	    gff_feature = g_strdup(strtok_r(NULL, " \"", &line_pos)) ;
	}
      else if (g_ascii_strcasecmp(tag, "Width") == 0)
	{
	  char *value ;
	  value = strtok_r(NULL, " ", &line_pos) ;

	  if ((status = zMapStr2Double(value, &width)))
	    {
	      width_set = TRUE ;
	    }
	  else
	    {
	      zMapLogWarning("Style \"%s\": No value for \"Width\".", name) ;
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Strand_sensitive") == 0)
	strand_specific = TRUE ;
      else if (g_ascii_strcasecmp(tag, "Show_up_strand") == 0)
	show_up_strand = TRUE ;
      else if (g_ascii_strcasecmp(tag, "Frame_sensitive") == 0)
	frame_mode = ZMAPSTYLE_3_FRAME_ALWAYS ;
      else if (g_ascii_strcasecmp(tag, "Show_only_as_3_frame") == 0)
	frame_mode = ZMAPSTYLE_3_FRAME_ONLY_3 ;
      else if (g_ascii_strcasecmp(tag, "Show_only_as_1_column") == 0)
	frame_mode = ZMAPSTYLE_3_FRAME_ONLY_1 ;
      else if (g_ascii_strcasecmp(tag, "Not_displayable") == 0)
	{
	  /* Objects that have the Not_displayable tag set should not be shown at all. */
	  displayable_set = displayable = FALSE ;

	  break ;
	}
      else if (g_ascii_strcasecmp(tag, "Hide") == 0)
	{
	  col_state = ZMAPSTYLE_COLDISPLAY_HIDE ;
	}
      else if (g_ascii_strcasecmp(tag, "Show_when_empty") == 0)
	{
	  show_when_empty_set = show_when_empty = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Directional_ends") == 0)
	{
	  directional_end_set = directional_end = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Min_mag") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &min_mag)))
	    {
	      zMapLogWarning("Style \"%s\": Bad value for \"Min_mag\"", name) ;
	      
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Max_mag") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &max_mag)))
	    {
	      zMapLogWarning("Style \"%s\": Bad value for \"Max_mag\".", name) ;
	      
	      break ;
	    }
	}
      else if (g_ascii_strcasecmp(tag, "Score_by_width") == 0)
	{
	  score_by_width = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Score_percent") == 0)
	{
	  score_is_percent = TRUE ;
	}
      else if (g_ascii_strcasecmp(tag, "Score_bounds") == 0)
	{
	  char *value ;

	  value = strtok_r(NULL, " ", &line_pos) ;

	  if (!(status = zMapStr2Double(value, &min_score)))
	    {
	      zMapLogWarning("Style \"%s\": Bad value for \"Score_bounds\".", name) ;
	      
	      break ;
	    }
	  else
	    {
	      value = strtok_r(NULL, " ", &line_pos) ;

	      if (!(status = zMapStr2Double(value, &max_score)))
		{
		  zMapLogWarning("Style \"%s\": Bad value for \"Score_bounds\".", name) ;
		  
		  break ;
		}
	    }
	}

    }
  while (++obj_lines && **end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;

  /* acedb can have empty objects which consist of a first line only. */
  if (obj_lines == 1)
    {
      status = FALSE ;
    }

  
  /* If we failed while processing a style we won't have reached the end of the current
   * style paragraph so we need to skip to the end so the next style can be processed. */
  if (!status)
    {
      while (**end_pos != '\n' && (next_line = strtok_r(NULL, "\n", end_pos))) ;
    }


  /* Create the style and add all the bits to it. */
  if (status)
    {
      /* NOTE that style is created with the style name, NOT the column_group, column
       * names are independent of style names, they may or may not be the same.
       * Also, there is no way of deriving the mode from the acedb style object
       * currently, we have to set it later. */
      style = zMapStyleCreate(name, description) ;

      if (mode != ZMAPSTYLE_MODE_INVALID)
	zMapStyleSetMode(style, mode) ;

      if (glyph_mode != ZMAPSTYLE_GLYPH_INVALID)
	zMapStyleSetGlyphMode(style, glyph_mode) ;

      if (parent)
	zMapStyleSetParent(style, parent) ;

      if (deferred)
	zMapStyleSetDeferred(style, deferred) ;

      if (some_colours)
	{
	  /* May need to put some checking code here to test which colours set. */
	  zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_NORMAL, ZMAPSTYLE_COLOURTYPE_NORMAL,
			      style_colours.normal.fill, style_colours.normal.draw, style_colours.normal.border) ;
	  zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_NORMAL, ZMAPSTYLE_COLOURTYPE_SELECTED,
			      style_colours.selected.fill, style_colours.selected.draw, style_colours.selected.border) ;
	}

      if (some_frame0_colours || some_frame1_colours || some_frame2_colours)
	{
	  if (some_frame0_colours && some_frame1_colours && some_frame2_colours)
	    {
	      /* May need to put some checking code here to test which colours set. */
	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME0, ZMAPSTYLE_COLOURTYPE_NORMAL,
				  frame0_style_colours.normal.fill, frame0_style_colours.normal.draw, frame0_style_colours.normal.border) ;
	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME0, ZMAPSTYLE_COLOURTYPE_SELECTED,
				  frame0_style_colours.selected.fill, frame0_style_colours.selected.draw, frame0_style_colours.selected.border) ;

	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME1, ZMAPSTYLE_COLOURTYPE_NORMAL,
				  frame1_style_colours.normal.fill, frame1_style_colours.normal.draw, frame1_style_colours.normal.border) ;
	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME1, ZMAPSTYLE_COLOURTYPE_SELECTED,
				  frame1_style_colours.selected.fill, frame1_style_colours.selected.draw, frame1_style_colours.selected.border) ;

	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME2, ZMAPSTYLE_COLOURTYPE_NORMAL,
				  frame2_style_colours.normal.fill, frame2_style_colours.normal.draw, frame2_style_colours.normal.border) ;
	      zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_FRAME2, ZMAPSTYLE_COLOURTYPE_SELECTED,
				  frame2_style_colours.selected.fill, frame2_style_colours.selected.draw, frame2_style_colours.selected.border) ;
	    }
	  else
	    zMapLogWarning("Style \"%s\": Bad frame colour spec, following were not set:%s%s%s", name, 
			   (some_frame0_colours ? "" : " frame0"),
			   (some_frame1_colours ? "" : " frame1"),
			   (some_frame2_colours ? "" : " frame2")) ;
	}

      if (some_CDS_colours)
	{
	  /* May need to put some checking code here to test which colours set. */
	  zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_CDS, ZMAPSTYLE_COLOURTYPE_NORMAL,
			      CDS_style_colours.normal.fill, CDS_style_colours.normal.draw, CDS_style_colours.normal.border) ;
	  zMapStyleSetColours(style, ZMAPSTYLE_COLOURTARGET_CDS, ZMAPSTYLE_COLOURTYPE_SELECTED,
			      CDS_style_colours.selected.fill, CDS_style_colours.selected.draw, CDS_style_colours.selected.border) ;
	}

      if (width_set)
	zMapStyleSetWidth(style, width) ;


      if (min_mag || max_mag)
	zMapStyleSetMag(style, min_mag, max_mag) ;


      /* OTHER SCORE STUFF MUST BE SET HERE.... */

      if (min_score && max_score)
	zMapStyleSetScore(style, min_score, max_score) ;

      if (strand_specific)
	zMapStyleSetStrandSpecific(style, strand_specific) ;

      if (show_up_strand)
	zMapStyleSetStrandShowReverse(style, show_up_strand) ;

      if (frame_mode)
	zMapStyleSetFrameMode(style, frame_mode) ;

      if (bump_mode_set || bump_default_set)
	zMapStyleInitBumpMode(style, default_bump_mode, curr_bump_mode) ;

      if (bump_spacing_set)
	zMapStyleSetBumpSpace(style, bump_spacing) ;

      if (bump_fixed)
	zMapStyleSet(style,
		     ZMAPSTYLE_PROPERTY_BUMP_FIXED, bump_fixed,
		     NULL) ;

      if (gff_source || gff_feature)
	zMapStyleSetGFF(style, gff_source, gff_feature) ;

      if (displayable_set)
	zMapStyleSetDisplayable(style, displayable) ;

      if (col_state != ZMAPSTYLE_COLDISPLAY_INVALID)
	zMapStyleSetDisplay(style, col_state) ;

      if (show_when_empty_set)
	zMapStyleSetShowWhenEmpty(style, show_when_empty) ;

      if(directional_end_set)
        zMapStyleSetEndStyle(style, directional_end);

      if (internal)
	zMapStyleSetGappedAligns(style, TRUE, within_align_error) ;

      if (external)
	zMapStyleSetJoinAligns(style, between_align_error) ;

      if (pfetchable)
	zMapStyleSetPfetch(style, pfetchable) ;

      /* Should be building the list dynamically.....could do this on the create step using
       * my zMapStyleCreateV() function. */
      if (blixem_type)
	zMapStyleSet(style,
		     ZMAPSTYLE_PROPERTY_ALIGNMENT_BLIXEM, blixem_type,
		     ZMAPSTYLE_PROPERTY_ALIGNMENT_ALLOW_MISALIGN, allow_misalign,
		     NULL) ;
    }


  /* Clean up, note g_free() does nothing if given NULL. */
  g_free(name) ;
  g_free(description) ;
  g_free(colour) ;
  g_free(foreground) ;
  g_free(column_group) ;
  g_free(orig_style) ;
  g_free(gff_source) ;
  g_free(gff_feature) ;

  return style ;
}


/* Gets a list of all styles by name on the server (this is the list of acedb methods/styles).
 * Returns TRUE and the list if database contained any methods, FALSE otherwise.
 * 
 * 
 * acedb> list
 * 
 * KeySet : Answer_1
 * Method:
 *  cDNA_for_RNAi
 *  code default
 *  Coding
 *  Coding_transcript
 *  Coil
 *  curated
 * 
 * 
 * // 6 object listed
 * // 6 Active Objects
 * acedb>
 * 
 *  */
static ZMapServerResponseType getObjNames(AcedbServer server, GList **style_names_out)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  char *command ;
  char *acedb_request = NULL ;
  void *reply = NULL ;
  int reply_len = 0 ;

  /* List all the methods in the current keyset on the serve. */
  command = "list" ;
  acedb_request =  g_strdup_printf("%s", command) ;

  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request,
						&reply, &reply_len)) == ACECONN_OK)
    {
      char *scan_text = (char *)reply ;
      char *next_line = NULL ;
      gboolean found_method = FALSE ;
      GList *style_names = NULL ;

      while ((next_line = strtok(scan_text, "\n")))
	{
	  scan_text = NULL ;

	  /* Look for start/end of methods list. */
	  if (!found_method && g_str_has_prefix(next_line, "Method:"))
	    {
	      found_method = TRUE ;
	      continue ;
	    }
	  else if (found_method && (*next_line == '/'))
	    break ;

	  if (found_method)
	    {
	      /* Watch out...hacky...method names have a space in front of them...sigh... */
	      style_names = g_list_append(style_names,
					  GINT_TO_POINTER(g_quark_from_string(next_line + 1))) ;
	    }
	}


      if (style_names)
	{
	  *style_names_out = style_names ;
	  result = ZMAP_SERVERRESPONSE_OK ;
	}
      else
	{
	  setErrMsg(server,  g_strdup_printf("No styles found.")) ;
	  result = ZMAP_SERVERRESPONSE_REQFAIL ;
	}

      g_free(reply) ;
      reply = NULL ;
    }
  else
    result = server->last_err_status ;

  g_free(acedb_request) ;


  return result ;
}



/* GCompareDataFunc () used to resort our list of styles to match users original sorting. */
gint resortStyles(gconstpointer a, gconstpointer b, gpointer user_data)
{
  gint result = 0 ;
  ZMapFeatureTypeStyle style_a = (ZMapFeatureTypeStyle)a, style_b = (ZMapFeatureTypeStyle)b ;
  GList *style_list = (GList *)user_data ;
  gint pos_a, pos_b ;
  
  pos_a = g_list_index(style_list, GUINT_TO_POINTER(zMapStyleGetUniqueID(style_a))) ;
  pos_b = g_list_index(style_list, GUINT_TO_POINTER(zMapStyleGetUniqueID(style_b))) ;
  zMapAssert(pos_a >= 0 && pos_b >= 0 && pos_a != pos_b) ;

  if (pos_a < pos_b)
    result = -1 ;
  else
    result = 1 ;

  return result ;
}



/* Parses the string returned by an acedb "find" command, if the command found objects
 * it returns a string of the form:
 *                                   "// Found 1 objects in this class"
 */
int getFoundObj(char *text)
{
  int num_obj = 0 ;

  if (strstr(text, "Found"))
    {
      char *next ;
	      
      next = strtok(text, " ") ;
      next = strtok(NULL, " ") ;
      next = strtok(NULL, " ") ;

      num_obj = atoi(next) ;
    }

  return num_obj ;
}

/* Process all the alignments in a context. */
static void eachAlignment(gpointer key, gpointer data, gpointer user_data)
{
  ZMapFeatureAlignment alignment = (ZMapFeatureAlignment)data ;
  DoAllAlignBlocks all_data = (DoAllAlignBlocks)user_data ;

  if (all_data->result == ZMAP_SERVERRESPONSE_OK && all_data->eachBlock)
    g_hash_table_foreach(alignment->blocks, all_data->eachBlock, (gpointer)all_data) ;

  return ;
}



static void eachBlockSequenceRequest(gpointer key_id, gpointer data, gpointer user_data)
{
  ZMapFeatureBlock feature_block = (ZMapFeatureBlock)data ;
  DoAllAlignBlocks get_features = (DoAllAlignBlocks)user_data ;

  if (get_features->result == ZMAP_SERVERRESPONSE_OK)
    {
      if (!sequenceRequest(get_features->server, get_features->styles, feature_block))
	{
	  /* If the call failed it may be that the connection failed or that the data coming
	   * back had a problem. */
	  if (get_features->server->last_err_status == ACECONN_OK)
	    {
	      get_features->result = ZMAP_SERVERRESPONSE_REQFAIL ;
	    }
	  else if (get_features->server->last_err_status == ACECONN_TIMEDOUT)
	    {
	      get_features->result = ZMAP_SERVERRESPONSE_TIMEDOUT ;
	    }
	  else
	    {
	      /* Probably we will want to analyse the response more than this ! */
	      get_features->result = ZMAP_SERVERRESPONSE_SERVERDIED ;
	    }

	  ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, get_features->server->host,
			 "Could not map %s because: %s",
			 g_quark_to_string(get_features->server->req_context->sequence_name),
			 get_features->server->last_err_msg) ;
	}
    }

  return ;
}


/* This table is derived from acedb/w2/graphcolour.c, since acedb colours have not changed
 * in a long time it is unlikely to need updating very often.
 * 
 * The reason for having this function is that acedb colour names do not ALL match the standard
 * colour names in the X11 colour database and so cannot be used as input to the gdk colour
 * functions. I tried to use a proper Xcms colour spec but stupid gdk_color_parse() does
 * not understand these colours so have used the now deprecated "#RRGGBB" format below.
 *  */
static char *getAcedbColourSpec(char *acedb_colour_name)
{
  char *colour_spec = NULL ;
  static AcedbColourSpecStruct colours[] =
    {
      {"WHITE", "#ffffff"},
      {"BLACK", "#000000"},
      {"LIGHTGRAY", "#c8c8c8"},
      {"DARKGRAY", "#646464"},
      {"RED", "#ff0000"},
      {"GREEN", "#00ff00"},
      {"BLUE", "#0000ff"},
      {"YELLOW", "#ffff00"},
      {"CYAN", "#00ffff"},
      {"MAGENTA", "#ff00ff"},
      {"LIGHTRED", "#ffa0a0"},
      {"LIGHTGREEN", "#a0ffa0"},
      {"LIGHTBLUE", "#a0c8ff"},
      {"DARKRED", "#af0000"},
      {"DARKGREEN", "#00af00"},
      {"DARKBLUE", "#0000af"},
      {"PALERED", "#ffe6d2"},
      {"PALEGREEN", "#d2ffd2"},
      {"PALEBLUE", "#d2ebff"},
      {"PALEYELLOW", "#ffffc8"},
      {"PALECYAN", "#c8ffff"},
      {"PALEMAGENTA", "#ffc8ff"},
      {"BROWN", "#a05000"},
      {"ORANGE", "#ff8000"},
      {"PALEORANGE", "#ffdc6e"},
      {"PURPLE", "#c000ff"},
      {"VIOLET", "#c8aaff"},
      {"PALEVIOLET", "#ebd7ff"},
      {"GRAY", "#969696"},
      {"PALEGRAY", "#ebebeb"},
      {"CERISE", "#ff0080"},
      {"MIDBLUE", "#56b2de"},
    } ;
  AcedbColourSpec colour ;

  colour = &(colours[0]) ;
  while (colour->name)
    {
      if (g_ascii_strcasecmp(colour->name, acedb_colour_name) == 0)
	{
	  colour_spec = colour->spec ;
	  break ;
	}

      colour++ ;
    }

  return colour_spec ;
}




#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
static void stylePrintCB(gpointer data, gpointer user_data)
{
  ZMapFeatureTypeStyle style = (ZMapFeatureTypeStyle)data ;

  printf("%s (%s)\n", g_quark_to_string(zMapStyleGetID(style)),
	 g_quark_to_string(zMapStyleGetUniqueID(style))) ;

  return ;
}
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */

static gboolean getStyleColour(StyleFeatureColours style_colours, char **line_pos)
{
  gboolean result = FALSE ;
  char *colour_type ;
  char *colour_target ;
  char *colour ;
  StyleColour style_colour = NULL ;


#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  if ((colour_type = strtok_r(NULL, " ", line_pos))
      && (colour_target = strtok_r(NULL, " ", line_pos))
      && (colour = strtok_r(NULL, "\"", line_pos))
      && (colour = strtok_r(NULL, "\"", line_pos)))
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */
    colour_type = strtok_r(NULL, " ", line_pos) ;
  colour_target = strtok_r(NULL, " ", line_pos) ;
  colour = strtok_r(NULL, "\"", line_pos) ;

#ifdef ED_G_NEVER_INCLUDE_THIS_CODE
  colour = strtok_r(NULL, "\"", line_pos) ;
#endif /* ED_G_NEVER_INCLUDE_THIS_CODE */


  if (colour)
    {
      if (g_ascii_strcasecmp(colour_type, "Normal") == 0)
	style_colour = &(style_colours->normal) ;
      else if (g_ascii_strcasecmp(colour_type, "Selected") == 0)
	style_colour = &(style_colours->selected) ;

      if (style_colour)
	{
	  result = TRUE ;
	  if (g_ascii_strcasecmp(colour_target, "Draw") == 0)
	    style_colour->draw = g_strdup(colour) ;
	  else if (g_ascii_strcasecmp(colour_target, "Fill") == 0)
	    style_colour->fill = g_strdup(colour) ;
	  else if (g_ascii_strcasecmp(colour_target, "Border") == 0)
	    style_colour->border = g_strdup(colour) ;
	  else
	    result = FALSE ;
	}
    }

  return result ;
}




/* For each sequence makes a request to find the sequence and then to dump its dna:
 * 
 * acedb> find sequence RDS00121111         
 * <blank line>
 * // Found 1 objects in this class
 * acedb> dna -u
 * gactctttgcaggggagaagctccacaacctcagcaaa....etc etc
 * acedb>
 * 
 * 
 * Function returns ZMAP_SERVERRESPONSE_OK if sequences were found and retrieved,
 * ZMAP_SERVERRESPONSE_REQFAIL otherwise.
 * 
 *  */
static ZMapServerResponseType doGetSequences(AcedbServer server, GList *sequences_inout)
{
  ZMapServerResponseType result = ZMAP_SERVERRESPONSE_REQFAIL ;
  GList *next_seq ;
  GString *acedb_request = NULL ;

  acedb_request = g_string_new(NULL) ;

  /* We need to loop round finding each sequence and then fetching its dna... */
  next_seq = sequences_inout ;
  while (next_seq)
    {
      char *command ;
      void *reply = NULL ;
      int reply_len = 0 ;
      ZMapSequence sequence = (ZMapSequence)(next_seq->data) ;


      /* Try to find the sequence... */
      command = "find sequence" ;
      g_string_printf(acedb_request, "%s %s", command, g_quark_to_string(sequence->name)) ;
      
      if ((server->last_err_status = AceConnRequest(server->connection, acedb_request->str,
						    &reply, &reply_len)) == ACECONN_OK)
	{
	  /* reply should be:
	   *
	   * <blank line>
	   * // Found 1 objects in this class
	   * // 1 Active Objects
	   */
	  char *scan_text = (char *)reply ;
	  char *next_line = NULL ;
	  int num_objs ;

	  while ((next_line = strtok(scan_text, "\n")))
	    {
	      scan_text = NULL ;

	      if (g_str_has_prefix(next_line, "// "))
		{
		  num_objs = getFoundObj(next_line) ;
		  if (num_objs == 1)
		    result = ZMAP_SERVERRESPONSE_OK ;
		  else
		    setErrMsg(server,  g_strdup_printf("Expected to find 1 sequence object"
						       "named \"%s\" but found %d.",
						       g_quark_to_string(sequence->name), num_objs)) ;
		  break ;
		}
	    }

	  g_free(reply) ;
	  reply = NULL ;
	}

      /* All ok ?  Then get the dna.... */
      if (server->last_err_status == ZMAP_SERVERRESPONSE_OK)
	{
	  command = "dna -u" ;
	  g_string_printf(acedb_request, "%s", command) ;
      
	  if ((server->last_err_status = AceConnRequest(server->connection, acedb_request->str,
							&reply, &reply_len)) == ACECONN_OK)
	    {
	      /* reply should be:
	       *
	       * gactctttgcaggggagaagctccacaacctcagcaaa....etc etc
	       */
	      sequence->length = reply_len - 1 ;
	      sequence->sequence = reply ;
	    }
	}

      next_seq = g_list_next(next_seq) ;
    }

  g_string_free(acedb_request, TRUE) ;


  return result ;
}


/* Checks that each name in query_names_inout can be found in reference_names, if a name
 * is not found then it is removed from query_names_inout (i.e. query_names_inout could be
 * NULL on return).
 * 
 * Returns the number of names missing.
 * 
 * Note function assumes names occur only once in each list.
 */
static int equaliseLists(AcedbServer server, GList **query_names_inout, GList *reference_names,
			 char *query_name, char *reference_name)
{
  int num_found = 0 ;
  int num_query ;
  GString *missing ;
  GList *curr ;
  GList *query_names ;

  query_names = *query_names_inout ;

  num_query = g_list_length(query_names) ;

  missing = g_string_sized_new(1000) ;

  /* Must loop round by steam because we are removing links. */
  curr = query_names ;
  do
    {
      if ((g_list_find_custom(reference_names, curr->data, quarkCaseCmp)))
	{
	  curr = curr->next ;
	  num_found++ ;
	}
      else
	{
	  GList *tmp ;

	  g_string_append_printf(missing, " \"%s\"", g_quark_to_string(GPOINTER_TO_INT(curr->data))) ;

	  tmp = curr->next ;				    /* Best move on before removing link. */

	  query_names = g_list_delete_link(query_names, curr) ;

	  curr = tmp ;
	}

    } while (curr) ;

  /* Log any missing methods. */
  if (!num_found)
    {
      ZMAPSERVER_LOG(Critical, ACEDB_PROTOCOL_STR, server->host,
		     "Complete %s -> %s mismatch, %d %s specified but none found in %s list. Missing %s were: %s",
		     query_name, reference_name, num_query, query_name, reference_name,
		     query_name, missing->str) ;
    }
  else if (num_found < num_query)
    {
      ZMAPSERVER_LOG(Warning, ACEDB_PROTOCOL_STR, server->host,
		     "Partial %s -> %s mismatch, %d %s specified but only %d found in %s list. Missing %s were: %s",
		     query_name, reference_name, num_query, query_name, num_found, reference_name,
		     query_name, missing->str) ;
    }

  g_string_free(missing, TRUE) ;

  /* Return the names list. */
  *query_names_inout = query_names ;

  return num_found ;
}


/* A GCompareFunc() to compare names in a case independent way in two lists of GQuarks. */
static gint quarkCaseCmp(gconstpointer a, gconstpointer b)
{
  gint result ;

  result = g_ascii_strcasecmp(g_quark_to_string(GPOINTER_TO_INT(a)), g_quark_to_string(GPOINTER_TO_INT(b))) ;

  return result ;
}



/* It's possible for us to have reported an error and then another error to come along. */
static void setErrMsg(AcedbServer server, char *new_msg)
{
  if (server->last_err_msg)
    g_free(server->last_err_msg) ;

  server->last_err_msg = new_msg ;

  return ;
}

/* Reset status/err_msg, needs doing before each command otherwise we can end up seeing the wrong
 * message. */
static void resetErr(AcedbServer server)
{
  if (server->last_err_msg)
    {
      g_free(server->last_err_msg) ;
      server->last_err_msg = NULL ;
    }

  server->last_err_status = ACECONN_OK ;

  return ;
}



static char *get_url_query_value(char *full_query, char *key)
{
  char *value = NULL,
    **split   = NULL, 
    **ptr     = NULL ;

  if(full_query != NULL)
    {  
      split = ptr = g_strsplit(full_query, "&", 0);
      
      while(ptr && *ptr != '\0')
	{
	  char **key_value = NULL, **kv_ptr;
	  key_value = kv_ptr = g_strsplit(*ptr, "=", 0);
	  if(key_value[0] && (g_ascii_strcasecmp(key, key_value[0]) == 0))
	    value = g_strdup(key_value[1]);
	  g_strfreev(kv_ptr);
	  ptr++;
	}
      
      g_strfreev(split);
    }

  return value;
}

static gboolean get_url_query_boolean(char *full_query, char *key)
{
  gboolean result = FALSE;
  char *value = NULL;

  if((value = get_url_query_value(full_query, key)))
    {
      if(g_ascii_strcasecmp("true", value) == 0)
	result = TRUE;
      g_free(value);
    }

  return result;
}


/* A GDestroyNotify() to free the method data structs in the method_2_data hash table. */
static void freeDataCB(gpointer data)
{
  ZMapGFFSet set_data = (ZMapGFFSet)data ;

  g_free(set_data) ;

  return ;
}

/* A GDestroyNotify() to free the method data structs in the method_2_data hash table. */
static void freeSetCB(gpointer data)
{
  ZMapGFFSource source_data = (ZMapGFFSource)data ;

  g_free(source_data) ;

  return ;
}