//
// File:        PythonServerCSource.java
// Package:     gov.llnl.babel.backend.python
// Revision:    @(#) $Revision: 6482 $
// Date:        $Date: 2008-08-21 15:50:53 -0700 (Thu, 21 Aug 2008) $
// Description: Generate skeleton's to link to a Python implementation
// 
// Copyright (c) 2000-2001, Lawrence Livermore National Security, LLC
// Produced at the Lawrence Livermore National Laboratory.
// Written by the Components Team <components@llnl.gov>
// UCRL-CODE-2002-054
// All rights reserved.
// 
// This file is part of Babel. For more information, see
// http://www.llnl.gov/CASC/components/. Please read the COPYRIGHT file
// for Our Notice and the LICENSE file for the GNU Lesser General Public
// License.
// 
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License (as published by
// the Free Software Foundation) version 2.1 dated February 1999.
// 
// 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 terms and
// conditions of the GNU Lesser General Public License for more details.
// 
// You should have recieved a copy of the GNU Lesser 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

package gov.llnl.babel.backend.python;

import gov.llnl.babel.BabelConfiguration;
import gov.llnl.babel.Context;
import gov.llnl.babel.backend.CodeGenerationException;
import gov.llnl.babel.backend.IOR;
import gov.llnl.babel.backend.LevelComparator;
import gov.llnl.babel.backend.Utilities;
import gov.llnl.babel.backend.c.C;
import gov.llnl.babel.backend.python.Python;
import gov.llnl.babel.backend.writers.LanguageWriterForC;
import gov.llnl.babel.symbols.Argument;
import gov.llnl.babel.symbols.Class;
import gov.llnl.babel.symbols.Extendable;
import gov.llnl.babel.symbols.Method;
import gov.llnl.babel.symbols.Struct;
import gov.llnl.babel.symbols.Symbol;
import gov.llnl.babel.symbols.SymbolID;
import gov.llnl.babel.symbols.SymbolTable;
import gov.llnl.babel.symbols.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * The purpose of this class is to generate the C skeleton code to support
 * sidl objects implemented in Python. The skeleton code makes the link
 * between the independent object representation (IOR) and the Python
 * implementation of the sidl class.
 *
 * The skeleton must map datatypes in C to datatypes in Python. It must
 * also provide C functions to populate the static and object entry
 * point vectors for the IOR.
 */
public class PythonServerCSource {
  /**
   * A writer for the C source file.
   */
  private LanguageWriterForC d_lw            = null;

  private Set                d_includedFiles = new HashSet();

  private Class d_cls = null;

  private Context d_context;

  private static final String s_epv = "epv";
  private static final String s_pre_epv = "pre_epv";
  private static final String s_post_epv = "post_epv";
  private static final String s_sepv = "sepv";
  private static final String s_pre_sepv = "pre_sepv";
  private static final String s_post_sepv = "post_sepv";

  public PythonServerCSource(Class cls,
                             Context context) {
    d_cls = cls;
    d_context = context;
  }

  private Collection argSymbols() throws CodeGenerationException {
    final String exceptionFundamentalName = BabelConfiguration.
      getBaseExceptionType();
    HashSet references = new HashSet();
    for(Iterator i = d_cls.getMethods(true).iterator(); i.hasNext() ; ){
      Method m = (Method)i.next();
      references.addAll(m.getSymbolReferences());
      if (!m.getThrows().isEmpty()) {
        references.add(Utilities.lookupSymbol(d_context, 
                                              exceptionFundamentalName).
                       getSymbolID());
      }
    }
    references.remove(d_cls.getSymbolID());
    return references;
  }

  /**
   * Write a comment explaining the contents of the source file to 
   * anyone who might happen to read it.
   */
  private void explainSkelSource() {
    d_lw.beginBlockComment(false);
    d_lw.println("THIS CODE IS AUTOMATICALLY GENERATED BY THE BABEL");
    d_lw.println("COMPILER. DO NOT EDIT THIS!");
    d_lw.println();
    d_lw.println("This file contains skeleton code to provide an interface");
    d_lw.println("between the sidl independent object representation and");
    d_lw.println("the Python implementation of a class.  This translates");
    d_lw.println("method calls to the IOR to method class in Python.");
    d_lw.println("This file is for the sidl type " + 
                 d_cls.getFullName() + ".");
    d_lw.endBlockComment(false);
    d_lw.println();
  }

  private void addInclude(String filename, boolean useGuard) {
    if (!d_includedFiles.contains(filename)) {
      d_includedFiles.add(filename);
      d_lw.generateInclude(filename, useGuard);
    }
  }

  /**
   * Add <code>#include</code> lines for all the system headers and
   * the referenced types.
   */
  private void includeHeaderFiles() throws CodeGenerationException {
    d_lw.printlnUnformatted("#include <Python.h>");
    addInclude(IOR.getHeaderFile(d_cls.getSymbolID()), true);
    addInclude("sidlObjA.h", false);
    addInclude("sidlPyArrays.h", false);
    d_lw.printlnUnformatted("#ifdef SIDL_HAVE_NUMPY");
    addInclude("oldnumeric.h", false);
    d_lw.printlnUnformatted("#elif defined(SIDL_HAVE_NUMERIC_PYTHON)");
    addInclude("Numeric/arrayobject.h", false);
    d_lw.printlnUnformatted("#else");
    d_lw.printlnUnformatted("#error Neither Numeric Python nor NumPy installed");
    d_lw.printlnUnformatted("#endif");
    addInclude("sidl_header.h", true);
    addInclude("sidl_Python.h", true);
    addInclude("sidl_BaseInterface_Module.h", false);
    addInclude(Python.getCHeaderPath(d_cls, "Module"), false);
    // add argument dependencies
    Iterator i = Utilities.sort(Utilities.convertIdsToSymbols(d_context, argSymbols())).iterator();
    while (i.hasNext()) {
      Symbol sym = (Symbol)i.next();
      if (Symbol.ENUM != sym.getSymbolType()) {
        addInclude(Python.getCHeaderPath(sym, "Module"), false);
      }
      if (sym instanceof Struct) {
        addInclude(IOR.getHeaderFile(sym), true);
      }
    }

    d_lw.printlnUnformatted("#include <stdlib.h>");
    d_lw.printlnUnformatted("#include <string.h>");
    d_lw.println();
  }
  
  private void writeMethodSignature(Method m) throws CodeGenerationException {
    SymbolID id = d_cls.getSymbolID();
    Iterator arguments = m.getArgumentList().iterator();
    boolean needComma = false;
    d_lw.println("static " + IOR.getReturnString(m.getReturnType(), 
                                                 d_context, true, false));
    d_lw.println(Python.getSkelMethod(id, m) + "(");
    if (!m.isStatic() || arguments.hasNext() || !m.getThrows().isEmpty()) {
      d_lw.tab();
      if (!m.isStatic()) {
        d_lw.print("/* in */ " + IOR.getObjectName(id) + " *self");
        needComma = true;
      }
      while (arguments.hasNext()) {
        Argument arg = (Argument)arguments.next();
        if (needComma) {
          d_lw.println(",");
        }
        d_lw.print("/* " + arg.getModeString() + " */ ");
        d_lw.print(IOR.getArgumentWithFormal(arg, d_context,
                                             true, false, false));
        needComma = true;
      }
      if (!m.getThrows().isEmpty()) {
        if (needComma) d_lw.println(",");
        d_lw.println(IOR.getExceptionFundamentalType() + " *_exception");
        needComma = true;
      }
   } else {
      /* if no argument put void */
      d_lw.print("void");
    }
    d_lw.println(")");
    if (needComma) {
      d_lw.backTab();
    }
  }

  private void declareLocalVariables() {
    d_lw.println("PyObject *_context;");
    d_lw.println("PyObject *_pfunc;");
    d_lw.println("PyObject *_args;");
    d_lw.println("PyObject *_result;");
    d_lw.println("PyGILState_STATE _gstate;");
  }

  private void getFunction(boolean isStatic, String  methodName) {
    if (isStatic) {
      d_lw.println("if ((_context = PyImport_ImportModule(\"" 
                   + d_cls.getFullName() + "_Impl" + "\"))) {");
    } else {
      d_lw.println("if ((_context = (self ? self->d_data : NULL))) {");
    }
    d_lw.tab();
    d_lw.println("if ((_pfunc = PyObject_GetAttrString(_context, \"" 
                 + methodName + "\"))) {");
    d_lw.tab();
  }

  private void generateInit() throws CodeGenerationException {
    boolean usesGeneric = false;
    d_lw.println("static void _importModules(void) {");
    d_lw.tab();
    d_lw.println("static int _import = 1;");
    d_lw.println("if (_import) {");
    d_lw.tab();
    d_lw.println("PyGILState_STATE _gstate;");
    d_lw.println("_import = 0;");
    acquirePythonLock();
    d_lw.println("import_SIDLObjA();");
    d_lw.println("import_SIDLPyArrays();");
    d_lw.println(Python.getExtendableImport(d_cls) + "();");
    for(Iterator i = Utilities.convertIdsToSymbols(d_context,
                                                   argSymbols()).iterator(); 
        i.hasNext(); ) {

      Symbol sym = (Symbol)i.next();
      if (Symbol.ENUM != sym.getSymbolType()) {
        d_lw.println(Python.getExtendableImport(sym) + "();");
      }
    }
    for(Iterator i = d_cls.getMethods(false).iterator(); i.hasNext() ; ){
      Method m = (Method)i.next();
      List args = m.getArgumentList();
      for(Iterator argIt = args.iterator(); argIt.hasNext();) {
        Argument arg = (Argument)argIt.next();
        Type argType = arg.getType();
        if(argType.getType() == Type.ARRAY && argType.getArrayType() == null)
          usesGeneric = true;
      }
    }
    if(usesGeneric == true)
      d_lw.println(Python.getImport(BabelConfiguration.getBaseInterface())
                   + "();");
    releasePythonLock();
    d_lw.backTab();
    d_lw.println("}");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println();
  }

  private boolean hasRarray(Collection arguments)
  {
    Iterator i = arguments.iterator();
    while (i.hasNext()) {
      Argument arg = (Argument)i.next();
      if ((arg.getMode() == Argument.INOUT) &&
          arg.getType().isRarray()) {
        return true;
      }
    }
    return false;
  }

  private void cleanupRarrays(Collection arguments)
  {
    boolean thread = hasRarray(arguments);
    Iterator i = arguments.iterator();
    if (thread) {
      Python.leavePython(d_lw);
    }
    while (i.hasNext()) {
      Argument arg = (Argument)i.next();
      if ((arg.getMode() == Argument.INOUT) &&
          arg.getType().isRarray()) {
        d_lw.println("sidl_python_deleteRef_array((struct sidl__array *)(*" +
                     arg.getFormalName() + "));");
      }
    }
    if (thread) {
      Python.resumePython(d_lw);
    }
  }

  private void handleExceptions(Method m, Set throwTypes)
    throws CodeGenerationException
  {
    Object [] exceptions = throwTypes.toArray();
    /*
     * Be sure to use the fundamental base exception type here.
     */
    final String baseType = IOR.getObjectName
      (Utilities.lookupSymbol(d_context, 
                              BabelConfiguration.getBaseExceptionType()).
       getSymbolID());
    Arrays.sort(exceptions, new LevelComparator(d_context.getSymbolTable()));
    int i;
    d_lw.println("else {");
    d_lw.tab();
    cleanupRarrays(m.getArgumentList());
    d_lw.println("if (PyErr_Occurred()) {");
    d_lw.tab();
    d_lw.println("PyObject *_exType, *_exValue, *_exTrace;");
    d_lw.println("PyErr_Fetch(&_exType, &_exValue, &_exTrace);");
    for(i = 0; i < exceptions.length ; ++i){
      SymbolID id = (SymbolID)exceptions[i];
      Symbol sym = Utilities.lookupSymbol(d_context, id);
      if (i > 0) {
        d_lw.print("else ");
      }
      d_lw.println("if (PyErr_GivenExceptionMatches(_exType, " + 
                   Python.getExceptionType(sym) + ")) {");
      d_lw.tab();
      d_lw.println(IOR.getObjectName(id) + " *_local_exception = NULL;");
      d_lw.println("PyObject *_sidlPyException = ");
      d_lw.tab();
      d_lw.println("PyObject_GetAttrString(_exValue, \"exception\");");
      d_lw.backTab();
      d_lw.println("(void)" + Python.getExtendableConverter(sym) + 
                   "(_sidlPyException , &_local_exception);");
      d_lw.println("if (_local_exception) {");
      d_lw.tab();
      d_lw.println("sidl_AddTrace(_sidlPyException, \"" +
                   m.getLongMethodName() + "\");");
      d_lw.println("*_exception = (" + baseType + " *)_local_exception;");
      d_lw.backTab();
      d_lw.println("}");
      d_lw.println("Py_XDECREF(_sidlPyException);");
      d_lw.backTab();
      d_lw.println("}");
    }
    d_lw.println("else {");
    d_lw.tab();
    d_lw.println("PyErr_Restore(_exType, _exValue, _exTrace);");
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"" +
                 m.getLongMethodName() + "\");");
    d_lw.println("_exType = NULL;");
    d_lw.println("_exValue = NULL;");
    d_lw.println("_exTrace = NULL;");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println("Py_XDECREF(_exType);");
    d_lw.println("Py_XDECREF(_exValue);");
    d_lw.println("Py_XDECREF(_exTrace);");
    d_lw.println("PyErr_Clear();");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.backTab();
    d_lw.println("}");
  }

  private void cleanupReferences(Method m) throws CodeGenerationException {
    Set throwTypes = m.getThrows();
    d_lw.println("Py_DECREF(_result);");
    d_lw.backTab();
    d_lw.println("}");
    if (!throwTypes.isEmpty()) {
      handleExceptions(m, throwTypes);
    }
    d_lw.println("Py_DECREF(_args);");
    d_lw.backTab();
    d_lw.println("} else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"" +
                 m.getLongMethodName() + "\");");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println("Py_DECREF(_pfunc);");
    d_lw.backTab();
    d_lw.println("} else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"" +
                 m.getLongMethodName() + "\");");
    d_lw.backTab();
    d_lw.println("}");
    if (m.isStatic()) {
      d_lw.println("Py_DECREF(_context);");
    }
    d_lw.backTab();
    d_lw.println("}");
    if (m.isStatic()) {
      d_lw.println("else {");
      d_lw.tab();
      d_lw.println("*_exception = sidl_Handle_Unexpected(\"" +
                   m.getLongMethodName() + "\");");
      d_lw.backTab();
      d_lw.println("}");
    }
  }

  private void declareReturnVariable(Method m) throws CodeGenerationException {
    final Type t = m.getReturnType();
    if (t.getDetailedType() != Type.VOID) {
      d_lw.print(IOR.getReturnString(t, d_context) +
                 " " + TranslateArguments.RETURN_VAR);
      if (Utilities.isPointer(t)) {
        d_lw.print(" = NULL");
      } else if (t.getDetailedType() == Type.DCOMPLEX) {
        d_lw.print(" = { 0.0, 0.0 }");
      } else if (t.getDetailedType() == Type.FCOMPLEX) {
        d_lw.print(" = { 0.0, 0.0 }");
      } else if (t.getDetailedType() != Type.STRUCT) {
        d_lw.print(" = (" + IOR.getReturnString(t, d_context) + ") 0");
      }
      d_lw.println(";");
    }
  }

  private void initializeOutVariables(Method m) 
    throws CodeGenerationException
  {
    Iterator i = m.getArgumentList().iterator();
    while (i.hasNext()) {
      Argument arg = (Argument)i.next();
      if (arg.getMode() == Argument.OUT) {
        if (Utilities.isPointer(arg.getType())) {
          d_lw.println('*' + arg.getFormalName() + " = NULL;");
        }
        else if (Type.STRUCT == arg.getType().getDetailedType()) {
          Symbol sym = Utilities.lookupSymbol(d_context,
                                              arg.getType().getSymbolID());
          d_lw.println(Python.getStructInit(sym) + "(" +
                       arg.getFormalName() + ");");
          
        }
      }
    }
    if (Type.STRUCT == m.getReturnType().getDetailedType()) {
      Symbol sym = Utilities.lookupSymbol(d_context,
                                          m.getReturnType().getSymbolID());
      d_lw.println(Python.getStructInit(sym) + "(&" +
                   TranslateArguments.RETURN_VAR + ");");
    }
    if (!m.getThrows().isEmpty()) {
      d_lw.println("*_exception = NULL;");
    }
  }

  private void releaseInOutParameters(Method m) throws CodeGenerationException {
    boolean first = true;
    Symbol sym;
    Iterator i = m.getArgumentList().iterator();
    while (i.hasNext()) {
      Argument arg = (Argument)i.next();
      if (Argument.INOUT == arg.getMode()) {
        final Type t = arg.getType();
        switch (t.getDetailedType()) {
        case Type.STRUCT:
          if (first) {
            Python.leavePython(d_lw);
            first = false;
          }
          sym = (Symbol)
            Utilities.lookupSymbol(d_context, t.getSymbolID());
          d_lw.println(Python.getStructDestroy(sym) +
                       "(" + arg.getFormalName() + ");");
          break;
        case Type.STRING:
          if (first) {
            Python.leavePython(d_lw);
            first = false;
          }
          d_lw.println("free((void *)*" + arg.getFormalName() + ");");
          d_lw.println("*" + arg.getFormalName() + " = NULL;");
          break;
        case Type.INTERFACE:
        case Type.CLASS:
          if (first) {
            Python.leavePython(d_lw);
            first = false;
          }
          sym = (Symbol)
            Utilities.lookupSymbol(d_context, t.getSymbolID());
          d_lw.println(Python.getExtendableDeref(sym) + "(*" +
                       arg.getFormalName() + ");");
          d_lw.println("*" + arg.getFormalName() + " = NULL;");
          break;
        case Type.ARRAY:
          if (!t.isRarray()) {
            if (first) {
              Python.leavePython(d_lw);
              first = false;
            }
            
            d_lw.println(Python.getDestroyArray(t.getArrayType()) + 
                         "((struct sidl__array *)(*" + 
                         arg.getFormalName() + "));");
            d_lw.println("*" + arg.getFormalName() + " = NULL;");
          }
          break;
        }
      }
    }
    if (!first) {
      Python.resumePython(d_lw);
    }
  }

  /**
   * Write code to acquire the Python GIL lock.
   */
  private void acquirePythonLock()
  {
    d_lw.printlnUnformatted("#if (PY_VERSION_HEX >= 0x02040000)");
    d_lw.println("_gstate = PyGILState_Ensure();");
    d_lw.println("sidl_Python_LogGILEnsure(__func__, __FILE__, __LINE__, (int)_gstate);");
    d_lw.printlnUnformatted("#endif /* Python 2.4 or later */");
  }

  private void releasePythonLock()
  {
    d_lw.printlnUnformatted("#if (PY_VERSION_HEX >= 0x02040000)");
    d_lw.println("PyGILState_Release(_gstate);");
    d_lw.println("sidl_Python_LogGILRelease(__func__, __FILE__, __LINE__, (int)_gstate);");
    d_lw.printlnUnformatted("#endif /* Python 2.4 or later */");
  }

  /**
   * Write the functions in C that will expose a Python method or
   * free function to the IOR.
   * 
   * @exception gov.llnl.babel.backend.CodeGeneration
   *    this is a catch all exception. It can be caused by I/O trouble or
   *    violations of the data type invariants.
   */
  private void convertMethod(Method m) throws CodeGenerationException {
    TranslateArguments trans = new TranslateArguments(d_lw, m, d_context,
                                                      false, false);
    writeMethodSignature(m);
    trans.setConvertIncoming(true);
    d_lw.println("{");
    d_lw.tab();
    declareLocalVariables();
    declareReturnVariable(m);
    initializeOutVariables(m);
    acquirePythonLock();
    getFunction(m.isStatic(), m.getLongMethodName());
    trans.declareProxies();
    trans.convertIncomingArguments(true);
    d_lw.print("_args =");
    trans.convertSidlToPython();
    d_lw.println("if (_args) {");
    d_lw.tab();
    releaseInOutParameters(m);
    d_lw.println("_result = PyObject_CallObject(_pfunc, _args);");
    d_lw.println("if (_result) {");
    d_lw.tab();
    trans.setConvertIncoming(false);
    trans.declareProxy(m.getReturnType(), TranslateArguments.RETURN_VAR,
                       Argument.OUT);
    d_lw.println("int _okay;");
    if (TranslateArguments.extractOut
        (m.getReturnType(), m.getArgumentList()).size() == 1) {
      d_lw.println("PyObject *_tmp = PyTuple_New(1);");
      d_lw.println("(void)PyTuple_SetItem(_tmp, 0, _result);");
      d_lw.println("_result = _tmp;");
    }
    d_lw.print("_okay = ");
    trans.convertPythonToSidl("_result");
    d_lw.println("if (_okay) {");
    d_lw.tab();
    trans.convertOutgoingArguments(false);
    d_lw.backTab();
    d_lw.println("}");
    cleanupReferences(m);
    releasePythonLock();
    if (m.getReturnType().getDetailedType() != Type.VOID) {
      d_lw.println("return " + TranslateArguments.RETURN_VAR + ";");
    }
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println();
  }

  /**
   * Write the functions in C that will expose Python object methods or
   * Python free functions to the IOR. This converts each method in turn.
   * 
   * @exception gov.llnl.babel.backend.CodeGeneration
   *    this is a catch all exception. It can be caused by I/O trouble or
   *    violations of the data type invariants.
   */
  private void convertMethods(Collection methods) throws CodeGenerationException
  {
    Iterator i = methods.iterator();
    while (i.hasNext()) {
      Method m = (Method)i.next();
      if (!m.isAbstract()) {
        convertMethod(m);
        if (IOR.generateHookMethods(d_cls, d_context)) {
          Method pre = m.spawnPreHook();
          Method post = m.spawnPostHook();
          convertMethod(pre);
          convertMethod(post);
        } 
      }
    }
  }

  private void writeInitializeSEPV(Collection methods) 
    throws CodeGenerationException 
  {
    SymbolID id = d_cls.getSymbolID();
    Iterator i = methods.iterator();
    d_lw.openCxxExtern();
    d_lw.println("void");
    d_lw.print(Python.getSetSEPVName(id) + "(" + IOR.getSEPVName(id) + " *"
      + s_sepv);
    if (IOR.generateHookEPVs(d_cls, d_context)) {
      d_lw.println(",");
      d_lw.tab();
      d_lw.println(IOR.getPreSEPVName(id) + " *" + s_pre_sepv + ", "
        + IOR.getPostSEPVName(id) + " *" + s_post_sepv + ")");
      d_lw.backTab();
    } else {
      d_lw.println(")");
    }

    d_lw.println("{");
    d_lw.tab();

    d_lw.println("_importModules();");
    while (i.hasNext()) {
      Method m = (Method)i.next();
      if (m.isStatic()) {
        initializeSEPVPointer(d_cls, m);
      }
    }
    d_lw.backTab();
    d_lw.println("}");
    d_lw.closeCxxExtern();
    d_lw.println();
  }

  private void initializeSEPVPointer(Class cls, Method m) 
        throws CodeGenerationException
  {
    final String methodName = m.getLongMethodName();
    final String ename      = IOR.getVectorEntry(methodName);
    final SymbolID id       = cls.getSymbolID();

    switch(m.getDefinitionModifier()) {
    case Method.STATIC:
      String sname = Python.getSkelMethod(id, m);
      if (!IOR.isBuiltinMethod(methodName)) {
        Method pre   = m.spawnPreHook();
        Method post  = m.spawnPostHook();

        /*
         * Order is (currently) critical!  If hook methods (i.e.,
         * pre- and post-hooks) are being generated then be sure to
         * initialize the EPV pointers to the skel/impl methods!
         * Otherwise, if we're just making sure to include the
         * basic hook skeletons (but not the implementations), then
         * initialize the EPV pointers to null.
         */
        if (IOR.generateHookMethods(cls, d_context)) {
          writeMethodAssignment(s_pre_sepv, 
                                IOR.getVectorEntry(pre.getLongMethodName()), 
                                Python.getSkelMethod(id, pre));
          writeMethodAssignment(s_sepv, ename, sname);
          writeMethodAssignment(s_post_sepv, 
                                IOR.getVectorEntry(post.getLongMethodName()), 
                                Python.getSkelMethod(id, post));
        } else if (IOR.generateHookEPVs(cls, d_context)) {
          writeMethodAssignment(s_pre_sepv, 
                                IOR.getVectorEntry(pre.getLongMethodName()), 
                                "NULL");
          writeMethodAssignment(s_sepv, ename, sname);
          writeMethodAssignment(s_post_sepv, 
                                IOR.getVectorEntry(post.getLongMethodName()), 
                                "NULL");
        } else {
          writeMethodAssignment(s_sepv, ename, sname);
        }
      } else {
        writeMethodAssignment(s_sepv, ename, sname);
      }
      break;
    default:
      /* do nothing */
      break;
    }
  }

  private void writeInitializeEPV(Collection methods) 
    throws CodeGenerationException 
  {
    SymbolID id = d_cls.getSymbolID();
    Iterator i = methods.iterator();
    d_lw.openCxxExtern();

    d_lw.println("void");
    d_lw.print(Python.getSetEPVName(id) + "(" + IOR.getEPVName(id) + " *"
      + s_epv);
    if (IOR.generateHookEPVs(d_cls, d_context)) {
      d_lw.println(",");
      d_lw.tab();
      d_lw.println(IOR.getPreEPVName(id) + " *" + s_pre_epv + ", "
        + IOR.getPostEPVName(id) + " *" + s_post_epv + ")");
      d_lw.backTab();
    } else {
      d_lw.println(")");
    }
    d_lw.println("{");
    d_lw.tab();
    d_lw.println("_importModules();");
    initializeEPVPointer(d_cls, IOR.getBuiltinMethod(IOR.CONSTRUCTOR, id, 
                         d_context));
    initializeEPVPointer(d_cls, IOR.getBuiltinMethod(IOR.CONSTRUCTOR2, id, 
                         d_context));
    initializeEPVPointer(d_cls, IOR.getBuiltinMethod(IOR.DESTRUCTOR, id, 
                         d_context));

    while (i.hasNext()) {
      Method m = (Method)i.next();
      if(!m.isStatic()) {
        initializeEPVPointer(d_cls, m);
      }
    }
    d_lw.backTab();
    d_lw.println("}");
    d_lw.closeCxxExtern();
    d_lw.println();
  }

  private void initializeEPVPointer(Class cls, Method m)
    throws CodeGenerationException 
  {
    final String methodName = m.getLongMethodName();
    final String ename      = IOR.getVectorEntry(methodName);
    final SymbolID id       = cls.getSymbolID();

    switch(m.getDefinitionModifier()) {
    case Method.FINAL:
    case Method.NORMAL:
      String sname = Python.getSkelMethod(id, m);
      if (!IOR.isBuiltinMethod(methodName)) {
        Method pre   = m.spawnPreHook();
        Method post  = m.spawnPostHook();

        /*
         * Order is (currently) critical!  If hook methods (i.e.,
         * pre- and post-hooks) are being generated then be sure to
         * initialize the EPV pointers to the skel/impl methods!
         * Otherwise, if we're just making sure to include the
         * basic hook skeletons (but not the implementations), then
         * initialize the EPV pointers to null.
         */
        if (IOR.generateHookMethods(cls, d_context)) {
          writeMethodAssignment(s_pre_epv, 
                                IOR.getVectorEntry(pre.getLongMethodName()), 
                                Python.getSkelMethod(id, pre));
          writeMethodAssignment(s_epv, ename, sname);
          writeMethodAssignment(s_post_epv, 
                                IOR.getVectorEntry(post.getLongMethodName()), 
                                Python.getSkelMethod(id, post));
        } else if (IOR.generateHookEPVs(cls, d_context)) {
          writeMethodAssignment(s_pre_epv, 
                                IOR.getVectorEntry(pre.getLongMethodName()), 
                                "NULL");
          writeMethodAssignment(s_epv, ename, sname);
          writeMethodAssignment(s_post_epv, 
                                IOR.getVectorEntry(post.getLongMethodName()), 
                                "NULL");
        } else {
          writeMethodAssignment(s_epv, ename, sname);
        }
      } else {
        writeMethodAssignment(s_epv, ename, sname);
      }
      break;
    case Method.ABSTRACT:
        writeMethodAssignment(s_epv, ename, "NULL");
    default:
      /* do nothing */
      break;
    }
  }

  /**
   * Write the function assignment for the specified var and method name
   * to the specified value.
   *
   * @param var        the pointer variable representing the EPV.
   * @param mname      the method name.
   * @param value      the desired value, or RHS.
   */
  private void writeMethodAssignment(String var, String mname, String value)
  {
    d_lw.println(var + "->" + mname + " = " + value + ";");
  }


  private boolean listSerialize(List ids,
                                boolean serialize,
                                boolean output_comma)
  {
    SymbolID id = d_cls.getSymbolID();
    Iterator i = ids.iterator();
    while (i.hasNext()) { 
      SymbolID did = (SymbolID)i.next();
      if (!output_comma) {
        output_comma = true;
      }
      else {
        d_lw.println(",");
      }
      d_lw.print(Python.getPSkelSerializeName(id, did, serialize, false));
    }
    return output_comma;
  }

  private void writeInitializeRMIExtern() throws CodeGenerationException {
    SymbolID id = d_cls.getSymbolID();
    ArrayList fconnectSIDs = Utilities.sort(IOR.getFConnectSymbolIDs(d_cls));
    ArrayList serializeSIDs = 
      Utilities.sort(IOR.getStructSymbolIDs(d_cls, true));
    ArrayList deserializeSIDs = 
      Utilities.sort(IOR.getStructSymbolIDs(d_cls, false));

    boolean output_comma = false;

    d_lw.printlnUnformatted("#ifdef WITH_RMI");

    if(!(fconnectSIDs.isEmpty() && serializeSIDs.isEmpty() &&
         deserializeSIDs.isEmpty())) {
      d_lw.println("static struct " + IOR.getSymbolName(id) + "__rmiExternals s_rmiExtern = {");
      d_lw.tab();
      for (Iterator i = fconnectSIDs.iterator(); i.hasNext(); ) {
        SymbolID destination_id = (SymbolID) i.next();
        if(!output_comma) {
          output_comma = true;
        } else {
          d_lw.println(",");
        }
        d_lw.print(Python.getPSkelFConnectName(id,destination_id));
      }
      output_comma = listSerialize(serializeSIDs, true, output_comma);
      listSerialize(deserializeSIDs, false, output_comma);

      d_lw.println();
      d_lw.backTab();
      d_lw.println("};");
      d_lw.println();
      
      
      d_lw.openCxxExtern();
      d_lw.println("struct " + IOR.getSymbolName(id) + "__rmiExternals *");
      d_lw.println(Python.getRMIExternName(id)+"(void) {");
      d_lw.tab();
      d_lw.println("return &s_rmiExtern;");
      d_lw.backTab();
      d_lw.println("}"); 
      d_lw.closeCxxExtern();
      d_lw.println();
      
    }
    d_lw.printlnUnformatted("#endif /*WITH_RMI*/");
  }

  /**
   * Add the implicit builtin methods to the list of methods. There are
   * implicit functions for the constructor and destructor.
   */
  private Collection extendMethods(Collection localMethods) 
    throws CodeGenerationException 
  {
    final SymbolID id = d_cls.getSymbolID();
    ArrayList  extendedMethods = new ArrayList(localMethods.size()+2);
    extendedMethods.add(IOR.getBuiltinMethod(IOR.CONSTRUCTOR, id, d_context));
    extendedMethods.add(IOR.getBuiltinMethod(IOR.CONSTRUCTOR2, id, d_context));
    extendedMethods.add(IOR.getBuiltinMethod(IOR.DESTRUCTOR, id, d_context));

    if (IOR.generateHookMethods(d_cls, d_context)) {
      for(Iterator i = localMethods.iterator(); i.hasNext();) {
        Method m = (Method) i.next();
        Method pre = m.spawnPreHook();
        Method post = m.spawnPostHook();
        extendedMethods.add(pre);
        extendedMethods.add(m);
        extendedMethods.add(post);
      }
    } else {
      extendedMethods.addAll(localMethods);
    }
    return extendedMethods;
  }

  private void writeConstructor() throws CodeGenerationException {
    final SymbolID id = d_cls.getSymbolID();
    Method ctor = IOR.getBuiltinMethod(IOR.CONSTRUCTOR, id, d_context);
    writeMethodSignature(ctor);
    d_lw.println("{");
    d_lw.tab();
    declareLocalVariables();
    d_lw.println("*_exception = NULL;");
    acquirePythonLock();
    getFunction(true, id.getShortName());
    d_lw.println("_args = Py_BuildValue(\"(O&)\", (void *)");
    d_lw.tab();
    d_lw.println(Python.getExtendableBorrow(d_cls) + ", self);");
    d_lw.backTab();
    d_lw.println("if (_args) {");
    d_lw.tab();
    d_lw.println("_result = PyInstance_New(_pfunc, _args, NULL);");
    d_lw.println("if (_result) {");
    d_lw.tab();
    d_lw.println("self->d_data = _result;");
    d_lw.println("sidl_Python_IncGlobalRef();");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println("else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"_ctor\");");
    d_lw.backTab();
    d_lw.println("}");
    
    d_lw.println("Py_DECREF(_args);");
    d_lw.backTab();
    d_lw.println("} else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"_ctor\");");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println("Py_DECREF(_pfunc);");
    d_lw.backTab();
    d_lw.println("} else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"_ctor\");");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println("Py_DECREF(_context);");
    d_lw.backTab();
    d_lw.println("} else {");
    d_lw.tab();
    d_lw.println("*_exception = sidl_Handle_Unexpected(\"_ctor\");");
    d_lw.backTab();
    d_lw.println("}");
    releasePythonLock();
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println();
    
    //Generate ctor2, which is empty for Python (doesn't need it)

    ctor = IOR.getBuiltinMethod(IOR.CONSTRUCTOR2, id, d_context);
    d_lw.writeCommentLine("ctor2, unused in Python.");
    writeMethodSignature(ctor);
    d_lw.println("{");
    d_lw.tab();
    d_lw.println("*_exception = NULL;");
    d_lw.backTab();
    d_lw.println("}");

  }

  private void writeDestructor() throws CodeGenerationException {
    final SymbolID id = d_cls.getSymbolID();
    Method dtor = IOR.getBuiltinMethod(IOR.DESTRUCTOR, id, d_context);
    writeMethodSignature(dtor);
    d_lw.println("{");
    d_lw.tab();
    d_lw.println("PyGILState_STATE _gstate;");
    d_lw.println("*_exception = NULL;");
    acquirePythonLock();
    d_lw.writeCommentLine("Remove only reference to Python object");
    d_lw.println("Py_XDECREF(((PyObject *)self->d_data));");
    releasePythonLock();
    d_lw.println("sidl_Python_DecGlobalRef();");
    d_lw.backTab();
    d_lw.println("}");
    d_lw.println();
  }

  /**
   * Generate the C skeleton source file for a Python implementation of
   * a sidl class.
   * 
   * @exception gov.llnl.babel.backend.CodeGenerationException
   *    this a catch all exception for problems during the code
   *    generation phase.
   */
  public synchronized void generateCode() throws CodeGenerationException {
    try {
      Collection methods = d_cls.getMethods(false);
      d_lw = Python.createSkel
        (d_cls, "skeletons to link IOR to Python implementation", d_context);
      explainSkelSource();
      includeHeaderFiles();
      Python.generateRMIExternStruct(d_cls, d_lw, d_context);
      generateInit();
      convertMethods(methods);
      writeConstructor();
      writeDestructor();
      writeInitializeEPV(methods);
      if (d_cls.hasStaticMethod(true)) {
        writeInitializeSEPV(methods);
      }
      writeRMIAccessFunctions(d_cls); 
      writeInitializeRMIExtern();
    } finally {
      if (d_lw != null) {
        d_lw.close();
        d_lw = null;
      }
    }
  }

  private void writeRMISerialize(Extendable ext, 
                                 boolean serialize)
    throws CodeGenerationException
  {
    final String baseType = IOR.getSymbolType
      (Utilities.lookupSymbol(d_context, BabelConfiguration.getBaseExceptionType()));
    SymbolID pipe = Utilities.
      lookupSymbol(d_context, serialize ? "sidl.rmi.Return" : "sidl.rmi.Call");
    final String pipeName = serialize ? "sidl.io.Serializer"
      : "sidl.io.Deserializer";
    Symbol pipeUp = Utilities.lookupSymbol(d_context, pipeName);
    final String pipeType = IOR.getSymbolType(pipeUp);
    Iterator i = IOR.getStructSymbolIDs(ext, serialize).iterator();
    ArrayList decl_args = new ArrayList(4);
    decl_args.add(null);
    decl_args.add(new 
                 Argument(Argument.IN, 
                          new Type(pipe, Type.INTERFACE, null, 0, 0, d_context),
                          "pipe"));
    decl_args.add(new Argument(Argument.IN, new Type(Type.STRING), "name"));
    decl_args.add(new Argument(Argument.IN, new Type(Type.BOOLEAN), "copy"));
    ArrayList call_args = (ArrayList)decl_args.clone();
    call_args.set(1, new
                  Argument(Argument.IN,
                           new Type(pipeUp, Type.INTERFACE, null, 0, 0, d_context),
                           "uppipe"));
    while (i.hasNext()) {
      final SymbolID id = (SymbolID)i.next();
      final Symbol sym = Utilities.lookupSymbol(d_context,id);
      Argument arg = new Argument(serialize ? Argument.IN : Argument.OUT,
                                  new Type(id, Type.STRUCT, null, 0, 0,
                                           d_context),
                                  "strct");
      d_lw.println("static void");
      d_lw.println(Python.getPSkelSerializeName(ext, id, serialize, false) +
                   "(");
      d_lw.tab();
      decl_args.set(0, arg);
      call_args.set(0, arg);
      IOR.generateArguments(d_lw, d_context,
                            "", decl_args, true, true, null, true, true, false, false);
      d_lw.println(")");
      d_lw.backTab();
      d_lw.println("{");
      d_lw.tab();
      d_lw.println(baseType + " throwaway_exception;");
      d_lw.println(baseType + " bipipe =");
      d_lw.tab();
      d_lw.println("(" + baseType + ")pipe;");
      d_lw.backTab();
      d_lw.println(pipeType + " uppipe =");
      d_lw.tab();
      d_lw.println("(" + pipeType + ")");
      d_lw.println("(*bipipe->d_epv->f__cast)(bipipe->d_object,");
      d_lw.println("\"" + pipeName + "\", &throwaway_exception);");
      d_lw.backTab();
      d_lw.println(Python.getExtendableImport(Utilities.
                                              lookupSymbol(d_context, id)) +
                   "();");
      d_lw.println((serialize 
                    ? Python.getStructSerialize(sym)
                    : Python.getStructDeserialize(sym)) + "(");
      d_lw.tab();
      IOR.generateArguments(d_lw, d_context,
                            "", call_args, true, true, null, false, true, false, false);
      d_lw.println(");");
      d_lw.backTab();
      d_lw.backTab();
      d_lw.println("}");
      d_lw.println();
    }
  }


  /**
   * Write functions to call each RMI fconnect function
   *
   * @param cls    the class for which an routine will be written.
   * @param writer the output writer to which the routine will be written.
   */
  private void writeRMIAccessFunctions(gov.llnl.babel.symbols.Class cls) 
    throws CodeGenerationException
  {
    SymbolID id = cls.getSymbolID();
    SymbolID bi_id = Utilities.lookupSymbol(d_context, "sidl.BaseInterface");
    String bi_name = C.getSymbolObjectPtr(bi_id);

    Set fconnectSIDs = IOR.getFConnectSymbolIDs(cls);
    SymbolTable table = d_context.getSymbolTable();

    for (Iterator i = fconnectSIDs.iterator(); i.hasNext(); ) {        
      SymbolID destination_id = (SymbolID) i.next();
      d_lw.println("static " + C.getSymbolObjectPtr(destination_id) + " " + Python.getPSkelFConnectName(id,destination_id)+ 
                   "(const char* url, sidl_bool ar, "+bi_name+" *_ex) { ");
      d_lw.tab();
      d_lw.println(Python.getExtendableImport(table.lookupSymbol(destination_id))+"();");
      d_lw.println("return "+
                   Python.getExtendableConnect(table.lookupSymbol(destination_id))+
                   "(url, ar, _ex);");
      d_lw.backTab();
      d_lw.println("}");
      d_lw.println();

    }
    writeRMISerialize(cls, true);
    writeRMISerialize(cls, false);
  }

}
