public string Generate()
        {
            using (var sb = new AutoIndentStringBuilder ()) {
                using (var hdr = new AutoIndentStringBuilder ()) {
                    using (var decls = new AutoIndentStringBuilder ()) {
                        using (var mthds = new AutoIndentStringBuilder ()) {
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"");
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wtypedef-redefinition\""); // temporary hack until we can stop including glib.h
                            hdr.WriteLine ("#pragma clang diagnostic ignored \"-Wobjc-designated-initializers\"");

                            if (Driver.EnableDebug)
                                hdr.WriteLine ("#define DEBUG 1");

                            hdr.WriteLine ("#include <stdarg.h>");
                            hdr.WriteLine ("#include <xamarin/xamarin.h>");
                            hdr.WriteLine ("#include <objc/objc.h>");
                            hdr.WriteLine ("#include <objc/runtime.h>");

                            header = hdr;
                            declarations = decls;
                            methods = mthds;

                            Specialize (sb);

                            header = null;
                            declarations = null;
                            methods = null;

                            FlushTrace ();

                            return hdr.ToString () + "\n" + decls.ToString () + "\n" + mthds.ToString () + "\n" + sb.ToString ();
                        }
                    }
                }
            }
        }
        string CheckStructure(TypeDefinition structure, string descriptiveMethodName, MemberReference inMember)
        {
            string n;
            StringBuilder name = new StringBuilder ();
            var body = new AutoIndentStringBuilder (1);
            int size = 0;

            ProcessStructure (name, body, structure, ref size, descriptiveMethodName, structure, inMember);

            n = "struct trampoline_struct_" + name.ToString ();
            if (!structures.Contains (n)) {
                structures.Add (n);
                declarations.WriteLine ("{0} {{\n{1}}};", n, body.ToString ());
            }

            return n;
        }
        void Specialize(AutoIndentStringBuilder sb)
        {
            List<Exception> exceptions = new List<Exception> ();
            List<ObjCMember> skip = new List<ObjCMember> ();

            var map = new AutoIndentStringBuilder (1);
            var map_init = new AutoIndentStringBuilder ();

            var i = 0;

            map.AppendLine ("static MTClassMap __xamarin_class_map [] = {");
            if (string.IsNullOrEmpty (single_assembly)) {
                map_init.AppendLine ("void xamarin_create_classes () {");
            } else {
                map_init.AppendLine ("void xamarin_create_classes_{0} () {{", single_assembly.Replace ('.', '_').Replace ('-', '_'));
            }

            // Select the types that needs to be registered.
            var allTypes = new List<ObjCType> ();
            foreach (var @class in Types.Values) {
                if (!string.IsNullOrEmpty (single_assembly) && single_assembly != @class.Type.Module.Assembly.Name.Name)
                    continue;

            #if !MONOMAC
                var isPlatformType = IsPlatformType (@class.Type);

                if (isPlatformType && IsSimulatorOrDesktop && IsMetalType (@class))
                    continue; // Metal isn't supported in the simulator.
            #endif

                if (@class.IsFakeProtocol)
                    continue;

                allTypes.Add (@class);
            }

            // Move all the custom types to the end of the list, respecting
            // existing order (so that a derived type always comes after
            // its base type; the Types.Values has that property, and we
            // need to keep it that way).

            var mappedEnd = allTypes.Count;
            var counter = 0;
            while (counter < mappedEnd) {
                if (!IsPlatformType (allTypes [counter].Type)) {
                    var t = allTypes [counter];
                    allTypes.RemoveAt (counter);
                    allTypes.Add (t);
                    mappedEnd--;
                } else {
                    counter++;
                }
            }

            var customTypeCount = 0;
            foreach (var @class in allTypes) {
                var isPlatformType = IsPlatformType (@class.Type);

                skip.Clear ();

                if ([email protected] && [email protected] && [email protected]) {
                    if (!isPlatformType)
                        customTypeCount++;

                    CheckNamespace (@class, exceptions);
                    map.AppendLine ("{{\"{0}\", \"{1}\", NULL }},", @class.ExportedName, GetAssemblyQualifiedName (@class.Type));

                    bool use_dynamic;

                    if (@class.Type.Resolve ().Module.Assembly.Name.Name == PlatformAssembly) {
                        // we don't need to use the static ref to prevent the linker from removing (otherwise unreferenced) code for monotouch.dll types.
                        use_dynamic = true;
                        // be smarter: we don't need to use dynamic refs for types available in the lowest version (target deployment) we building for.
                        // We do need to use dynamic class lookup when the following conditions are all true:
                        // * The class is not available in the target deployment version.
                        // * The class is not in a weakly linked framework (for instance if an existing framework introduces a new class, we don't
                        //   weakly link the framework because it already exists in the target deployment version - but since the class doesn't, we
                        //   must use dynamic class lookup to determine if it's available or not.
                    } else {
                        use_dynamic = false;
                    }

                    switch (@class.ExportedName) {
                    case "EKObject":
                        // EKObject's class is a private symbol, so we can't link with it...
                        use_dynamic = true;
                        break;
                    }

                    string get_class;
                    if (use_dynamic) {
                        get_class = string.Format ("objc_getClass (\"{0}\")", @class.ExportedName);
                    } else {
                        get_class = string.Format ("[{0} class]", EncodeNonAsciiCharacters (@class.ExportedName));
                    }

                    map_init.AppendLine ("__xamarin_class_map [{1}].handle = {0};", get_class, i++);
                }

                if (@class.IsWrapper && isPlatformType)
                    continue;

                if (@class.Methods == null && isPlatformType && [email protected] && [email protected])
                    continue;

                if (@class.IsModel)
                    continue;

                CheckNamespace (@class, exceptions);
                if (@class.BaseType != null)
                    CheckNamespace (@class.BaseType, exceptions);

                var class_name = EncodeNonAsciiCharacters (@class.ExportedName);
                var is_protocol = @class.IsProtocol;
                if (@class.IsCategory) {
                    sb.Write ("@interface {0} ({1})", EncodeNonAsciiCharacters (@class.BaseType.ExportedName), @class.CategoryName);
                } else if (is_protocol) {
                    sb.Write ("@protocol ").Write (EncodeNonAsciiCharacters (@class.ProtocolName));
                } else {
                    sb.Write ("@interface {0} : {1}", class_name, EncodeNonAsciiCharacters (@class.SuperType.ExportedName));
                }
                bool any_protocols = false;
                ObjCType tp = @class;
                while (tp != null && tp != tp.BaseType) {
                    if (tp.IsWrapper)
                        break; // no need to declare protocols for wrapper types, they do it already in their headers.
                    if (tp.Protocols != null) {
                        for (int p = 0; p < tp.Protocols.Length; p++) {
                            if (tp.Protocols [p].ProtocolName == "UIAppearance")
                                continue;
                            sb.Append (any_protocols ? ", " : "<");
                            any_protocols = true;
                            sb.Append (tp.Protocols [p].ProtocolName);
                            CheckNamespace (tp.Protocols [p], exceptions);
                        }
                    }
                    tp = tp.BaseType;
                }
                if (any_protocols)
                    sb.Append (">");

                if (is_protocol) {
                    sb.WriteLine ();
                } else {
                    sb.WriteLine (" {");

                    if (@class.Fields != null) {
                        foreach (var field in @class.Fields.Values) {
                            try {
                                switch (field.FieldType) {
                                case "@":
                                    sb.Write ("id ");
                                    break;
                                case "^v":
                                    sb.Write ("void *");
                                    break;
                                case "XamarinObject":
                                    sb.Write ("XamarinObject ");
                                    break;
                                default:
                                    throw ErrorHelper.CreateError (4120, "The registrar found an unknown field type '{0}' in field '{1}.{2}'. Please file a bug report at http://bugzilla.xamarin.com",
                                        field.FieldType, field.DeclaringType.Type.FullName, field.Name);
                                }
                                sb.Write (field.Name);
                                sb.WriteLine (";");
                            } catch (Exception ex) {
                                exceptions.Add (ex);
                            }
                        }
                    }
                    sb.WriteLine ("}");
                }

                sb.Indent ();
                if (@class.Properties != null) {
                    foreach (var property in @class.Properties) {
                        try {
                            if (is_protocol)
                                sb.Write (property.IsOptional ? "@optional " : "@required ");
                            sb.Write ("@property (nonatomic");
                            switch (property.ArgumentSemantic) {
                            case ArgumentSemantic.Copy:
                                sb.Write (", copy");
                                break;
                            case ArgumentSemantic.Retain:
                                sb.Write (", retain");
                                break;
                            case ArgumentSemantic.Assign:
                            case ArgumentSemantic.None:
                            default:
                                sb.Write (", assign");
                                break;
                            }
                            if (property.IsReadOnly)
                                sb.Write (", readonly");

                            if (property.Selector != null) {
                                if (property.GetterSelector != null && property.Selector != property.GetterSelector)
                                    sb.Write (", getter = ").Write (property.GetterSelector);
                                if (property.SetterSelector != null) {
                                    var setterSel = string.Format ("set{0}{1}:", char.ToUpperInvariant (property.Selector [0]), property.Selector.Substring (1));
                                    if (setterSel != property.SetterSelector)
                                        sb.Write (", setter = ").Write (property.SetterSelector);
                                }
                            }

                            sb.Write (") ");
                            try {
                                sb.Write (ToObjCParameterType (property.PropertyType, property.DeclaringType.Type.FullName, exceptions, property.Property));
                            } catch (ProductException mte) {
                                exceptions.Add (CreateException (4138, mte, property.Property, "The registrar cannot marshal the property type '{0}' of the property '{1}.{2}'.",
                                    GetTypeFullName (property.PropertyType), property.DeclaringType.Type.FullName, property.Name));
                            }
                            sb.Write (" ").Write (property.Selector);
                            sb.WriteLine (";");
                        } catch (Exception ex) {
                            exceptions.Add (ex);
                        }
                    }
                }

                if (@class.Methods != null) {
                    foreach (var method in @class.Methods) {
                        try {
                            if (is_protocol)
                                sb.Write (method.IsOptional ? "@optional " : "@required ");
                            sb.WriteLine ("{0};", GetObjCSignature (method, exceptions));
                        } catch (ProductException ex) {
                            skip.Add (method);
                            exceptions.Add (ex);
                        } catch (Exception ex) {
                            skip.Add (method);
                            exceptions.Add (ErrorHelper.CreateError (4114, ex, "Unexpected error in the registrar for the method '{0}.{1}' - Please file a bug report at http://bugzilla.xamarin.com", method.DeclaringType.Type.FullName, method.Method.Name));
                        }
                    }
                }
                sb.Unindent ();
                sb.WriteLine ("@end");

                if (!is_protocol && [email protected]) {
                    if (@class.IsCategory) {
                        sb.WriteLine ("@implementation {0} ({1})", EncodeNonAsciiCharacters (@class.BaseType.ExportedName), @class.CategoryName);
                    } else {
                        sb.WriteLine ("@implementation {0} {{ }} ", class_name);
                    }
                    sb.Indent ();
                    if (@class.Methods != null) {
                        foreach (var method in @class.Methods) {
                            if (skip.Contains (method))
                                continue;

                            try {
                                Specialize (sb, method, exceptions);
                            } catch (Exception ex) {
                                exceptions.Add (ex);
                            }
                        }
                    }
                    sb.Unindent ();
                    sb.WriteLine ("@end");
                }
                sb.WriteLine ();
            }

            map.AppendLine ("{ NULL, NULL, NULL },");
            map.AppendLine ("};");
            map.AppendLine ();

            map.AppendLine ("static const char *__xamarin_registration_assemblies []= {");
            int count = 0;
            var registered_assemblies = new List<string> ();
            if (string.IsNullOrEmpty (single_assembly)) {
                foreach (var assembly in GetAssemblies ())
                    registered_assemblies.Add (GetAssemblyName (assembly));
            } else {
                registered_assemblies.Add (single_assembly);
            }
            foreach (var assembly in registered_assemblies) {
                count++;
                if (count > 1)
                    map.AppendLine (", ");
                map.Append ("\"");
                map.Append (assembly);
                map.Append ("\"");
            }
            map.AppendLine ();
            map.AppendLine ("};");
            map.AppendLine ();

            map.AppendLine ("static struct MTRegistrationMap __xamarin_registration_map = {");
            map.AppendLine ("NULL,");
            map.AppendLine ("__xamarin_registration_assemblies,");
            map.AppendLine ("__xamarin_class_map,");
            map.AppendLine ("{0},", count);
            map.AppendLine ("{0},", i);
            map.AppendLine ("{0}", customTypeCount);
            map.AppendLine ("};");

            map_init.AppendLine ("xamarin_add_registration_map (&__xamarin_registration_map);");
            map_init.AppendLine ("}");

            sb.WriteLine (map.ToString ());
            sb.WriteLine (map_init.ToString ());

            if (exceptions.Count > 0)
                throw new AggregateException (exceptions);
        }
        public AutoIndentStringBuilder AppendLine(AutoIndentStringBuilder isb)
        {
            if (isb.Length > 0) {
                sb.Append (isb.ToString ());
                AppendLine ();
            }

            return this;
        }