/*******************************************************************************
 * Simplified Wrapper and Interface Generator  (SWIG)
 * 
 * Author : David Beazley
 *
 * Department of Computer Science        
 * University of Chicago
 * 1100 E 58th Street
 * Chicago, IL  60637
 * beazley@cs.uchicago.edu
 *
 * Please read the file LICENSE for the copyright and terms by which SWIG
 * can be used and distributed.
 *******************************************************************************/

#include "internal.h"

#include <limits.h>

// ------------------------------------------------------------------------
// $Header: /pack/cvsroots/wxwindows/wxWindows/wxPython/wxSWIG/SWIG/typemap.cxx,v 1.1 2002/04/29 19:56:49 RD Exp $
//
// typemap.cxx
//
// This file provides universal support for typemaps.   Typemaps are created
// using the following SWIG command in an interface file:
//
//       %typemap(lang,operation) type { code }        Make a new typemap
//       %typemap(lang,operation) type;                Clears any previous typemap
//
// lang is an identifier indicating the target language.  The typemap will
// simply be ignored if its for a different language.  The code is the
// corresponding C code for the mapping.  An example typemap might look
// like this :
//
//       %typemap(tcl,get) double {
//              $target = atof($source);
//       }
//       %typemap(tcl,set) double {
//              sprintf($target,"%0.17f",$source);
//       }
//
// The variables $target and $source should be used in any type-mappings.
// Additional local variables can be created, but this should be done 
// by enclosing the entire code fragment in an extra set of braces.
//
// The C++ API to the type-mapper is as follows :
//
// void typemap_register(char *op, char *lang, DataType *type, char *pname, String &getcode, ParmList *args)
// char *typemap_lookup(char *op, char *lang, DataType *type, char *pname, char *source, char *target);
// void typemap_clear(char *op, char *lang, DataType *type, char *pname);
//
// The lookup functions return a character string corresponding to the type-mapping
// code or NULL if none exists.   The string return will have the source and target
// strings substituted for the strings "$source" and "$target" in the type-mapping code.
//
// (2/19/97) This module has been extended somewhat to provide generic mappings
// of other parts of the code--most notably exceptions.
//
// void fragment_register(char *op, char *lang, String &code)
// char fragment_lookup(char *op, char *lang, int age);
// char fragment_clear(char *op, char *lang);
//
// ------------------------------------------------------------------------

// Structure for holding a typemap

struct TypeMap {
  char       *lang;
  DataType   *type;
  String      code;
  int         first;
  int         last;
  TypeMap     *next;
  TypeMap     *previous;                // Previously defined typemap (if any)
  ParmList    *args;                    // Local variables (if any)

  TypeMap(char *l, DataType *t, String &c, ParmList *p = 0) {
    lang = copy_string(l);
    type = new DataType(t);
    code << c;
    first = type_id;
    last = INT_MAX;
    next = 0;
    previous = 0;
    if (p) {
      args = new ParmList(p);
    } else {
      args = 0;
    }
  }
  TypeMap(char *l, DataType *t, char *c, ParmList *p = 0) {
    lang = copy_string(l);
    type = new DataType(t);
    code << c;
    first = type_id;
    last = INT_MAX;
    next = 0;
    previous = 0;
    if (p) {
      args = new ParmList(p);
    } else {
      args = 0;
    }
  }
  TypeMap(char *l, char *c) {
    lang = copy_string(l);
    type = 0;
    code << c;
    first = type_id;
    last = INT_MAX;
    next = 0;
    previous = 0;
    args = 0;
  }
  TypeMap(TypeMap *t) {
    lang = copy_string(t->lang);
    type = new DataType(t->type);
    code << t->code;
    first = type_id;
    last = INT_MAX;
    next = 0;
    previous = t->previous;
    if (args) {
      args = new ParmList(args);
    } else {
      args = 0;
    }
  }
};

// Hash tables for storing type-mappings

static Hash  typemap_hash;

// Structure for holding "applications of a typemap"

struct TmMethod {
  char *name;          // Typemap name;
  DataType *type;      // Typemap type
  TmMethod *next;      // Next method
  TmMethod(char *n, DataType *t, TmMethod *m = 0) {
    if (n) name = copy_string(n);
    else name = 0;
    if (t) {
      type = new DataType(t);
    } else {
      type = 0;
    }
    next = m;
  }
};

// Hash table for storing applications of a datatype

static Hash application_hash;

// ------------------------------------------------------------------------
// void typemap_apply(DataType *tm_type, char *tm_name, DataType *type, char *pname)
//
// Attempts to apply a typemap given by (tm_type,tm_name) to (type,pname)
// Called by the %apply directive.
// ------------------------------------------------------------------------

void typemap_apply(DataType *tm_type, char *tm_name, DataType *type, char *pname) {
  TmMethod *m,*m1;
  char temp[512];

  // Form the application name
  if (!pname) pname = "";
  sprintf(temp,"%s$%s",type->print_type(),pname);

  // See if there is a method already defined

  m = (TmMethod *) application_hash.lookup(temp);
  
  if (!m) {
    m = new TmMethod(temp,type,0);
    application_hash.add(temp,m);
  }

  // Check to see if an array typemap has been applied to a non-array type

  if ((tm_type->arraystr) && (!type->arraystr)) {
    fprintf(stderr,"%s:%d: Warning. Array typemap has been applied to a non-array type.\n",
	    input_file,line_number);
  }

  // If both are arrays, make sure they have the same dimension 

  if ((tm_type->arraystr) && (type->arraystr)) {
    char s[128],*t;
    if (tm_type->array_dimensions() != type->array_dimensions()) {
      fprintf(stderr,"%s:%d: Warning. Array types have different number of dimensions.\n",
	      input_file,line_number);
    } else {
      for (int i = 0; i < tm_type->array_dimensions(); i++) {
	strcpy(s,tm_type->get_dimension(i));
	t = type->get_dimension(i);
	if (strcmp(s,"ANY") != 0) {
	  if (strcmp(s,t)) 
	    fprintf(stderr,"%s:%d: Warning. Array typemap applied to an array of different size.\n",
		    input_file, line_number);
	}
      }
    }
  }

  // Add a new mapping corresponding to the typemap

  m1 = new TmMethod(tm_name,tm_type,m->next);
  m->next = m1;
  
}
// ------------------------------------------------------------------------
// void typemap_clear_apply(DataType *type, char *pname)
//
// Clears the application of a typemap.
// Called by the %clear directive.
// ------------------------------------------------------------------------

void typemap_clear_apply(DataType *type, char *pname) {
  char temp[512];
  if (!pname) pname = "";
  sprintf(temp,"%s$%s", type->print_type(), pname);
  application_hash.remove(temp);
}

// ------------------------------------------------------------------------
// char *typemap_string(char *lang, DataType *type, char *pname, char *ary, char *suffix)
//
// Produces a character string corresponding to a lang, datatype, and
// method.   This string is used as the key for our typemap hash table.
// ------------------------------------------------------------------------

static char *typemap_string(char *lang, DataType *type, char *pname, char *ary, char *suffix) {
  static String str;

  int old_status;
  old_status = type->status;
  type->status = 0;
  str = "";

  if (ary)
    str << lang << type->print_type() << pname << ary << suffix;
  else
    str << lang << type->print_type() << pname << suffix;

  type->status = old_status;
  return str;
}

// ------------------------------------------------------------------------
// void typemap_register(char *op, char *lang, DataType *type, char *pname,
//                       char *getcode, ParmList *args)
//
// Register a new mapping with the type-mapper.  
// ------------------------------------------------------------------------

void typemap_register(char *op, char *lang, DataType *type, char *pname, 
                      char *getcode, ParmList *args) {
  
  char     *key;
  TypeMap  *tm,*tm_old;
  char     temp[256];
  int      is_default = 0;

  // printf("Registering : %s %s %s %s\n%s\n", op, lang, type->print_type(), pname, getcode);


  tm = new TypeMap(lang,type,getcode,args);
  // If this is a default typemap, downgrade the type!

  if (strcmp(pname,"SWIG_DEFAULT_TYPE") == 0) {
    tm->type->primitive();   
    is_default = 1;
  }

  key = typemap_string(lang,tm->type,pname,tm->type->arraystr, op);

  // Get any previous setting of the typemap

  tm_old = (TypeMap *) typemap_hash.lookup(key);

  if (tm_old) {

    // Perform a chaining operation, but only if the last typemap is
    // active.

    if (type_id < tm_old->last) {
      sprintf(temp,"$%s",op);
      tm->code.replace(temp,tm_old->code);
    }

    // If found, we need to attach the old version to the new one

    tm->previous = tm_old;
    tm->next = tm_old;
    tm_old->last = type_id;

    // Remove the old one from the hash
  
    typemap_hash.remove(key);
  } 

  // Add new typemap to the hash table
  typemap_hash.add(key,(void *) tm);

  // Now try to perform default chaining operation (if available)
  //  if (!is_default) {
  //    sprintf(temp,"$%s",op);
  //    if (strstr(tm->code,temp)) {
  //      tm->code.replace(temp,typemap_resolve_default(op,lang,type));
  //    }
  //  }

  // Just a sanity check to make sure args look okay.
  
  if (args) {
    Parm *p;
    p = tm->args->get_first();
    while (p) {
      if (p->name) {
	//	printf("    %s %s\n", p->t->print_type(),p->name);
      } else {
	fprintf(stderr,"%s:%d:  Typemap error. Local variables must have a name\n",
		input_file, line_number);
      }
      // If a call by reference thingy, fix that
      if (p->call_type & CALL_REFERENCE) {
	p->t->is_pointer--;
	p->call_type = 0;
      }
      p = tm->args->get_next();
    }
  }
}

// ------------------------------------------------------------------------
// void typemap_register(char *op, char *lang, char *type, char *pname,
//                       char *getcode, ParmList *args)
//
// Register a new mapping with the type-mapper. Special version that uses a
// string instead of a datatype.
// ------------------------------------------------------------------------

void typemap_register(char *op, char *lang, char *type, char *pname, 
                      char *getcode, ParmList *args) {
  DataType temp;
  strcpy(temp.name,type);
  temp.is_pointer = 0;
  temp.type = T_USER;
  typemap_register(op,lang,&temp,pname,getcode,args);
}


// ------------------------------------------------------------------------
// void typemap_register_default(char *op, char *lang, int type, int ptr, char *arraystr,
//                               char *code, ParmList *args)
//
// Registers a default typemap with the system using numerical type codes.
// type is the numerical code, ptr is the level of indirection. 
// ------------------------------------------------------------------------

void typemap_register_default(char *op, char *lang, int type, int ptr, char *arraystr,
			   char *code, ParmList *args) {

  DataType *t = new DataType(type);

  // Create a raw datatype from the arguments

  t->is_pointer = ptr;
  t->arraystr = copy_string(arraystr);

  // Now, go register this as a default type

  typemap_register(op,lang,t,"SWIG_DEFAULT_TYPE",code,args);
  delete t;
}


// ------------------------------------------------------------------------
// static TypeMap *typemap_search(char *key, int id) 
//
// An internal function for searching for a particular typemap given
// a key value and datatype id.
//
// Basically this checks the hash table and then checks the id against
// first and last values, looking for a match.   This is to properly
// handle scoping problems.
// ------------------------------------------------------------------------

TypeMap *typemap_search(char *key, int id) {
  
  TypeMap *tm;

  tm = (TypeMap *) typemap_hash.lookup(key);
  while (tm) {
    if ((id >= tm->first) && (id < tm->last)) return tm;
    else tm = tm->next;
  }
  return tm;
}

// ------------------------------------------------------------------------
// TypeMap *typemap_search_array(char *op, char *lang, DataType *type, char *pname, String &str)
//
// Performs a typemap lookup on an array type.  This is abit complicated
// because we need to look for ANY tags specifying that any array dimension
// is valid.   The resulting code will be placed in str with dimension variables
// substituted.
// ------------------------------------------------------------------------

TypeMap *typemap_search_array(char *op, char *lang, DataType *type, char *pname, String &str) {
  char      *origarr = type->arraystr;
  char      *key;
  int       ndim,i,j,k,n;
  TypeMap   *tm;
  char      temp[10];

  if (!type->arraystr) return 0;

  // First check to see if exactly this array has been mapped

  key = typemap_string(lang,type,pname,type->arraystr,op);
  tm = typemap_search(key,type->id);

  // Check for unnamed array of specific dimensions
  if (!tm) {
    key = typemap_string(lang,type,"",type->arraystr,op);
    tm = typemap_search(key,type->id);
  } 

  if (!tm) {
    // We're going to go search for matches with the ANY tag
    String  tempastr;
    ndim = type->array_dimensions();             // Get number of dimensions
    j = (1 << ndim) - 1;                         // Status bits
    for (i = 0; i < (1 << ndim); i++) {
      // Form an array string
      tempastr = "";
      k = j;
      for (n = 0; n < ndim; n++) {
	if (k & 1) {
	  tempastr << "[" << type->get_dimension(n) << "]";
	} else {
	  tempastr << "[ANY]";
	}
	k = k >> 1;
      }
      //      printf("checking (%s) : %s\n",origarr,tempastr.get());
      type->arraystr = tempastr.get();
      key = typemap_string(lang,type,pname,type->arraystr,op);
      tm = typemap_search(key,type->id);
      if (!tm) {
	key = typemap_string(lang,type,"",type->arraystr,op);
	tm = typemap_search(key,type->id);
      }
      type->arraystr = origarr;
      if (tm) break;
      j--;
    }
  }      
	
  if (tm) {
    str << tm->code;
    ndim = type->array_dimensions();
    for (i = 0; i < ndim; i++) {
      sprintf(temp,"$dim%d",i);
      str.replace(temp,type->get_dimension(i));
    }
  }
  return tm;
}

// ------------------------------------------------------------------------
// static typemap_locals(Datatype *t, char *pname, String &s, ParmList *l, WrapperFunction &f)
//
// Takes a string, a parameter list and a wrapper function argument and
// starts creating local variables.
//
// Substitutes locals in the string with actual values used.
// ------------------------------------------------------------------------

static void typemap_locals(DataType *t, char *pname, String &s, ParmList *l, WrapperFunction &f) {
  Parm *p;
  char *new_name;
  
  p = l->get_first();
  while (p) {
    if (p->name) {
      if (strlen(p->name) > 0) {
	String str;
	DataType *tt;

	// If the user gave us $type as the name of the local variable, we'll use
	// the passed datatype instead

	if (strcmp(p->t->name,"$type")==0 || strcmp(p->t->name,"$basetype")==0) {
	  tt = t;
	} else {
	  tt = p->t;
	}
        
	// Have a real parameter here
        if (tt->arraystr) {
	  tt->is_pointer--;
	  str << p->name << tt->arraystr;
	} 
        else {
	  str << p->name;
	}

	// Substitute parameter names
        str.replace("$arg",pname);
        if (strcmp(p->t->name,"$basetype")==0) {
          // use $basetype
          char temp_ip = tt->is_pointer;
          char temp_ip1 = tt->implicit_ptr;
          tt->is_pointer = 0;
          tt->implicit_ptr = 0;
          new_name = f.new_local(tt->print_type(),str);
          tt->is_pointer = temp_ip;
          tt->implicit_ptr = temp_ip1;
        } 
        else 
          new_name = f.new_local(tt->print_full(),str);

	if (tt->arraystr) tt->is_pointer++;
	// Substitute 
	s.replaceid(p->name,new_name);
      }
    }
    p = l->get_next();
  }
  // If the original datatype was an array. We're going to go through and substitute
  // it's array dimensions

  if (t->arraystr) {
    char temp[10];
    for (int i = 0; i < t->array_dimensions(); i++) {
      sprintf(temp,"$dim%d",i);
      f.locals.replace(temp,t->get_dimension(i));
    }
  }

}

// ------------------------------------------------------------------------
// char *typemap_lookup(char *op, char *lang, DataType *type, char *pname, char *source,
//                      char *target, WrapperFunction *f)
//            
// Looks up a "get" function in the type-map and returns a character string
// containing the appropriate translation code.
//
// op       is string code for type of mapping
// lang     is the target language string
// type     is the datatype
// pname    is an optional parameter name
// source   is a string with the source variable
// target   is a string containing the target value
// f        is a wrapper function object (optional)
//
// Returns NULL if no mapping is found.
//
// Typemaps follow a few rules regarding naming and C pointers by checking
// declarations in this order.
//
//         1.   type name []         - A named array (most specific)
//         2.   type name            - Named argument
//         3.   type []              - Type with array
//         4.   type                 - Ordinary type
// 
// Array checking is only made if the datatype actally has an array specifier      
// 
// Array checking uses a special token "ANY" that indicates that any
// dimension will match.  Since we are passed a real datatype here, we
// need to hack this a special case.
//
// Array dimensions are substituted into the variables $dim1, $dim2,...,$dim9
// ------------------------------------------------------------------------

static DataType *realtype;       // This is a gross hack
static char     *realname = 0;   // Real parameter name

char *typemap_lookup_internal(char *op, char *lang, DataType *type, char *pname, char *source,
                     char *target, WrapperFunction *f) {
  static String str;
  char *key = 0;
  TypeMap *tm = 0;

  if (!lang) {
    return 0;
  }

  // First check for named array
  str = "";
  tm = typemap_search_array(op,lang,type,pname,str);

  // Check for named argument
  if (!tm) {
    key = typemap_string(lang,type,pname,0,op);
    tm = typemap_search(key,type->id);
    if (tm)
      str << tm->code;
  }

  // Check for unnamed type
  if (!tm) {
    key = typemap_string(lang,type,"",0,op);
    tm = typemap_search(key,type->id);
    if (tm)
      str << tm->code;
  }
  if (!tm) return 0;
  
  // Now perform character replacements

  str.replace("$source",source);
  str.replace("$target",target);
  str.replace("$type", realtype->print_type());
  if (realname) {
    str.replace("$parmname", realname);
  } else {
    str.replace("$parmname","");
  }
  // Print base type (without any pointers)
  {
    char temp_ip = realtype->is_pointer;
    char temp_ip1 = realtype->implicit_ptr;
    realtype->is_pointer = 0;
    realtype->implicit_ptr = 0;
    char *bt = realtype->print_type();
    if (bt[strlen(bt)-1] == ' ') 
      bt[strlen(bt)-1] = 0;
    str.replace("$basetype",bt);
    str.replace("$basemangle",realtype->print_mangle());
    realtype->is_pointer = temp_ip;
    realtype->implicit_ptr = temp_ip1;
  }
  
  str.replace("$mangle",realtype->print_mangle());

  // If there were locals and a wrapper function, replace
  if ((tm->args) && f) {
    typemap_locals(realtype, pname, str,tm->args,*f);
  }

  // If there were locals and no wrapper function, print a warning
  if ((tm->args) && !f) {
    if (!pname) pname = "";
    fprintf(stderr,"%s:%d: Warning. '%%typemap(%s,%s) %s %s' being applied with ignored locals.\n",
	    input_file, line_number, lang,op, type->print_type(), pname);
  }

  // Return character string

  return str;
}

// ----------------------------------------------------------
// Real function call that takes care of application mappings
// ----------------------------------------------------------

char *typemap_lookup(char *op, char *lang, DataType *type, char *pname, char *source,
                     char *target, WrapperFunction *f) {
  TmMethod *m;
  char temp[512];
  char *result;
  char *ppname;
  char *tstr;

  realtype = type;         // The other half of the gross hack
  realname = pname;

  // Try to apply typemap right away

  result = typemap_lookup_internal(op,lang,type,pname,source,target,f);

  // If not found, try to pick up anything that might have been
  // specified with %apply

  if ((!result) && (pname)) {
    int drop_pointer = 0;
    ppname = pname;
    if (!ppname) ppname = "";
    
    // The idea : We're going to cycle through applications and
    // drop pointers off until we get a match.   

    while (drop_pointer <= (type->is_pointer - type->implicit_ptr)) {
      type->is_pointer -= drop_pointer;
      tstr = type->print_type();
      sprintf(temp,"%s$%s",tstr,ppname);
      // No mapping was found.  See if the name has been mapped with %apply
      m = (TmMethod *) application_hash.lookup(temp);
      if (!m) {
	sprintf(temp,"%s$",tstr);
	m = (TmMethod *) application_hash.lookup(temp);
      }
      if (m) {
	m = m->next;
	while (m) {
	  char *oldary = 0;
	  static String newarray;
	  if (*(m->name)) ppname = m->name;
	  else ppname = pname;
	  m->type->is_pointer += drop_pointer;

	  // Copy old array string (just in case)

	  oldary = m->type->arraystr; 

	  // If the mapping type is an array and has the 'ANY' keyword, we
          // have to play some magic

	  if ((m->type->arraystr) && (type->arraystr)) {
	    // Build up the new array string
	    newarray = "";
	    for (int n = 0; n < m->type->array_dimensions(); n++) {
	      char *d = m->type->get_dimension(n);
	      if (strcmp(d,"ANY") == 0) {
		newarray << "[" << type->get_dimension(n) << "]";
	      } else {
		newarray << "[" << d << "]";
	      }
	    }
	    m->type->arraystr = newarray.get();
	  } else if (type->arraystr) {
	    // If an array string is available for the current datatype,
	    // make it available.
	    m->type->arraystr = type->arraystr;
	  }
	  result = typemap_lookup_internal(op,lang,m->type,ppname,source,target,f);
	  m->type->arraystr = oldary;
	  m->type->is_pointer -= drop_pointer;
	  if (result) {
	    type->is_pointer += drop_pointer;
	    return result;
	  }
	  m = m->next;
	}
      }
      type->is_pointer += drop_pointer;
      drop_pointer++;
    }
  }
  // Still no idea, try to find a default typemap

  if (!result) {
    DataType *t = new DataType(type);
    t->primitive(); // Knock it down to its basic type
    result = typemap_lookup_internal(op,lang,t,"SWIG_DEFAULT_TYPE",source,target,f);
    if (result) {
      delete t;
      return result;
    }
    if ((t->type == T_USER) || (t->is_pointer)) {
      if ((t->type == T_CHAR) && (t->is_pointer == 1)) return 0;
    
      // Still no result, go even more primitive
      t->type = T_USER;
      t->is_pointer = 1;
      if (t->arraystr) delete [] t->arraystr;
      t->arraystr = 0;
      t->primitive();
      result = typemap_lookup_internal(op,lang,t,"SWIG_DEFAULT_TYPE",source,target,f);
    }
    delete t;
  }
  return result;
}

// ----------------------------------------------------------------------------
// char *typemap_check(char *op, char *lang, DataType *type, char *pname)
//
// Checks to see if there is a typemap.  Returns typemap string if found, NULL
// if not.
// ----------------------------------------------------------------------------

char *typemap_check_internal(char *op, char *lang, DataType *type, char *pname) {
  static String str;
  char *key = 0;
  TypeMap *tm = 0;

  if (!lang) {
    return 0;
  }
  // First check for named array
  str = "";
  tm = typemap_search_array(op,lang,type,pname,str);

  // First check for named array
  //
  //  if (type->arraystr) {
  //    key = typemap_string(lang,type,pname,type->arraystr,op);
  //    tm = typemap_search(key,type->id);
  //  }

  // Check for named argument
  if (!tm) {
    key = typemap_string(lang,type,pname,0,op);
    tm = typemap_search(key,type->id);
  }

  // Check for unnamed array
  if ((!tm) && (type->arraystr)) {
    key = typemap_string(lang,type,"",type->arraystr,op);
    tm = typemap_search(key,type->id);
  } 

  // Check for unname type
  if (!tm) {
    key = typemap_string(lang,type,"",0,op);
    tm = typemap_search(key,type->id);
  }
  if (!tm) return 0;
  
  str = "";
  str << tm->code;

  // Return character string

  return str;
}

// Function for checking with applications

char *typemap_check(char *op, char *lang, DataType *type, char *pname) {
  TmMethod *m;
  char temp[512];
  char *result;
  char *ppname;
  char *tstr;
  // Try to apply typemap right away

  result = typemap_check_internal(op,lang,type,pname);

  if (!result) {
    int drop_pointer = 0;
    ppname = pname;
    if (!ppname) ppname = "";
    
    // The idea : We're going to cycle through applications and
    // drop pointers off until we get a match.   

    while (drop_pointer <= (type->is_pointer - type->implicit_ptr)) {
      type->is_pointer -= drop_pointer;
      tstr = type->print_type();
      sprintf(temp,"%s$%s",tstr,ppname);
      // No mapping was found.  See if the name has been mapped with %apply
      m = (TmMethod *) application_hash.lookup(temp);
      if (!m) {
	sprintf(temp,"%s$",tstr);
	m = (TmMethod *) application_hash.lookup(temp);
      }
      if (m) {
	m = m->next;
	while (m) {
	  char *oldary = 0;
	  static String newarray;
	  if (*(m->name)) ppname = m->name;
	  else ppname = pname;
	  m->type->is_pointer += drop_pointer;
	  oldary = m->type->arraystr;

	  // If the mapping type is an array and has the 'ANY' keyword, we
          // have to play some magic
	  
	  if ((m->type->arraystr) && (type->arraystr)) {
	    // Build up the new array string
	    newarray = "";
	    for (int n = 0; n < m->type->array_dimensions(); n++) {
	      char *d = m->type->get_dimension(n);
	      if (strcmp(d,"ANY") == 0) {
		newarray << "[" << type->get_dimension(n) << "]";
	      } else {
		newarray << "[" << d << "]";
	      }
	    }
	    oldary = m->type->arraystr;
	    m->type->arraystr = newarray.get();
	  } else if (type->arraystr) {
	    m->type->arraystr = type->arraystr;
	  }
	  result = typemap_check_internal(op,lang,m->type,ppname);
	  m->type->arraystr = oldary;
	  m->type->is_pointer -= drop_pointer;
	  if (result) {
	    type->is_pointer += drop_pointer;
	    return result;
	  }
	  m = m->next;
	}
      }
      type->is_pointer += drop_pointer;
      drop_pointer++;
    }
  }

  // If still no result, might have a default typemap
  if (!result) {
    DataType *t = new DataType(type);
    t->primitive(); // Knock it down to its basic type
    result = typemap_check_internal(op,lang,t,"SWIG_DEFAULT_TYPE");
    if (result) {
      delete t;
      return result;
    }
    if ((t->type == T_USER) || (t->is_pointer)) {
      if ((t->type == T_CHAR) && (t->is_pointer == 1)) return 0;
      // Still no result, go even more primitive
      t->type = T_USER;
      t->is_pointer = 1;
      if (t->arraystr) delete [] t->arraystr;
      t->arraystr = 0;
      t->primitive();
      result = typemap_check_internal(op,lang,t,"SWIG_DEFAULT_TYPE");
    }
    delete t;
  }
  return result;
}

// ------------------------------------------------------------------------
// void typemap_clear(char *op, char *lang, DataType *type, char *pname)
//
// Clears any previous typemap.   This works like a stack.  Clearing a
// typemap returns to any previous typemap in force.   If there is no
// previous map, then don't worry about it.
// ------------------------------------------------------------------------

void typemap_clear(char *op, char *lang, DataType *type, char *pname) {
  
  char     *key;
  TypeMap  *tm;

  key = typemap_string(lang,type,pname,type->arraystr,op);

  // Look for any previous version, simply set the last id if
  // applicable.
  
  tm = (TypeMap *) typemap_hash.lookup(key);
  if (tm) {
    if (tm->last > type_id) tm->last = type_id;
  }
}

// ------------------------------------------------------------------------
// void typemap_copy(char *op, char *lang, DataType *stype, char *sname,
//                   DataType *ttype, char *tname)
//
// Copies the code associate with a typemap
// ------------------------------------------------------------------------

void typemap_copy(char *op, char *lang, DataType *stype, char *sname,
		      DataType *ttype, char *tname) {
  
  char     *key;
  TypeMap  *tm, *tk, *tn;

  // Try to locate a previous typemap

  key = typemap_string(lang,stype,sname,stype->arraystr,op);
  tm = typemap_search(key,stype->id);
  if (!tm) return;
  if (strcmp(ttype->name,"PREVIOUS") == 0) {
    // Pop back up to the previous typemap (if any)
    tk = tm->next;
    if (tk) {
      tn = new TypeMap(tk);       // Make a copy of the previous typemap
      tn->next = tm;              // Set up symlinks
      typemap_hash.remove(key);   // Remove old hash entry
      typemap_hash.add(key,(void *) tn);
    }
  } else {
    typemap_register(op,lang,ttype,tname,tm->code,tm->args);
  }
}

// ------------------------------------------------------------------------
// char *fragment_string(char *op, char *lang)
//
// Produces a character string corresponding to a language and method
// This string is used as the key for our typemap hash table.
// ------------------------------------------------------------------------

static char *fragment_string(char *op, char *lang) {
  static String str;

  str = "";

  str << "fragment:" << lang << op;
  return str;
}

// ------------------------------------------------------------------------
// void fragment_register(char *op, char *lang, char *code)
//
// Register a code fragment with the type-mapper.
// ------------------------------------------------------------------------

void fragment_register(char *op, char *lang, char *code) {
  
  char     *key;
  TypeMap  *tm,*tm_old;
  char      temp[256];
  
  tm = new TypeMap(lang,code);
  key = fragment_string(op,lang);

  // Get any previous setting of the typemap

  tm_old = (TypeMap *) typemap_hash.lookup(key);
  if (tm_old) {
    // If found, we need to attach the old version to the new one

    // Perform a chaining operation 

    sprintf(temp,"$%s",op);
    if (type_id < tm_old->last)
      tm->code.replace(temp,tm_old->code);

    tm->next = tm_old;
    tm_old->last = type_id;

    // Remove the old one from the hash
  
    typemap_hash.remove(key);
  }
  
  // Perform a default chaining operation if needed (defaults to nothing)
  sprintf(temp,"$%s",op);
  tm->code.replace(temp,"");

  // Add new typemap to the hash table
  typemap_hash.add(key,(void *) tm);
    
}


// ------------------------------------------------------------------------
// char *fragment_lookup(char *op, char *lang, int age)
//
// op       is string code for type of mapping
// lang     is the target language string
// age      is age of fragment.
//
// Returns NULL if no mapping is found.
//
// ------------------------------------------------------------------------

char *fragment_lookup(char *op, char *lang, int age) {
  static String str;
  char *key = 0;
  TypeMap *tm = 0;

  if (!lang) {
    return 0;
  }

  str = "";
  key = fragment_string(op,lang);
  tm = typemap_search(key,age);

  if (!tm) return 0;

  str << tm->code;
  return str;
}

// ------------------------------------------------------------------------
// void fragment_clear(char *op, char *lang)
//
// Clears any previous fragment definition.   Is a stack operation--will
// restore any previously declared typemap.
// ------------------------------------------------------------------------

void fragment_clear(char *op, char *lang) {
  
  char     *key;
  TypeMap  *tm;

  key = fragment_string(op,lang);

  // Look for any previous version, simply set the last id if
  // applicable.
  
  tm = (TypeMap *) typemap_hash.lookup(key);
  if (tm) {
    if (tm->last > type_id) tm->last = type_id;
  }
}
