PageRenderTime 25ms CodeModel.GetById 2ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 1ms

/Foundation/CPObject.j

http://github.com/cacaodev/cappuccino
Unknown | 665 lines | 565 code | 100 blank | 0 comment | 0 complexity | 40a10c2a328f85979caef19bc4d25f51 MD5 | raw file
  1/*
  2 * CPObject.j
  3 * Foundation
  4 *
  5 * Created by Francisco Tolmasky.
  6 * Copyright 2008, 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/*!
 24    @class CPObject
 25    @ingroup foundation
 26    @brief The root class from which most classes are subclassed.
 27
 28    CPObject is the root class for most Cappuccino classes. Like in Objective-C,
 29    you have to declare parent class explicitly in Objective-J, so your custom
 30    classes should almost always subclass CPObject or one of its children.
 31
 32    CPObject provides facilities for class allocation and initialization,
 33    querying runtime about parent classes and available selectors, using KVC
 34    (key-value coding).
 35
 36    When you subclass CPObject, most of the time you override one selector - init.
 37    It is called for default initialization of custom object. You must call
 38    parent class init in your overridden code:
 39    <pre>- (id)init
 40{
 41    self = [super init];
 42    if (self) {
 43        ... provide default initialization code for your object ...
 44    }
 45    return self;
 46}</pre>
 47
 48    One more useful thing to override is description(). This selector
 49    is used to provide developer-readable information about object. description
 50    selector is often used with CPLog debugging:
 51    <pre>- (CPString)description
 52{
 53    return [CPString stringWithFormat:@"<SomeClass %d>", someValue];
 54}</pre>
 55    To get description value you can use %@ specifier everywhere where format
 56    specifiers are allowed:
 57    <pre>var inst = [[SomeClass alloc] initWithSomeValue:10];
 58CPLog(@"Got some class: %@", inst);
 59    would output:
 60    \c Got \c some \c class: \c <SomeClass \c 10>
 61
 62    @todo document KVC usage.
 63*/
 64
 65@import "_CPTypeDefinitions.j"
 66
 67@class CPString
 68@class CPException
 69
 70@global CPInvalidArgumentException
 71
 72
 73@protocol CPObject
 74
 75- (BOOL)isEqual:(id)object;
 76- (CPUInteger)hash;
 77
 78- (Class)superclass;
 79- (Class)class;
 80- (id)self;
 81
 82- (id)performSelector:(SEL)aSelector;
 83- (id)performSelector:(SEL)aSelector withObject:(id)object;
 84- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
 85
 86- (BOOL)isProxy;
 87
 88- (BOOL)isKindOfClass:(Class)aClass;
 89- (BOOL)isMemberOfClass:(Class)aClass;
 90- (BOOL)conformsToProtocol:(Protocol)aProtocol;
 91
 92- (BOOL)respondsToSelector:(SEL)aSelector;
 93
 94- (CPString)description;
 95@optional
 96- (CPString)debugDescription;
 97
 98@end
 99
100@protocol CPCoding
101
102- (void)encodeWithCoder:(CPCoder)aCoder;
103- (id)initWithCoder:(CPCoder)aDecoder;
104
105@end
106
107@implementation CPObject <CPObject>
108{
109    id   isa;
110}
111
112+ (void)load
113{
114}
115
116+ (void)initialize
117{
118//    CPLog("calling initialize "+self.name);
119}
120
121/*!
122    Allocates a new instance of the receiver, and sends it an \c -init
123    @return the new object
124*/
125+ (id)new
126{
127    return [[self alloc] init];
128}
129
130/*!
131    Allocates a new instance of the receiving class
132*/
133+ (id)alloc
134{
135//    CPLog("calling alloc on " + self.name + ".");
136    return class_createInstance(self);
137}
138
139+ (id)allocWithCoder:(CPCoder)aCoder
140{
141    return [self alloc];
142}
143
144/*!
145    Initializes the receiver
146    @return the initialized receiver
147*/
148- (id)init
149{
150    return self;
151}
152
153/*!
154    Makes a deep copy of the receiver. The copy should be functionally equivalent to the receiver.
155    @return the copy of the receiver
156*/
157- (id)copy
158{
159    return self;
160}
161
162/*!
163    Creates a deep mutable copy of the receiver.
164    @return the mutable copy of the receiver
165*/
166- (id)mutableCopy
167{
168    return [self copy];
169}
170
171/*!
172    Not necessary to call in Objective-J. Only exists for code compatibility.
173*/
174- (void)dealloc
175{
176}
177
178// Identifying classes
179/*!
180    Returns the Class object for this class definition.
181*/
182+ (Class)class
183{
184    return self;
185}
186
187/*!
188    Returns the receiver's Class
189*/
190- (Class)class
191{
192    return isa;
193}
194
195/*!
196    Returns the class object super class
197*/
198+ (Class)superclass
199{
200    return self.super_class;
201}
202
203/*!
204    Returns \c YES if the receiving class is a subclass of \c aClass.
205    @param aClass the class to test inheritance from
206*/
207+ (BOOL)isSubclassOfClass:(Class)aClass
208{
209    var theClass = self;
210
211    for (; theClass; theClass = theClass.super_class)
212        if (theClass === aClass)
213            return YES;
214
215    return NO;
216}
217
218/*!
219    Returns \c YES if the receiver is a \c aClass type, or a subtype of it.
220    @param aClass the class to test as the receiver's class or super class.
221*/
222- (BOOL)isKindOfClass:(Class)aClass
223{
224    return [isa isSubclassOfClass:aClass];
225}
226
227+ (BOOL)isKindOfClass:(Class)aClass
228{
229    return [self isSubclassOfClass:aClass];
230}
231
232/*!
233    Returns \c YES if the receiver is of the \c aClass class type.
234    @param aClass the class to test the receiver
235*/
236- (BOOL)isMemberOfClass:(Class)aClass
237{
238    return self.isa === aClass;
239}
240
241+ (BOOL)isMemberOfClass:(Class)aClass
242{
243    return self === aClass;
244}
245
246/*!
247    Determines whether the receiver's root object is a proxy.
248    @return \c YES if the root object is a proxy
249*/
250- (BOOL)isProxy
251{
252    return NO;
253}
254
255// Testing class functionality
256/*!
257    Test whether instances of this class respond to the provided selector.
258    @param aSelector the selector for which to test the class
259    @return \c YES if instances of the class respond to the selector
260*/
261+ (BOOL)instancesRespondToSelector:(SEL)aSelector
262{
263    return !!class_getInstanceMethod(self, aSelector);
264}
265
266/*!
267    Tests whether the receiver responds to the provided selector.
268    @param aSelector the selector for which to test the receiver
269    @return \c YES if the receiver responds to the selector
270*/
271- (BOOL)respondsToSelector:(SEL)aSelector
272{
273    // isa is isa.isa in class case.
274    return !!class_getInstanceMethod(isa, aSelector);
275}
276
277/*!
278    Tests whether the receiver implements to the provided selector regardless of inheritance.
279    @param aSelector the selector for which to test the receiver
280    @return \c YES if the receiver implements the selector
281*/
282- (BOOL)implementsSelector:(SEL)aSelector
283{
284    var methods = class_copyMethodList(isa),
285        count = methods.length;
286
287    while (count--)
288        if (method_getName(methods[count]) === aSelector)
289            return YES;
290
291    return NO;
292}
293
294/*!
295    Test whether instances of this class conforms to the provided protocol.
296    @param aProtocol the protocol for which to test the class
297    @return \c YES if instances of the class conforms to the protocol
298*/
299+ (BOOL)conformsToProtocol:(Protocol)aProtocol
300{
301    return class_conformsToProtocol(self, aProtocol);
302}
303
304/*!
305    Tests whether the receiver conforms to the provided protocol.
306    @param protocol the protocol for which to test the class
307    @return \c YES if instances of the class conforms to the protocol
308*/
309- (BOOL)conformsToProtocol:(Protocol)aProtocol
310{
311    return class_conformsToProtocol(isa, aProtocol);
312}
313
314// Obtaining method information
315
316/*!
317    Returns the implementation of the receiver's method for the provided selector.
318    @param aSelector the selector for the method to return
319    @return the method implementation ( a function )
320*/
321- (IMP)methodForSelector:(SEL)aSelector
322{
323    return class_getMethodImplementation(isa, aSelector);
324}
325
326/*!
327    Returns the implementation of the receiving class' method for the provided selector.
328    @param aSelector the selector for the class method to return
329    @return the method implementation ( a function )
330*/
331+ (IMP)instanceMethodForSelector:(SEL)aSelector
332{
333    return class_getMethodImplementation(self, aSelector);
334}
335
336/*!
337    Returns the method signature for the provided selector.
338    @param aSelector the selector for which to find the method signature
339    @return the selector's method signature
340*/
341- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
342{
343    // FIXME: We need to implement method signatures.
344    return nil;
345}
346
347// Describing objects
348/*!
349    Returns a human readable string describing the receiver
350*/
351- (CPString)description
352{
353    return "<" + class_getName(isa) + " 0x" + [CPString stringWithHash:[self UID]] + ">";
354}
355
356+ (CPString)description
357{
358    return class_getName(self.isa);
359}
360
361// Sending Messages
362/*!
363    Sends the specified message to the receiver.
364    @param aSelector the message to send
365    @return the return value of the message
366*/
367- (id)performSelector:(SEL)aSelector
368{
369    return self.isa.objj_msgSend0(self, aSelector);
370}
371
372/*!
373    Sends the specified message to the receiver, with one argument.
374    @param aSelector the message to send
375    @param anObject the message argument
376    @return the return value of the message
377*/
378- (id)performSelector:(SEL)aSelector withObject:(id)anObject
379{
380    return self.isa.objj_msgSend1(self, aSelector, anObject);
381}
382
383/*!
384    Sends the specified message to the receiver, with two arguments.
385    @param aSelector the message to send
386    @param anObject the first message argument
387    @param anotherObject the second message argument
388    @return the return value of the message
389*/
390- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
391{
392    return self.isa.objj_msgSend2(self, aSelector, anObject, anotherObject);
393}
394
395/*!
396    Sends the specified message to the reciever, with any number of arguments.
397    @param aSelector the message to send
398    @param anObject... comma seperated objects to pass to the selector
399    @return the return value of the message
400*/
401- (id)performSelector:(SEL)aSelector withObjects:(id)anObject, ...
402{
403    var params = [self, aSelector].concat(Array.prototype.slice.apply(arguments, [3]));
404    return objj_msgSend.apply(this, params);
405}
406
407- (id)forwardingTargetForSelector:(SEL)aSelector
408{
409    return nil;
410}
411
412// Forwarding Messages
413/*!
414    Subclasses can override this method to forward message to
415    other objects. Overwriting this method in conjunction with
416    \c -methodSignatureForSelector: allows the receiver to
417    forward messages for which it does not respond, to another object that does.
418*/
419- (void)forwardInvocation:(CPInvocation)anInvocation
420{
421    [self doesNotRecognizeSelector:[anInvocation selector]];
422}
423
424// Error Handling
425/*!
426    Called by the Objective-J runtime when an object can't respond to
427    a message. It's not recommended to call this method directly, unless
428    you need your class to not support a method that it has inherited from a super class.
429*/
430- (void)doesNotRecognizeSelector:(SEL)aSelector
431{
432    [CPException raise:CPInvalidArgumentException reason:
433        (class_isMetaClass(isa) ? "+" : "-") + " [" + [self className] + " " + aSelector + "] unrecognized selector sent to " +
434        (class_isMetaClass(isa) ? "class " + class_getName(isa) : "instance 0x" + [CPString stringWithHash:[self UID]])];
435}
436
437// Archiving
438/*!
439    Subclasses override this method to possibly substitute
440    the unarchived object with another. This would be
441    useful if your program utilizes a
442    <a href="http://en.wikipedia.org/wiki/Flyweight_pattern">flyweight pattern</a>.
443    The method is called by CPCoder.
444    @param aCoder the coder that contained the receiver's data
445*/
446- (id)awakeAfterUsingCoder:(CPCoder)aCoder
447{
448    return self;
449}
450
451/*!
452    Can be overridden by subclasses to substitute a different class to represent the receiver for keyed archiving.
453    @return the class to use. A \c nil means to ignore the method result.
454*/
455- (Class)classForKeyedArchiver
456{
457    return [self classForCoder];
458}
459
460/*!
461    Can be overridden by subclasses to substitute a different class to represent the receiver during coding.
462    @return the class to use for coding
463*/
464- (Class)classForCoder
465{
466    return [self class];
467}
468
469/*!
470    Can be overridden by subclasses to substitute another object during archiving.
471    @param anArchiver that archiver
472    @return the object to archive
473*/
474- (id)replacementObjectForArchiver:(CPArchiver)anArchiver
475{
476    return [self replacementObjectForCoder:anArchiver];
477}
478
479/*!
480    Can be overridden by subclasses to substitute another object during keyed archiving.
481    @param anArchive the keyed archiver
482    @return the object to archive
483*/
484- (id)replacementObjectForKeyedArchiver:(CPKeyedArchiver)anArchiver
485{
486    return [self replacementObjectForCoder:anArchiver];
487}
488
489/*!
490    Can be overridden by subclasses to substitute another object during coding.
491    @param aCoder the coder
492    @return the object to code
493*/
494- (id)replacementObjectForCoder:(CPCoder)aCoder
495{
496    return self;
497}
498
499/*!
500    Sets the class version number.
501    @param the new version number for the class
502*/
503+ (void)setVersion:(int)aVersion
504{
505    class_setVersion(self, aVersion);
506}
507
508/*!
509    Returns the class version number.
510*/
511+ (int)version
512{
513    return class_getVersion(self);
514}
515
516// Scripting (?)
517/*!
518    Returns the class name
519*/
520- (CPString)className
521{
522    // FIXME: Why doesn't this work in KVO???
523    // return class_getName([self class]);
524    return isa.name;
525}
526
527// Extras
528/*!
529    Does nothing.
530    @return the receiver
531*/
532- (id)autorelease
533{
534    return self;
535}
536
537/*!
538    Returns a hash for the object
539*/
540- (unsigned)hash
541{
542    return [self UID];
543}
544
545- (CPString)UID
546{
547    if (typeof self._UID === "undefined")
548        self._UID = objj_generateObjectUID();
549
550    return self._UID + "";
551}
552
553/*!
554    Determines if \c anObject is functionally equivalent to the receiver.
555    @return \c YES if \c anObject is functionally equivalent to the receiver.
556*/
557- (BOOL)isEqual:(id)anObject
558{
559    return self === anObject || [self UID] === [anObject UID];
560}
561
562/*!
563    Does nothing.
564    @return the receiver
565*/
566- (id)retain
567{
568    return self;
569}
570
571/*!
572    Does nothing.
573*/
574- (void)release
575{
576}
577
578/*!
579    Returns the receiver.
580*/
581- (id)self
582{
583    return self;
584}
585
586/*!
587    Returns the receiver's super class.
588*/
589- (Class)superclass
590{
591    return isa.super_class;
592}
593
594@end
595
596function CPDescriptionOfObject(anObject, maximumRecursionDepth)
597{
598    if (anObject === nil)
599        return "nil";
600
601    if (anObject === undefined)
602        return "undefined";
603
604    if (anObject === window)
605        return "window";
606
607    if (maximumRecursionDepth === 0)
608        return "...";
609
610    if (anObject.isa)
611    {
612        if ([anObject isKindOfClass:CPString])
613            return '@"' + [anObject description] + '"';
614
615        if ([anObject respondsToSelector:@selector(_descriptionWithMaximumDepth:)])
616            return [anObject _descriptionWithMaximumDepth:maximumRecursionDepth !== undefined ? maximumRecursionDepth - 1 : maximumRecursionDepth];
617
618        return [anObject description];
619    }
620
621    if (typeof(anObject) !== "object")
622        return String(anObject);
623
624    var properties = [],
625        desc;
626
627    for (var property in anObject)
628        if (anObject.hasOwnProperty(property))
629            properties.push(property);
630
631    properties.sort();
632
633    if (properties.length === 2 && anObject.hasOwnProperty("width") && anObject.hasOwnProperty("height"))
634        desc = [CPString stringWithFormat:@"CGSize: (%f, %f)", anObject.width, anObject.height];
635    else if (properties.length === 2 && anObject.hasOwnProperty("x") && anObject.hasOwnProperty("y"))
636        desc = [CPString stringWithFormat:@"CGPoint: (%f, %f)", anObject.x, anObject.y];
637    else if (properties.length === 2 && anObject.hasOwnProperty("origin") && anObject.hasOwnProperty("size"))
638        desc = [CPString stringWithFormat:@"CGRect: (%f, %f), (%f, %f)", anObject.origin.x, anObject.origin.y, anObject.size.width, anObject.size.height];
639    else if (properties.length === 4 && anObject.hasOwnProperty("top") && anObject.hasOwnProperty("right") && anObject.hasOwnProperty("bottom") && anObject.hasOwnProperty("left"))
640        desc = [CPString stringWithFormat:@"CGInset: { top:%f, right:%f, bottom:%f, left:%f }", anObject.top, anObject.right, anObject.bottom, anObject.left];
641    else
642    {
643        desc = "{";
644
645        for (var i = 0; i < properties.length; ++i)
646        {
647            if (i === 0)
648                desc += "\n";
649
650            var value = anObject[properties[i]],
651                valueDescription = CPDescriptionOfObject(value, maximumRecursionDepth !== undefined ? maximumRecursionDepth - 1 : maximumRecursionDepth).split("\n").join("\n    ");
652
653            desc += "    " + properties[i] + ": " + valueDescription;
654
655            if (i < properties.length - 1)
656                desc += ",\n";
657            else
658                desc += "\n";
659        }
660
661        desc += "}";
662    }
663
664    return desc;
665}