mummy  1.0.2
MummyCsharpGenerator.cxx
Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 //
00003 //  $Id: MummyCsharpGenerator.cxx 517 2010-11-11 15:00:41Z david.cole $
00004 //
00005 //  $Author: david.cole $
00006 //  $Date: 2010-11-11 10:00:41 -0500 (Thu, 11 Nov 2010) $
00007 //  $Revision: 517 $
00008 //
00009 //  Copyright (C) 2006-2009 Kitware, Inc.
00010 //
00011 //----------------------------------------------------------------------------
00012 
00013 #include "MummyCsharpGenerator.h"
00014 #include "MummyLineOrientedTextFileReader.h"
00015 #include "MummyLog.h"
00016 #include "MummySettings.h"
00017 
00018 #include "cableArrayType.h"
00019 #include "cableClass.h"
00020 #include "cableClassType.h"
00021 #include "cableConstructor.h"
00022 #include "cableEnumeration.h"
00023 #include "cableEnumerationType.h"
00024 #include "cableField.h"
00025 #include "cableFunctionType.h"
00026 #include "cableFundamentalType.h"
00027 #include "cableMethod.h"
00028 #include "cablePointerType.h"
00029 #include "cableReferenceType.h"
00030 #include "cableType.h"
00031 #include "cableTypedef.h"
00032 
00033 #include "cxxFundamentalType.h"
00034 
00035 #include "gxsys/RegularExpression.hxx"
00036 #include "gxsys/SystemTools.hxx"
00037 #include "gxsys/ios/sstream"
00038 #include "gxsys/stl/algorithm"
00039 #include "gxsys/stl/map"
00040 #include "gxsys/stl/set"
00041 #include "gxsys/stl/string"
00042 #include "gxsys/stl/vector"
00043 
00044 #include <string.h> // strstr
00045 #include <stdio.h> // sprintf
00046 
00047 
00048 //----------------------------------------------------------------------------
00049 MummyCsharpGenerator::MummyCsharpGenerator()
00050 {
00051   this->CurrentMethodId = 0;
00052   this->ClassLineNumber = 0;
00053   //this->MethodIdMap; // empty, as constructed
00054   //this->TargetInterface; // empty, as constructed
00055   //this->HintsMap; // empty, as constructed
00056 }
00057 
00058 
00059 //----------------------------------------------------------------------------
00060 MummyCsharpGenerator::~MummyCsharpGenerator()
00061 {
00062 }
00063 
00064 
00065 //----------------------------------------------------------------------------
00066 bool MummyCsharpGenerator::GenerateWrappers()
00067 {
00068   gxsys_stl::string elDllVariableName(this->GetSettings()->GetGroup());
00069   elDllVariableName += "EL_dll";
00070 
00071   this->EmitCSharpWrapperClass(*GetStream(), elDllVariableName.c_str(),
00072     GetTargetClass());
00073 
00074   if (this->GetSettings()->GetVerbose())
00075     {
00076     gxsys_stl::map<const gxsys_stl::string, gxsys_stl::string>::iterator it;
00077     for (it = this->HintsMap.begin(); it != this->HintsMap.end(); ++it)
00078       {
00079       LogInfo(mi_VerboseInfo, << it->first << ": '" << it->second << "'");
00080       }
00081     }
00082 
00083   return true;
00084 }
00085 
00086 
00087 //----------------------------------------------------------------------------
00088 void MummyCsharpGenerator::SetTargetClass(const cable::Class *c)
00089 {
00090   this->MummyGenerator::SetTargetClass(c);
00091 
00092   // Ensure HeaderFileReader is always called first with the target class.
00093   // (Presently, there is only one cached HeaderFileReader and it only reads
00094   // the file when it is first called... Subsequent calls ignore the input
00095   // data and simply return the cached HeaderFileReader. Multiple readers
00096   // may eventually be necessary to support BTX/ETX exclusion fully...)
00097   //
00098   this->GetHeaderFileReader(c);
00099 
00100   // Only populate the lookup entries with stuff from the *parent* class
00101   // (and its parents...) That way, we can use the existence of a signature
00102   // in the table as evidence that a virtual is being overridden (for C#
00103   // keyword override purposes) or that a static is being redefined at a
00104   // more derived level (hence hiding the parent's definition and requiring
00105   // use of the C# keyword "new" to avoid the warning about hiding...)
00106   //
00107   ClearLookupEntries();
00108   AddLookupEntries(GetWrappableParentClass(c));
00109 
00110   // Cache a map of the externalHints file:
00111   //
00112   this->CacheExternalHints(this->GetSettings()->GetExternalHints(c));
00113 }
00114 
00115 
00116 //----------------------------------------------------------------------------
00117 void MummyCsharpGenerator::CacheExternalHints(const gxsys_stl::string& hintsfile)
00118 {
00119   gxsys_stl::vector<gxsys_stl::string> hints;
00120   gxsys_stl::string line;
00121 
00122   gxsys::RegularExpression re;
00123   re.compile("([^\t ]+)[\t ]+([^\t ]+)[\t ]+([^\t ]+)[\t ]+([^\t ]+)");
00124 
00125   MummyLineOrientedTextFileReader reader;
00126   reader.SetFileName(hintsfile.c_str());
00127 
00128   this->HintsMap.clear();
00129 
00130   unsigned int n = reader.GetNumberOfLines();
00131   unsigned int i = 0;
00132   for (i= 1; i<=n; ++i)
00133   {
00134     line = reader.GetLine(i);
00135 
00136     if (!line.empty())
00137       {
00138       if (re.find(line))
00139         {
00140         gxsys_stl::string className = re.match(1);
00141         gxsys_stl::string methodName = re.match(2);
00142         gxsys_stl::string type = re.match(3);
00143         gxsys_stl::string count = re.match(4);
00144         gxsys_stl::string key(className + "::" + methodName);
00145 
00146         if (this->HintsMap.find(key) == this->HintsMap.end())
00147           {
00148           this->HintsMap[key] = line;
00149           }
00150         else
00151           {
00152           LogFileLineWarningMsg(hintsfile, i, mw_MultipleHints,
00153             "More than one line in the hints file for " << className
00154             << "::" << methodName);
00155           }
00156         }
00157       }
00158   }
00159 }
00160 
00161 
00162 //----------------------------------------------------------------------------
00163 void MummyCsharpGenerator::AddTargetInterface(const gxsys_stl::string& iface)
00164 {
00165   if (this->TargetInterface != "")
00166     {
00167     LogWarning(mw_MultipleTargetInterfaces,
00168       "AddTargetInterface being called more than once. " <<
00169       "Implementation currently only supports 1 interface. " <<
00170       "Clobbering existing target interface: " <<
00171       this->TargetInterface);
00172     }
00173 
00174   this->TargetInterface = iface;
00175 }
00176 
00177 
00178 //----------------------------------------------------------------------------
00179 bool MummyCsharpGenerator::HasTargetInterface(const char *iface) const
00180 {
00181   if (iface)
00182     {
00183     return this->TargetInterface == iface;
00184     }
00185 
00186   return false;
00187 }
00188 
00189 
00190 //----------------------------------------------------------------------------
00191 bool MummyCsharpGenerator::IsKeyword(const char *p)
00192 {
00193   static gxsys_stl::set<gxsys_stl::string> keywords;
00194 
00195   if (0 == keywords.size())
00196     {
00197     keywords.insert("abstract");
00198     keywords.insert("as");
00199     keywords.insert("base");
00200     keywords.insert("bool");
00201     keywords.insert("break");
00202     keywords.insert("byte");
00203     keywords.insert("case");
00204     keywords.insert("catch");
00205     keywords.insert("char");
00206     keywords.insert("checked");
00207     keywords.insert("class");
00208     keywords.insert("const");
00209     keywords.insert("continue");
00210     keywords.insert("decimal");
00211     keywords.insert("default");
00212     keywords.insert("delegate");
00213     keywords.insert("do");
00214     keywords.insert("double");
00215     keywords.insert("else");
00216     keywords.insert("enum");
00217     keywords.insert("event");
00218     keywords.insert("explicit");
00219     keywords.insert("extern");
00220     keywords.insert("false");
00221     keywords.insert("finally");
00222     keywords.insert("fixed");
00223     keywords.insert("float");
00224     keywords.insert("for");
00225     keywords.insert("foreach");
00226     keywords.insert("goto");
00227     keywords.insert("if");
00228     keywords.insert("implicit");
00229     keywords.insert("in");
00230     keywords.insert("int");
00231     keywords.insert("interface");
00232     keywords.insert("internal");
00233     keywords.insert("is");
00234     keywords.insert("lock");
00235     keywords.insert("long");
00236     keywords.insert("namespace");
00237     keywords.insert("new");
00238     keywords.insert("null");
00239     keywords.insert("object");
00240     keywords.insert("operator");
00241     keywords.insert("out");
00242     keywords.insert("override");
00243     keywords.insert("params");
00244     keywords.insert("private");
00245     keywords.insert("protected");
00246     keywords.insert("public");
00247     keywords.insert("readonly");
00248     keywords.insert("ref");
00249     keywords.insert("return");
00250     keywords.insert("sbyte");
00251     keywords.insert("sealed");
00252     keywords.insert("short");
00253     keywords.insert("sizeof");
00254     keywords.insert("stackalloc");
00255     keywords.insert("static");
00256     keywords.insert("string");
00257     keywords.insert("struct");
00258     keywords.insert("switch");
00259     keywords.insert("this");
00260     keywords.insert("throw");
00261     keywords.insert("true");
00262     keywords.insert("try");
00263     keywords.insert("typeof");
00264     keywords.insert("uint");
00265     keywords.insert("ulong");
00266     keywords.insert("unchecked");
00267     keywords.insert("unsafe");
00268     keywords.insert("ushort");
00269     keywords.insert("using");
00270     keywords.insert("virtual");
00271     keywords.insert("void");
00272     keywords.insert("volatile");
00273     keywords.insert("while");
00274     }
00275 
00276   gxsys_stl::set<gxsys_stl::string>::iterator it = keywords.find(gxsys_stl::string(p));
00277   if (it != keywords.end())
00278     {
00279     return true;
00280     }
00281 
00282   return false;
00283 }
00284 
00285 
00286 //----------------------------------------------------------------------------
00287 bool MummyCsharpGenerator::IsReservedMethodName(const gxsys_stl::string &name)
00288 {
00289   return (
00290     name == "Equals" ||
00291     name == "Finalize" ||
00292     name == "GetHashCode" ||
00293     name == "GetType" ||
00294     name == "MemberwiseClone" ||
00295     name == "Object" ||
00296     name == "ToString");
00297 }
00298 
00299 
00300 //----------------------------------------------------------------------------
00301 gxsys_stl::string MummyCsharpGenerator::GetFundamentalTypeString(const cable::Type *t)
00302 {
00303   gxsys_stl::string s;
00304 
00305   if (cable::Type::FundamentalTypeId == t->GetTypeId())
00306     {
00307     switch (cxx::FundamentalType::SafeDownCast(t->GetCxxType().GetType())->GetId())
00308       {
00309       case cxx::FundamentalType::UnsignedChar:
00310         s = "byte";
00311       break;
00312 
00313       case cxx::FundamentalType::UnsignedShortInt:
00314         s = "ushort";
00315       break;
00316 
00317       case cxx::FundamentalType::UnsignedInt:
00318       case cxx::FundamentalType::UnsignedLongInt:
00319         s = "uint";
00320       break;
00321 
00322       case cxx::FundamentalType::SignedChar:
00323       case cxx::FundamentalType::Char:
00324         s = "sbyte";
00325       break;
00326 
00327       case cxx::FundamentalType::ShortInt:
00328         s = "short";
00329       break;
00330 
00331       case cxx::FundamentalType::Int:
00332       case cxx::FundamentalType::LongInt:
00333         s = "int";
00334       break;
00335 
00336       case cxx::FundamentalType::Bool:
00337         s = "bool";
00338       break;
00339 
00340       case cxx::FundamentalType::Float:
00341         s = "float";
00342       break;
00343 
00344       case cxx::FundamentalType::Double:
00345         s = "double";
00346       break;
00347 
00348       case cxx::FundamentalType::Void:
00349         s = "void";
00350       break;
00351 
00352       case cxx::FundamentalType::UnsignedLongLongInt:
00353         s = "ulong";
00354       break;
00355 
00356       case cxx::FundamentalType::LongLongInt:
00357         s = "long";
00358       break;
00359 
00360       //case cxx::FundamentalType::WChar_t:
00361       //case cxx::FundamentalType::LongDouble:
00362       //case cxx::FundamentalType::ComplexFloat:
00363       //case cxx::FundamentalType::ComplexDouble:
00364       //case cxx::FundamentalType::ComplexLongDouble:
00365       //case cxx::FundamentalType::NumberOfTypes:
00366 
00367       default:
00368       break;
00369       }
00370     }
00371 
00372   if (s == "")
00373     {
00374     LogError(me_UnknownFundamentalType,
00375       << "Unhandled variable type. GetFundamentalTypeString returning the empty string...");
00376     }
00377 
00378   return s;
00379 }
00380 
00381 
00382 //----------------------------------------------------------------------------
00383 gxsys_stl::string MummyCsharpGenerator::GetWrappedMethodName(const cable::Method *m)
00384 {
00385   gxsys_stl::string name(m->GetName());
00386 
00387   if (IsReservedMethodName(name))
00388     {
00389     name = name + "Wrapper";
00390 
00391     LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_ReservedMethodName,
00392       << "Reserved method name '" << m->GetName() << "' used. Rename it to eliminate this warning...");
00393     }
00394 
00395   return name;
00396 }
00397 
00398 
00399 //----------------------------------------------------------------------------
00400 gxsys_stl::string GetWrappedEnumName(const cable::Enumeration *e)
00401 {
00402   gxsys_stl::string ename(e->GetName());
00403 
00404   // Don't name unnamed enums with invalid C#/C++ identifiers like "$"...
00405   //
00406   if (ename == "" ||
00407       strstr(ename.c_str(), "$"))
00408     {
00409     if (e->Begin() != e->End())
00410       {
00411       ename = *(e->Begin());
00412       ename += "_WrapperEnum";
00413       }
00414     else
00415       {
00416       ename = "WARNING_unnamed_enum";
00417       }
00418 
00419     LogFileLineWarningMsg(e->GetFile(), e->GetLine(), mw_UnnamedEnum,
00420       "Unnamed enum found. Name it to eliminate this warning...");
00421     }
00422 
00423   return ename;
00424 }
00425 
00426 
00427 //----------------------------------------------------------------------------
00428 bool ExtractTypeAndCountFromHintLine(
00429   const gxsys_stl::string& hint,
00430   gxsys_stl::string& type,
00431   gxsys_stl::string& count
00432 )
00433 {
00434   gxsys::RegularExpression re;
00435   re.compile("([^\t ]+)[\t ]+([^\t ]+)[\t ]+([^\t ]+)[\t ]+([^\t ]+)");
00436 
00437   if (re.find(hint))
00438     {
00439     //className = re.match(1);
00440     //methodName = re.match(2);
00441     type = re.match(3);
00442     count = re.match(4);
00443     return true;
00444     }
00445 
00446   return false;
00447 }
00448 
00449 
00450 //----------------------------------------------------------------------------
00451 bool ReturnTypeMatchesHintType(
00452   cable::Type *t,
00453   const gxsys_stl::string& type
00454   )
00455 {
00456   gxsys_stl::string utype = gxsys::SystemTools::UpperCase(type);
00457 
00458   //  Init with the "not a real enum value" value:
00459   //
00460   cxx::FundamentalType::Id ftid = cxx::FundamentalType::NumberOfTypes;
00461 
00462   // Values of 'type' currently present in the VTK hints file are:
00463   //
00464   if      (utype ==  "301") { ftid = cxx::FundamentalType::Float; }
00465   else if (utype ==  "304") { ftid = cxx::FundamentalType::Int; }
00466   else if (utype ==  "307") { ftid = cxx::FundamentalType::Double; }
00467   else if (utype ==  "30A") { ftid = cxx::FundamentalType::Int; } // vtkIdType (could be 32 or 64 bit...)
00468   else if (utype == "2307") { ftid = cxx::FundamentalType::Double; } // 0x2307 == double* + something?
00469 
00470   if (cxx::FundamentalType::NumberOfTypes == ftid)
00471     {
00472     LogWarning(mw_UnknownHintDataType, "Unknown externalHints data type string '" << type << "'");
00473     }
00474   else if (IsFundamentalPointer(t, ftid))
00475     {
00476     return true;
00477     }
00478 
00479   return false;
00480 }
00481 
00482 
00483 //----------------------------------------------------------------------------
00484 bool ExtractCountFromMethodDeclarationLine(
00485   const gxsys_stl::string& line,
00486   gxsys_stl::string& count
00487 )
00488 {
00489   // Grudgingly allow this VTK-ism to creep into the mummy codebase to avoid
00490   // introducing a list of regular expressions in the MummySettings.xml file
00491   // just for VTK-wrapping-hint-based array size specification...
00492   //
00493   // In VTK, in addition to the explicit external hints file VTK/Wrapping/hints,
00494   // the following macros from VTK/Common/vtkSetGet.h are also recognized as
00495   // implicitly having hint sizes associated with them:
00496   //
00497   gxsys::RegularExpression re;
00498 
00499   re.compile("^[\t ]*vtkGetVector([0-9]+)Macro\\(.*,.*\\)");
00500   if (re.find(line))
00501     {
00502     count = re.match(1);
00503     return true;
00504     }
00505 
00506   re.compile("^[\t ]*vtkGetVectorMacro\\(.*,.*,.*([0-9]+).*\\)");
00507   if (re.find(line))
00508     {
00509     count = re.match(1);
00510     return true;
00511     }
00512 
00513   re.compile("^[\t ]*vtkViewportCoordinateMacro\\(.*\\)");
00514   if (re.find(line))
00515     {
00516     count = "2";
00517     return true;
00518     }
00519 
00520   re.compile("^[\t ]*vtkWorldCoordinateMacro\\(.*\\)");
00521   if (re.find(line))
00522     {
00523     count = "3";
00524     return true;
00525     }
00526 
00527   return false;
00528 }
00529 
00530 
00531 //----------------------------------------------------------------------------
00532 // Callers of GetMethodArgumentArraySize pass this value as "i" to query
00533 // about the return value of the method. Otherwise, "i" is presumed to be an
00534 // index (0 to cArgs-1) of one of the method's arguments.
00535 #define RETURN_VALUE (0x84848484)
00536 
00537 
00538 //----------------------------------------------------------------------------
00539 gxsys_stl::string MummyCsharpGenerator::GetMethodArgumentArraySize(const cable::Class *c,
00540   const cable::Method *m, const cable::FunctionType *ft, unsigned int i)
00541 {
00542   gxsys_stl::string argArraySize;
00543   cable::Type *retType = 0;
00544 
00545   // First check for an attribute directly in the source code:
00546   //
00547   gxsys_stl::string atts;
00548 
00549   if (RETURN_VALUE == i)
00550     {
00551     atts = m->GetAttributes();
00552     }
00553   else
00554     {
00555     atts = ft->GetArgumentAttributes(i);
00556     }
00557 
00558   if (atts != "")
00559     {
00560     argArraySize = ExtractArraySize(atts);
00561     }
00562 
00563   // If no direct hint, check the externalHints file, if any:
00564   //
00565   if ((argArraySize == "") && (this->HintsMap.size() > 0))
00566     {
00567     // The VTK/Wrapping/hints file only tells us about the size of the pointer
00568     // returned by the method. Only return a non-empty array size from a
00569     // VTK/Wrapping/hints-style file if this is for a return value of a method:
00570     //
00571     if (RETURN_VALUE == i)
00572       {
00573       retType = ft->GetReturns();
00574       if (!IsVoid(retType))
00575         {
00576         gxsys_stl::string methodName(m->GetName());
00577         gxsys_stl::map<const gxsys_stl::string, gxsys_stl::string>::iterator it;
00578 
00579         const cable::Class *cIt = c;
00580         while (cIt != NULL && (argArraySize == ""))
00581           {
00582           gxsys_stl::string fullClassName(GetFullyQualifiedNameForCPlusPlus(cIt));
00583           gxsys_stl::string key(fullClassName + "::" + methodName);
00584           gxsys_stl::string hintline;
00585 
00586           it = this->HintsMap.find(key);
00587           if (it != this->HintsMap.end())
00588             {
00589             hintline = it->second;
00590 
00591             gxsys_stl::string type;
00592             gxsys_stl::string count;
00593             if (ExtractTypeAndCountFromHintLine(hintline, type, count) &&
00594               ReturnTypeMatchesHintType(retType, type))
00595               {
00596               argArraySize = count;
00597               LogVerboseInfo("using external array size hint: " << argArraySize << " " << key);
00598               }
00599             }
00600 
00601           // If not found yet, keep looking up at parent class hints to see
00602           // if it is inherited. Method name and return type should still match
00603           // even if the hint is at the parent class level.
00604           //
00605           if (argArraySize == "")
00606             {
00607             cIt = GetParentClass(cIt);
00608             }
00609           }
00610         }
00611       }
00612     }
00613 
00614   // If still no hint, see if the line declaring the method in the header
00615   // file uses a macro that gives us a hint about the size of the array...
00616   //
00617   if (argArraySize == "")
00618     {
00619     if (RETURN_VALUE == i)
00620       {
00621       retType = ft->GetReturns();
00622       if (!IsVoid(retType) && !IsObjectPointer(retType))
00623         {
00624         gxsys_stl::string line(this->GetHeaderFileReader(c)->GetLine(m->GetLine()));
00625         gxsys_stl::string count;
00626 
00627         if (ExtractCountFromMethodDeclarationLine(line, count))
00628           {
00629           argArraySize = count;
00630 
00631           if (this->GetSettings()->GetVerbose())
00632             {
00633             LogFileLineInfoMsg(c->GetFile(), m->GetLine(), mi_VerboseInfo,
00634               "inferred array size hint '" << count <<
00635               "' from method declaration '" << line << "'");
00636             }
00637           }
00638         }
00639       }
00640     }
00641 
00642   return argArraySize;
00643 }
00644 
00645 
00646 //----------------------------------------------------------------------------
00647 //
00648 // GetMethodSignature is very similar to EmitCSharpMethodDeclaration.
00649 // Changes in either may need to be made in both places...
00650 //
00651 gxsys_stl::string MummyCsharpGenerator::GetMethodSignature(const cable::Class *c, const cable::Method *m)
00652 {
00653   gxsys_ios::ostringstream os;
00654 
00655   cable::FunctionType *ft = m->GetFunctionType();
00656   unsigned int cArgs = ft->GetNumberOfArguments();
00657   unsigned int i = 0;
00658   cable::Type *argType = 0;
00659 
00660   // Method name:
00661   Emit(os, GetWrappedMethodName(m).c_str());
00662 
00663   // Open args:
00664   Emit(os, "(");
00665 
00666   // The C# arg types:
00667   for (i= 0; i<cArgs; ++i)
00668     {
00669     argType = ft->GetArgument(i);
00670 
00671     // Is arg an array?
00672     //
00673     gxsys_stl::string argArraySize(GetMethodArgumentArraySize(c, m, ft, i));
00674 
00675     // arg type:
00676     Emit(os, GetCSharpTypeString(argType, false, argArraySize != "").c_str());
00677 
00678     // array notation:
00679     if (argArraySize != "")
00680       {
00681       Emit(os, "[]");
00682       }
00683 
00684     if (i<cArgs-1)
00685       {
00686       Emit(os, ", ");
00687       }
00688     }
00689 
00690   // Close args:
00691   Emit(os, ")");
00692 
00693   return os.str();
00694 }
00695 
00696 
00697 //----------------------------------------------------------------------------
00698 const char *MummyCsharpGenerator::GetArgName(cable::FunctionType *ftype, unsigned int i)
00699 {
00700   const char *p = ftype->GetArgumentName(i);
00701 
00702   if (p && *p && !IsKeyword(p))
00703     {
00704     return p;
00705     }
00706 
00707   // Hacky, but for now:
00708   static char buf[32];
00709   sprintf(buf, "arg%u", i);
00710   return buf;
00711 }
00712 
00713 
00714 //----------------------------------------------------------------------------
00715 bool MummyCsharpGenerator::FundamentalTypeIsWrappable(const cable::Type* t)
00716 {
00717   if (cable::Type::FundamentalTypeId == t->GetTypeId())
00718     {
00719     switch (cxx::FundamentalType::SafeDownCast(t->GetCxxType().GetType())->GetId())
00720       {
00721       case cxx::FundamentalType::UnsignedChar:
00722       case cxx::FundamentalType::UnsignedShortInt:
00723       case cxx::FundamentalType::UnsignedInt:
00724       case cxx::FundamentalType::UnsignedLongInt:
00725       case cxx::FundamentalType::SignedChar:
00726       case cxx::FundamentalType::Char:
00727       case cxx::FundamentalType::ShortInt:
00728       case cxx::FundamentalType::Int:
00729       case cxx::FundamentalType::LongInt:
00730       case cxx::FundamentalType::Bool:
00731       case cxx::FundamentalType::Float:
00732       case cxx::FundamentalType::Double:
00733       case cxx::FundamentalType::Void:
00734       case cxx::FundamentalType::UnsignedLongLongInt:
00735       case cxx::FundamentalType::LongLongInt:
00736         return true;
00737       break;
00738 
00739       case cxx::FundamentalType::WChar_t:
00740       case cxx::FundamentalType::LongDouble:
00741       case cxx::FundamentalType::ComplexFloat:
00742       case cxx::FundamentalType::ComplexDouble:
00743       case cxx::FundamentalType::ComplexLongDouble:
00744       case cxx::FundamentalType::NumberOfTypes:
00745       default:
00746       break;
00747       }
00748     }
00749 
00750   return false;
00751 }
00752 
00753 
00754 //----------------------------------------------------------------------------
00755 bool MummyCsharpGenerator::TypeIsWrappable(const cable::Type* t)
00756 {
00757   bool wrappable = false;
00758 
00759   switch (t->GetTypeId())
00760     {
00761     case cable::Type::EnumerationTypeId:
00762       wrappable = true;
00763     break;
00764 
00765     case cable::Type::FundamentalTypeId:
00766       wrappable = FundamentalTypeIsWrappable(t);
00767     break;
00768 
00769     case cable::Type::ArrayTypeId:
00770       wrappable = false;//TypeIsWrappable(cable::ArrayType::SafeDownCast(t)->GetTarget());
00771     break;
00772 
00773     case cable::Type::ClassTypeId:
00774       wrappable = ClassIsWrappable(cable::ClassType::SafeDownCast(t)->GetClass());
00775     break;
00776 
00777     case cable::Type::PointerTypeId:
00778       {
00779       cable::Type *nested_type = cable::PointerType::SafeDownCast(t)->GetTarget();
00780       cable::Type::TypeIdType nested_type_id = nested_type->GetTypeId();
00781 
00782       // Only pointers to enums, fundamentals and objects are wrappable
00783       // and then only if the nested type is also wrappable...
00784       //
00785       // A function pointer is "nearly" wrappable (as a delegate definition)
00786       // if its return type and all of its argument types are wrappable.
00787       // And it has a typedef that names it within this class (because
00788       // that typedef is the thing that generates the delegate def in
00789       // the C# class...)
00790       //
00791       // (This is what prevents pointers to pointers from being wrapped
00792       // in the general case...)
00793       //
00794       if (cable::Type::EnumerationTypeId == nested_type_id ||
00795         cable::Type::FundamentalTypeId == nested_type_id ||
00796         cable::Type::ClassTypeId == nested_type_id)
00797         {
00798         wrappable = TypeIsWrappable(nested_type);
00799         }
00800       else if (cable::Type::FunctionTypeId == nested_type_id)
00801         {
00802         gxsys_stl::string s;
00803 
00804         // Only really wrappable if nested_type is wrappable and an
00805         // equivalent typedef name exists:
00806         //
00807         wrappable = TypeIsWrappable(nested_type) &&
00808           EquivalentTypedefNameExists(this->GetTargetClass(),
00809             cable::FunctionType::SafeDownCast(nested_type), s);
00810         }
00811       else
00812         {
00813         wrappable = false; // as initialized...
00814         }
00815       }
00816     break;
00817 
00818     case cable::Type::ReferenceTypeId:
00819       {
00820       cable::Type *nested_type = cable::ReferenceType::SafeDownCast(t)->GetTarget();
00821       cable::Type::TypeIdType nested_type_id = nested_type->GetTypeId();
00822 
00823       wrappable = false; // as initialized...
00824 
00825       if (cable::Type::EnumerationTypeId == nested_type_id ||
00826         cable::Type::FundamentalTypeId == nested_type_id ||
00827         cable::Type::ClassTypeId == nested_type_id)
00828         {
00829         wrappable = TypeIsWrappable(nested_type);
00830         }
00831       else if (cable::Type::PointerTypeId == nested_type_id)
00832         {
00833         // References to pointers are allowed, but only if the pointer points
00834         // to a wrappable class...
00835         //
00836         cable::Type *doubly_nested_type = cable::PointerType::SafeDownCast(nested_type)->GetTarget();
00837         cable::Type::TypeIdType doubly_nested_type_id = doubly_nested_type->GetTypeId();
00838 
00839         if (cable::Type::ClassTypeId == doubly_nested_type_id)
00840           {
00841           wrappable = TypeIsWrappable(doubly_nested_type);
00842           }
00843         }
00844       }
00845     break;
00846 
00847     case cable::Type::OffsetTypeId:
00848     case cable::Type::MethodTypeId:
00849       wrappable = false;
00850     break;
00851 
00852     case cable::Type::FunctionTypeId:
00853       wrappable = FunctionTypeIsWrappable(cable::FunctionType::SafeDownCast(t));
00854     break;
00855 
00856     default:
00857       wrappable = false;
00858     break;
00859     }
00860 
00861   return wrappable;
00862 }
00863 
00864 
00865 //----------------------------------------------------------------------------
00866 bool IsCxxMainStyleParamPair(const cable::FunctionType* ft, unsigned int i)
00867 {
00868   // Special case C++ "main" style functions, as indicated by *this* *exact*
00869   // *signature* including arg names:
00870   //   (..., int argc, char* argv[], ...)
00871   //
00872   if (i < ft->GetNumberOfArguments()-1 &&
00873     gxsys_stl::string("argc") == ft->GetArgumentName(i) &&
00874     cable::Type::FundamentalTypeId == ft->GetArgument(i)->GetTypeId() &&
00875     cxx::FundamentalType::Int == cxx::FundamentalType::SafeDownCast(ft->GetArgument(i)->GetCxxType().GetType())->GetId() &&
00876     gxsys_stl::string("argv") == ft->GetArgumentName(i+1) &&
00877     IsCharPointerPointer(ft->GetArgument(i+1))
00878     )
00879     {
00880     return true;
00881     }
00882 
00883   return false;
00884 }
00885 
00886 
00887 //----------------------------------------------------------------------------
00888 bool MummyCsharpGenerator::FunctionTypeIsWrappable(const cable::FunctionType* ft)
00889 {
00890   bool wrappable = true;
00891 
00892   cable::Type *argType = 0;
00893   cable::Type *retType = 0;
00894   unsigned int i = 0;
00895   unsigned int cArgs = ft->GetNumberOfArguments();
00896 
00897   retType = ft->GetReturns();
00898   wrappable = TypeIsWrappable(retType);
00899 
00900   for (i= 0; wrappable && i<cArgs; ++i)
00901     {
00902     argType = ft->GetArgument(i);
00903     wrappable = TypeIsWrappable(argType);
00904 
00905     // If not wrappable because argType is "char**" but it is a cxx main style
00906     // param pair, then go ahead and say it is wrappable...
00907     //
00908     if (!wrappable && IsCxxMainStyleParamPair(ft, i-1))
00909       {
00910       wrappable = true;
00911       }
00912     }
00913 
00914   return wrappable;
00915 }
00916 
00917 
00918 //----------------------------------------------------------------------------
00919 bool MummyCsharpGenerator::MethodWrappableAsEvent(const cable::Method* m, const cable::Context::Access& access)
00920 {
00921   bool wrappableAsEvent = false;
00922   gxsys_stl::string atts;
00923 
00924   if (m)
00925     {
00926     if (cable::Context::Public == access)
00927       {
00928       if (cable::Function::FunctionId == m->GetFunctionId() ||
00929         cable::Function::MethodId == m->GetFunctionId())
00930         {
00931         wrappableAsEvent = HasAttribute(m, "gccxml(iwhEvent)");
00932         }
00933       }
00934     }
00935 
00936   return wrappableAsEvent;
00937 }
00938 
00939 //----------------------------------------------------------------------------
00940 bool MummyCsharpGenerator::MethodIsWrappable(const cable::Method* m, const cable::Context::Access& access)
00941 {
00942   bool wrappable = false;
00943   bool hasDeprecatedAttribute = false;
00944   bool hasExcludeAttribute = false;
00945   bool isExcludedViaBtxEtx = false;
00946 
00947   if (m)
00948     {
00949     if (cable::Context::Public == access)
00950       {
00951       if (cable::Function::FunctionId == m->GetFunctionId() ||
00952         cable::Function::MethodId == m->GetFunctionId())
00953         {
00954         if (FunctionTypeIsWrappable(m->GetFunctionType()))
00955           {
00956           wrappable = true;
00957           }
00958         }
00959       }
00960     }
00961 
00962   if (m && wrappable)
00963     {
00964     hasDeprecatedAttribute = HasAttribute(m, "deprecated");
00965     hasExcludeAttribute = HasAttribute(m, "gccxml(iwhExclude)");
00966 
00967     if (hasDeprecatedAttribute || hasExcludeAttribute)
00968       {
00969       wrappable = false;
00970       }
00971     }
00972 
00973   if (m && wrappable)
00974     {
00975     cable::Class* c = cable::Class::SafeDownCast(m->GetContext());
00976 
00977     // BTX ETX style exclusion can only be applied to methods of the current target
00978     // class... We need to add multiple HeaderFileReader support to enable BTX ETX style
00979     // exclusion fully (including parent class method exclusion for purposes of constructing
00980     // the initial "what virtual methods do I inherit?" table...)
00981     //
00982     if (c && c == this->GetTargetClass() &&
00983       this->GetHeaderFileReader(c)->IsLineExcluded(m->GetLine()))
00984       {
00985       isExcludedViaBtxEtx = true;
00986       wrappable = false;
00987       }
00988     }
00989 
00990   if (this->GetSettings()->GetVerbose())
00991     {
00992     if (m)
00993       {
00994       if (cable::Context::Public == access)
00995         {
00996         if (cable::Function::FunctionId == m->GetFunctionId() ||
00997           cable::Function::MethodId == m->GetFunctionId())
00998           {
00999           if (wrappable)
01000             {
01001             LogInfo(mi_VerboseInfo, << m->GetNameOfClass() << " '" << m->GetName()
01002               << "' is wrappable..." << gxsys_ios::endl);
01003             }
01004           else
01005             {
01006             if (hasDeprecatedAttribute)
01007               {
01008               LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01009                 << "' could not be wrapped because it is marked with the 'deprecated' attribute..." << gxsys_ios::endl);
01010               }
01011             else if (hasExcludeAttribute)
01012               {
01013               LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01014                 << "' could not be wrapped because it is marked with the 'gccxml(iwhExclude)' attribute..." << gxsys_ios::endl);
01015               }
01016             else if (isExcludedViaBtxEtx)
01017               {
01018               LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01019                 << "' could not be wrapped because it is in between begin/end exclude markers (BTX/ETX)..." << gxsys_ios::endl);
01020               }
01021             else
01022               {
01023               LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01024                 << "' could not be wrapped because of its return type or one of its arguments' types..." << gxsys_ios::endl);
01025               }
01026             }
01027           }
01028         else
01029           {
01030           LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01031             << "' could not be wrapped because it's not a function or method..." << gxsys_ios::endl);
01032           }
01033         }
01034       else
01035         {
01036         LogWarning(mw_CouldNotWrap, << m->GetNameOfClass() << " '" << m->GetName()
01037           << "' is not considered for wrapping because of its non-public access level..." << gxsys_ios::endl);
01038         }
01039       }
01040     else
01041       {
01042       LogWarning(mw_CouldNotWrap, << "NULL m!!" << gxsys_ios::endl);
01043       }
01044     }
01045 
01046   return wrappable;
01047 }
01048 
01049 
01050 //----------------------------------------------------------------------------
01051 bool MummyCsharpGenerator::ClassIsWrappable(const cable::Class* c)
01052 {
01053   return MummyGenerator::ClassIsWrappable(c);
01054 }
01055 
01056 
01057 //----------------------------------------------------------------------------
01058 const cable::Class* MummyCsharpGenerator::GetWrappableParentClass(const cable::Class *c)
01059 {
01060   const cable::Class* wrappableParent = GetParentClass(c);
01061 
01062   while (wrappableParent && !this->ClassIsWrappable(wrappableParent))
01063     {
01064     wrappableParent = GetParentClass(wrappableParent);
01065     }
01066 
01067   return wrappableParent;
01068 }
01069 
01070 
01071 //----------------------------------------------------------------------------
01072 //
01073 // WARNING: GetIsRefArg, GetPInvokeTypeString and GetCSharpTypeString need to
01074 // stay in sync. Changes in one likely imply that changes in the other are
01075 // also required...
01076 //
01077 // This function needs to stay in sync with the case in GetCSharpTypeString
01078 // where "ref " is prepended to the C# type string... If "ref " is prepended
01079 // then this function should return true.
01080 //
01081 bool MummyCsharpGenerator::GetIsRefArg(const cable::Type *t)
01082 {
01083   cable::Type *nested_type = 0;
01084 
01085   if (cable::Type::ReferenceTypeId == t->GetTypeId())
01086     {
01087     nested_type = cable::ReferenceType::SafeDownCast(t)->GetTarget();
01088     cable::Type::TypeIdType nested_type_id = nested_type->GetTypeId();
01089 
01090     if (cable::Type::EnumerationTypeId == nested_type_id ||
01091       cable::Type::FundamentalTypeId == nested_type_id)
01092       {
01093       return true;
01094       }
01095     }
01096 
01097   return false;
01098 }
01099 
01100 
01101 //----------------------------------------------------------------------------
01102 gxsys_stl::string /* MummyCsharpGenerator:: */ GetEnumerationTypeString(const cable::Type *t)
01103 {
01104   gxsys_stl::string s;
01105   cable::Enumeration* e = cable::EnumerationType::SafeDownCast(t)->GetEnumeration();
01106 
01107   s = GetFullyQualifiedNameForCSharp(e);
01108 
01109   // Don't name unnamed enums with invalid C#/C++ identifiers like "$"...
01110   //
01111   if (strstr(s.c_str(), "$"))
01112     {
01113     s = "uint /* WARNING_unnamed_enum */";
01114     }
01115 
01116   return s;
01117 }
01118 
01119 
01120 //----------------------------------------------------------------------------
01121 //
01122 // WARNING: GetIsRefArg, GetPInvokeTypeString and GetCSharpTypeString need to
01123 // stay in sync. Changes in one likely imply that changes in the other are
01124 // also required...
01125 //
01126 gxsys_stl::string MummyCsharpGenerator::GetPInvokeTypeString(const cable::Type *t, bool forReturn, bool isArray, bool forDelegate)
01127 {
01128   gxsys_stl::string s;
01129   cable::Type *nested_type = 0;
01130 
01131   switch (t->GetTypeId())
01132     {
01133     case cable::Type::EnumerationTypeId:
01134       s = GetEnumerationTypeString(t);
01135     break;
01136 
01137     case cable::Type::FundamentalTypeId:
01138       s = GetFundamentalTypeString(t);
01139 
01140       // C# byte maps automatically to C++ bool via PInvoke
01141       //
01142       if (s == "bool")
01143         {
01144         s = "byte";
01145         }
01146     break;
01147 
01148     case cable::Type::ArrayTypeId:
01149       s = "ERROR_ArrayTypeId_not_yet_implemented";
01150       LogError(me_InternalError, << s.c_str());
01151     break;
01152 
01153     case cable::Type::ClassTypeId:
01154       //s = cable::ClassType::SafeDownCast(t)->GetClass()->GetName();
01155       s = GetWrappedClassNameFullyQualified(cable::ClassType::SafeDownCast(t)->GetClass());
01156     break;
01157 
01158     case cable::Type::PointerTypeId:
01159       nested_type = cable::PointerType::SafeDownCast(t)->GetTarget();
01160 
01161       if (IsObject(nested_type))
01162         {
01163         if (forReturn || forDelegate)
01164           {
01165           s = "IntPtr";
01166           }
01167         else
01168           {
01169           cable::Class *c = cable::ClassType::SafeDownCast(nested_type)->GetClass();
01170           if (IsUtilityClass(c))
01171             {
01172             //s = c->GetName();
01173             s = GetWrappedClassNameFullyQualified(c);
01174             }
01175           else
01176             {
01177             s = "HandleRef";
01178             }
01179           }
01180         }
01181       else if (IsChar(nested_type))
01182         {
01183         if (forReturn)
01184           {
01185           s = "IntPtr";
01186           }
01187         else
01188           {
01189           s = "string";
01190           }
01191         }
01192       else if (IsVoid(nested_type))
01193         {
01194         s = "IntPtr";
01195         }
01196       else if (cable::Type::FundamentalTypeId == nested_type->GetTypeId())
01197         {
01198         if (isArray && !forReturn)
01199           {
01200           s = GetPInvokeTypeString(nested_type, forReturn, isArray, forDelegate);
01201           }
01202         else
01203           {
01204           s = "IntPtr";
01205           }
01206         }
01207       else if (cable::Type::PointerTypeId == nested_type->GetTypeId())
01208         {
01209         if (IsCharPointer(nested_type))
01210           {
01211           s = "[In, Out] string[]";
01212           }
01213         else
01214           {
01215           s = "IntPtr /* pointer-to-pointer */";
01216           }
01217         }
01218       else if (cable::Type::FunctionTypeId == nested_type->GetTypeId())
01219         {
01220         if (!EquivalentTypedefNameExists(this->GetTargetClass(),
01221           cable::FunctionType::SafeDownCast(nested_type), s))
01222           {
01223           s = "ERROR_No_equivalent_typedef_name_for_function_pointer";
01224           LogError(me_InternalError, << s.c_str());
01225           }
01226         }
01227       else
01228         {
01229         s = "ERROR_PointerTypeId_not_yet_implemented_for_nested_type";
01230         LogError(me_InternalError, << s.c_str());
01231         }
01232     break;
01233 
01234     case cable::Type::ReferenceTypeId:
01235       {
01236       cable::Type *nested_type = cable::ReferenceType::SafeDownCast(t)->GetTarget();
01237       cable::Type::TypeIdType nested_type_id = nested_type->GetTypeId();
01238 
01239       s = "ERROR_ReferenceTypeId_not_yet_implemented_for_nested_type";
01240 
01241       if (cable::Type::EnumerationTypeId == nested_type_id ||
01242         cable::Type::FundamentalTypeId == nested_type_id ||
01243         cable::Type::ClassTypeId == nested_type_id)
01244         {
01245         s = GetPInvokeTypeString(nested_type, forReturn, isArray, forDelegate);
01246 
01247         if (!isArray && (cable::Type::EnumerationTypeId == nested_type_id ||
01248           cable::Type::FundamentalTypeId == nested_type_id))
01249           {
01250           if (forReturn)
01251             {
01252             s = "IntPtr";
01253             }
01254           else
01255             {
01256             s = gxsys_stl::string("ref ") + s;
01257             }
01258           }
01259         }
01260       else if (cable::Type::PointerTypeId == nested_type_id)
01261         {
01262         // References to pointers are allowed, but only if the pointer points
01263         // to a wrappable class...
01264         //
01265         cable::Type *doubly_nested_type = cable::PointerType::SafeDownCast(nested_type)->GetTarget();
01266         cable::Type::TypeIdType doubly_nested_type_id = doubly_nested_type->GetTypeId();
01267 
01268         if (cable::Type::ClassTypeId == doubly_nested_type_id)
01269           {
01270           s = GetPInvokeTypeString(nested_type, forReturn, isArray, forDelegate);
01271           }
01272         }
01273 
01274       if (s == "ERROR_ReferenceTypeId_not_yet_implemented_for_nested_type")
01275         {
01276         LogError(me_InternalError, << s.c_str());
01277         }
01278       }
01279     break;
01280 
01281     case cable::Type::OffsetTypeId:
01282     case cable::Type::MethodTypeId:
01283     case cable::Type::FunctionTypeId:
01284     default:
01285       s = "ERROR_No_CSharp_type_for_cable_Type_TypeId";
01286       LogError(me_InternalError, << s.c_str());
01287     break;
01288     }
01289 
01290   return s;
01291 }
01292 
01293 
01294 //----------------------------------------------------------------------------
01295 //
01296 // WARNING: GetIsRefArg, GetPInvokeTypeString and GetCSharpTypeString need to
01297 // stay in sync. Changes in one likely imply that changes in the other are
01298 // also required...
01299 //
01300 gxsys_stl::string MummyCsharpGenerator::GetCSharpTypeString(const cable::Type *t, bool forReturn, bool isArray)
01301 {
01302   gxsys_stl::string s;
01303   cable::Type *nested_type = 0;
01304 
01305   switch (t->GetTypeId())
01306     {
01307     case cable::Type::EnumerationTypeId:
01308       s = GetEnumerationTypeString(t);
01309     break;
01310 
01311     case cable::Type::FundamentalTypeId:
01312       s = GetFundamentalTypeString(t);
01313     break;
01314 
01315     case cable::Type::ArrayTypeId:
01316       s = "ERROR_ArrayTypeId_not_yet_implemented";
01317       LogError(me_InternalError, << s.c_str());
01318     break;
01319 
01320     case cable::Type::ClassTypeId:
01321       //s = cable::ClassType::SafeDownCast(t)->GetClass()->GetName();
01322       s = GetWrappedClassNameFullyQualified(cable::ClassType::SafeDownCast(t)->GetClass());
01323     break;
01324 
01325     case cable::Type::PointerTypeId:
01326       nested_type = cable::PointerType::SafeDownCast(t)->GetTarget();
01327 
01328       if (IsObject(nested_type))
01329         {
01330         s = GetCSharpTypeString(nested_type, forReturn, isArray);
01331         }
01332       else if (IsChar(nested_type))
01333         {
01334         s = "string";
01335         }
01336       else if (IsVoid(nested_type))
01337         {
01338         s = "IntPtr";
01339         }
01340       else if (cable::Type::FundamentalTypeId == nested_type->GetTypeId())
01341         {
01342         if (isArray)
01343           {
01344           s = GetCSharpTypeString(nested_type, forReturn, isArray);
01345           }
01346         else
01347           {
01348           s = "IntPtr";
01349           }
01350         }
01351       else if (cable::Type::PointerTypeId == nested_type->GetTypeId())
01352         {
01353         s = GetCSharpTypeString(nested_type, forReturn, isArray);
01354 
01355         if (IsCharPointer(nested_type))
01356           {
01357           s += "[]";
01358           }
01359         else
01360           {
01361           s += " /* pointer-to-pointer */ ";
01362           }
01363         }
01364       else if (cable::Type::FunctionTypeId == nested_type->GetTypeId())
01365         {
01366         s = GetCSharpTypeString(nested_type, forReturn, isArray);
01367         }
01368       else
01369         {
01370         s = "ERROR_PointerTypeId_not_yet_implemented_for_nested_type";
01371         LogError(me_InternalError, << s.c_str());
01372         }
01373     break;
01374 
01375     case cable::Type::ReferenceTypeId:
01376       {
01377       cable::Type *nested_type = cable::ReferenceType::SafeDownCast(t)->GetTarget();
01378       cable::Type::TypeIdType nested_type_id = nested_type->GetTypeId();
01379 
01380       s = "ERROR_ReferenceTypeId_not_yet_implemented_for_nested_type";
01381 
01382       if (cable::Type::EnumerationTypeId == nested_type_id ||
01383         cable::Type::FundamentalTypeId == nested_type_id ||
01384         cable::Type::ClassTypeId == nested_type_id)
01385         {
01386         s = GetCSharpTypeString(nested_type, forReturn, isArray);
01387 
01388         if (!isArray && (cable::Type::EnumerationTypeId == nested_type_id ||
01389           cable::Type::FundamentalTypeId == nested_type_id))
01390           {
01391           if (forReturn)
01392             {
01393             s = "IntPtr";
01394             }
01395           else if (s == "bool")
01396             {
01397             // Special case: transform "ref bool" args to "ref byte" args so
01398             // that we can call the PInvoke method, which changes all "bool"
01399             // to "byte" -- we don't do it in the non-ref case because it works
01400             // automatically and "bool" is a nicer C# signature. But in the ref
01401             // case, we need:
01402             //
01403             s = gxsys_stl::string("ref byte");
01404             }
01405           else
01406             {
01407             s = gxsys_stl::string("ref ") + s;
01408             }
01409           }
01410         }
01411       else if (cable::Type::PointerTypeId == nested_type_id)
01412         {
01413         // References to pointers are allowed, but only if the pointer points
01414         // to a wrappable class...
01415         //
01416         cable::Type *doubly_nested_type = cable::PointerType::SafeDownCast(nested_type)->GetTarget();
01417         cable::Type::TypeIdType doubly_nested_type_id = doubly_nested_type->GetTypeId();
01418 
01419         if (cable::Type::ClassTypeId == doubly_nested_type_id)
01420           {
01421           s = GetCSharpTypeString(doubly_nested_type, forReturn, isArray);
01422           }
01423         }
01424 
01425       if (s == "ERROR_ReferenceTypeId_not_yet_implemented_for_nested_type")
01426         {
01427         LogError(me_InternalError, << s.c_str());
01428         }
01429       }
01430     break;
01431 
01432     case cable::Type::FunctionTypeId:
01433       if (!EquivalentTypedefNameExists(this->GetTargetClass(),
01434         cable::FunctionType::SafeDownCast(t), s))
01435         {
01436         s = "ERROR_No_equivalent_typedef_name_for_function_pointer";
01437         LogError(me_InternalError, << s.c_str());
01438         }
01439     break;
01440 
01441     case cable::Type::OffsetTypeId:
01442     case cable::Type::MethodTypeId:
01443     default:
01444       s = "ERROR_No_CSharp_type_for_cable_Type_TypeId";
01445       LogError(me_InternalError, << s.c_str());
01446     break;
01447     }
01448 
01449   return s;
01450 }
01451 
01452 
01453 //----------------------------------------------------------------------------
01454 class MethodInstance
01455 {
01456 public:
01457   const cable::Class* Class;
01458   const cable::Method* Method;
01459 
01460   MethodInstance(const cable::Class* c, const cable::Method* m) :
01461     Class(c), Method(m)
01462     {
01463     }
01464 };
01465 
01466 
01467 //----------------------------------------------------------------------------
01468 gxsys_stl::map<gxsys_stl::string, MethodInstance> OtherMethods;
01469 gxsys_stl::map<gxsys_stl::string, MethodInstance> StaticMethods;
01470 gxsys_stl::map<gxsys_stl::string, MethodInstance> VirtualMethods;
01471 gxsys_stl::map<gxsys_stl::string, MethodInstance> WrappedMethods;
01472 gxsys_stl::map<gxsys_stl::string, MethodInstance> WrappedEnums; // in this case all MethodInstance.Method members are null
01473 
01474 
01475 //----------------------------------------------------------------------------
01476 void MummyCsharpGenerator::ClearLookupEntries()
01477 {
01478   OtherMethods.clear();
01479   StaticMethods.clear();
01480   VirtualMethods.clear();
01481   WrappedMethods.clear();
01482   WrappedEnums.clear();
01483 
01484   // Pretend that "System.Object" is in our inheritance chain and that its
01485   // virtual method signatures exist in our lookup map:
01486   //
01487 //  VirtualMethods.insert(gxsys_stl::make_pair("Equals(object)", MethodInstance(0, 0)));
01488 //  VirtualMethods.insert(gxsys_stl::make_pair("Finalize()", MethodInstance(0, 0)));
01489 //  VirtualMethods.insert(gxsys_stl::make_pair("GetHashCode()", MethodInstance(0, 0)));
01490 //  VirtualMethods.insert(gxsys_stl::make_pair("GetType()", MethodInstance(0, 0)));
01491 //  VirtualMethods.insert(gxsys_stl::make_pair("MemberwiseClone()", MethodInstance(0, 0)));
01492 //  VirtualMethods.insert(gxsys_stl::make_pair("Object()", MethodInstance(0, 0)));
01493 //  VirtualMethods.insert(gxsys_stl::make_pair("ToString()", MethodInstance(0, 0)));
01494 }
01495 
01496 
01497 //----------------------------------------------------------------------------
01498 void MummyCsharpGenerator::AddLookupEntries(const cable::Class* c)
01499 {
01500   if (c)
01501     {
01502     const cable::Class *parent = GetWrappableParentClass(c);
01503 
01504     if (parent)
01505       {
01506       AddLookupEntries(parent);
01507       }
01508 
01509     for(cable::Context::Iterator it = c->Begin(); it != c->End(); ++it)
01510       {
01511       cable::Method *m = cable::Method::SafeDownCast(*it);
01512 
01513       if (m && MethodIsWrappable(m, it.GetAccess()))
01514         {
01515         if (m->GetVirtual())
01516           {
01517           VirtualMethods.insert(gxsys_stl::make_pair(GetMethodSignature(c, m), MethodInstance(c, m)));
01518           }
01519         else if (m->GetStatic())
01520           {
01521           StaticMethods.insert(gxsys_stl::make_pair(GetMethodSignature(c, m), MethodInstance(c, m)));
01522           }
01523         else
01524           {
01525           OtherMethods.insert(gxsys_stl::make_pair(GetMethodSignature(c, m), MethodInstance(c, m)));
01526           }
01527         }
01528 
01529       if (!m)
01530         {
01531         cable::Enumeration *e = cable::Enumeration::SafeDownCast(*it);
01532 
01533         if (e && (cable::Context::Public == it.GetAccess()))
01534           {
01535           WrappedEnums.insert(gxsys_stl::make_pair(GetWrappedEnumName(e), MethodInstance(c, 0)));
01536           }
01537         }
01538       }
01539     }
01540 }
01541 
01542 
01543 //----------------------------------------------------------------------------
01544 void MummyCsharpGenerator::DumpLookupEntries()
01545 {
01546   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it;
01547 
01548   LogInfo(mi_Info, << "<DumpLookupEntries>");
01549   LogInfo(mi_Info, << "inherited static methods:");
01550   for (it= StaticMethods.begin(); it!=StaticMethods.end(); ++it)
01551     {
01552     LogInfo(mi_Info, << "  " << it->first);
01553     }
01554 
01555   LogInfo(mi_Info, << "inherited virtual methods:");
01556   for (it= VirtualMethods.begin(); it!=VirtualMethods.end(); ++it)
01557     {
01558     LogInfo(mi_Info, << "  " << it->first);
01559     }
01560 
01561   LogInfo(mi_Info, << "inherited other methods:");
01562   for (it= OtherMethods.begin(); it!=OtherMethods.end(); ++it)
01563     {
01564     LogInfo(mi_Info, << "  " << it->first);
01565     }
01566 
01567   LogInfo(mi_Info, << "wrapped methods:");
01568   for (it= WrappedMethods.begin(); it!=WrappedMethods.end(); ++it)
01569     {
01570     LogInfo(mi_Info, << "  " << it->first);
01571     }
01572 
01573   LogInfo(mi_Info, << "wrapped enums:");
01574   for (it= WrappedEnums.begin(); it!=WrappedEnums.end(); ++it)
01575     {
01576     LogInfo(mi_Info, << "  " << it->first);
01577     }
01578 
01579   LogInfo(mi_Info, << "</DumpLookupEntries>");
01580 }
01581 
01582 
01583 //----------------------------------------------------------------------------
01584 bool MummyCsharpGenerator::OtherMethodRedefined(const gxsys_stl::string &signature)
01585 {
01586   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it =
01587     OtherMethods.find(signature);
01588   return it != OtherMethods.end();
01589 }
01590 
01591 
01592 //----------------------------------------------------------------------------
01593 bool MummyCsharpGenerator::StaticMethodRedefined(const gxsys_stl::string &signature)
01594 {
01595   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it =
01596     StaticMethods.find(signature);
01597   return it != StaticMethods.end();
01598 }
01599 
01600 
01601 //----------------------------------------------------------------------------
01602 bool MummyCsharpGenerator::VirtualMethodOverridden(const gxsys_stl::string &signature)
01603 {
01604   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it =
01605     VirtualMethods.find(signature);
01606   return it != VirtualMethods.end();
01607 }
01608 
01609 
01610 //----------------------------------------------------------------------------
01611 bool MummyCsharpGenerator::WrappedMethodExists(const gxsys_stl::string &signature)
01612 {
01613   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it =
01614     WrappedMethods.find(signature);
01615   return it != WrappedMethods.end();
01616 }
01617 
01618 
01619 //----------------------------------------------------------------------------
01620 bool MummyCsharpGenerator::WrappedEnumExists(const gxsys_stl::string &name)
01621 {
01622   gxsys_stl::map<gxsys_stl::string, MethodInstance>::iterator it =
01623     WrappedEnums.find(name);
01624   return it != WrappedEnums.end();
01625 }
01626 
01627 
01628 //----------------------------------------------------------------------------
01629 bool MummyCsharpGenerator::IsFactoryMethod(const cable::Class *c, const cable::Method *m)
01630 {
01631   if (m->GetStatic() &&
01632     0 == m->GetFunctionType()->GetNumberOfArguments() &&
01633     gxsys_stl::string(m->GetName()) == this->GetSettings()->GetFactoryMethod(c))
01634     {
01635     return true;
01636     }
01637 
01638   return false;
01639 }
01640 
01641 
01642 //----------------------------------------------------------------------------
01643 bool MummyCsharpGenerator::IsDisposalMethod(const cable::Class *c, const cable::Method *m)
01644 {
01645   if (m->GetVirtual() &&
01646     0 == m->GetFunctionType()->GetNumberOfArguments() &&
01647     gxsys_stl::string(m->GetName()) == this->GetSettings()->GetDisposalMethod(c))
01648     {
01649     return true;
01650     }
01651 
01652   return false;
01653 }
01654 
01655 
01656 //----------------------------------------------------------------------------
01657 bool MummyCsharpGenerator::MethodReturnValueIsCounted(const cable::Class *c, const cable::Method *m)
01658 {
01659   if (this->IsFactoryMethod(c, m))
01660     {
01661     return true;
01662     }
01663 
01664   if (HasAttribute(m, "gccxml(iwhCounted)"))
01665     {
01666     return true;
01667     }
01668 
01669   gxsys_stl::string countedMethodsRegex(this->GetSettings()->GetCountedMethodsRegex(c));
01670   if (!countedMethodsRegex.empty())
01671     {
01672     gxsys::RegularExpression re;
01673     re.compile(countedMethodsRegex.c_str());
01674     if (re.find(m->GetName()))
01675       {
01676       LogVerboseInfo(
01677         << "MethodReturnValueIsCounted match:" << gxsys_ios::endl
01678         << "  countedMethodsRegex: " << countedMethodsRegex << gxsys_ios::endl
01679         << "  method: " << c->GetName() << "::" << m->GetName()
01680         );
01681       return true;
01682       }
01683     }
01684 
01685   return false;
01686 }
01687 
01688 
01689 //----------------------------------------------------------------------------
01690 gxsys_stl::string MummyCsharpGenerator::GetExportLayerFunctionName(const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname)
01691 {
01692   gxsys_stl::string s(GetFullyQualifiedName(c, "_"));
01693   s += "_";
01694   s += mname;
01695 
01696   if (this->IsFactoryMethod(c, m) || this->IsDisposalMethod(c, m) || mname == "new" || mname == "delete")
01697     {
01698     // No suffix...
01699     }
01700   else
01701     {
01702     // Append a unique-fying suffix...
01703     char idStr[32];
01704     unsigned int methodId = 0;
01705 
01706     gxsys_stl::map<const cable::Method*, unsigned int>::iterator it = this->MethodIdMap.find(m);
01707     if (it != this->MethodIdMap.end())
01708       {
01709       methodId = it->second;
01710       }
01711     else
01712       {
01713       // Any methods passed in to here should have been inserted into the
01714       // MethodIdMap during a previous call to GatherWrappedMethods...
01715       // What happened...!?!?
01716       //
01717       LogError(me_InternalError, << "Could not find method in MethodIdMap.");
01718       }
01719 
01720     sprintf(idStr, "%02u", methodId);
01721     s += "_";
01722     s += idStr;
01723     }
01724 
01725   return s;
01726 }
01727 
01728 
01729 //----------------------------------------------------------------------------
01730 //
01731 // GetMethodSignature is very similar to EmitCSharpMethodDeclaration.
01732 // Changes in either may need to be made in both places...
01733 //
01734 void MummyCsharpGenerator::EmitCSharpMethodDeclaration(gxsys_ios::ostream &os, const cable::Class *c, const cable::Method *m, bool asProperty, bool useArg0AsReturn, const gxsys_stl::string& accessLevel)
01735 {
01736   gxsys_stl::string signature(GetMethodSignature(c, m));
01737 
01738   gxsys_stl::string arraySize;
01739   gxsys_stl::string s;
01740   cable::FunctionType *ft = m->GetFunctionType();
01741   unsigned int cArgs = ft->GetNumberOfArguments();
01742   unsigned int i = 0;
01743   cable::Type *argType = 0;
01744   cable::Type *retType = ft->GetReturns();
01745 
01746   i = RETURN_VALUE;
01747 
01748   // If emitting a declaration based on a "property set" method,
01749   // the type of the first argument to the set method is the type
01750   // of the C# property... typically retType will be "void" when
01751   // this is the case...
01752   //
01753   if (asProperty && useArg0AsReturn)
01754     {
01755     if (!IsVoid(retType))
01756       {
01757       LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropSetReturnsNonVoid,
01758         "Method transformed to a property 'set' returns non-void. The return value will be ignored in generated code.");
01759       }
01760 
01761     if (cArgs!=1)
01762       {
01763       LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropSetUnexpectedArgCount,
01764         "Unexpected number of arguments for method transformed to a property 'set' - cArgs: " << cArgs);
01765       }
01766 
01767     // :-)
01768     //
01769     // Pretend return type and attributes come from arg0 for the
01770     // rest of this method call:
01771     //
01772     if (cArgs!=0)
01773       {
01774       i = 0;
01775       retType = ft->GetArgument(0);
01776       }
01777     }
01778 
01779   // Do attributes indicate an array?
01780   //
01781   arraySize = GetMethodArgumentArraySize(c, m, ft, i);
01782 
01783 
01784   // Public, protected, private, internal, hmmmm...?
01785   //
01786   Emit(os, accessLevel.c_str());
01787   Emit(os, " ");
01788 
01789 
01790   if (m->GetStatic())
01791     {
01792     // C# static == C++ static:
01793     //
01794     if (StaticMethodRedefined(signature))
01795       {
01796       Emit(os, "new ");
01797       }
01798 
01799     Emit(os, "static ");
01800     }
01801 
01802   if (!asProperty)
01803     {
01804     if (m->GetVirtual())
01805       {
01806       // C# : "virtual" or "override" ?
01807       //
01808       if (VirtualMethodOverridden(signature))
01809         {
01810         Emit(os, "override ");
01811         }
01812       else
01813         {
01814         // If I am virtual, but my parent is not then:
01815         //
01816         if (OtherMethodRedefined(signature))
01817           {
01818           Emit(os, "new ");
01819           }
01820   
01821         Emit(os, "virtual ");
01822         }
01823       }
01824     else
01825       {
01826       if (OtherMethodRedefined(signature))
01827         {
01828         Emit(os, "new ");
01829         }
01830       }
01831     }
01832 
01833   // Return type. (Has special transformations for supporting collections
01834   // and enumerators. Method name + class supports target interface ==
01835   // special generated code...)
01836   //
01837   gxsys_stl::string mname = m->GetName();
01838 
01839   if ((mname == "GetEnumerator") &&
01840     this->HasTargetInterface("IEnumerable"))
01841     {
01842     Emit(os, "System.Collections.IEnumerator");
01843     }
01844   else if ((mname == "GetCurrent") &&
01845     this->HasTargetInterface("IEnumerator"))
01846     {
01847     Emit(os, "object");
01848     }
01849   else
01850     {
01851     Emit(os, GetCSharpTypeString(retType, true, arraySize != "").c_str());
01852     if (arraySize != "")
01853       {
01854       Emit(os, "[]");
01855       }
01856     }
01857 
01858   Emit(os, " ");
01859 
01860   // Use the wrapped method name or the property name:
01861   //
01862   s = GetWrappedMethodName(m);
01863   if (asProperty)
01864     {
01865     s = ExtractDerivedName(s.c_str(), m, this->GetSettings()->GetVerbose());
01866     }
01867 
01868   Emit(os, s.c_str());
01869 
01870   if (!asProperty)
01871     {
01872   // Open args:
01873   Emit(os, "(");
01874 
01875   // The C# args:
01876   for (i= 0; i<cArgs; ++i)
01877     {
01878     // If it's a CxxMain param pair, only declare the 2nd (string[] argv) part
01879     // of the pair... So, do nothing here and skip the 1st (int argc) part...
01880     //
01881     if (IsCxxMainStyleParamPair(ft, i))
01882       {
01883       }
01884     else
01885       {
01886       argType = ft->GetArgument(i);
01887 
01888       // Is arg an array?
01889       //
01890       arraySize = GetMethodArgumentArraySize(c, m, ft, i);
01891 
01892       // arg type:
01893       Emit(os, GetCSharpTypeString(argType, false, arraySize != "").c_str());
01894 
01895       // array notation:
01896       if (arraySize != "")
01897         {
01898         Emit(os, "[]");
01899         }
01900 
01901       // arg name:
01902       Emit(os, " ");
01903       Emit(os, GetArgName(ft, i));
01904 
01905       if (i<cArgs-1)
01906         {
01907         Emit(os, ", ");
01908         }
01909       }
01910     }
01911 
01912   // Close args:
01913   Emit(os, ")");
01914     }
01915 }
01916 
01917 
01918 //----------------------------------------------------------------------------
01919 void MummyCsharpGenerator::EmitCSharpDllImportDeclaration(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname, const char *f, bool emitExceptionParams)
01920 {
01921   gxsys_stl::string arraySize;
01922   cable::FunctionType *ft = m->GetFunctionType();
01923   unsigned int cArgs = ft->GetNumberOfArguments();
01924   unsigned int cArgsEmitted = 0;
01925   unsigned int i = 0;
01926   cable::Type *argType = 0;
01927   cable::Type *retType = ft->GetReturns();
01928 
01929   Emit(os, "[DllImport(");
01930   Emit(os, dllname);
01931   Emit(os, ", EntryPoint = \"");
01932   Emit(os, f);
01933   Emit(os, "\")]");
01934   Emit(os, "\n");
01935 
01936   // Does method return an array?
01937   //
01938   arraySize = GetMethodArgumentArraySize(c, m, ft, RETURN_VALUE);
01939 
01940   EmitIndent(os);
01941   Emit(os, "internal static extern ");
01942 
01943   if (mname == "new")
01944     {
01945     Emit(os, "IntPtr ");
01946     Emit(os, f);
01947     Emit(os, "(ref uint mteStatus, ref uint mteIndex, ref uint rawRefCount");
01948     cArgsEmitted += 3;
01949 
01950     if (emitExceptionParams)
01951       {
01952       Emit(os, ", ref uint mteExceptionIndex, ref IntPtr clonedException");
01953       cArgsEmitted += 2;
01954       }
01955 
01956     Emit(os, ");");
01957     }
01958   else if (mname == "delete")
01959     {
01960     Emit(os, "void ");
01961     Emit(os, f);
01962     Emit(os, "(HandleRef pThis");
01963     cArgsEmitted += 1;
01964 
01965     if (emitExceptionParams)
01966       {
01967       Emit(os, ", ref uint mteExceptionIndex, ref IntPtr clonedException");
01968       cArgsEmitted += 2;
01969       }
01970 
01971     Emit(os, ");");
01972     }
01973   else
01974     {
01975     Emit(os, GetPInvokeTypeString(retType, true, arraySize != "", false).c_str());
01976     Emit(os, " ");
01977     Emit(os, f);
01978     Emit(os, "(");
01979 
01980     if (!m->GetStatic())
01981       {
01982       Emit(os, "HandleRef pThis");
01983       cArgsEmitted += 1;
01984 
01985       if (cArgs!=0)
01986         {
01987         Emit(os, ", ");
01988         }
01989       }
01990 
01991     for (i= 0; i<cArgs; ++i)
01992       {
01993       argType = ft->GetArgument(i);
01994 
01995       // Is arg an array?
01996       //
01997       arraySize = GetMethodArgumentArraySize(c, m, ft, i);
01998 
01999       // array MarshalAs directive:
02000       if (arraySize != "")
02001         {
02002         Emit(os, "[MarshalAs(UnmanagedType.LPArray, SizeConst = ");
02003         Emit(os, arraySize.c_str());
02004         Emit(os, ")] ");
02005         }
02006 
02007       // arg type:
02008       Emit(os, GetPInvokeTypeString(argType, false, arraySize != "", false).c_str());
02009 
02010       // array notation:
02011       if (arraySize != "")
02012         {
02013         Emit(os, "[]");
02014         }
02015 
02016       // arg name:
02017       Emit(os, " ");
02018       Emit(os, GetArgName(ft, i));
02019 
02020       cArgsEmitted += 1;
02021 
02022       if (i<cArgs-1)
02023         {
02024         Emit(os, ", ");
02025         }
02026       }
02027 
02028     // Add "generated args" to method signatures that return object pointers:
02029     //
02030     if (!HasMapToType(retType) && IsObjectPointer(retType))
02031       {
02032       if (cArgsEmitted)
02033         {
02034         Emit(os, ", ");
02035         }
02036 
02037       Emit(os, "ref uint mteStatus, ref uint mteIndex, ref uint rawRefCount");
02038       cArgsEmitted += 3;
02039       }
02040 
02041     // And to those that handle wrapped exceptions:
02042     //
02043     if (emitExceptionParams)
02044       {
02045       if (cArgsEmitted)
02046         {
02047         Emit(os, ", ");
02048         }
02049 
02050       Emit(os, "ref uint mteExceptionIndex, ref IntPtr clonedException");
02051       cArgsEmitted += 2;
02052       }
02053 
02054     // Close args:
02055     //
02056     Emit(os, ");");
02057     }
02058 }
02059 
02060 
02061 //----------------------------------------------------------------------------
02062 gxsys_stl::string GetQualifiedEventName(const cable::Method *m)
02063 {
02064   const cable::Class *c = cable::ClassType::SafeDownCast(
02065     cable::PointerType::SafeDownCast(
02066     m->GetFunctionType()->GetReturns()
02067     )->GetTarget())->GetClass();
02068 
02069   gxsys_stl::string eventName;
02070 
02071   if (c && c->GetContext())
02072   {
02073     eventName = c->GetContext()->GetName();
02074     eventName += GetWrappedClassName(c);
02075   }
02076   else
02077   {
02078     eventName = "ERROR_invalid_input_to_GetQualifiedEventName";
02079     LogError(me_InvalidArg, << eventName.c_str());
02080   }
02081 
02082   return eventName;
02083 }
02084 
02085 
02086 //----------------------------------------------------------------------------
02087 gxsys_stl::string GetEventName(const cable::Method *m)
02088 {
02089   gxsys_stl::string eventName = GetWrappedClassName(
02090     cable::ClassType::SafeDownCast(cable::PointerType::SafeDownCast(
02091     m->GetFunctionType()->GetReturns()
02092     )->GetTarget())->GetClass()
02093     );
02094 
02095   return eventName;
02096 }
02097 
02098 
02099 //----------------------------------------------------------------------------
02100 void MummyCsharpGenerator::EmitCSharpEvent(gxsys_ios::ostream& os, const char *, const cable::Class* c, const cable::Method* m, const gxsys_stl::string& eventName)
02101 {
02102   // Caller provides eventName because caller is the one with the knowledge
02103   // about whether there are multiple events with the same unqualified name
02104   // due to classes with the same name existing in multiple namespaces.
02105   // So, if there are not, eventName will be from "GetEventName", but if
02106   // there are, eventName will be from "GetQualifiedEventName"...
02107   //
02108   gxsys_stl::string e(eventName);
02109   gxsys_stl::string eFull;
02110   gxsys_stl::string mname(m->GetName());
02111 
02112   // An "event method" is expected to return a pointer to the event class
02113   // that it is supposed to wrap... If that's not true, the code generated
02114   // by this following block is likely not to compile! So make sure it's
02115   // true in your sources to be wrapped.
02116   //
02117   cable::FunctionType *ft = m->GetFunctionType();
02118   cable::Type *retType = ft->GetReturns();
02119 
02120   if (retType->GetTypeId() != cable::Type::PointerTypeId)
02121     {
02122     LogFileLineErrorMsg(m->GetFile(), m->GetLine(), me_EventMethodIncorrectReturnType,
02123       "iwhEvent method '" << mname << "' does not return a pointer to an event object");
02124     return;
02125     }
02126 
02127   if (cable::PointerType::SafeDownCast(retType)->GetTarget()->GetTypeId() !=
02128     cable::Type::ClassTypeId)
02129     {
02130     LogFileLineErrorMsg(m->GetFile(), m->GetLine(), me_EventMethodIncorrectReturnType,
02131       "iwhEvent method '" << mname << "' does not return a pointer to an event object");
02132     return;
02133     }
02134 
02135   eFull = GetWrappedClassNameFullyQualified(cable::ClassType::SafeDownCast(
02136     cable::PointerType::SafeDownCast(retType)->GetTarget())->GetClass());
02137 
02138   // Assumptions:
02139   //
02140   //   (These could all be codified and made into non-assumptions, but for now
02141   //    they all *are* assumptions...)
02142   //
02143   // There's a type called "Handler" in the Event class (typedef to a function pointer in unmanaged C++)
02144   // The typedef signature is "void blah(object sender, object args)"
02145   // There's a corresponding EventArgs class named the same but with "EventArgs" at the end
02146   // There's an AddHandler method in the Event class
02147   // There's a RemoveHandler method in the Event class
02148   // The handler is id'able by a C# "uint"
02149 
02150   // Make this next section collapsible inside a "region":
02151   //
02152   EmitIndent(os);
02153   Emit(os, "#region ");
02154   Emit(os, e.c_str());
02155   Emit(os, " event implementation details\n");
02156         Emit(os, "\n");
02157 
02158   // A data member (*Impl) to hold the managed listeners:
02159   //
02160   EmitIndent(os);
02161         Emit(os, "private ");
02162   Emit(os, e.c_str());
02163   Emit(os, "EventHandler ");
02164   Emit(os, e.c_str());
02165   Emit(os, "Impl;\n");
02166 
02167   // A data member (*Instance) to cache the managed wrapper of the unmanaged
02168   // event object:
02169   //
02170   EmitIndent(os);
02171         Emit(os, "private ");
02172   Emit(os, eFull.c_str());
02173   Emit(os, " ");
02174   Emit(os, e.c_str());
02175   Emit(os, "Instance;\n");
02176 
02177   // A data member (*RelayHandler) and an id (*RelayHandlerId) for the handler
02178   // when we are actively connected...
02179   //
02180   EmitIndent(os);
02181         Emit(os, "private ");
02182   Emit(os, eFull.c_str());
02183   Emit(os, ".Handler ");
02184   Emit(os, e.c_str());
02185   Emit(os, "RelayHandler;\n");
02186   EmitIndent(os);
02187         Emit(os, "private uint ");
02188   Emit(os, e.c_str());
02189   Emit(os, "RelayHandlerId;\n");
02190   Emit(os, "\n");
02191 
02192   // A listener to receive events from the unmanaged code and relay them on to
02193   // managed listeners. Conforms to event's "unmanaged" delegate signature.
02194   // (The delegate named "Handler" in the event class itself.) One improvement
02195   // we could make here would be to generate RelayHandler's signature based
02196   // on the actual signature of "Handler."
02197   //
02198   EmitIndent(os);
02199         Emit(os, "private void ");
02200   Emit(os, e.c_str());
02201   Emit(os, "RelayHandlerMethod(IntPtr rawSender, IntPtr rawArgs)\n");
02202   EmitIndent(os);
02203   Emit(os, "{\n");
02204   EmitIndent(os, 2);
02205   Emit(os, "if (this.");
02206   Emit(os, e.c_str());
02207   Emit(os, "Impl != null)\n");
02208   EmitIndent(os, 2);
02209   Emit(os, "{\n");
02210   EmitIndent(os, 3);
02211   Emit(os, eFull.c_str());
02212   Emit(os, "EventArgs rv = null;\n");
02213   EmitIndent(os, 3);
02214   Emit(os, "if (IntPtr.Zero != rawArgs)\n");
02215   EmitIndent(os, 3);
02216   Emit(os, "{\n");
02217 
02218 
02219 
02220 
02221   const cable::Class* cRetType = cable::ClassType::SafeDownCast(
02222     cable::PointerType::SafeDownCast(retType)->GetTarget()
02223     )->GetClass();
02224 
02225   gxsys_stl::string registerMethod = this->GetSettings()->GetRegisterMethod(cRetType);
02226   gxsys_stl::string unRegisterMethod = this->GetSettings()->GetUnRegisterMethod(cRetType);
02227 
02228 
02229 
02230 
02231   // Technique #1 : using "new" (and ignoring potential issues with
02232   // the mummy Runtime table...
02233   //
02234   EmitIndent(os, 4);
02235   Emit(os, "rv = new ");
02236   Emit(os, eFull.c_str());
02237   Emit(os, "EventArgs(rawArgs, ");
02238 
02239   if (!registerMethod.empty())
02240     {
02241     Emit(os, "true");
02242     }
02243   else
02244     {
02245     Emit(os, "false");
02246     }
02247 
02248   Emit(os, ", false);\n");
02249 
02250   if (!registerMethod.empty())
02251     {
02252     EmitIndent(os, 4);
02253     Emit(os, "rv.");
02254     Emit(os, registerMethod.c_str());
02255     Emit(os, ";\n");
02256     }
02257 
02258 
02259 
02260 
02261   // Technique #2 : using "CreateWrappedObject" (and forcing a call
02262   // back across the boundary to "Register" the EventArgs if the wrapper
02263   // was just created...)
02264   //
02265   // This imposes an additional assumption/constraint on Event and EventArgs
02266   // classes. They must share Register/UnRegister methods for this to work
02267   // properly...
02268   //
02269   // Either that, or we need to get a "cable::Class*" to the EventArgs class
02270   // somehow (which we may or may not be able to do depending on how the
02271   // gccxml step and the input header files were crafted...)
02272   //
02273   //unsigned int indent = 3;
02274   //EmitIndent(os, indent+1);
02275   //Emit(os, "bool mteCreated;\n");
02276   //EmitIndent(os, indent+1);
02277   //Emit(os, "rv = (");
02278   //Emit(os, eFull.c_str());
02279   //Emit(os, "EventArgs)\n");
02280   //EmitIndent(os, indent+2);
02281   //Emit(os, "Kitware.mummy.Runtime.Methods.CreateWrappedObject(\n");
02282   //EmitIndent(os, indent+3);
02283   //Emit(os, "\"class ");
02284   //Emit(os, eFull.c_str());
02285   //Emit(os, "EventArgs\", rawArgs, ");
02286 
02287   //if (!unRegisterMethod.empty())
02288   //  {
02289   //  Emit(os, "true");
02290   //  }
02291   //else
02292   //  {
02293   //  Emit(os, "false");
02294   //  }
02295 
02296   //Emit(os, ", out mteCreated");
02297   //Emit(os, ");\n");
02298 
02299   //if (!registerMethod.empty())
02300   //  {
02301   //  //if (this->MethodReturnValueIsCounted(c, m))
02302   //  //  {
02303   //  //  EmitIndent(os, indent+1);
02304   //  //  Emit(os, "Returned object is counted already, (factory method or iwhCounted hint),\n");
02305   //  //  EmitIndent(os, indent+1);
02306   //  //  Emit(os, "// no 'rv.");
02307   //  //  Emit(os, registerMethod.c_str());
02308   //  //  Emit(os, "' call is necessary...\n");
02309   //  //  }
02310   //  //else
02311   //  //  {
02312   //    EmitIndent(os, indent+1);
02313   //    Emit(os, "if (mteCreated)\n");
02314   //    EmitIndent(os, indent+1);
02315   //    Emit(os, "{\n");
02316   //    EmitIndent(os, indent+2);
02317   //    Emit(os, "rv.");
02318   //    Emit(os, registerMethod.c_str());
02319   //    Emit(os, ";\n");
02320   //    EmitIndent(os, indent+1);
02321   //    Emit(os, "}\n");
02322   //  //  }
02323   //  }
02324 
02325 
02326 
02327 
02328   EmitIndent(os, 3);
02329   Emit(os, "}\n");
02330   EmitIndent(os, 3);
02331   Emit(os, "this.");
02332   Emit(os, e.c_str());
02333   Emit(os, "Impl(this, rv);\n");
02334   EmitIndent(os, 2);
02335   Emit(os, "}\n");
02336   EmitIndent(os);
02337         Emit(os, "}\n");
02338         Emit(os, "\n");
02339 
02340   // Methods to add and remove the RelayHandler:
02341   //
02342   EmitIndent(os);
02343         Emit(os, "private void Add");
02344   Emit(os, e.c_str());
02345   Emit(os, "RelayHandler()\n");
02346   EmitIndent(os);
02347   Emit(os, "{\n");
02348   EmitIndent(os, 2);
02349   Emit(os, "if (0 == this.");
02350   Emit(os, e.c_str());
02351   Emit(os, "RelayHandlerId)\n");
02352   EmitIndent(os, 2);
02353   Emit(os, "{\n");
02354 
02355   EmitIndent(os, 3);
02356   Emit(os, "this.");
02357   Emit(os, e.c_str());
02358   Emit(os, "RelayHandler =\n");
02359   EmitIndent(os, 4);
02360   Emit(os, "new ");
02361   Emit(os, eFull.c_str());
02362   Emit(os, ".Handler(\n");
02363   EmitIndent(os, 5);
02364   Emit(os, "this.");
02365   Emit(os, e.c_str());
02366   Emit(os, "RelayHandlerMethod);\n");
02367         Emit(os, "\n");
02368 
02369   EmitIndent(os, 3);
02370   Emit(os, "this.");
02371   Emit(os, e.c_str());
02372   Emit(os, "Instance =\n");
02373   EmitIndent(os, 4);
02374   Emit(os, "this.");
02375   Emit(os, mname.c_str());
02376   Emit(os, "();\n");
02377         Emit(os, "\n");
02378 
02379   EmitIndent(os, 3);
02380   Emit(os, "this.");
02381   Emit(os, e.c_str());
02382   Emit(os, "RelayHandlerId =\n");
02383   EmitIndent(os, 4);
02384   Emit(os, "this.");
02385   Emit(os, e.c_str());
02386   Emit(os, "Instance.AddHandler(\n");
02387   EmitIndent(os, 5);
02388   Emit(os, "this.");
02389   Emit(os, e.c_str());
02390   Emit(os, "RelayHandler);\n");
02391 
02392   EmitIndent(os, 2);
02393   Emit(os, "}\n");
02394   EmitIndent(os);
02395   Emit(os, "}\n");
02396         Emit(os, "\n");
02397 
02398   EmitIndent(os);
02399         Emit(os, "private void Remove");
02400   Emit(os, e.c_str());
02401   Emit(os, "RelayHandler()\n");
02402   EmitIndent(os);
02403   Emit(os, "{\n");
02404   EmitIndent(os, 2);
02405   Emit(os, "if (0 != this.");
02406   Emit(os, e.c_str());
02407   Emit(os, "RelayHandlerId)\n");
02408   EmitIndent(os, 2);
02409   Emit(os, "{\n");
02410 
02411   EmitIndent(os, 3);
02412   Emit(os, "if (System.IntPtr.Zero != this.");
02413   Emit(os, e.c_str());
02414   Emit(os, "Instance.GetCppThis().Handle)\n");
02415   EmitIndent(os, 3);
02416   Emit(os, "{\n");
02417   EmitIndent(os, 4);
02418   Emit(os, "this.");
02419   Emit(os, e.c_str());
02420   Emit(os, "Instance.RemoveHandler(\n");
02421   EmitIndent(os, 5);
02422   Emit(os, "this.");
02423   Emit(os, e.c_str());
02424   Emit(os, "RelayHandlerId);\n");
02425   EmitIndent(os, 3);
02426   Emit(os, "}\n");
02427         Emit(os, "\n");
02428 
02429   EmitIndent(os, 3);
02430   Emit(os, "this.");
02431   Emit(os, e.c_str());
02432   Emit(os, "Instance = null;\n");
02433 
02434   EmitIndent(os, 3);
02435   Emit(os, "this.");
02436   Emit(os, e.c_str());
02437   Emit(os, "RelayHandler = null;\n");
02438 
02439   EmitIndent(os, 3);
02440   Emit(os, "this.");
02441   Emit(os, e.c_str());
02442   Emit(os, "RelayHandlerId = 0;\n");
02443 
02444   EmitIndent(os, 2);
02445   Emit(os, "}\n");
02446   EmitIndent(os);
02447   Emit(os, "}\n");
02448         Emit(os, "\n");
02449 
02450   // End the private details "region":
02451   //
02452   EmitIndent(os);
02453   Emit(os, "#endregion\n");
02454         Emit(os, "\n");
02455         Emit(os, "\n");
02456 
02457   // A delegate definition that managed C# handlers must conform to in order
02458   // to listen to this event:
02459   //
02460   EmitIndent(os);
02461   Emit(os, "/// <summary>\n");
02462   EmitIndent(os);
02463   Emit(os, "/// Delegate signature for the ");
02464   Emit(os, e.c_str());
02465   Emit(os, " event.\n");
02466   EmitIndent(os);
02467   Emit(os, "/// </summary>\n");
02468   EmitIndent(os);
02469   Emit(os, "public delegate void ");
02470   Emit(os, e.c_str());
02471   Emit(os, "EventHandler(");
02472   Emit(os, c->GetName());
02473   Emit(os, " sender, ");
02474   Emit(os, eFull.c_str());
02475   Emit(os, "EventArgs args);\n");
02476   Emit(os, "\n");
02477   Emit(os, "\n");
02478 
02479   // Documentation:
02480   gxsys_stl::vector<gxsys_stl::string> docblock;
02481   this->GetHeaderFileReader(c)->GetCommentBlockBefore(m->GetLine(), docblock, this->ClassLineNumber);
02482   EmitDocumentationBlock(os, docblock, 1);
02483 
02484   // The actual event with add and remove handlers.
02485   //
02486   EmitIndent(os);
02487         Emit(os, "public event ");
02488   Emit(os, e.c_str());
02489   Emit(os, "EventHandler ");
02490   Emit(os, e.c_str());
02491   Emit(os, "\n");
02492 
02493   EmitIndent(os);
02494         Emit(os, "{\n");
02495 
02496   EmitIndent(os, 2);
02497   Emit(os, "add\n");
02498   EmitIndent(os, 2);
02499         Emit(os, "{\n");
02500   EmitIndent(os, 3);
02501   Emit(os, "if (this.");
02502   Emit(os, e.c_str());
02503   Emit(os, "Impl == null)\n");
02504   EmitIndent(os, 3);
02505   Emit(os, "{\n");
02506   EmitIndent(os, 4);
02507         Emit(os, "Add");
02508   Emit(os, e.c_str());
02509   Emit(os, "RelayHandler();\n");
02510   EmitIndent(os, 3);
02511   Emit(os, "}\n");
02512         Emit(os, "\n");
02513   EmitIndent(os, 3);
02514   Emit(os, "this.");
02515   Emit(os, e.c_str());
02516   Emit(os, "Impl += value;\n");
02517   EmitIndent(os, 2);
02518         Emit(os, "}\n");
02519         Emit(os, "\n");
02520 
02521   EmitIndent(os, 2);
02522         Emit(os, "remove\n");
02523   EmitIndent(os, 2);
02524         Emit(os, "{\n");
02525   EmitIndent(os, 3);
02526   Emit(os, "this.");
02527   Emit(os, e.c_str());
02528   Emit(os, "Impl -= value;\n");
02529         Emit(os, "\n");
02530   EmitIndent(os, 3);
02531   Emit(os, "if (this.");
02532   Emit(os, e.c_str());
02533   Emit(os, "Impl == null)\n");
02534   EmitIndent(os, 3);
02535   Emit(os, "{\n");
02536   EmitIndent(os, 4);
02537         Emit(os, "Remove");
02538   Emit(os, e.c_str());
02539   Emit(os, "RelayHandler();\n");
02540   EmitIndent(os, 3);
02541   Emit(os, "}\n");
02542   EmitIndent(os, 2);
02543   Emit(os, "}\n");
02544 
02545   EmitIndent(os);
02546         Emit(os, "}\n");
02547 }
02548 
02549 
02550 //----------------------------------------------------------------------------
02551 void MummyCsharpGenerator::EmitCSharpProperty(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *propGetMethod, const cable::Method *propSetMethod, bool emitExceptionParams)
02552 {
02553   gxsys_stl::string fGet;
02554   gxsys_stl::string fSet;
02555   unsigned int indent = 3;
02556   const cable::Method *declMethod = propGetMethod;
02557 
02558   // Declare the DllImport functions.
02559   //
02560   if (propGetMethod)
02561     {
02562     fGet = GetExportLayerFunctionName(c, propGetMethod, propGetMethod->GetName());
02563     EmitIndent(os);
02564     EmitCSharpDllImportDeclaration(os, dllname, c, propGetMethod, propGetMethod->GetName(), fGet.c_str(), emitExceptionParams);
02565     Emit(os, "\n");
02566     Emit(os, "\n");
02567     }
02568 
02569   if (propSetMethod)
02570     {
02571     fSet = GetExportLayerFunctionName(c, propSetMethod, propSetMethod->GetName());
02572     EmitIndent(os);
02573     EmitCSharpDllImportDeclaration(os, dllname, c, propSetMethod, propSetMethod->GetName(), fSet.c_str(), emitExceptionParams);
02574     Emit(os, "\n");
02575     Emit(os, "\n");
02576 
02577     if (!declMethod)
02578       {
02579       declMethod = propSetMethod;
02580       }
02581     }
02582 
02583   if (!declMethod)
02584     {
02585     // What? no propGetMethod or propSetMethod? Caller has made a mistake somewhere...
02586     //
02587     LogError(me_InternalError, << "No declMethod in MummyCsharpGenerator::EmitCSharpProperty.");
02588     return;
02589     }
02590 
02591   // Documentation:
02592   gxsys_stl::vector<gxsys_stl::string> docblock;
02593   this->GetHeaderFileReader(c)->GetCommentBlockBefore(declMethod->GetLine(), docblock, this->ClassLineNumber);
02594   EmitDocumentationBlock(os, docblock, 1);
02595 
02596   // Declaration:
02597   EmitIndent(os);
02598   EmitCSharpMethodDeclaration(os, c, declMethod, true, declMethod == propSetMethod, "public");
02599   Emit(os, "\n");
02600 
02601   // Open body:
02602   EmitIndent(os);
02603   Emit(os, "{");
02604   Emit(os, "\n");
02605 
02606   // The "get" body:
02607   if (propGetMethod)
02608     {
02609     EmitIndent(os, 2);
02610     Emit(os, "get\n");
02611     EmitIndent(os, 2);
02612     Emit(os, "{\n");
02613     EmitIndent(os, 3);
02614     Emit(os, "// iwhPropGet\n");
02615     EmitCSharpMethodBody(os, 3, c, propGetMethod, fGet, 0, emitExceptionParams);
02616     indent = 3;
02617     EmitIndent(os, 2);
02618     Emit(os, "}\n");
02619     }
02620 
02621   // The "set" body:
02622   if (propSetMethod)
02623     {
02624     if (propGetMethod)
02625       {
02626       Emit(os, "\n");
02627       }
02628 
02629     EmitIndent(os, 2);
02630     Emit(os, "set\n");
02631     EmitIndent(os, 2);
02632     Emit(os, "{\n");
02633     EmitIndent(os, 3);
02634     Emit(os, "// iwhPropSet\n");
02635     EmitCSharpMethodBody(os, 3, c, propSetMethod, fSet, "value", emitExceptionParams);
02636     indent = 3;
02637     EmitIndent(os, 2);
02638     Emit(os, "}\n");
02639     }
02640 
02641   // Close body:
02642   EmitIndent(os);
02643   Emit(os, "}");
02644   Emit(os, "\n");
02645 }
02646 
02647 
02648 //----------------------------------------------------------------------------
02649 void EmitThrowClonedException(gxsys_ios::ostream &os, unsigned int indent)
02650 {
02651   EmitIndent(os, indent);
02652   Emit(os, "if (IntPtr.Zero != clonedException)\n");
02653   EmitIndent(os, indent);
02654   Emit(os, "{\n");
02655 
02656   EmitIndent(os, indent+1);
02657   Emit(os, "bool mteCreatedException;\n");
02658   EmitIndent(os, indent+1);
02659   Emit(os, "System.Exception wrappedException = (System.Exception)\n");
02660   EmitIndent(os, indent+2);
02661   Emit(os, "Kitware.mummy.Runtime.Methods.CreateWrappedObject(\n");
02662   EmitIndent(os, indent+3);
02663   Emit(os, "0, mteExceptionIndex, 0, clonedException, true, out mteCreatedException);\n");
02664   EmitIndent(os, indent+1);
02665   Emit(os, "throw wrappedException;\n");
02666 
02667   //EmitIndent(os, indent+1);
02668   //Emit(os, "System.Exception exc = new System.Exception(\"received clonedException from unmanaged layer...\");\n");
02669   //EmitIndent(os, indent+1);
02670   //Emit(os, "throw exc;\n");
02671 
02672   EmitIndent(os, indent);
02673   Emit(os, "}\n");
02674 }
02675 
02676 
02677 //----------------------------------------------------------------------------
02678 void MummyCsharpGenerator::EmitCSharpMethodBody(gxsys_ios::ostream &os, unsigned int indent, const cable::Class *c, const cable::Method *m, gxsys_stl::string& f, const char *impliedArg0, bool emitExceptionParams)
02679 {
02680   // need qualified name?
02681   gxsys_stl::string cname(c->GetName());
02682 
02683   gxsys_stl::string retArraySize;
02684   gxsys_stl::string argArraySize;
02685   cable::FunctionType *ft = m->GetFunctionType();
02686   unsigned int cArgs = ft->GetNumberOfArguments();
02687   unsigned int cArgsEmitted = 0;
02688   unsigned int i = 0;
02689   cable::Type *argType = 0;
02690   cable::Type *retType = ft->GetReturns();
02691   bool voidReturn = false;
02692   gxsys_stl::string rvType;
02693   gxsys_stl::string rvpType;
02694   bool argIsRef = false;
02695   gxsys_stl::string argTypeString;
02696   gxsys_stl::string emittedArg;
02697   bool callGetCppThis = false;
02698 
02699   // Does method return anything?
02700   //
02701   if (IsVoid(retType))
02702     {
02703     voidReturn = true;
02704     }
02705 
02706   // Does method return an array?
02707   //
02708   retArraySize = GetMethodArgumentArraySize(c, m, ft, RETURN_VALUE);
02709 
02710   rvpType = GetPInvokeTypeString(retType, true, retArraySize != "", false);
02711   rvType = GetCSharpTypeString(retType, true, retArraySize != "");
02712 
02713 
02714   // Body:
02715   //
02716 
02717 
02718   // Set up to handle wrapped exceptions if this class may throw them:
02719   //
02720   if (emitExceptionParams)
02721     {
02722     EmitIndent(os, indent);
02723     Emit(os, "uint mteExceptionIndex = 0;\n");
02724     EmitIndent(os, indent);
02725     Emit(os, "IntPtr clonedException = IntPtr.Zero;\n");
02726     Emit(os, "\n");
02727     }
02728 
02729   // Delegate the call through the PInvoke/DllImport layer:
02730   //
02731   EmitIndent(os, indent);
02732 
02733   if (!voidReturn)
02734     {
02735     if (retArraySize != "")
02736       {
02737       Emit(os, rvpType.c_str());
02738       Emit(os, " rvp = "); // rvp == return value pointer
02739       }
02740     else if (IsObjectPointer(retType))
02741       {
02742       Emit(os, rvType.c_str());
02743       Emit(os, " rv = null;\n");
02744 
02745       Emit(os, "\n");
02746 
02747       EmitIndent(os, indent);
02748       Emit(os, "uint mteStatus = 0;\n");
02749       EmitIndent(os, indent);
02750       Emit(os, "uint mteIndex = UInt32.MaxValue;\n");
02751       EmitIndent(os, indent);
02752       Emit(os, "uint rawRefCount = 0;\n");
02753 
02754       EmitIndent(os, indent);
02755       Emit(os, rvpType.c_str());
02756       Emit(os, " rvp = "); // rvp == return value pointer
02757       }
02758     else
02759       {
02760       Emit(os, rvType.c_str());
02761       Emit(os, " rv = "); // rv == return value
02762       }
02763     }
02764 
02765   // Open any special marshalling blocks required:
02766   //
02767   if (IsCharPointer(retType))
02768     {
02769     Emit(os, "Marshal.PtrToStringAnsi(");
02770     }
02771 
02772   // Call the DllImport function:
02773   //
02774   Emit(os, f.c_str());
02775   Emit(os, "(");
02776 
02777   // Arguments, 'this' first, then loop over C++ method formal params:
02778   //
02779   if (!m->GetStatic())
02780     {
02781     Emit(os, "this.GetCppThis()");
02782     cArgsEmitted += 1;
02783 
02784     if (cArgs!=0)
02785       {
02786       Emit(os, ", ");
02787       }
02788     }
02789 
02790   for (i= 0; i<cArgs; ++i)
02791     {
02792     // If it's a CxxMain param pair, use the length of the 2nd (string[] argv)
02793     // part as the value of the 1st (int argc) part...
02794     //
02795     if (IsCxxMainStyleParamPair(ft, i))
02796       {
02797       Emit(os, GetArgName(ft, i+1));
02798       Emit(os, ".Length");
02799       }
02800     else
02801       {
02802       argType = ft->GetArgument(i);
02803 
02804       // Is arg an array?
02805       //
02806       argArraySize = GetMethodArgumentArraySize(c, m, ft, i);
02807 
02808       argIsRef = false;
02809       if (argArraySize == "" && GetIsRefArg(argType))
02810         {
02811         argIsRef = true;
02812         Emit(os, "ref ");
02813         }
02814 
02815       argTypeString = GetCSharpTypeString(argType, true, argArraySize != "");
02816       if (!argIsRef && (argTypeString == "bool"))
02817         {
02818         Emit(os, "(byte)(");
02819         }
02820 
02821       if (impliedArg0 && 0==i)
02822         {
02823         emittedArg = impliedArg0;
02824         }
02825       else
02826         {
02827         emittedArg = GetArgName(ft, i);
02828         }
02829 
02830       Emit(os, emittedArg.c_str());
02831 
02832       if (argIsRef)
02833         {
02834         LogInfo(mi_InfoRefArgEncountered,
02835           << "reference arg: "
02836           << cname << "." << m->GetName()
02837           << " " << argType->GetCxxType().GetName() << " " << emittedArg
02838           << " (arg " << i << ")"
02839           );
02840         }
02841 
02842       callGetCppThis = false;
02843 
02844       if (IsObjectPointer(argType))
02845         {
02846         const cable::Class* argClass = cable::ClassType::SafeDownCast(
02847           cable::PointerType::SafeDownCast(argType)->GetTarget())->GetClass();
02848         if (!IsUtilityClass(argClass))
02849           {
02850           callGetCppThis = true;
02851           }
02852         }
02853       else if (IsObjectPointerReference(argType))
02854         {
02855         const cable::PointerType* ptrClass = cable::PointerType::SafeDownCast(
02856           cable::ReferenceType::SafeDownCast(argType)->GetTarget());
02857         const cable::Class* argClass = cable::ClassType::SafeDownCast(
02858           ptrClass->GetTarget())->GetClass();
02859         if (!IsUtilityClass(argClass))
02860           {
02861           callGetCppThis = true;
02862           }
02863         }
02864 
02865       if (callGetCppThis)
02866         {
02867         Emit(os, " == null ? new HandleRef() : ");
02868         Emit(os, emittedArg.c_str());
02869         Emit(os, ".GetCppThis()");
02870         }
02871 
02872       if (!argIsRef && (argTypeString == "bool"))
02873         {
02874         Emit(os, " ? 1 : 0)");
02875         }
02876       }
02877 
02878     cArgsEmitted += 1;
02879 
02880     if (i<cArgs-1)
02881       {
02882       Emit(os, ", ");
02883       }
02884     }
02885 
02886   if (IsObjectPointer(retType))
02887     {
02888     if (cArgsEmitted)
02889       {
02890       Emit(os, ", ");
02891       }
02892 
02893     Emit(os, "ref mteStatus, ref mteIndex, ref rawRefCount");
02894     cArgsEmitted += 3;
02895     }
02896 
02897   if (emitExceptionParams)
02898     {
02899     if (cArgsEmitted)
02900       {
02901       Emit(os, ", ");
02902       }
02903 
02904     Emit(os, "ref mteExceptionIndex, ref clonedException");
02905     cArgsEmitted += 2;
02906     }
02907 
02908   Emit(os, ")");
02909 
02910   // Close special marshalling for object casting or char * to string mapping
02911   // or handle bool/byte specially for PInvoke:
02912   //
02913   if (IsObjectPointer(retType))
02914     {
02915     const cable::Class* cRetType = cable::ClassType::SafeDownCast(
02916       cable::PointerType::SafeDownCast(retType)->GetTarget()
02917       )->GetClass();
02918 
02919     gxsys_stl::string registerMethod = this->GetSettings()->GetRegisterMethod(cRetType);
02920     gxsys_stl::string unRegisterMethod = this->GetSettings()->GetUnRegisterMethod(cRetType);
02921 
02922     Emit(os, ";\n");
02923 
02924     EmitIndent(os, indent);
02925     Emit(os, "if (IntPtr.Zero != rvp)\n");
02926     EmitIndent(os, indent);
02927     Emit(os, "{\n");
02928 
02929     EmitIndent(os, indent+1);
02930     Emit(os, "bool mteCreated;\n");
02931     EmitIndent(os, indent+1);
02932     Emit(os, "rv = (");
02933     Emit(os, rvType.c_str());
02934     Emit(os, ")\n");
02935     EmitIndent(os, indent+2);
02936     Emit(os, "Kitware.mummy.Runtime.Methods.CreateWrappedObject(\n");
02937     EmitIndent(os, indent+3);
02938     Emit(os, "mteStatus, mteIndex, rawRefCount, rvp, ");
02939 
02940     if (!unRegisterMethod.empty())
02941       {
02942       Emit(os, "true");
02943       }
02944     else
02945       {
02946       Emit(os, "false");
02947       }
02948 
02949     Emit(os, ", out mteCreated");
02950     Emit(os, ");\n");
02951 
02952     if (!registerMethod.empty())
02953       {
02954       if (this->MethodReturnValueIsCounted(c, m))
02955         {
02956         EmitIndent(os, indent+1);
02957         Emit(os, "// Returned object is counted already, (factory method or iwhCounted hint),\n");
02958         EmitIndent(os, indent+1);
02959         Emit(os, "// no 'rv.");
02960         Emit(os, registerMethod.c_str());
02961         Emit(os, "' call is necessary...\n");
02962         }
02963       else
02964         {
02965         EmitIndent(os, indent+1);
02966         Emit(os, "if (mteCreated)\n");
02967         EmitIndent(os, indent+1);
02968         Emit(os, "{\n");
02969         EmitIndent(os, indent+2);
02970         Emit(os, "rv.");
02971         Emit(os, registerMethod.c_str());
02972         Emit(os, ";\n");
02973         EmitIndent(os, indent+1);
02974         Emit(os, "}\n");
02975         }
02976       }
02977 
02978     EmitIndent(os, indent);
02979     Emit(os, "}\n");
02980 
02981     Emit(os, "\n");
02982     }
02983   else if (IsCharPointer(retType))
02984     {
02985     Emit(os, ");\n");
02986     }
02987   else if (rvType == "bool")
02988     {
02989     Emit(os, " == 0 ? false : true;\n");
02990     }
02991   else
02992     {
02993     Emit(os, ";\n");
02994     }
02995 
02996 
02997   if (emitExceptionParams)
02998     {
02999     Emit(os, "\n");
03000     EmitThrowClonedException(os, indent);
03001     Emit(os, "\n");
03002     }
03003 
03004 
03005   // Specially marshal array return values:
03006   if (retArraySize != "")
03007     {
03008     EmitIndent(os, indent);
03009     Emit(os, rvType.c_str());
03010     Emit(os, "[] rv = null;");
03011     Emit(os, "\n");
03012 
03013     EmitIndent(os, indent);
03014     Emit(os, "if (IntPtr.Zero != rvp)");
03015     Emit(os, "\n");
03016     EmitIndent(os, indent);
03017     Emit(os, "{");
03018     Emit(os, "\n");
03019       EmitIndent(os, indent+1);
03020       Emit(os, "rv = new ");
03021       Emit(os, rvType.c_str());
03022       Emit(os, "[");
03023       Emit(os, retArraySize.c_str());
03024       Emit(os, "];");
03025       Emit(os, "\n");
03026 
03027       // Special case "uint" since Marshal.Copy does not have
03028       // a "uint" overload...
03029       //
03030       if (rvType == "uint")
03031         {
03032         EmitIndent(os, indent+1);
03033         Emit(os, "int[] rv2 = new int[");
03034         Emit(os, retArraySize.c_str());
03035         Emit(os, "];");
03036         Emit(os, "\n");
03037 
03038         EmitIndent(os, indent+1);
03039         Emit(os, "Marshal.Copy(rvp, rv2, 0, rv.Length);");
03040         Emit(os, "\n");
03041 
03042         EmitIndent(os, indent+1);
03043         Emit(os, "for (int rv2i = 0; rv2i < ");
03044         Emit(os, retArraySize.c_str());
03045         Emit(os, "; ++rv2i)");
03046         Emit(os, "\n");
03047 
03048         EmitIndent(os, indent+1);
03049         Emit(os, "{");
03050         Emit(os, "\n");
03051 
03052         EmitIndent(os, indent+2);
03053         Emit(os, "rv[rv2i] = (uint)rv2[rv2i];");
03054         Emit(os, "\n");
03055 
03056         EmitIndent(os, indent+1);
03057         Emit(os, "}");
03058         Emit(os, "\n");
03059         }
03060       else
03061         {
03062         EmitIndent(os, indent+1);
03063         Emit(os, "Marshal.Copy(rvp, rv, 0, rv.Length);");
03064         Emit(os, "\n");
03065         }
03066 
03067     EmitIndent(os, indent);
03068     Emit(os, "}");
03069     Emit(os, "\n");
03070     }
03071 
03072   // Return statement:
03073   if (!voidReturn)
03074     {
03075     EmitIndent(os, indent);
03076     Emit(os, "return rv;");
03077     Emit(os, "\n");
03078     }
03079 }
03080 
03081 
03082 //----------------------------------------------------------------------------
03083 void MummyCsharpGenerator::EmitCSharpMethod(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname, const gxsys_stl::string& accessLevel, bool emitExceptionParams)
03084 {
03085   gxsys_stl::string f(GetExportLayerFunctionName(c, m, mname));
03086 
03087   // First declare the DllImport function. This gets called within the method body.
03088   //
03089   EmitIndent(os);
03090   EmitCSharpDllImportDeclaration(os, dllname, c, m, mname, f.c_str(), emitExceptionParams);
03091   Emit(os, "\n");
03092   Emit(os, "\n");
03093 
03094   // Documentation:
03095   gxsys_stl::vector<gxsys_stl::string> docblock;
03096   this->GetHeaderFileReader(c)->GetCommentBlockBefore(m->GetLine(), docblock, this->ClassLineNumber);
03097   EmitDocumentationBlock(os, docblock, 1);
03098 
03099   // Declaration:
03100   EmitIndent(os);
03101   EmitCSharpMethodDeclaration(os, c, m, false, false, accessLevel);
03102   Emit(os, "\n");
03103 
03104   // Open body:
03105   EmitIndent(os);
03106   Emit(os, "{");
03107   Emit(os, "\n");
03108 
03109   // Body:
03110   EmitCSharpMethodBody(os, 2, c, m, f, 0, emitExceptionParams);
03111 
03112   // Close body:
03113   EmitIndent(os);
03114   Emit(os, "}");
03115   Emit(os, "\n");
03116 }
03117 
03118 
03119 //----------------------------------------------------------------------------
03120 void MummyCsharpGenerator::EmitCSharpEnums(gxsys_ios::ostream &os, const cable::Class *c)
03121 {
03122   gxsys_stl::vector<gxsys_stl::string> docblock;
03123 
03124   for (cable::Context::Iterator it = c->Begin(); it != c->End(); ++it)
03125     {
03126     cable::Enumeration *e = cable::Enumeration::SafeDownCast(*it);
03127 
03128     if (e && (cable::Context::Public == it.GetAccess()))
03129       {
03130       gxsys_stl::string ename(GetWrappedEnumName(e));
03131 
03132       LogVerboseInfo(<< "public enum - wrapped name: " << ename);
03133 
03134       Emit(os, "\n");
03135       Emit(os, "\n");
03136 
03137       docblock.clear();
03138       this->GetHeaderFileReader(c)->GetCommentBlockBefore(e->GetLine(), docblock, this->ClassLineNumber);
03139       EmitDocumentationBlock(os, docblock, 1);
03140 
03141       EmitIndent(os);
03142       Emit(os, "public ");
03143       if (WrappedEnumExists(ename))
03144         {
03145         Emit(os, "new ");
03146         }
03147       Emit(os, "enum ");
03148       Emit(os, ename.c_str());
03149       Emit(os, "\n");
03150 
03151       EmitIndent(os);
03152       Emit(os, "{\n");
03153 
03154       for (cable::Enumeration::Iterator eit = e->Begin(); eit != e->End(); ++eit)
03155         {
03156         EmitIndent(os, 2);
03157         Emit(os, "/// <summary>enum member</summary>\n");
03158 
03159         EmitIndent(os, 2);
03160         Emit(os, *eit);
03161         Emit(os, " = ");
03162         EmitInt(os, eit.GetValue());
03163         Emit(os, ",");
03164         Emit(os, "\n");
03165 
03166         Emit(os, "\n");
03167         }
03168 
03169       EmitIndent(os);
03170       Emit(os, "}\n");
03171       }
03172     }
03173 }
03174 
03175 
03176 //----------------------------------------------------------------------------
03177 void MummyCsharpGenerator::EmitCSharpConstructor(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname, bool emitExceptionParams)
03178 {
03179   // need qualified name?
03180   gxsys_stl::string cname(c->GetName());
03181   gxsys_stl::string f;
03182 
03183   if (this->GetSettings()->GetUseShadow(c))
03184     {
03185     // Special case shadow class factory method since m might be NULL
03186     // (like it *is* with vtkCommand...)
03187     //
03188     f = cname + "Shadow_CreateShadow";
03189 
03190     EmitIndent(os);
03191     Emit(os, "[DllImport(");
03192     Emit(os, dllname);
03193     Emit(os, ", EntryPoint = \"");
03194     Emit(os, f.c_str());
03195     Emit(os, "\")]");
03196     Emit(os, "\n");
03197 
03198     EmitIndent(os);
03199     Emit(os, "static extern IntPtr ");
03200     Emit(os, f.c_str());
03201     Emit(os, "(IntPtr primary);\n");
03202 
03203     Emit(os, "\n");
03204 
03205     // Documentation:
03206     gxsys_stl::vector<gxsys_stl::string> docblock;
03207     this->GetHeaderFileReader(c)->GetCommentBlockBefore(
03208       (m ? m->GetLine() : c->GetLine()), docblock, this->ClassLineNumber);
03209     EmitDocumentationBlock(os, docblock, 1);
03210 
03211     EmitIndent(os);
03212     Emit(os, "public ");
03213     Emit(os, cname.c_str());
03214     Emit(os, "() : this(IntPtr.Zero, false, false)\n");
03215 
03216     EmitIndent(os);
03217     Emit(os, "{\n");
03218 
03219     EmitIndent(os, 2);
03220     Emit(os, "IntPtr primary = Marshal.GetIDispatchForObject(this);\n");
03221 
03222     EmitIndent(os, 2);
03223     Emit(os, "this.SetCppThis(");
03224     Emit(os, f.c_str());
03225     Emit(os, "(primary), true, false);\n");
03226 
03227     EmitIndent(os, 2);
03228     Emit(os, "Marshal.Release(primary);\n");
03229 
03230     EmitIndent(os);
03231     Emit(os, "}\n");
03232     }
03233   else
03234     {
03235     f = GetExportLayerFunctionName(c, m, mname);
03236 
03237     // Explanation of the "emitDefaultFactoryMethod" MummySettings.xml attribute.
03238     // ==========================================================================
03239     //
03240     // Either: emit the factory method itself, including the DllImport declaration,
03241     // and then emit the default C# constructor, too, which gives two ways to get an
03242     // instance of an object of class c...
03243     //    (emitDefaultFactoryMethod="true" in MummySettings.xml)
03244     //
03245     // Or: emit just the DllImport declaration and the default C# constructor and
03246     // *skip* emitting the factory method itself, which only gives one way to get an
03247     // instance of class c.
03248     //    (emitDefaultFactoryMethod="false" in or absent from MummySettings.xml)
03249     //
03250     // We code for both ways because we have some clients who want abstract C# classes
03251     // to be really abstract and *uncreatable* (even via a separate factory method) at
03252     // this class level. Only concrete subclasses can even be instantiated.
03253     //
03254     // On the other hand, we also have clients (think VTK and its ubiquitous New method)
03255     // that want the factory method behavior specifically so that abstract class instances
03256     // *can* be created, even though behind the scenes a concrete subclass of the factory
03257     // method's choice is the thing actually being instantiated.
03258     //
03259     if (this->GetSettings()->GetEmitDefaultFactoryMethod(c))
03260       {
03261       EmitCSharpMethod(os, dllname, c, m, mname, "public", emitExceptionParams);
03262       }
03263     else
03264       {
03265       EmitIndent(os);
03266       EmitCSharpDllImportDeclaration(os, dllname, c, m, mname, f.c_str(), emitExceptionParams);
03267       }
03268 
03269     Emit(os, "\n");
03270     Emit(os, "\n");
03271 
03272     // Documentation:
03273     gxsys_stl::vector<gxsys_stl::string> docblock;
03274     this->GetHeaderFileReader(c)->GetCommentBlockBefore(m->GetLine(), docblock, this->ClassLineNumber);
03275     EmitDocumentationBlock(os, docblock, 1);
03276 
03277     EmitIndent(os);
03278     Emit(os, "public ");
03279     Emit(os, cname.c_str());
03280     Emit(os, "()\n");
03281     EmitIndent(os, 2);
03282     Emit(os, ": base(IntPtr.Zero, false, false)\n");
03283 
03284     EmitIndent(os);
03285     Emit(os, "{\n");
03286 
03287     EmitIndent(os, 2);
03288     Emit(os, "// mummy generated default C# constructor\n");
03289     EmitIndent(os, 2);
03290     Emit(os, "uint mteStatus = 0;\n");
03291     EmitIndent(os, 2);
03292     Emit(os, "uint mteIndex = UInt32.MaxValue;\n");
03293     EmitIndent(os, 2);
03294     Emit(os, "uint rawRefCount = 0;\n");
03295     Emit(os, "\n");
03296 
03297     if (emitExceptionParams)
03298       {
03299       EmitIndent(os, 2);
03300       Emit(os, "uint mteExceptionIndex = UInt32.MaxValue;\n");
03301       EmitIndent(os, 2);
03302       Emit(os, "IntPtr clonedException = IntPtr.Zero;\n");
03303       Emit(os, "\n");
03304       }
03305 
03306     EmitIndent(os, 2);
03307     Emit(os, "IntPtr rawCppThis = ");
03308     Emit(os, f.c_str());
03309     Emit(os, "(ref mteStatus, ref mteIndex, ref rawRefCount");
03310 
03311     if (emitExceptionParams)
03312       {
03313       Emit(os, ", ref mteExceptionIndex, ref clonedException");
03314       }
03315 
03316     Emit(os, ");\n");
03317 
03318     if (emitExceptionParams)
03319       {
03320       Emit(os, "\n");
03321       EmitThrowClonedException(os, 2);
03322       Emit(os, "\n");
03323       }
03324 
03325     EmitIndent(os, 2);
03326     Emit(os, "this.SetCppThis(rawCppThis, true, (0==mteStatus || rawRefCount<2 ? false : true));\n");
03327 
03328     EmitIndent(os);
03329     Emit(os, "}\n");
03330     }
03331 }
03332 
03333 
03334 //----------------------------------------------------------------------------
03335 void MummyCsharpGenerator::EmitCSharpRegister(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname, bool emitExceptionParams)
03336 {
03337   EmitCSharpMethod(os, dllname, c, m, mname, "public", emitExceptionParams);
03338 }
03339 
03340 
03341 //----------------------------------------------------------------------------
03342 void MummyCsharpGenerator::EmitCSharpDispose(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c, const cable::Method *m, const gxsys_stl::string& mname, const unsigned int eventCount, bool emitExceptionParams)
03343 {
03344   cable::FunctionType *ft = 0;
03345   cable::Type *argType = 0;
03346   unsigned int cArgs = 0;
03347   unsigned int i = 0;
03348   gxsys_stl::string f;
03349 
03350   if (m)
03351     {
03352     ft = m->GetFunctionType();
03353     cArgs = ft->GetNumberOfArguments();
03354 
03355     // need qualified name?
03356     f = GetExportLayerFunctionName(c, m, mname);
03357 
03358     EmitIndent(os);
03359     EmitCSharpDllImportDeclaration(os, dllname, c, m, mname, f.c_str(), emitExceptionParams);
03360     Emit(os, "\n");
03361     Emit(os, "\n");
03362 
03363     // Documentation:
03364     gxsys_stl::vector<gxsys_stl::string> docblock;
03365     this->GetHeaderFileReader(c)->GetCommentBlockBefore(m->GetLine(), docblock, this->ClassLineNumber);
03366     EmitDocumentationBlock(os, docblock, 1);
03367     }
03368   else
03369     {
03370     EmitIndent(os);
03371     Emit(os, "/// <summary>\n");
03372     EmitIndent(os);
03373     Emit(os, "/// Automatically generated protected Dispose method - called from\n");
03374     EmitIndent(os);
03375     Emit(os, "/// public Dispose or the C# destructor. DO NOT call directly.\n");
03376     EmitIndent(os);
03377     Emit(os, "/// </summary>\n");
03378     }
03379 
03380 
03381   EmitIndent(os);
03382   Emit(os, "protected override void Dispose(bool disposing)\n");
03383   EmitIndent(os);
03384   Emit(os, "{\n");
03385 
03386 
03387   // If we are going to emit any code other than "base.Dispose(disposing);"
03388   // then make sure it is wrapped in a try/finally block so that we still call
03389   // base.Dispose if one of these other bits of code we're calling throws
03390   // an exception...
03391   //
03392   bool openedTryFinally = false;
03393   unsigned int indent = 2;
03394   if ((0 != eventCount) || m)
03395     {
03396     openedTryFinally = true;
03397     indent = 3;
03398 
03399     EmitIndent(os, indent-1);
03400     Emit(os, "try\n");
03401     EmitIndent(os, indent-1);
03402     Emit(os, "{\n");
03403     }
03404 
03405 
03406   if (0 != eventCount)
03407     {
03408     EmitIndent(os, indent);
03409     Emit(os, "this.RemoveAllRelayHandlers();\n");
03410     }
03411 
03412   if (m)
03413     {
03414     EmitIndent(os, indent);
03415     Emit(os, "if (this.GetCallDisposalMethod())\n");
03416     EmitIndent(os, indent);
03417     Emit(os, "{\n");
03418 
03419     if (emitExceptionParams)
03420       {
03421       EmitIndent(os, indent+1);
03422       Emit(os, "uint mteExceptionIndex = 0;\n");
03423       EmitIndent(os, indent+1);
03424       Emit(os, "IntPtr clonedException = IntPtr.Zero;\n");
03425       Emit(os, "\n");
03426       }
03427 
03428     EmitIndent(os, indent+1);
03429     Emit(os, f.c_str());
03430     Emit(os, "(this.GetCppThis()");
03431 
03432     for (i = 0; i<cArgs; ++i)
03433       {
03434       argType = ft->GetArgument(i);
03435 
03436       if (IsObjectPointer(argType))
03437         {
03438         Emit(os, ", new HandleRef()");
03439         }
03440       else if (IsVoidPointer(argType))
03441         {
03442         Emit(os, ", System.IntPtr.Zero");
03443         }
03444       else
03445         {
03446         Emit(os, ", 0");
03447         }
03448       }
03449 
03450     if (emitExceptionParams)
03451       {
03452       Emit(os, ", ref mteExceptionIndex, ref clonedException");
03453       }
03454 
03455     Emit(os, ");\n");
03456     EmitIndent(os, indent+1);
03457     Emit(os, "this.ClearCppThis();\n");
03458 
03459     if (emitExceptionParams)
03460       {
03461       Emit(os, "\n");
03462       EmitThrowClonedException(os, indent+1);
03463       }
03464 
03465     EmitIndent(os, indent);
03466     Emit(os, "}\n");
03467     Emit(os, "\n");
03468     }
03469 
03470   if (openedTryFinally)
03471     {
03472     EmitIndent(os, indent-1);
03473     Emit(os, "}\n");
03474     EmitIndent(os, indent-1);
03475     Emit(os, "finally\n");
03476     EmitIndent(os, indent-1);
03477     Emit(os, "{\n");
03478     }
03479 
03480   EmitIndent(os, indent);
03481   Emit(os, "base.Dispose(disposing);\n");
03482 
03483   if (openedTryFinally)
03484     {
03485     EmitIndent(os, indent-1);
03486     Emit(os, "}\n");
03487     }
03488 
03489   EmitIndent(os);
03490   Emit(os, "}\n");
03491 }
03492 
03493 
03494 //----------------------------------------------------------------------------
03495 struct SortByFieldOffset
03496 {
03497   bool operator()(const cable::Field* f1, const cable::Field* f2)
03498   {
03499     return f1->GetOffset() < f2->GetOffset();
03500   }
03501 };
03502 
03503 
03504 //----------------------------------------------------------------------------
03505 void MummyCsharpGenerator::EmitCSharpWrapperClassAsStruct(gxsys_ios::ostream &os, const cable::Class *c)
03506 {
03507   gxsys_stl::vector<cable::Field*> fields;
03508   gxsys_stl::vector<cable::Field*>::iterator fit;
03509   gxsys_stl::string derivedName;
03510   gxsys_stl::string fieldType;
03511   gxsys_stl::vector<gxsys_stl::string> docblock;
03512   bool isPartial = this->GetSettings()->GetPartialClass(c);
03513   bool fieldAccess = !HasAttribute(c, "gccxml(iwhNoFieldAccess)");
03514 
03515   // First iterate and collect all the fields in a local vector:
03516   //
03517   for (cable::Context::Iterator it = c->Begin(); it != c->End(); ++it)
03518     {
03519     cable::Field* f = cable::Field::SafeDownCast(*it);
03520     if (f)
03521       {
03522       fields.push_back(f);
03523       }
03524     }
03525 
03526   // Sort the vector so that we can emit the fields in the same order
03527   // in which they appear in the original C++ struct/class:
03528   //
03529   gxsys_stl::sort(fields.begin(), fields.end(), SortByFieldOffset());
03530 
03531   // Emit class opening:
03532   //
03533   Emit(os, "public ");
03534   if (isPartial)
03535     {
03536     Emit(os, "partial ");
03537     }
03538   Emit(os, "struct ");
03539   Emit(os, GetWrappedClassName(c).c_str());
03540   Emit(os, "\n");
03541   Emit(os, "{\n");
03542 
03543   // Enums:
03544   //
03545   EmitCSharpEnums(os, c);
03546 
03547   // Now iterate and emit a private data member for each field:
03548   //
03549   for (fit = fields.begin(); fit != fields.end(); ++fit)
03550     {
03551     cable::Field* f = *fit;
03552     if (f)
03553       {
03554       derivedName = ExtractDerivedName(f->GetName(), f, this->GetSettings()->GetVerbose());
03555       fieldType = GetCSharpTypeString(f->GetType(), false, false);
03556 
03557       // "bool" is special - help it marshal properly to a C++ bool struct
03558       // data member...
03559       //
03560       // Using C# bool here causes an InteropServices.MarshalDirectiveException
03561       // with a message that says the type cannot be marshalled via PInvoke.
03562       // So we use C# byte instead and make it look like a bool through the
03563       // public accessors emitted in the next step.
03564       //
03565       if (fieldType == "bool")
03566         {
03567         fieldType = "byte";
03568         }
03569 
03570       EmitIndent(os);
03571       Emit(os, "private ");
03572       Emit(os, fieldType.c_str());
03573       Emit(os, " m_");
03574       Emit(os, derivedName.c_str());
03575       Emit(os, ";");
03576       Emit(os, "\n");
03577       }
03578     }
03579 
03580   // Iterate and emit public accessors for each private data member:
03581   //
03582   for (fit = fields.begin(); fieldAccess && fit != fields.end(); ++fit)
03583     {
03584     cable::Field* f = *fit;
03585     if (f)
03586       {
03587       derivedName = ExtractDerivedName(f->GetName(), f, false);
03588       fieldType = GetCSharpTypeString(f->GetType(), false, false);
03589 
03590       Emit(os, "\n");
03591 
03592       this->GetHeaderFileReader(c)->GetCommentBlockBefore(f->GetLine(), docblock, this->ClassLineNumber);
03593       EmitDocumentationBlock(os, docblock, 1);
03594       docblock.clear();
03595 
03596       EmitIndent(os);
03597       Emit(os, "public ");
03598       Emit(os, fieldType.c_str());
03599       Emit(os, " ");
03600       Emit(os, derivedName.c_str());
03601       Emit(os, "\n");
03602 
03603       EmitIndent(os);
03604       Emit(os, "{");
03605       Emit(os, "\n");
03606 
03607       EmitIndent(os, 2);
03608       Emit(os, "get\n");
03609 
03610       EmitIndent(os, 2);
03611       Emit(os, "{\n");
03612 
03613       EmitIndent(os, 3);
03614       Emit(os, "return this.m_");
03615       Emit(os, derivedName.c_str());
03616       if (fieldType == "bool")
03617         {
03618         // "bool" is special -- see above comments...
03619         //
03620         Emit(os, " == 0 ? false : true");
03621         }
03622       Emit(os, ";\n");
03623 
03624       EmitIndent(os, 2);
03625       Emit(os, "}\n");
03626 
03627       Emit(os, "\n");
03628 
03629       EmitIndent(os, 2);
03630       Emit(os, "set\n");
03631 
03632       EmitIndent(os, 2);
03633       Emit(os, "{\n");
03634 
03635       EmitIndent(os, 3);
03636       Emit(os, "this.m_");
03637       Emit(os, derivedName.c_str());
03638       Emit(os, " = ");
03639       if (fieldType == "bool")
03640         {
03641         // "bool" is special -- see above comments...
03642         //
03643         Emit(os, "(byte)(value ? 1 : 0)");
03644         }
03645       else
03646         {
03647         Emit(os, "value");
03648         }
03649       Emit(os, ";\n");
03650 
03651       EmitIndent(os, 2);
03652       Emit(os, "}\n");
03653 
03654       EmitIndent(os);
03655       Emit(os, "}");
03656       Emit(os, "\n");
03657       }
03658     }
03659 
03660   // Caller emits closing brace for struct so that he can emit
03661   // extraCSharpCode if necessary before the closing brace...
03662 }
03663 
03664 
03665 //----------------------------------------------------------------------------
03666 void MummyCsharpGenerator::GatherWrappedMethods(
03667   const cable::Class *c,
03668   gxsys_stl::vector<cable::Method*>& wrapped_methods,
03669   cable::Method*& factoryM,
03670   cable::Method*& disposalM,
03671   cable::Method*& registerM,
03672   cable::Method*& unRegisterM,
03673   bool includeParentMethods
03674   )
03675 {
03676   // When recursion hits the top of the class hierarchy, we're done:
03677   //
03678   if (!c)
03679     {
03680     return;
03681     }
03682 
03683   gxsys_stl::string factoryMethod(this->GetSettings()->GetFactoryMethod(c));
03684   gxsys_stl::string disposalMethod(this->GetSettings()->GetDisposalMethod(c));
03685   gxsys_stl::string registerMethod(this->GetSettings()->GetRegisterMethod(c));
03686   gxsys_stl::string unRegisterMethod(this->GetSettings()->GetUnRegisterMethod(c));
03687 
03688   // Reset state only if we are iterating the methods of the *target* class.
03689   // (And only track factory method if iterating the target class...)
03690   //
03691   if (c == this->GetTargetClass())
03692     {
03693     this->CurrentMethodId = 0;
03694     this->MethodIdMap.clear();
03695     WrappedMethods.clear();
03696     wrapped_methods.clear();
03697     factoryM = 0;
03698     disposalM = 0;
03699     registerM = 0;
03700     unRegisterM = 0;
03701     }
03702 
03703   // If including parents, do so first so that the list of methods is in
03704   // "superclass first" order... And so that tracked pointers get set
03705   // to the most derived override possible.
03706   //
03707   if (includeParentMethods)
03708     {
03709     this->GatherWrappedMethods(GetWrappableParentClass(c), wrapped_methods,
03710       factoryM, disposalM, registerM, unRegisterM, true);
03711     }
03712 
03713   // Iterate class c's methods, adding wrappable ones to wrapped_methods and
03714   // tracking "special" methods as we encounter them:
03715   //
03716   for (cable::Context::Iterator it = c->Begin(); it != c->End(); ++it)
03717     {
03718     cable::Method *m = cable::Method::SafeDownCast(*it);
03719 
03720     if (m && MethodIsWrappable(m, it.GetAccess()))
03721       {
03722       gxsys_stl::string signature(GetMethodSignature(c, m));
03723 
03724       if ((c == this->GetTargetClass()) && signature == factoryMethod + "()")
03725         {
03726         LogVerboseInfo(<< "Found factory method, signature: " << signature);
03727         factoryM = m;
03728         this->MethodIdMap.insert(gxsys_stl::make_pair(m, ++this->CurrentMethodId));
03729         }
03730       else if (signature == disposalMethod + "()")
03731         {
03732         LogVerboseInfo(<< "Found disposal method, signature: " << signature);
03733         disposalM = m;
03734         this->MethodIdMap.insert(gxsys_stl::make_pair(m, ++this->CurrentMethodId));
03735         }
03736       else if (gxsys::SystemTools::StringStartsWith(registerMethod.c_str(),
03737         (gxsys_stl::string(m->GetName())+"(").c_str()))
03738         {
03739         LogVerboseInfo(<< "Found register method, signature: " << signature);
03740         registerM = m;
03741         this->MethodIdMap.insert(gxsys_stl::make_pair(m, ++this->CurrentMethodId));
03742         }
03743       else if (gxsys::SystemTools::StringStartsWith(unRegisterMethod.c_str(),
03744         (gxsys_stl::string(m->GetName())+"(").c_str()))
03745         {
03746         LogVerboseInfo(<< "Found unregister method, signature: " << signature);
03747         unRegisterM = m;
03748         this->MethodIdMap.insert(gxsys_stl::make_pair(m, ++this->CurrentMethodId));
03749         }
03750       else if (!WrappedMethodExists(signature))
03751         {
03752         WrappedMethods.insert(gxsys_stl::make_pair(signature, MethodInstance(c, m)));
03753         wrapped_methods.push_back(m);
03754         this->MethodIdMap.insert(gxsys_stl::make_pair(m, ++this->CurrentMethodId));
03755         }
03756       else
03757         {
03758         if (this->GetSettings()->GetVerbose())
03759           {
03760           //Emit(os, "//WARNING: ");
03761           //Emit(os, GetAccessString(it.GetAccess()));
03762           //Emit(os, " ");
03763           //Emit(os, (*it)->GetNameOfClass());
03764           //Emit(os, " '");
03765           //Emit(os, (*it)->GetName());
03766           //Emit(os, "' wrappable method *NOT WRAPPED* because its signature matches a method that was already wrapped...\n");
03767           }
03768         }
03769       }
03770 
03771     if (this->GetSettings()->GetVerbose())
03772       {
03773       if (!m)
03774         {
03775         LogInfo(mi_VerboseInfo, << GetAccessString(it.GetAccess())
03776           << " "
03777           << (*it)->GetNameOfClass()
03778           << " '"
03779           << (*it)->GetName()
03780           << "' not wrapped because it's not a method...\n"
03781           );
03782         }
03783       }
03784     }
03785 }
03786 
03787 
03788 //----------------------------------------------------------------------------
03789 struct SortByMethodDeclarationLineNumber
03790 {
03791   bool operator()(const cable::Method* m1, const cable::Method* m2)
03792   {
03793     return m1->GetLine() < m2->GetLine();
03794   }
03795 };
03796 
03797 
03798 //----------------------------------------------------------------------------
03799 bool MummyCsharpGenerator::ValidateWrappedMethods(
03800   const cable::Class *,
03801   gxsys_stl::vector<cable::Method*>& wrapped_methods,
03802   cable::Method*&,
03803   cable::Method*&,
03804   cable::Method*&,
03805   cable::Method*&
03806   )
03807 {
03808   bool valid = true;
03809   gxsys_stl::vector<cable::Method*> wrapped_methods_local;
03810   gxsys_stl::vector<cable::Method*>::iterator mit;
03811   cable::Method* m = 0;
03812   gxsys::RegularExpression reGet;
03813   gxsys::RegularExpression reSet;
03814 
03815   reGet.compile("^[Gg]et");
03816   reSet.compile("^[Ss]et");
03817 
03818   // Make a local copy of the wrapped_methods vector so we can sort it by
03819   // "method declaration line number"...
03820   //
03821   gxsys_stl::copy(wrapped_methods.begin(), wrapped_methods.end(),
03822     gxsys_stl::back_inserter(wrapped_methods_local));
03823 
03824   // Sort the vector so that we can emit the fields in the same order
03825   // in which they appear in the original C++ struct/class:
03826   //
03827   gxsys_stl::sort(wrapped_methods_local.begin(), wrapped_methods_local.end(),
03828     SortByMethodDeclarationLineNumber());
03829 
03830   for (mit = wrapped_methods_local.begin(); mit != wrapped_methods_local.end(); ++mit)
03831     {
03832     m = *mit;
03833 
03834     cable::FunctionType *ft = m->GetFunctionType();
03835     unsigned int cArgs = ft->GetNumberOfArguments();
03836     unsigned int cReqArgs = ft->GetNumberOfRequiredArguments();
03837     cable::Type *retType = ft->GetReturns();
03838     bool voidReturn = false;
03839     bool iwhPropGetExempt = false;
03840 
03841     if (IsVoid(retType))
03842       {
03843       voidReturn = true;
03844       }
03845 
03846     if (cArgs != cReqArgs)
03847       {
03848       // Method has at least one default arg... Warn that mummy is ignoring
03849       // any default argument values...
03850       //
03851       LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_DefaultArgumentValuesIgnored,
03852         "ignoring default argument values for method '" << m->GetName() << "'.");
03853       }
03854 
03855     if (reGet.find(m->GetName()))
03856       {
03857       //
03858       // It's a "getter" : warn if it returns "void" or if it's not a const
03859       // method or if it's missing the iwhPropGet hint...
03860       //
03861 
03862       if (gxsys_stl::string("GetEnumerator") == m->GetName())
03863         {
03864         iwhPropGetExempt = true;
03865         }
03866 
03867       // The mw_PropGetReturnsVoid and mw_PropGetHasArgs warnings are based on
03868       // the desire that a simple getter method should return one and only one
03869       // thing by return value, not through one or more "byref" arguments...
03870       //
03871       if (voidReturn)
03872         {
03873         LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropGetReturnsVoid,
03874           "'Getter' method '" << m->GetName() << "' returns void.");
03875         }
03876 
03877       if (cArgs)
03878         {
03879         LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropGetHasArgs,
03880           "'Getter' method '" << m->GetName() << "' has arguments. Should it?");
03881         }
03882 
03883       if (!iwhPropGetExempt && !HasAttribute(m, "gccxml(iwhPropGet)"))
03884         {
03885         if (!voidReturn && 0==cArgs)
03886           {
03887           LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_SeriousMissingPropGetHint,
03888             "'Getter' method '" << m->GetName() << "' is a perfect candidate for the 'iwhPropGet' hint. Add the 'iwhPropGet' hint to eliminate this warning.");
03889           }
03890         else
03891           {
03892           LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_MissingPropGetHint,
03893             "'Getter' method '" << m->GetName() << "' does not have the 'iwhPropGet' hint. Should it?");
03894           }
03895         }
03896 
03897       if (!m->GetConst() && !m->GetStatic())
03898         {
03899         LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropGetNotConst,
03900           "'Getter' method '" << m->GetName() << "' is not const. Should it be const?");
03901         }
03902       }
03903 
03904     if (reSet.find(m->GetName()))
03905       {
03906       // It's a "setter" : warn if it's missing the iwhPropSet hint:
03907       //
03908       if (!HasAttribute(m, "gccxml(iwhPropSet)"))
03909         {
03910         if (voidReturn && 1==cArgs)
03911           {
03912           LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_SeriousMissingPropSetHint,
03913             "'Setter' method '" << m->GetName() << "' is a perfect candidate for the 'iwhPropSet' hint. Add the 'iwhPropSet' hint to eliminate this warning.");
03914           }
03915         else
03916           {
03917           LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_MissingPropSetHint,
03918             "'Setter' method '" << m->GetName() << "' does not have the 'iwhPropSet' hint. Should it?");
03919           }
03920         }
03921 
03922       if (cArgs!=1)
03923         {
03924         LogFileLineWarningMsg(m->GetFile(), m->GetLine(), mw_PropSetUnexpectedArgCount,
03925           "'Setter' method '" << m->GetName() << "' has " << cArgs << " arguments. Should it have exactly one argument instead?");
03926         }
03927       }
03928     }
03929 
03930   return valid;
03931 }
03932 
03933 
03934 //----------------------------------------------------------------------------
03935 void MummyCsharpGenerator::BuildPropGetsAndSetsMap(
03936   gxsys_stl::vector<cable::Method*>& wrapped_methods,
03937   gxsys_stl::map<gxsys_stl::string, gxsys_stl::pair<cable::Method*, cable::Method*> >& wrapped_properties
03938   )
03939 {
03940   gxsys_stl::vector<cable::Method*>::iterator mit;
03941   gxsys_stl::map<gxsys_stl::string, gxsys_stl::pair<cable::Method*, cable::Method*> >::iterator gsit;
03942   bool addingPropGet = false;
03943   bool addingPropSet = false;
03944   cable::Method* propGetMethod = 0;
03945   cable::Method* propSetMethod = 0;
03946   gxsys_stl::string propName;
03947 
03948   for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
03949     {
03950     addingPropGet = HasAttribute(*mit, "gccxml(iwhPropGet)");
03951     addingPropSet = addingPropGet ? false : HasAttribute(*mit, "gccxml(iwhPropSet)");
03952 
03953     if (addingPropGet || addingPropSet)
03954       {
03955       if (addingPropGet)
03956         {
03957         propGetMethod = *mit;
03958         }
03959       else
03960         {
03961         propGetMethod = 0;
03962         }
03963 
03964       if (addingPropSet)
03965         {
03966         propSetMethod = *mit;
03967         }
03968       else
03969         {
03970         propSetMethod = 0;
03971         }
03972 
03973       propName = ExtractDerivedName(GetWrappedMethodName(*mit).c_str(), *mit,
03974         this->GetSettings()->GetVerbose());
03975 
03976       gsit = wrapped_properties.find(propName);
03977 
03978       if (gsit == wrapped_properties.end())
03979         {
03980         // propName not in our map yet, add it:
03981         //
03982         wrapped_properties.insert(gxsys_stl::make_pair(propName,
03983           gxsys_stl::make_pair(propGetMethod, propSetMethod)));
03984         }
03985       else
03986         {
03987         // We already have an entry for propName...
03988         // This should be the "other" half of the pair.
03989         // So, if we are adding the *get*, then save the
03990         // existing "set" that's already in the map and
03991         // vice versa.
03992         //
03993         if (addingPropGet)
03994           {
03995           propSetMethod = gsit->second.second;
03996           }
03997 
03998         if (addingPropSet)
03999           {
04000           propGetMethod = gsit->second.first;
04001           }
04002 
04003         // The iterator points to the real map entry. Just
04004         // overwrite it:
04005         //
04006         gsit->second = gxsys_stl::make_pair(propGetMethod, propSetMethod);
04007         }
04008       }
04009     }
04010 
04011 
04012   // Analyze wrapped_properties and report anything suspicious.
04013   // Or just report info if verbose...
04014   //
04015   bool verbose = this->GetSettings()->GetVerbose();
04016   if (!wrapped_properties.empty())
04017     {
04018     gxsys_stl::string comment;
04019 
04020     if (verbose)
04021       {
04022       LogInfo(mi_VerboseInfo, << "Properties:");
04023       }
04024 
04025     for (gsit = wrapped_properties.begin(); gsit != wrapped_properties.end(); ++gsit)
04026       {
04027       if (gsit->second.first!=0 && gsit->second.second!=0)
04028         {
04029         comment = "ReadWrite property.";
04030         }
04031       else if (gsit->second.first!=0)
04032         {
04033         comment = "ReadOnly property.";
04034         }
04035       else if (gsit->second.second!=0)
04036         {
04037         LogFileLineWarningMsg(gsit->second.second->GetFile(),
04038           gsit->second.second->GetLine(),
04039           mw_WriteOnlyProperty,
04040           "A WriteOnly property '" << gsit->first <<
04041           "' is very unusual - did you forget to mark the 'Get' method with 'iwhPropGet'?");
04042         }
04043       else
04044         {
04045         LogError(me_InternalError, "Property with no 'get' and no 'set'... Impossible!");
04046         }
04047 
04048       if (verbose)
04049         {
04050         LogInfo(mi_VerboseInfo, << comment << "  propName: " << gsit->first
04051           << "  propGetMethod: " << gsit->second.first
04052           << "  propSetMethod: " << gsit->second.second
04053           );
04054         }
04055       }
04056     }
04057 }
04058 
04059 
04060 //----------------------------------------------------------------------------
04061 void MummyCsharpGenerator::EmitCSharpWrapperClass(gxsys_ios::ostream &os, const char *dllname, const cable::Class *c)
04062 {
04063   // Gather wrapped elements:
04064   //
04065   gxsys_stl::vector<cable::Method*> wrapped_methods;
04066   gxsys_stl::vector<cable::Method*>::iterator mit;
04067   cable::Method *factoryM = 0;
04068   cable::Method *disposalM = 0;
04069   cable::Method *registerM = 0;
04070   cable::Method *unRegisterM = 0;
04071   bool verbose = this->GetSettings()->GetVerbose();
04072   gxsys_stl::string atts(c->GetAttributes());
04073   gxsys_stl::string mname;
04074 
04075   // The "package" directive from the gccxml input is used as a base
04076   // namespace. If it's not empty, prepend it to the class's namespace.
04077   //
04078   gxsys_stl::string target_namespace;
04079   gxsys_stl::string base_namespace(this->GetSettings()->GetPackage());
04080   gxsys_stl::string class_namespace(GetFullyQualifiedNameForCSharp(c->GetContext()));
04081 
04082   // C++ global scope means "no namespace please"
04083   //
04084   if (class_namespace == "::")
04085     {
04086     class_namespace = "";
04087     }
04088 
04089   if (base_namespace == "")
04090     {
04091     target_namespace = class_namespace;
04092     }
04093   else if (class_namespace == "")
04094     {
04095     target_namespace = base_namespace;
04096     }
04097   else
04098     {
04099     target_namespace = base_namespace + "." + class_namespace;
04100     }
04101 
04102 
04103   // Emit code:
04104   //
04105   EmitMummyVersionComments(os, "//");
04106 
04107 
04108   // If the class maps directly to a builtin type, then DO NOT emit any code.
04109   //
04110   gxsys_stl::string mapToType = ExtractMapToType(c);
04111   if (mapToType != "")
04112     {
04113     Emit(os, "\n");
04114     Emit(os, "//----------------------------------------------------------------------------\n");
04115     Emit(os, "// Unmanaged class '");
04116     Emit(os, GetFullyQualifiedNameForCPlusPlus(c).c_str());
04117     Emit(os, "' maps directly to type '");
04118     Emit(os, mapToType.c_str());
04119     Emit(os, "'.\n");
04120     Emit(os, "// No code generated for '");
04121     Emit(os, GetFullyQualifiedNameForCPlusPlus(c).c_str());
04122     Emit(os, "'...\n");
04123 
04124     if (verbose)
04125       {
04126       LogInfo(mi_VerboseInfo, << "Skipping code generation because class maps directly to native type.");
04127       }
04128 
04129     return;
04130     }
04131 
04132 
04133   Emit(os, "\n");
04134   Emit(os, "//----------------------------------------------------------------------------\n");
04135   Emit(os, "using System;\n");
04136   Emit(os, "using System.Runtime.InteropServices; // DllImport and HandleRef both live here\n");
04137   Emit(os, "\n");
04138 
04139 
04140   // If this project depends on "using" any other C# namespaces, they will be listed
04141   // as "Reference" elements in MummySettings.xml...
04142   //
04143   gxsys_stl::vector<gxsys_stl::string> refs;
04144   this->GetSettings()->GetReferences(refs);
04145   if (refs.size())
04146     {
04147     Emit(os, "// References\n");
04148     gxsys_stl::vector<gxsys_stl::string>::iterator rit;
04149     for (rit = refs.begin(); rit != refs.end(); ++rit)
04150       {
04151       Emit(os, "using ");
04152       Emit(os, rit->c_str());
04153       Emit(os, ";\n");
04154       }
04155     Emit(os, "\n");
04156     }
04157 
04158 
04159   // Open the (optional) namespace:
04160   //
04161   if (target_namespace != "")
04162     {
04163     Emit(os, "namespace ");
04164     Emit(os, target_namespace.c_str());
04165     Emit(os, "\n");
04166     Emit(os, "{\n");
04167     Emit(os, "\n");
04168     }
04169 
04170 
04171   // Documentation:
04172   //
04173   gxsys_stl::vector<gxsys_stl::string> docblock;
04174   this->ClassLineNumber = c->GetLine();
04175 
04176   if (gxsys::SystemTools::StringStartsWith(GetFullyQualifiedNameForCPlusPlus(c).c_str(), "vtk"))
04177     {
04178     this->GetHeaderFileReader(c)->GetFirstCommentBlock(docblock);
04179     }
04180   else
04181     {
04182     this->GetHeaderFileReader(c)->GetCommentBlockBefore(c->GetLine(), docblock, 1);
04183     }
04184 
04185   EmitDocumentationBlock(os, docblock, 0, true);
04186 
04187 
04188   if (IsUtilityClass(c))
04189     {
04190     // Verify no virtual methods... If any, emit error:
04191     //
04192     cable::Method* um = 0;
04193     bool bVirtual = false;
04194     for (cable::Context::Iterator umit = c->Begin(); !bVirtual && umit != c->End(); ++umit)
04195       {
04196       um = cable::Method::SafeDownCast(*umit);
04197       if (um && um->GetVirtual())
04198         {
04199         bVirtual = true;
04200         }
04201       }
04202 
04203     if (bVirtual)
04204       {
04205       LogFileLineErrorMsg(um->GetFile(), um->GetLine(), me_NoVirtualMethodsAllowed,
04206         "A utility class cannot have any virtual methods. The '" << um->GetName() <<
04207         "' method should not be virtual.");
04208       return;
04209       }
04210 
04211     // Utility classes get wrapped as structs:
04212     //
04213     EmitCSharpWrapperClassAsStruct(os, c);
04214     }
04215   else
04216     {
04217     if (verbose)
04218       {
04219       LogInfo(mi_VerboseInfo, << "Calling GatherWrappedMethods...");
04220       }
04221 
04222     this->GatherWrappedMethods(c, wrapped_methods, factoryM, disposalM, registerM, unRegisterM, false);
04223 
04224     if (verbose)
04225       {
04226       DumpLookupEntries();
04227       }
04228 
04229     this->ValidateWrappedMethods(c, wrapped_methods, factoryM, disposalM, registerM, unRegisterM);
04230 
04231     // Filter out prop gets and sets, putting them in their very own data structure.
04232     // Key in the map is the name of the property. 1st method in pair is propget,
04233     // 2nd method is propset.
04234     //
04235     gxsys_stl::map<gxsys_stl::string, gxsys_stl::pair<cable::Method*, cable::Method*> > wrapped_properties;
04236     this->BuildPropGetsAndSetsMap(wrapped_methods, wrapped_properties);
04237 
04238     // Now remove any entries found in the props *map* from the methods *vector*.
04239     // Otherwise, we'd end up with all of the properties "repeated" as methods, too.
04240     //
04241     gxsys_stl::map<gxsys_stl::string, gxsys_stl::pair<cable::Method*, cable::Method*> >::iterator gsit;
04242     for (gsit = wrapped_properties.begin(); gsit != wrapped_properties.end(); ++gsit)
04243       {
04244       if (gsit->second.first)
04245         {
04246         mit = gxsys_stl::find(wrapped_methods.begin(), wrapped_methods.end(),
04247           gsit->second.first);
04248         if (mit != wrapped_methods.end())
04249           {
04250           wrapped_methods.erase(mit);
04251           }
04252         else
04253           {
04254           LogWarning(mw_InternalWarning, << "Unexpected unfound propget method...");
04255           }
04256         }
04257 
04258       if (gsit->second.second)
04259         {
04260         mit = gxsys_stl::find(wrapped_methods.begin(), wrapped_methods.end(),
04261           gsit->second.second);
04262         if (mit != wrapped_methods.end())
04263           {
04264           wrapped_methods.erase(mit);
04265           }
04266         else
04267           {
04268           LogWarning(mw_InternalWarning, << "Unexpected unfound propset method...");
04269           }
04270         }
04271       }
04272 
04273 
04274     ClassWrappingSettings cws;
04275     if (!this->GetSettings()->FindClassWrappingSettings(GetFullyQualifiedNameForCPlusPlus(c).c_str(), &cws))
04276       {
04277       LogError(me_NoClassWrappingSettings,
04278         << "error: no ClassWrappingSettings for class " << GetFullyQualifiedNameForCPlusPlus(c).c_str());
04279       }
04280 
04281     const cable::Class *parent = GetWrappableParentClass(c);
04282     bool isPartial = cws.partialClass;
04283     bool emitExceptionParams = !cws.exceptionBaseClass.empty();
04284 
04285     // Class declaration:
04286     //
04287     Emit(os, "public ");
04288     if (c->GetAbstract())
04289       {
04290       Emit(os, "abstract ");
04291       }
04292     if (isPartial)
04293       {
04294       Emit(os, "partial ");
04295       }
04296     Emit(os, "class ");
04297     Emit(os, GetWrappedClassName(c).c_str());
04298     Emit(os, " : ");
04299     if (parent)
04300       {
04301       Emit(os, GetWrappedClassNameFullyQualified(parent).c_str());
04302       }
04303     else
04304       {
04305       gxsys_stl::string wrappedObjectBase(cws.wrappedObjectBase);
04306 
04307       if (wrappedObjectBase.empty())
04308         {
04309         wrappedObjectBase = "Kitware.mummy.Runtime.WrappedObject";
04310         }
04311 
04312       Emit(os, wrappedObjectBase.c_str());
04313       }
04314 
04315 
04316     // Any interface(s)? david.cole::fix - allow potentially many interfaces,
04317     // extract a list here if necessary rather than just one string...
04318     //
04319     gxsys_stl::string iface(ExtractImplementsInterface(atts));
04320 
04321     if (!iface.empty())
04322       {
04323       if (iface == "IEnumerable")
04324         {
04325         this->AddTargetInterface(iface);
04326         Emit(os, ", System.Collections.IEnumerable");
04327         }
04328       else if (iface == "IEnumerator")
04329         {
04330         this->AddTargetInterface(iface);
04331         Emit(os, ", System.Collections.IEnumerator");
04332         }
04333       else
04334         {
04335         this->AddTargetInterface(iface);
04336         Emit(os, ", ");
04337         Emit(os, iface.c_str());
04338         }
04339       }
04340 
04341     Emit(os, "\n");
04342 
04343 
04344     // Open class:
04345     //
04346     Emit(os, "{\n");
04347 
04348 
04349     // david.cole::fix - GetFullyQualifiedNameForCSharp should handle
04350     // target_namespace being set in the gccxml input file, but it currently
04351     // does not. It relies on exact mapping of the C++ namespaces, which does
04352     // not allow for "pushing" stuff in the global C++ namespace into a C#
04353     // namespace as we do in the Vehicles example and Kitware.VTK wrappers...
04354     //
04355     gxsys_stl::string fullCSharpName;
04356     if (target_namespace != "")
04357       {
04358       fullCSharpName = target_namespace + "." + GetWrappedClassName(c);
04359       }
04360     else
04361       {
04362       fullCSharpName = GetWrappedClassName(c);
04363       }
04364 
04365 
04366     // Register type info with the mummy.Runtime for *all* classes,
04367     // even abstract classes. Type registration needs to occur even
04368     // if the first call to the dll is a static method on an abstract
04369     // class (which forces the static constructor to run prior to
04370     // entering the static method...)
04371     //
04372     EmitIndent(os);
04373     Emit(os, "/// <summary>\n");
04374     EmitIndent(os);
04375     Emit(os, "/// Automatically generated type registration mechanics.\n");
04376     EmitIndent(os);
04377     Emit(os, "/// </summary>\n");
04378     EmitIndent(os);
04379     Emit(os, "public new static readonly string MRClassNameKey = \"");
04380     Emit(os, GetFullyQualifiedCPlusPlusTypeIdName(c).c_str());
04381     Emit(os, "\";\n");
04382     Emit(os, "\n");
04383 
04384     EmitIndent(os);
04385     Emit(os, "/// <summary>\n");
04386     EmitIndent(os);
04387     Emit(os, "/// Automatically generated type registration mechanics.\n");
04388     EmitIndent(os);
04389     Emit(os, "/// </summary>\n");
04390     EmitIndent(os);
04391     Emit(os, "public new const string MRFullTypeName = \"");
04392     Emit(os, fullCSharpName.c_str());
04393     Emit(os, "\";\n");
04394     Emit(os, "\n");
04395 
04396     EmitIndent(os);
04397     Emit(os, "/// <summary>\n");
04398     EmitIndent(os);
04399     Emit(os, "/// Automatically generated type registration mechanics.\n");
04400     EmitIndent(os);
04401     Emit(os, "/// </summary>\n");
04402     EmitIndent(os);
04403     Emit(os, "static ");
04404     Emit(os, GetWrappedClassName(c).c_str());
04405     Emit(os, "()\n");
04406     EmitIndent(os);
04407     Emit(os, "{\n");
04408 
04409     EmitIndent(os, 2);
04410     Emit(os, "Kitware.mummy.Runtime.Methods.RegisterType(\n");
04411     EmitIndent(os, 3);
04412     Emit(os, "System.Reflection.Assembly.GetExecutingAssembly(),\n");
04413     EmitIndent(os, 3);
04414     Emit(os, "MRClassNameKey,\n");
04415     EmitIndent(os, 3);
04416     Emit(os, "System.Type.GetType(MRFullTypeName)\n");
04417     EmitIndent(os, 3);
04418     Emit(os, ");\n");
04419 
04420     EmitIndent(os);
04421     Emit(os, "}\n");
04422 
04423     Emit(os, "\n");
04424     Emit(os, "\n");
04425 
04426 
04427     // Count events *now* before EmitCSharpDisposalMethod.
04428     // But emit code for the events further below.
04429     //
04430     // Also, examine the full set of events to be generated for this class.
04431     // For any duplicate names, use GetQualifiedEventName instead of
04432     // GetEventName in the methodEventNames map.
04433     //
04434     unsigned int eventCount = 0;
04435     gxsys_stl::map<gxsys_stl::string, int> event_name_counter;
04436     gxsys_stl::map<const cable::Method*, gxsys_stl::string> methodEventNames;
04437     gxsys_stl::string eventName;
04438 
04439     // Count events and fill in event_name_counter as we go:
04440     //
04441     for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04442       {
04443       if (MethodWrappableAsEvent(*mit, cable::Context::Public))
04444         {
04445         ++eventCount;
04446 
04447         eventName = GetEventName(*mit);
04448 
04449         event_name_counter[eventName]++;
04450         }
04451       }
04452 
04453     // Use event_name_counter to build a map of cable::Method* to event names.
04454     // If a method's GetEventName is a duplicate according to event_name_counter
04455     // then use GetQualifiedEventName for that method's event name... With this
04456     // data, we can simply look up the proper event name when given the
04457     // cable::Method* later when we are calling EmitCSharpEvent.
04458     //
04459     for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04460       {
04461       if (MethodWrappableAsEvent(*mit, cable::Context::Public))
04462         {
04463         eventName = GetEventName(*mit);
04464 
04465         if (1 == event_name_counter[eventName])
04466           {
04467           methodEventNames[*mit] = eventName;
04468           }
04469         else
04470           {
04471           methodEventNames[*mit] = GetQualifiedEventName(*mit);
04472           }
04473         }
04474       }
04475 
04476     // Now verify that we have non-duplicate event names. Reset event_name_counter
04477     // and this time analyze whether there are duplicate strings within the
04478     // methodEventNames map.
04479     //
04480     gxsys_stl::map<const cable::Method*, gxsys_stl::string>::iterator menIt;
04481     event_name_counter.clear();
04482     for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04483       {
04484       if (MethodWrappableAsEvent(*mit, cable::Context::Public))
04485         {
04486         menIt = methodEventNames.find(*mit);
04487 
04488         if (menIt != methodEventNames.end())
04489           {
04490           eventName = menIt->second;
04491           event_name_counter[eventName]++;
04492 
04493           if (event_name_counter[eventName] > 1)
04494             {
04495             LogFileLineWarningMsg((*mit)->GetFile(), (*mit)->GetLine(), mw_DuplicateGeneratedName,
04496               << "duplicate event name found: " << eventName.c_str()
04497               << " (a C# compile error will likely follow...)"
04498               );
04499             }
04500           }
04501         else
04502           {
04503           LogFileLineErrorMsg((*mit)->GetFile(), (*mit)->GetLine(), me_InternalError,
04504             << "event method not found in methodEventNames map...");
04505           }
04506         }
04507       }
04508 
04509 
04510     // Constructor(s):
04511     //
04512     gxsys_stl::string ctorModifier(this->GetSettings()->GetCsharpConstructorModifier(c));
04513     if (ctorModifier.empty())
04514       {
04515       ctorModifier = "public";
04516       }
04517     EmitIndent(os);
04518     Emit(os, "/// <summary>\n");
04519     EmitIndent(os);
04520     Emit(os, "/// Automatically generated constructor - called from generated code.\n");
04521     EmitIndent(os);
04522     Emit(os, "/// DO NOT call directly.\n");
04523     EmitIndent(os);
04524     Emit(os, "/// </summary>\n");
04525     EmitIndent(os);
04526     Emit(os, ctorModifier.c_str());
04527     Emit(os, " ");
04528     Emit(os, GetWrappedClassName(c).c_str());
04529     Emit(os, "(IntPtr rawCppThis, bool callDisposalMethod, bool strong) :\n");
04530     EmitIndent(os, 2);
04531     Emit(os, "base(rawCppThis, callDisposalMethod, strong)\n");
04532     EmitIndent(os);
04533     Emit(os, "{\n");
04534     EmitIndent(os);
04535     Emit(os, "}\n");
04536 
04537 
04538     // Factory method:
04539     //
04540     const cable::Method* fmp = 0;
04541     gxsys_stl::string factoryMethod = this->GetSettings()->GetFactoryMethod(c);
04542     const cable::Constructor* ctor = FindNonAbstractPublicDefaultConstructor(c);
04543 
04544     if (!factoryMethod.empty() && factoryMethod!="new")
04545       {
04546       if (factoryM)
04547         {
04548         fmp = factoryM;
04549         mname = fmp->GetName();
04550         }
04551       }
04552     else
04553       {
04554       fmp = ctor;
04555       mname = "new";
04556       }
04557 
04558     if (fmp || this->GetSettings()->GetUseShadow(c))
04559       {
04560       Emit(os, "\n");
04561       Emit(os, "\n");
04562       EmitCSharpConstructor(os, dllname, c, fmp, mname, emitExceptionParams);
04563       }
04564 
04565 
04566     // Register method:
04567     //
04568     if (registerM)
04569       {
04570       mname = registerM->GetName();
04571 
04572       Emit(os, "\n");
04573       Emit(os, "\n");
04574       EmitCSharpRegister(os, dllname, c, registerM, mname, emitExceptionParams);
04575       }
04576 
04577 
04578     // Disposal method:
04579     //
04580     // (disposalM/unRegisterM may be NULL, but we always emit a Dispose...
04581     // the guts of it vary based on disposalM/unRegisterM, but it's always
04582     // there and can be used as a cleaning spot for disconnecting any
04583     // outstanding event RelayHandler delegates...)
04584     //
04585     // If this is a ref-counted class, then use unRegisterM instead of disposalM:
04586     //
04587     const cable::Method* dmp = 0;
04588     gxsys_stl::string disposalMethod = this->GetSettings()->GetDisposalMethod(c);
04589     gxsys_stl::string unRegisterMethod = this->GetSettings()->GetUnRegisterMethod(c);
04590 
04591     if (!unRegisterMethod.empty())
04592       {
04593       if (unRegisterM)
04594         {
04595         dmp = unRegisterM;
04596         mname = dmp->GetName();
04597         }
04598       }
04599     else if (!disposalMethod.empty())
04600       {
04601       if (disposalM)
04602         {
04603         dmp = disposalM;
04604         mname = dmp->GetName();
04605         }
04606       }
04607     else
04608       {
04609       dmp = ctor;
04610       mname = "delete";
04611       }
04612 
04613     // Call EmitCSharpDispose even if dmp is NULL...
04614     //
04615     Emit(os, "\n");
04616     Emit(os, "\n");
04617     EmitCSharpDispose(os, dllname, c, dmp, mname, eventCount, emitExceptionParams);
04618 
04619 
04620     // Enums:
04621     //
04622     EmitCSharpEnums(os, c);
04623 
04624 
04625     // Delegates:
04626     //
04627     for (cable::Context::Iterator it = c->Begin(); it != c->End(); ++it)
04628       {
04629       cable::Typedef *t = cable::Typedef::SafeDownCast(*it);
04630 
04631       if (t && (cable::Context::Public == it.GetAccess()))
04632         {
04633         bool isDelegate = false;
04634         gxsys_stl::string tname(t->GetName());
04635 
04636         //isDelegate = HasAttribute(t, "gccxml(iwhDelegate)");
04637 
04638         cable::PointerType *pt = cable::PointerType::SafeDownCast(t->GetType());
04639         cable::FunctionType *ft = 0;
04640         if (pt)
04641           {
04642           ft = cable::FunctionType::SafeDownCast(pt->GetTarget());
04643           }
04644         if (ft)
04645           {
04646           isDelegate = true;
04647           }
04648 
04649         if (isDelegate)
04650           {
04651           Emit(os, "\n");
04652           Emit(os, "\n");
04653 
04654           docblock.clear();
04655                   this->GetHeaderFileReader(c)->GetCommentBlockBefore(t->GetLine(), docblock, this->ClassLineNumber);
04656           EmitDocumentationBlock(os, docblock, 1);
04657 
04658           EmitIndent(os);
04659           Emit(os, "public delegate ");
04660 
04661           unsigned int cArgs = ft->GetNumberOfArguments();
04662           cable::Type *argType = 0;
04663           cable::Type *retType = ft->GetReturns();
04664 
04665           // The following chunk is a near-copy of the bottom of
04666           // EmitCSharpMethodDeclaration...
04667           // <Chunk>
04668 
04669           // Does method return an array?
04670           //
04671           gxsys_stl::string arraySize = "";
04672           atts = t->GetAttributes();
04673           if (atts != "")
04674             {
04675             // Use ExtractArraySize instead of GetMethodArgumentArraySize here.
04676             // This is a delegate definition and cannot be in the VTK hints file.
04677             // (since the hints file only covers "normal" C++ methods...)
04678             // GetMethodArgumentArraySize uses ExtractArraySize internally, and
04679             // if there is no inline hint, it then examines the externalHints
04680             // file to see if there's a match. In this case, there could be no
04681             // match anyhow (as evidenced by the fact that we do not have access
04682             // to a cable::Method here...) so simply use ExtractArraySize directly.
04683             // Same reasoning applies below with the next use of ExtractArraySize.
04684             //
04685             arraySize = ExtractArraySize(atts);
04686             }
04687 
04688           // Return type:
04689           Emit(os, GetPInvokeTypeString(retType, true, arraySize != "", true).c_str());
04690           if (arraySize != "")
04691             {
04692             Emit(os, "[]");
04693             }
04694           Emit(os, " ");
04695 
04696           // Use the typedef name:
04697           Emit(os, t->GetName());
04698 
04699           // Open args:
04700           Emit(os, "(");
04701 
04702           // The C# args:
04703           unsigned int i;
04704           for (i= 0; i<cArgs; ++i)
04705             {
04706             argType = ft->GetArgument(i);
04707 
04708             // Is arg an array?
04709             //
04710             arraySize = "";
04711             atts = ft->GetArgumentAttributes(i);
04712             if (atts != "")
04713               {
04714               // See comments above regarding direct use of ExtractArraySize
04715               // instead of GetMethodArgumentArraySize.
04716               //
04717               arraySize = ExtractArraySize(atts);
04718               }
04719 
04720             // arg type:
04721             Emit(os, GetPInvokeTypeString(argType, false, arraySize != "", true).c_str());
04722 
04723             // array notation:
04724             if (arraySize != "")
04725               {
04726               Emit(os, "[]");
04727               }
04728 
04729             // arg name:
04730             Emit(os, " ");
04731             Emit(os, GetArgName(ft, i));
04732 
04733             if (i<cArgs-1)
04734               {
04735               Emit(os, ", ");
04736               }
04737             }
04738 
04739           // Close args:
04740           Emit(os, ")");
04741 
04742           // </Chunk>
04743 
04744 
04745           Emit(os, ";\n");
04746           }
04747         }
04748       }
04749 
04750 
04751     // Events:
04752     //
04753     if (0 != eventCount)
04754       {
04755       for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04756         {
04757         if (MethodWrappableAsEvent(*mit, cable::Context::Public))
04758           {
04759           Emit(os, "\n");
04760           Emit(os, "\n");
04761 
04762           menIt = methodEventNames.find(*mit);
04763           if (menIt != methodEventNames.end())
04764             {
04765             eventName = menIt->second;
04766             }
04767           else
04768             {
04769             eventName = GetEventName(*mit);
04770             LogFileLineErrorMsg((*mit)->GetFile(), (*mit)->GetLine(), me_InternalError,
04771               << "event method not found in methodEventNames map...");
04772             }
04773 
04774           EmitCSharpEvent(os, dllname, c, *mit, eventName);
04775           }
04776         }
04777 
04778       // Add a special "disconnect all" method that can be called at Dispose
04779       // time so that the unmanaged side does not end up with a stale pointer
04780       // to a garbage collected event RelayHandler...
04781       //
04782       Emit(os, "\n");
04783       Emit(os, "\n");
04784       EmitIndent(os);
04785       Emit(os, "/// <summary>\n");
04786       EmitIndent(os);
04787       Emit(os, "/// Method to disconnect all RelayHandler event members.\n");
04788       EmitIndent(os);
04789       Emit(os, "/// Called automatically from Dispose. DO NOT call directly.\n");
04790       EmitIndent(os);
04791       Emit(os, "/// </summary>\n");
04792       EmitIndent(os);
04793       Emit(os, "private void RemoveAllRelayHandlers()\n");
04794       EmitIndent(os);
04795       Emit(os, "{\n");
04796 
04797       for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04798         {
04799         if (MethodWrappableAsEvent(*mit, cable::Context::Public))
04800           {
04801           menIt = methodEventNames.find(*mit);
04802           if (menIt != methodEventNames.end())
04803             {
04804             eventName = menIt->second;
04805             }
04806           else
04807             {
04808             eventName = GetEventName(*mit);
04809             LogFileLineErrorMsg((*mit)->GetFile(), (*mit)->GetLine(), me_InternalError,
04810               << "event method not found in methodEventNames map...");
04811             }
04812 
04813           EmitIndent(os, 2);
04814           Emit(os, "this.Remove");
04815           Emit(os, eventName.c_str());
04816           Emit(os, "RelayHandler();\n");
04817           }
04818         }
04819 
04820       EmitIndent(os);
04821       Emit(os, "}\n");
04822       }
04823 
04824 
04825     // Properties:
04826     //
04827     for (gsit = wrapped_properties.begin(); gsit != wrapped_properties.end(); ++gsit)
04828       {
04829       Emit(os, "\n");
04830       Emit(os, "\n");
04831       EmitCSharpProperty(os, dllname, c, gsit->second.first, gsit->second.second, emitExceptionParams);
04832       }
04833 
04834 
04835     // Plain old methods:
04836     //
04837     for (mit = wrapped_methods.begin(); mit != wrapped_methods.end(); ++mit)
04838       {
04839       Emit(os, "\n");
04840       Emit(os, "\n");
04841       EmitCSharpMethod(os, dllname, c, *mit, (*mit)->GetName(), "public", emitExceptionParams);
04842       }
04843     }
04844 
04845 
04846   // Hand written shtuff:
04847   //
04848   // If there is extraCSharpCode, emit it *within* the class definition.
04849   // If it's there, it's the name of a file that we are to include in
04850   // its entirety...
04851   //
04852   gxsys_stl::string extraCode = this->GetSettings()->GetExtraCsharpCode(c);
04853   if (extraCode != "")
04854     {
04855     Emit(os, "\n");
04856     Emit(os, "\n");
04857     Emit(os, "// Begin extraCsharpCode\n");
04858     Emit(os, "\n");
04859     EmitFile(os, extraCode.c_str());
04860     Emit(os, "\n");
04861     Emit(os, "// End extraCsharpCode\n");
04862     Emit(os, "\n");
04863     }
04864 
04865 
04866   // Close the struct/class:
04867   //
04868   Emit(os, "}\n");
04869 
04870 
04871   // Close the namespace:
04872   //
04873   if (target_namespace != "")
04874     {
04875     Emit(os, "\n");
04876     Emit(os, "}\n");
04877     }
04878 }