/*
    CategoryPoser.h
    ===============

    John Holdsworth
    Coldwave Programs Ltd.

    This software is in the public domain.

    This category contains a method to find the base class implementation
    of a method that has been overridden by a category. This is used to
    find the original implementation of the +new method of the Appliaction
    class.

*/

#import "CategoryPoser.h"

#import <objc/objc.h> // definition of what an Objective-C object is.
#import <objc/objc-class.h> // definition of what a class object is.

#import <stdio.h>

/*
    Confession...

    This class isn't as generic as it may appear. It will not be able to
    find the overloaded implementation for instances of subclasses of the
    overloaded class. It works for Class methods which if enough for now.
*/
 
@implementation Object(CategoryPoser)
/* category of Object that can find the base implementation of a method that has been overloaded by a category */

static id noImplementation( id object, SEL _cmd, ... )
/* this is what an method implementation actually looks like as a function */
{
    fprintf( stderr, "No implementation for method %s in class %s",
	SELNAME( _cmd ), [[object class] name] );

    return nil;
}

static IMP findOverloadedMethod( struct objc_object *object, SEL selector )
/* A slightly dubious function which searches through the linked list of method implementations for a class to find an implementation for a method that has been overloaded by a category method of the same name. */
{
    IMP categoryImplementation = [object methodFor:selector];
    Class hackToRemoveWarning = object; // necessary since 3.3 ug.
    Class classOrMetaClass = hackToRemoveWarning->isa;
    struct objc_method_list *methods = classOrMetaClass->methods;

    while ( methods ) {
	int c;

	/* index through methods for this implementation source */
	for( c = 0 ; c < methods->method_count ; c++  ) {
	    struct objc_method *method = &methods->method_list[c];
	    IMP methodImplementation = method->method_imp;
 
	    /* In Objective-C a selector is an NXUniqueString */
	    if( method->method_name == selector &&
			methodImplementation != categoryImplementation )
		return methodImplementation; /* overloaded implementation */
	}

	/* move onto next implementation source */
	methods = methods->method_next;
    }

    /* should not get here */
    return noImplementation;
}

+ performOverloadedMethod:(SEL)selector with:anObject;
/* find the original implementation of a class method and perform it */
{
    return findOverloadedMethod( self, selector )( self, selector, anObject );
}

+ performOverloadedMethod:(SEL)selector;
/* find the original implementation of a class method and perform it */
{
    return [self performOverloadedMethod:selector with:nil];
}

- performOverloadedMethod:(SEL)selector with:anObject;
/* find the original implementation of an instance method and perform it */
{
    return findOverloadedMethod( self, selector )( self, selector, anObject );
}

- performOverloadedMethod:(SEL)selector;
/* find the original implementation of an instance method and perform it */
{
    return [self performOverloadedMethod:selector with:nil];
}

@end
