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