// Copyright 1997-2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import <OmniFoundation/NSObject-OFExtensions.h>

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniFoundation/OpenStepExtensions.subproj/NSObject-OFExtensions.m,v 1.23 2000/11/11 16:42:22 wjs Exp $")

@implementation NSObject (OFExtensions)

// +initializeAllClasses is necessary because class_initialize() isn't thread safe.  That is, a given +initialize can be called simultaneously in several threads, which not only confuses it, but can also confuse the runtime.

+ (void)didLoad;
{
    NSString *processName;

    // Call +initializeAllClasses when the app is about to go multithreaded.  This gives IB fits, so don't do it in common utility applications.
    processName = [[NSProcessInfo processInfo] processName];
    if (![processName isEqualToString:@"InterfaceBuilder"] && ![processName isEqualToString:@"EOModeler"])
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initializeAllClasses) name:NSWillBecomeMultiThreadedNotification object:nil];
}


#define SKIP_CLASS_NAMED(aName) \
do { \
    Class aClass; \
 \
    aClass = NSClassFromString(aName); \
    if (aClass) \
        NSHashInsertKnownAbsent(skipClasssesHashTable, aClass); \
} while (0)

+ (void)initializeAllClasses;
{
    static NSHashTable *skipClasssesHashTable = NULL;
    int classCount = 0, newClassCount;
    Class *classes = NULL;
    Method initializeMethod;
    SEL initializeSelector = @selector(initialize);

    OMNI_POOL_START {

        if (!skipClasssesHashTable) {
            skipClasssesHashTable = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 20);
            SKIP_CLASS_NAMED(@"HTMLCheckBoxInspector");
            SKIP_CLASS_NAMED(@"HTMLPageToolbarController");
            SKIP_CLASS_NAMED(@"NMSScriptedClass");
            SKIP_CLASS_NAMED(@"NSDataLinkManager");
            SKIP_CLASS_NAMED(@"NSInvocationBuilder");
            SKIP_CLASS_NAMED(@"NSMovieView");
            SKIP_CLASS_NAMED(@"NSTabView");
            SKIP_CLASS_NAMED(@"OFForwardObject");
            SKIP_CLASS_NAMED(@"OFRetainableObject");
        }

        // Get the class list
        newClassCount = objc_getClassList(NULL, 0);
        while (classCount < newClassCount) {
            classCount = newClassCount;
            classes = realloc(classes, sizeof(Class) * classCount);
            newClassCount = objc_getClassList(classes, classCount);
        }
        // Now, use the class list; if NULL, there are no classes.  (And that would happen how?  Oh well, might as well code this safely...)
        if (classes != NULL) {
            unsigned int classIndex;
            
            // Loop over the gathered classes
            for (classIndex = 0; classIndex < classCount; classIndex++) {
                Class aClass;
                NSString *className;

                aClass = classes[classIndex];
                className = NSStringFromClass(aClass);
                if (CLS_GETINFO(aClass->isa, CLS_INITIALIZED) || NSHashGet(skipClasssesHashTable, aClass))
                    continue;
                if ([className hasPrefix:@"NSZombie"]) {
                    // Hangs in GDB under Mac OS X DP3.
                    SKIP_CLASS_NAMED(className);
                    continue;
                }
                initializeMethod = class_getClassMethod(aClass, initializeSelector);
                if (initializeMethod) {
                    NS_DURING {
                        // NSLog(@"Initializing class: %s", aClass->name);
                        [aClass class];
                    } NS_HANDLER {
                        fprintf(stderr, "Exception raised by +[%s class]: %s\n", aClass->name, [[localException reason] UTF8String]);
                    } NS_ENDHANDLER;
                }
            }
        }

        // Free the class list
        free(classes);

    } OMNI_POOL_END;
}

static BOOL implementsInstanceMethod(struct objc_class *class, SEL aSelector)
{
    struct objc_method_list *methodList;
    int methodIndex;

    /* Check only this class, NOT any superclasses. */

#if defined(OBJC_NEXT_METHOD_LIST)
    void *iterator = 0;
    while ((methodList = class_nextMethodList(class, &iterator))) {
#else
    for (methodList = class->methods; methodList; methodList = methodList->method_next) {
#endif
        for (methodIndex = 0; methodIndex < methodList->method_count; methodIndex++) {
            if (methodList->method_list[methodIndex].method_name == aSelector)
                return YES;
        }
    }
    return NO;
}

+ (Class)classImplementingSelector:(SEL)aSelector;
{
    Class aClass = self;

    while (aClass) {
        if (implementsInstanceMethod(aClass, aSelector))
            return aClass;
        aClass = aClass->super_class;
    }

    return Nil;
}

+ (NSBundle *)bundle;
{
    return [NSBundle bundleForClass:[self class]];
}

- (NSBundle *)bundle;
{
    return [isa bundle];
}

@end

