PageRenderTime 147ms CodeModel.GetById 12ms app.highlight 129ms RepoModel.GetById 1ms app.codeStats 1ms

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