PageRenderTime 27ms CodeModel.GetById 19ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 0ms

/Tools/nib2cib/NSClassSwapper.j

http://github.com/cacaodev/cappuccino
Unknown | 135 lines | 109 code | 26 blank | 0 comment | 0 complexity | a08117a0f6ee37689b88cc69662fb1e8 MD5 | raw file
  1/*
  2 * NSClassSwapper.j
  3 * nib2cib
  4 *
  5 * Created by Francisco Tolmasky
  6 * Copyright 2009, 280 North, Inc.
  7 *
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation; either
 11 * version 2.1 of the License, or (at your option) any later version.
 12 *
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 16 * Lesser General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 21 */
 22
 23@import <Foundation/CPObject.j>
 24@import <Foundation/CPString.j>
 25@import <AppKit/_CPCibClassSwapper.j>
 26
 27@class Nib2Cib
 28
 29@global CP_NSMapClassName
 30
 31var NSClassSwapperClassNames                = {},
 32    NSClassSwapperOriginalClassNames        = {};
 33
 34var _CPCibClassSwapperClassNameKey          = @"_CPCibClassSwapperClassNameKey",
 35    _CPCibClassSwapperOriginalClassNameKey  = @"_CPCibClassSwapperOriginalClassNameKey";
 36
 37@implementation NSClassSwapper : _CPCibClassSwapper
 38{
 39}
 40
 41+ (id)swapperClassForClassName:(CPString)aClassName originalClassName:(CPString)anOriginalClassName
 42{
 43    var swapperClassName = "$NSClassSwapper_" + aClassName + "_" + anOriginalClassName,
 44        swapperClass = objj_lookUpClass(swapperClassName);
 45
 46    if (!swapperClass)
 47    {
 48        // If this is a userland NS class, call its KVC methods directly
 49        var nsClass = nil;
 50
 51        if ([[[Nib2Cib sharedNib2Cib] userNSClasses] containsObject:aClassName])
 52            nsClass = objj_lookUpClass("NS_" + aClassName);
 53
 54        var originalClass = nsClass || objj_lookUpClass(anOriginalClassName);
 55
 56        swapperClass = objj_allocateClassPair(originalClass, swapperClassName);
 57
 58        objj_registerClassPair(swapperClass);
 59
 60        /*
 61            When calling userland KVC methods, they should think that the class is
 62            the NS class (not the swapper class) so that they are in their userland space,
 63            not in AppKit space. For example, this ensures that bundleForClass:[self class] will work correctly.
 64            We can accomplish this safely by changing the class of self temporarily and sending directly
 65            to self instead of to super. This swizzle is safe because NSClassSwapper and _CPCibClassSwapper
 66            do not add any ivars.
 67        */
 68
 69        class_addMethod(swapperClass, @selector(initWithCoder:), function(self, _cmd, aCoder)
 70        {
 71            if (nsClass)
 72            {
 73                // Switch to userland temporarily
 74                self.isa = nsClass;
 75                self = objj_msgSend(self, _cmd, aCoder);
 76                self.isa = swapperClass;
 77            }
 78            else
 79                self = objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder);
 80
 81            if (self)
 82            {
 83                var UID = [self UID];
 84
 85                NSClassSwapperClassNames[UID] = aClassName;
 86                NSClassSwapperOriginalClassNames[UID] = anOriginalClassName;
 87            }
 88
 89            return self;
 90        }, "");
 91
 92        class_addMethod(swapperClass, @selector(classForKeyedArchiver), function(self, _cmd)
 93        {
 94            return [_CPCibClassSwapper class];
 95        }, "");
 96
 97        class_addMethod(swapperClass, @selector(encodeWithCoder:), function(self, _cmd, aCoder)
 98        {
 99            if (nsClass)
100            {
101                // Switch to userland temporarily
102                self.isa = nsClass;
103                objj_msgSend(self, _cmd, aCoder);
104                self.isa = swapperClass;
105            }
106            else
107                objj_msgSendSuper({super_class:originalClass, receiver:self}, _cmd, aCoder);
108
109            // If this is a custom NS class, lookup its archiver class so that
110            // the correct class is swapped during unarchiving.
111            if (nsClass)
112            {
113                var classForArchiver = objj_msgSend(nsClass, "classForKeyedArchiver");
114
115                if (classForArchiver)
116                    aClassName = [classForArchiver className];
117            }
118
119            [aCoder encodeObject:aClassName forKey:_CPCibClassSwapperClassNameKey];
120            [aCoder encodeObject:CP_NSMapClassName(anOriginalClassName) forKey:_CPCibClassSwapperOriginalClassNameKey];
121        }, "");
122    }
123
124    return swapperClass;
125}
126
127+ (id)allocWithCoder:(CPCoder)aCoder
128{
129    var className = [aCoder decodeObjectForKey:@"NSClassName"],
130        originalClassName = [aCoder decodeObjectForKey:@"NSOriginalClassName"];
131
132    return [[self swapperClassForClassName:className originalClassName:originalClassName] alloc];
133}
134
135@end