/core/externals/google-toolbox-for-mac/Foundation/GTMNSAppleScript+Handler.m

http://macfuse.googlecode.com/ · Objective C · 663 lines · 551 code · 58 blank · 54 comment · 78 complexity · 32f2fe499731b5e509f750707015a243 MD5 · raw file

  1. //
  2. // GTMNSAppleScript+Handler.m
  3. //
  4. // Copyright 2008 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. #import <Carbon/Carbon.h>
  19. #import "GTMNSAppleScript+Handler.h"
  20. #import "GTMNSAppleEventDescriptor+Foundation.h"
  21. #import "GTMNSAppleEventDescriptor+Handler.h"
  22. #import "GTMFourCharCode.h"
  23. #import "GTMMethodCheck.h"
  24. #import "GTMDebugThreadValidation.h"
  25. // Keys for passing AppleScript calls from other threads to the main thread
  26. // and back through gtm_internalExecuteAppleEvent:
  27. static NSString *const GTMNSAppleScriptEventKey = @"GTMNSAppleScriptEvent";
  28. static NSString *const GTMNSAppleScriptResultKey = @"GTMNSAppleScriptResult";
  29. static NSString *const GTMNSAppleScriptErrorKey = @"GTMNSAppleScriptError";
  30. // Error keys that we may return in the error dictionary on top of the standard
  31. // NSAppleScriptError* keys.
  32. NSString const* GTMNSAppleScriptErrorPartialResult
  33. = @"GTMNSAppleScriptErrorPartialResult";
  34. NSString const* GTMNSAppleScriptErrorOffendingObject
  35. = @"GTMNSAppleScriptErrorOffendingObject";
  36. NSString const* GTMNSAppleScriptErrorExpectedType
  37. = @"GTMNSAppleScriptErrorExpectedType";
  38. // Some private methods that we need to call
  39. @interface NSAppleScript (NSPrivate)
  40. + (ComponentInstance)_defaultScriptingComponent;
  41. - (OSAID) _compiledScriptID;
  42. - (id)_initWithData:(NSData*)data error:(NSDictionary**)error;
  43. - (id)_initWithScriptIDNoCopy:(OSAID)osaID;
  44. @end
  45. @interface NSMethodSignature (NSPrivate)
  46. + (id)signatureWithObjCTypes:(const char *)fp8;
  47. @end
  48. // Our own private interfaces.
  49. @interface NSAppleScript (GTMAppleScriptHandlerAdditionsPrivate)
  50. // Return an descriptor for a property. Properties are only supposed to be
  51. // of type NSString or GTMFourCharCode. GTMFourCharCode's need special handling
  52. // as they must be turned into NSAppleEventDescriptors of typeProperty.
  53. - (NSAppleEventDescriptor*)gtm_descriptorForPropertyValue:(id)property;
  54. // Return an NSAppleEventDescriptor for a given property.
  55. // |property| must be kind of class GTMFourCharCode
  56. - (NSAppleEventDescriptor*)gtm_valueDescriptorForProperty:(id)property;
  57. // Utility routine for extracting multiple values in scripts and their
  58. // parents.
  59. - (NSSet*)gtm_allValuesUsingSelector:(SEL)selector;
  60. // Utility routine for extracting the handlers for a specific script without
  61. // referring to parent scripts.
  62. - (NSSet*)gtm_scriptHandlers;
  63. // Utility routine for extracting the properties for a specific script without
  64. // referring to parent scripts.
  65. - (NSSet*)gtm_scriptProperties;
  66. // Handles creating an NSAppleEventDescriptor from an OSAID
  67. - (NSAppleEventDescriptor*)descForScriptID:(OSAID)scriptID
  68. component:(ComponentInstance)component;
  69. // Utility methods for converting between real and generic OSAIDs.
  70. - (OSAID)gtm_genericID:(OSAID)osaID forComponent:(ComponentInstance)component;
  71. - (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component;
  72. - (void)gtm_internalExecuteAppleEvent:(NSMutableDictionary *)data;
  73. - (NSDictionary *)gtm_errorDictionaryFromOSStatus:(OSStatus)status
  74. component:(ComponentInstance)component;
  75. @end
  76. // Private methods for dealing with Scripts/Events and NSAppleEventDescriptors
  77. @interface NSAppleEventDescriptor (GTMAppleEventDescriptorScriptAdditions)
  78. // Return an NSAppleScript for a desc of typeScript. This will create a new
  79. // Applescript that is a copy of the script that you want.
  80. // Returns nil on failure.
  81. - (NSAppleScript*)gtm_scriptValue;
  82. // Return an NSAppleScript for a desc of typeGTMOSAID. This will not copy the
  83. // script, but will create an NSAppleScript wrapping the script represented
  84. // by the OSAID.
  85. // Returns nil on failure.
  86. - (NSAppleScript*)gtm_osaIDValue;
  87. // Return a NSString with [eventClass][eventID] for typeEvent 'evnt'
  88. - (NSString*)gtm_eventValue;
  89. @end
  90. @implementation NSAppleScript(GTMAppleScriptHandlerAdditions)
  91. GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithPositionalHandler:parametersArray:);
  92. GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_descriptorWithLabeledHandler:labels:parameters:count:);
  93. GTM_METHOD_CHECK(NSAppleEventDescriptor, gtm_registerSelector:forTypes:count:);
  94. + (void)load {
  95. DescType types[] = {
  96. typeScript
  97. };
  98. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  99. [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_scriptValue)
  100. forTypes:types
  101. count:sizeof(types)/sizeof(DescType)];
  102. DescType types2[] = {
  103. 'evnt' // No type code for this one
  104. };
  105. [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_eventValue)
  106. forTypes:types2
  107. count:sizeof(types2)/sizeof(DescType)];
  108. DescType types3[] = {
  109. typeGTMOSAID
  110. };
  111. [NSAppleEventDescriptor gtm_registerSelector:@selector(gtm_osaIDValue)
  112. forTypes:types3
  113. count:sizeof(types3)/sizeof(DescType)];
  114. [pool drain];
  115. }
  116. - (NSAppleEventDescriptor *)gtm_executeAppleEvent:(NSAppleEventDescriptor *)event
  117. error:(NSDictionary **)error {
  118. NSMutableDictionary *data = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  119. event, GTMNSAppleScriptEventKey, nil];
  120. [self performSelectorOnMainThread:@selector(gtm_internalExecuteAppleEvent:)
  121. withObject:data
  122. waitUntilDone:YES];
  123. if (error) {
  124. *error = [data objectForKey:GTMNSAppleScriptErrorKey];
  125. }
  126. return [data objectForKey:GTMNSAppleScriptResultKey];
  127. }
  128. - (NSAppleEventDescriptor*)gtm_executePositionalHandler:(NSString*)handler
  129. parameters:(NSArray*)params
  130. error:(NSDictionary**)error {
  131. NSAppleEventDescriptor *event
  132. = [NSAppleEventDescriptor gtm_descriptorWithPositionalHandler:handler
  133. parametersArray:params];
  134. return [self gtm_executeAppleEvent:event error:error];
  135. }
  136. - (NSAppleEventDescriptor*)gtm_executeLabeledHandler:(NSString*)handler
  137. labels:(AEKeyword*)labels
  138. parameters:(id*)params
  139. count:(NSUInteger)count
  140. error:(NSDictionary **)error {
  141. NSAppleEventDescriptor *event
  142. = [NSAppleEventDescriptor gtm_descriptorWithLabeledHandler:handler
  143. labels:labels
  144. parameters:params
  145. count:count];
  146. return [self gtm_executeAppleEvent:event error:error];
  147. }
  148. - (NSSet*)gtm_handlers {
  149. return [self gtm_allValuesUsingSelector:@selector(gtm_scriptHandlers)];
  150. }
  151. - (NSSet*)gtm_properties {
  152. return [self gtm_allValuesUsingSelector:@selector(gtm_scriptProperties)];
  153. }
  154. // Set a value for a property by type (eg pASTopLevelScript)
  155. - (BOOL)gtm_setValue:(id)value
  156. forPropertyEnum:(DescType)property
  157. addingDefinition:(BOOL)adding {
  158. GTMFourCharCode *fcc
  159. = [GTMFourCharCode fourCharCodeWithFourCharCode:property];
  160. return [self gtm_setValue:value forProperty:fcc addingDefinition:adding];
  161. }
  162. - (BOOL)gtm_setValue:(id)value
  163. forProperty:(id)property
  164. addingDefinition:(BOOL)adding{
  165. OSAError error = paramErr;
  166. BOOL wasGood = NO;
  167. NSAppleEventDescriptor *propertyName
  168. = [self gtm_descriptorForPropertyValue:property];
  169. NSAppleEventDescriptor *desc = [value gtm_appleEventDescriptor];
  170. if (propertyName && desc) {
  171. NSAppleScript *script = self;
  172. OSAID valueID = kOSANullScript;
  173. ComponentInstance component = NULL;
  174. OSAID scriptID = [script gtm_realIDAndComponent:&component];
  175. error = OSACoerceFromDesc(component,
  176. [desc aeDesc],
  177. kOSAModeNull,
  178. &valueID);
  179. if (error == noErr) {
  180. error = OSASetProperty(component,
  181. adding ? kOSAModeNull : kOSAModeDontDefine,
  182. scriptID,
  183. [propertyName aeDesc],
  184. valueID);
  185. if (error == noErr) {
  186. wasGood = YES;
  187. }
  188. }
  189. }
  190. if (!wasGood) {
  191. _GTMDevLog(@"Unable to setValue:%@ forProperty:%@ from %@ (%d)",
  192. value, property, self, (int)error);
  193. }
  194. return wasGood;
  195. }
  196. - (id)gtm_valueForProperty:(id)property {
  197. return [[self gtm_valueDescriptorForProperty:property] gtm_objectValue];
  198. }
  199. - (id)gtm_valueForPropertyEnum:(DescType)property {
  200. GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:property];
  201. return [self gtm_valueForProperty:fcc];
  202. }
  203. - (NSAppleEventDescriptor*)gtm_appleEventDescriptor {
  204. ComponentInstance component;
  205. OSAID osaID = [self gtm_realIDAndComponent:&component];
  206. AEDesc result = { typeNull, NULL };
  207. NSAppleEventDescriptor *desc = nil;
  208. OSAError error = OSACoerceToDesc(component,
  209. osaID,
  210. typeScript,
  211. kOSAModeNull,
  212. &result);
  213. if (error == noErr) {
  214. desc = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&result]
  215. autorelease];
  216. } else {
  217. _GTMDevLog(@"Unable to coerce script %ld", (long)error);
  218. }
  219. return desc;
  220. }
  221. - (BOOL)gtm_hasOpenDocumentsHandler {
  222. ComponentInstance component = NULL;
  223. OSAID osaID = [self gtm_realIDAndComponent:&component];
  224. long value = 0;
  225. OSAError error = OSAGetScriptInfo(component,
  226. osaID,
  227. kASHasOpenHandler,
  228. &value);
  229. if (error) {
  230. _GTMDevLog(@"Unable to get script info about open handler %ld", (long)error);
  231. value = 0;
  232. }
  233. return value != 0;
  234. }
  235. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
  236. NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
  237. if (!signature) {
  238. NSMutableString *types = [NSMutableString stringWithString:@"@@:"];
  239. NSString *selName = NSStringFromSelector(aSelector);
  240. NSArray *selArray = [selName componentsSeparatedByString:@":"];
  241. NSUInteger count = [selArray count];
  242. for (NSUInteger i = 1; i < count; i++) {
  243. [types appendString:@"@"];
  244. }
  245. signature = [NSMethodSignature signatureWithObjCTypes:[types UTF8String]];
  246. }
  247. return signature;
  248. }
  249. - (void)forwardInvocation:(NSInvocation *)invocation {
  250. SEL sel = [invocation selector];
  251. NSMutableString *handlerName
  252. = [NSMutableString stringWithString:NSStringFromSelector(sel)];
  253. NSUInteger handlerOrigLength = [handlerName length];
  254. [handlerName replaceOccurrencesOfString:@":"
  255. withString:@""
  256. options:0
  257. range:NSMakeRange(0,handlerOrigLength)];
  258. NSUInteger argCount = handlerOrigLength - [handlerName length];
  259. NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
  260. for (NSUInteger i = 0; i < argCount; ++i) {
  261. id arg;
  262. // +2 to ignore _sel and _cmd
  263. [invocation getArgument:&arg atIndex:i + 2];
  264. [args addObject:arg];
  265. }
  266. NSDictionary *error = nil;
  267. NSAppleEventDescriptor *desc = [self gtm_executePositionalHandler:handlerName
  268. parameters:args
  269. error:&error];
  270. if ([[invocation methodSignature] methodReturnLength] > 0) {
  271. id returnValue = [desc gtm_objectValue];
  272. [invocation setReturnValue:&returnValue];
  273. }
  274. }
  275. @end
  276. @implementation NSAppleScript (GTMAppleScriptHandlerAdditionsPrivate)
  277. - (NSAppleEventDescriptor*)gtm_descriptorForPropertyValue:(id)property {
  278. NSAppleEventDescriptor *propDesc = nil;
  279. if ([property isKindOfClass:[GTMFourCharCode class]]) {
  280. propDesc = [property gtm_appleEventDescriptorOfType:typeProperty];
  281. } else if ([property isKindOfClass:[NSString class]]) {
  282. propDesc = [property gtm_appleEventDescriptor];
  283. }
  284. return propDesc;
  285. }
  286. - (NSAppleEventDescriptor*)gtm_valueDescriptorForProperty:(id)property {
  287. GTMAssertRunningOnMainThread();
  288. OSAError error = paramErr;
  289. NSAppleEventDescriptor *desc = nil;
  290. NSAppleEventDescriptor *propertyName
  291. = [self gtm_descriptorForPropertyValue:property];
  292. if (propertyName) {
  293. ComponentInstance component = NULL;
  294. OSAID scriptID = [self gtm_realIDAndComponent:&component];
  295. OSAID valueID = kOSANullScript;
  296. error = OSAGetProperty(component,
  297. kOSAModeNull,
  298. scriptID,
  299. [propertyName aeDesc],
  300. &valueID);
  301. if (error == noErr) {
  302. desc = [self descForScriptID:valueID component:component];
  303. }
  304. }
  305. if (error) {
  306. _GTMDevLog(@"Unable to get valueForProperty:%@ from %@ (%d)",
  307. property, self, (int)error);
  308. }
  309. return desc;
  310. }
  311. - (NSSet*)gtm_allValuesUsingSelector:(SEL)selector {
  312. NSMutableSet *resultSet = [NSMutableSet set];
  313. NSAppleEventDescriptor *scriptDesc = [self gtm_appleEventDescriptor];
  314. NSMutableSet *scriptDescsWeveSeen = [NSMutableSet set];
  315. GTMFourCharCode *fcc = [GTMFourCharCode fourCharCodeWithFourCharCode:pASParent];
  316. Class appleScriptClass = [NSAppleScript class];
  317. while (scriptDesc) {
  318. NSAppleScript *script = [scriptDesc gtm_objectValue];
  319. if ([script isKindOfClass:appleScriptClass]) {
  320. NSData *data = [scriptDesc data];
  321. if (!data || [scriptDescsWeveSeen containsObject:data]) {
  322. break;
  323. } else {
  324. [scriptDescsWeveSeen addObject:data];
  325. }
  326. NSSet *newSet = [script performSelector:selector];
  327. [resultSet unionSet:newSet];
  328. scriptDesc = [script gtm_valueDescriptorForProperty:fcc];
  329. } else {
  330. break;
  331. }
  332. }
  333. return resultSet;
  334. }
  335. - (NSSet*)gtm_scriptHandlers {
  336. GTMAssertRunningOnMainThread();
  337. AEDescList names = { typeNull, NULL };
  338. NSArray *array = nil;
  339. ComponentInstance component = NULL;
  340. OSAID osaID = [self gtm_realIDAndComponent:&component];
  341. OSAError error = OSAGetHandlerNames(component, kOSAModeNull, osaID, &names);
  342. if (error == noErr) {
  343. NSAppleEventDescriptor *desc
  344. = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names]
  345. autorelease];
  346. array = [desc gtm_objectValue];
  347. }
  348. if (error != noErr) {
  349. _GTMDevLog(@"Error getting handlers: %d", (int)error); // COV_NF_LINE
  350. }
  351. return [NSSet setWithArray:array];
  352. }
  353. - (NSSet*)gtm_scriptProperties {
  354. GTMAssertRunningOnMainThread();
  355. AEDescList names = { typeNull, NULL };
  356. NSArray *array = nil;
  357. ComponentInstance component = NULL;
  358. OSAID osaID = [self gtm_realIDAndComponent:&component];
  359. OSAError error = OSAGetPropertyNames(component, kOSAModeNull, osaID, &names);
  360. if (error == noErr) {
  361. NSAppleEventDescriptor *desc
  362. = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy:&names]
  363. autorelease];
  364. array = [desc gtm_objectValue];
  365. }
  366. if (error != noErr) {
  367. _GTMDevLog(@"Error getting properties: %d", (int)error); // COV_NF_LINE
  368. }
  369. return [NSSet setWithArray:array];
  370. }
  371. - (OSAID)gtm_genericID:(OSAID)osaID forComponent:(ComponentInstance)component {
  372. GTMAssertRunningOnMainThread();
  373. ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent];
  374. OSAID exactID = osaID;
  375. OSAError error = OSARealToGenericID(genericComponent, &exactID, component);
  376. if (error != noErr) {
  377. _GTMDevLog(@"Unable to get real id script: %@ %d", self, (int)error); // COV_NF_LINE
  378. exactID = kOSANullScript; // COV_NF_LINE
  379. }
  380. return exactID;
  381. }
  382. - (NSAppleEventDescriptor*)descForScriptID:(OSAID)osaID
  383. component:(ComponentInstance)component {
  384. GTMAssertRunningOnMainThread();
  385. NSAppleEventDescriptor *desc = nil;
  386. // If we have a script, return a typeGTMOSAID, otherwise convert it to
  387. // it's default AEDesc using OSACoerceToDesc with typeWildCard.
  388. long value = 0;
  389. OSAError err = noErr;
  390. if (osaID == 0) {
  391. desc = [NSAppleEventDescriptor nullDescriptor];
  392. } else {
  393. err = OSAGetScriptInfo(component,
  394. osaID,
  395. kOSAScriptBestType,
  396. &value);
  397. if (err == noErr) {
  398. if (value == typeScript) {
  399. osaID = [self gtm_genericID:osaID forComponent:component];
  400. desc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeGTMOSAID
  401. bytes:&osaID
  402. length:sizeof(osaID)];
  403. } else {
  404. AEDesc aeDesc;
  405. err = OSACoerceToDesc(component,
  406. osaID,
  407. typeWildCard,
  408. kOSAModeNull,
  409. &aeDesc);
  410. if (err == noErr) {
  411. desc = [[[NSAppleEventDescriptor alloc]
  412. initWithAEDescNoCopy:&aeDesc] autorelease];
  413. }
  414. }
  415. }
  416. }
  417. if (err != noErr) {
  418. _GTMDevLog(@"Unable to create desc for id:%lu (%ld)", (unsigned long)osaID, (long)err); // COV_NF_LINE
  419. }
  420. return desc;
  421. }
  422. - (OSAID)gtm_realIDAndComponent:(ComponentInstance*)component {
  423. GTMAssertRunningOnMainThread();
  424. if (![self isCompiled]) {
  425. NSDictionary *error;
  426. if (![self compileAndReturnError:&error]) {
  427. _GTMDevLog(@"Unable to compile script: %@ %@", self, error);
  428. return kOSANullScript;
  429. }
  430. }
  431. OSAID genericID = [self _compiledScriptID];
  432. ComponentInstance genericComponent = [NSAppleScript _defaultScriptingComponent];
  433. OSAError error = OSAGenericToRealID(genericComponent, &genericID, component);
  434. if (error != noErr) {
  435. _GTMDevLog(@"Unable to get real id script: %@ %d", self, (int)error); // COV_NF_LINE
  436. genericID = kOSANullScript; // COV_NF_LINE
  437. }
  438. return genericID;
  439. }
  440. - (void)gtm_internalExecuteAppleEvent:(NSMutableDictionary *)data {
  441. GTMAssertRunningOnMainThread();
  442. NSDictionary *error = nil;
  443. if (![self isCompiled]) {
  444. [self compileAndReturnError:&error];
  445. }
  446. if (!error) {
  447. NSAppleEventDescriptor *desc = nil;
  448. NSAppleEventDescriptor *event = [data objectForKey:GTMNSAppleScriptEventKey];
  449. ComponentInstance component = NULL;
  450. OSAID scriptID = [self gtm_realIDAndComponent:&component];
  451. OSAID valueID;
  452. OSAError err = OSAExecuteEvent(component, [event aeDesc], scriptID,
  453. kOSAModeNull, &valueID);
  454. if (err == noErr) {
  455. // descForScriptID:component: is what sets this apart from the
  456. // standard executeAppleEvent:error: in that it handles
  457. // taking script results and turning them into AEDescs of typeGTMOSAID
  458. // instead of typeScript.
  459. desc = [self descForScriptID:valueID component:component];
  460. if (desc) {
  461. [data setObject:desc forKey:GTMNSAppleScriptResultKey];
  462. }
  463. } else {
  464. error = [self gtm_errorDictionaryFromOSStatus:err component:component];
  465. }
  466. }
  467. if (error) {
  468. [data setObject:error forKey:GTMNSAppleScriptErrorKey];
  469. }
  470. }
  471. - (NSDictionary *)gtm_errorDictionaryFromOSStatus:(OSStatus)status
  472. component:(ComponentInstance)component {
  473. NSMutableDictionary *error = nil;
  474. if (status == errOSAScriptError) {
  475. error = [NSMutableDictionary dictionary];
  476. struct {
  477. OSType selector;
  478. DescType desiredType;
  479. SEL extractor;
  480. id key;
  481. } errMap[] = {
  482. {
  483. kOSAErrorNumber,
  484. typeSInt16,
  485. @selector(gtm_numberValue),
  486. NSAppleScriptErrorNumber
  487. },
  488. {
  489. kOSAErrorMessage,
  490. typeText,
  491. @selector(stringValue),
  492. NSAppleScriptErrorMessage
  493. },
  494. {
  495. kOSAErrorBriefMessage,
  496. typeText,
  497. @selector(stringValue),
  498. NSAppleScriptErrorBriefMessage
  499. },
  500. { kOSAErrorApp,
  501. typeText,
  502. @selector(stringValue),
  503. NSAppleScriptErrorAppName
  504. },
  505. { kOSAErrorRange,
  506. typeOSAErrorRange,
  507. @selector(gtm_OSAErrorRangeValue),
  508. NSAppleScriptErrorRange
  509. },
  510. {
  511. kOSAErrorPartialResult,
  512. typeBest,
  513. @selector(gtm_objectValue),
  514. GTMNSAppleScriptErrorPartialResult
  515. },
  516. {
  517. kOSAErrorOffendingObject,
  518. typeBest,
  519. @selector(gtm_objectValue),
  520. GTMNSAppleScriptErrorOffendingObject
  521. },
  522. {
  523. kOSAErrorExpectedType,
  524. typeType,
  525. @selector(gtm_fourCharCodeValue),
  526. GTMNSAppleScriptErrorExpectedType
  527. },
  528. };
  529. for (size_t i = 0; i < sizeof(errMap) / sizeof(errMap[0]); ++i) {
  530. AEDesc errorResult = { typeNull, NULL };
  531. OSStatus err = OSAScriptError(component,
  532. errMap[i].selector,
  533. errMap[i].desiredType,
  534. &errorResult);
  535. if (err == noErr) {
  536. NSAppleEventDescriptor *desc = [[[NSAppleEventDescriptor alloc]
  537. initWithAEDescNoCopy:&errorResult] autorelease];
  538. id value = [desc performSelector:errMap[i].extractor];
  539. if (value) {
  540. [error setObject:value forKey:errMap[i].key];
  541. }
  542. }
  543. }
  544. } else if (status != noErr) {
  545. // Unknown error. Do our best to give the user something good.
  546. NSNumber *errNum = [NSNumber numberWithInt:status];
  547. error
  548. = [NSMutableDictionary dictionaryWithObject:errNum
  549. forKey:NSAppleScriptErrorNumber];
  550. NSString *briefMessage
  551. = [NSString stringWithUTF8String:GetMacOSStatusErrorString(status)];
  552. if (briefMessage) {
  553. [error setValue:briefMessage forKey:NSAppleScriptErrorBriefMessage];
  554. }
  555. NSString *message
  556. = [NSString stringWithUTF8String:GetMacOSStatusCommentString(status)];
  557. if (message) {
  558. [error setValue:message forKey:NSAppleScriptErrorMessage];
  559. }
  560. }
  561. return error;
  562. }
  563. @end
  564. @implementation NSAppleEventDescriptor (GMAppleEventDescriptorScriptAdditions)
  565. - (NSAppleScript*)gtm_scriptValue {
  566. NSDictionary *error;
  567. NSAppleScript *script = [[[NSAppleScript alloc] _initWithData:[self data]
  568. error:&error] autorelease];
  569. if (!script) {
  570. _GTMDevLog(@"Unable to create script: %@", error); // COV_NF_LINE
  571. }
  572. return script;
  573. }
  574. - (NSAppleScript*)gtm_osaIDValue {
  575. _GTMDevAssert([[self data] length] == sizeof(OSAID), nil);
  576. OSAID osaID = *(const OSAID*)[[self data] bytes];
  577. return [[[NSAppleScript alloc] _initWithScriptIDNoCopy:osaID] autorelease];
  578. }
  579. - (NSString*)gtm_eventValue {
  580. struct AEEventRecordStruct {
  581. AEEventClass eventClass;
  582. AEEventID eventID;
  583. };
  584. NSData *data = [self data];
  585. const struct AEEventRecordStruct *record
  586. = (const struct AEEventRecordStruct*)[data bytes];
  587. NSString *eClass = [GTMFourCharCode stringWithFourCharCode:record->eventClass];
  588. NSString *eID = [GTMFourCharCode stringWithFourCharCode:record->eventID];
  589. return [eClass stringByAppendingString:eID];
  590. }
  591. @end
  592. @implementation NSAppleEventDescriptor (GTMAppleEventDescriptorOSAAdditions)
  593. - (id)gtm_OSAErrorRangeValue {
  594. id value = nil;
  595. NSAppleEventDescriptor *start = [self descriptorForKeyword:keyOSASourceStart];
  596. if (start) {
  597. NSAppleEventDescriptor *end = [self descriptorForKeyword:keyOSASourceEnd];
  598. if (end) {
  599. NSRange range = NSMakeRange([start int32Value], [end int32Value]);
  600. value = [NSValue valueWithRange:range];
  601. }
  602. }
  603. return value;
  604. }
  605. @end