
/**********************************************************************
 * Simplified Wrapper and Interface Generator  (SWIG)
 * 
 * Dave Beazley
 * 
 * Theoretical Division (T-11)           Department of Computer Science
 * Los Alamos National Laboratory        University of Utah
 * Los Alamos, New Mexico  87545         Salt Lake City, Utah  84112
 * beazley@lanl.gov                      beazley@cs.utah.edu
 *
 * Copyright (c) 1995-1998
 * The Regents of the University of California and the University of Utah
 * All Rights Reserved
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that 
 * (1) The above copyright notice and the following two paragraphs
 * appear in all copies of the source code and (2) redistributions
 * including binaries reproduces these notices in the supporting
 * documentation.   Substantial modifications to this software may be
 * copyrighted by their authors and need not follow the licensing terms
 * described here, provided that the new terms are clearly indicated in
 * all files where they apply.
 * 
 * IN NO EVENT SHALL THE AUTHOR, THE UNIVERSITY OF CALIFORNIA, THE 
 * UNIVERSITY OF UTAH OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
 * EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE AUTHOR, THE UNIVERSITY OF CALIFORNIA, AND THE UNIVERSITY OF UTAH
 * SPECIFICALLY DISCLAIM ANY WARRANTIES,INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND 
 * THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 **************************************************************************/

#include "internal.h"

#include <limits.h>

// ------------------------------------------------------------------------
// $Header: /home/beazley/SWIG/SWIG1.2/SWIG/RCS/typemap.cxx,v 1.20 1997/08/22 05:51:48 beazley 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;
    str.replace("$basetype",realtype->print_type());
    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

  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++;
    }
  }

#ifdef OLD_METHOD
  if (!result) {
    ppname = pname;
    if (!ppname) ppname = "";
    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) {
	if (*(m->name)) ppname = m->name;
	else ppname = pname;
	result = typemap_check_internal(op,lang,m->type,ppname);
	if (result)
	  return result;
	m = m->next;
      }
    }
  }
#endif

  // 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;
  }
}


// --------------------------------------------------------------------------------
// Revision History
// ----------------
// $Log: typemap.cxx,v $
// Revision 1.20  1997/08/22 05:51:48  beazley
// Changed format of error messages
//
// Revision 1.19  1997/08/20 04:34:50  beazley
// Modified the local variable emitter to allow the $arg variable to
// appear in variable names.
//
// Revision 1.18  1997/08/11 06:14:37  beazley
// Added substitution of $type variable.
//
// Revision 1.17  1997/07/27 20:30:13  beazley
// Many bug fixes to the %apply mechanism.
//
// Revision 1.16  1997/07/14 03:57:13  beazley
// Fixed some problems with array locals.
//
// Revision 1.15  1997/06/20 06:19:53  beazley
// Fixed memory leak
//
// Revision 1.14  1997/05/28 17:00:44  beazley
// Moved revision history to end.
//
// Revision 1.13  1997/05/24 04:14:23  beazley
// Added default typemap support
//
// Revision 1.12  1997/05/13 04:11:39  beazley
// Fixed bug in local variable declarations with structures.
//
// Revision 1.11  1997/05/10 17:47:02  beazley
// Fixed bugs when declaring local variables of arrays.
//
// Revision 1.10  1997/05/08 05:19:30  beazley
// Fixed a few minor quirks with typemap renaming and lookup.
//
// Revision 1.9  1997/05/05 15:54:33  beazley
// Modified to support parameterized typemaps.   A typemap can now
// add local variables to a wrapper function (if supplied).
//
// Support for %apply and %clear added.   These can be used to apply
// a whole group of typemaps to a new datatype/name.
//
// Revision 1.8  1997/04/18 04:07:45  beazley
// Added PREVIOUS symbol.   Also made typemaps more stack-like.
//
// Revision 1.7  1997/03/18 22:40:48  beazley
// Added better support for chaining typemaps together.   This isn't
// really documented very well however.
//
// Revision 1.6  1997/03/12 05:01:14  beazley
// Removed compiler warning.
//
// Revision 1.5  1997/03/08 19:18:09  beazley
// Added better support for typemaps involving arrays.
//
// Revision 1.4  1997/02/19 23:00:47  beazley
// Added support for generic code fragments.  These are used to
// support exceptions.
//
// Revision 1.3  1997/02/16 20:15:29  beazley
// Fixed major bug in typemap scoping
//
// Revision 1.2  1997/01/08 01:24:51  beazley
// Pre 1.1b3 checkin
//
// Revision 1.1  1997/01/06 17:08:17  beazley
// Initial revision
// --------------------------------------------------------------------------------
