aboutsummaryrefslogtreecommitdiff
path: root/Foundation
diff options
context:
space:
mode:
authorGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-08-21 03:02:17 +0000
committerGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-08-21 03:02:17 +0000
commit5a1a87dbfba0ad19a9bb9a8a3ea36496cc359da8 (patch)
tree4715a1d73c11681ec3c0be6be4cad4f58cb2f8ae /Foundation
parent7063d76a007fbf636250d7199d6f24ec487163b1 (diff)
- Added GTMGetURLHandler class that gives you a very easy way of supporting
Get URL events just by adding a key to your plists, and adding a single method to your class. See GTMGetURLHandler.m for more details. - Added XcodeProject, AppleScript, and InterfaceBuilder Spotlight Plugins. Allows you to index .xcodeproj, .scpt, .scptd, .xib, .nib, and .aib files. See Readmes beside individual projects in SpotlightPlugins.
Diffstat (limited to 'Foundation')
-rw-r--r--Foundation/GTMGetURLHandler.m270
-rw-r--r--Foundation/GTMStackTrace.m4
2 files changed, 273 insertions, 1 deletions
diff --git a/Foundation/GTMGetURLHandler.m b/Foundation/GTMGetURLHandler.m
new file mode 100644
index 0000000..30d516f
--- /dev/null
+++ b/Foundation/GTMGetURLHandler.m
@@ -0,0 +1,270 @@
+//
+// GTMGetURLHandler.m
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// Add this class to your app to have get URL handled almost automatically for
+// you. For each entry in your CFBundleURLTypes dictionaries, add a new
+// key/object pair of GTMBundleURLClass/the name of the class you want
+// to have handle the scheme(s).
+// Then have that class respond to the class method:
+// + (BOOL)gtm_openURL:(NSURL*)url
+// and voila, it will just work.
+// Note that in Debug mode we will do extensive testing to make sure that this
+// is all hooked up correctly, and will spew out to the console if we
+// find anything amiss.
+//
+// Example plist entry
+// ...
+//
+// <key>CFBundleURLTypes</key>
+// <array>
+// <dict>
+// <key>CFBundleURLName</key>
+// <string>Google Suggestion URL</string>
+// <key>GTMBundleURLClass</key>
+// <string>GoogleSuggestURLHandler</string>
+// <key>CFBundleURLSchemes</key>
+// <array>
+// <string>googlesuggest</string>
+// <string>googlesuggestextreme</string>
+// </array>
+// </dict>
+// </array>
+//
+//
+// Example implementation
+// @interface GoogleSuggestURLHandler
+// @end
+// @implementation GoogleSuggestURLHandler
+// + (BOOL)gtm_openURL:(NSURL*)url {
+// NSLog(@"%@", url);
+// }
+// @end
+
+#import <Foundation/Foundation.h>
+#import "GTMGarbageCollection.h"
+#import "GTMNSAppleEventDescriptor+Foundation.h"
+#import "GTMMethodCheck.h"
+
+static NSString *const kGTMBundleURLClassKey = @"GTMBundleURLClass";
+// A variety of constants Apple really should have defined somewhere to
+// allow the compiler to find your typos.
+static NSString *const kGTMCFBundleURLSchemesKey = @"CFBundleURLSchemes";
+static NSString *const kGTMCFBundleURLNameKey = @"CFBundleURLName";
+static NSString *const kGTMCFBundleTypeRoleKey = @"CFBundleTypeRole";
+static NSString *const kGTMCFBundleURLTypesKey = @"CFBundleURLTypes";
+static NSString *const kGTMCFBundleViewerRole = @"Viewer";
+static NSString *const kGTMCFBundleEditorRole = @"Editor";
+
+// Set this macro elsewhere is you want to force the
+// bundle checks on/off. They are nice for debugging
+// problems, but shouldn't be required in a release version
+// unless you are paranoid about your users messing with your
+// Info.plist
+#ifndef GTM_CHECK_BUNDLE_URL_CLASSES
+#define GTM_CHECK_BUNDLE_URL_CLASSES DEBUG
+#endif // GTM_CHECK_BUNDLE_URL_CLASSES
+
+@protocol GTMGetURLHandlerProtocol
++ (BOOL)gtm_openURL:(NSURL*)url;
+@end
+
+@interface GTMGetURLHandler : NSObject {
+ NSArray *urlTypes_;
+}
+- (id)initWithTypes:(NSArray*)urlTypes;
+- (void)getUrl:(NSAppleEventDescriptor *)event
+withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
+- (void)addError:(OSStatus)error
+ withDescription:(NSString*)string
+ toDescriptor:(NSAppleEventDescriptor *)desc;
++ (id)handlerForBundle:(NSBundle *)bundle;
+@end
+
+@implementation GTMGetURLHandler
+GTM_METHOD_CHECK(NSNumber, gtm_appleEventDescriptor);
+GTM_METHOD_CHECK(NSString, gtm_appleEventDescriptor);
+
++ (void)load {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSBundle *bundle = [NSBundle mainBundle];
+ GTMGetURLHandler *handler = [GTMGetURLHandler handlerForBundle:bundle];
+ if (handler) {
+ [handler retain];
+ GTMNSMakeUncollectable(handler);
+ NSAppleEventManager *man = [NSAppleEventManager sharedAppleEventManager];
+ [man setEventHandler:handler
+ andSelector:@selector(getUrl:withReplyEvent:)
+ forEventClass:kInternetEventClass
+ andEventID:kAEGetURL];
+ }
+ [pool release];
+}
+
++ (id)handlerForBundle:(NSBundle *)bundle {
+ GTMGetURLHandler *handler = nil;
+ NSArray *urlTypes = [bundle objectForInfoDictionaryKey:kGTMCFBundleURLTypesKey];
+ if (urlTypes) {
+ handler = [[[GTMGetURLHandler alloc] initWithTypes:urlTypes] autorelease];
+ } else {
+ _GTMDevLog(@"If you don't have CFBundleURLTypes in your plist, you may want"
+ @" to remove GTMGetURLHandler.m from your project");
+ }
+ return handler;
+}
+
+- (id)initWithTypes:(NSArray*)urlTypes {
+ if ((self = [super init])) {
+ urlTypes_ = [urlTypes retain];
+#if GTM_CHECK_BUNDLE_URL_CLASSES
+ // Some debug handling to check to make sure we can handle the
+ // classes properly.
+ NSEnumerator *enumerator = [urlTypes_ objectEnumerator];
+ NSDictionary *urlType;
+ while ((urlType = [enumerator nextObject])) {
+ NSString *className = [urlType objectForKey:kGTMBundleURLClassKey];
+ if ([className length]) {
+ Class cls = NSClassFromString(className);
+ if (cls) {
+ if (![cls respondsToSelector:@selector(gtm_openURL:)]) {
+ _GTMDevLog(@"Class %@ for URL handler %@ "
+ "(URL schemes: %@) doesn't respond to openURL:",
+ className,
+ [urlType objectForKey:kGTMCFBundleURLNameKey],
+ [urlType objectForKey:kGTMCFBundleURLSchemesKey]);
+ }
+ } else {
+ _GTMDevLog(@"Unable to get class %@ for URL handler %@ "
+ "(URL schemes: %@)",
+ className,
+ [urlType objectForKey:kGTMCFBundleURLNameKey],
+ [urlType objectForKey:kGTMCFBundleURLSchemesKey]);
+ }
+ } else {
+ NSString *role = [urlType objectForKey:kGTMCFBundleTypeRoleKey];
+ if ([role caseInsensitiveCompare:kGTMCFBundleViewerRole] == NSOrderedSame ||
+ [role caseInsensitiveCompare:kGTMCFBundleEditorRole] == NSOrderedSame) {
+ _GTMDevLog(@"Missing %@ for URL handler %@ "
+ "(URL schemes: %@)",
+ kGTMBundleURLClassKey,
+ [urlType objectForKey:kGTMCFBundleURLNameKey],
+ [urlType objectForKey:kGTMCFBundleURLSchemesKey]);
+ }
+ }
+ }
+#endif // GTM_CHECK_BUNDLE_URL_CLASSES
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [urlTypes_ release];
+ [super dealloc];
+}
+
+- (NSURL*)extractURLFromEvent:(NSAppleEventDescriptor*)event
+ withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
+ NSAppleEventDescriptor *desc
+ = [event paramDescriptorForKeyword:keyDirectObject];
+ NSString *urlstring = [desc stringValue];
+ NSURL *url = [NSURL URLWithString:urlstring];
+ if (!url) {
+ [self addError:errAECoercionFail
+ withDescription:@"Unable to extract url from key direct object."
+ toDescriptor:replyEvent];
+ }
+ return url;
+}
+
+- (Class)getClassForScheme:(NSString *)scheme
+ withReplyEvent:(NSAppleEventDescriptor*)replyEvent {
+ NSEnumerator *typeEnumerator = [urlTypes_ objectEnumerator];
+ NSDictionary *urlType;
+ Class cls = nil;
+ NSString *typeScheme = nil;
+ while (!typeScheme && (urlType = [typeEnumerator nextObject])) {
+ NSArray *schemes = [urlType objectForKey:kGTMCFBundleURLSchemesKey];
+ NSEnumerator *schemeEnumerator = [schemes objectEnumerator];
+ while ((typeScheme = [schemeEnumerator nextObject])) {
+ if ([typeScheme caseInsensitiveCompare:scheme] == NSOrderedSame) {
+ break;
+ }
+ }
+ }
+ if (typeScheme) {
+ NSString *class = [urlType objectForKey:kGTMBundleURLClassKey];
+ if (class) {
+ cls = NSClassFromString(class);
+ }
+ if (!cls) {
+ NSString *errorString
+ = [NSString stringWithFormat:@"Unable to instantiate class for "
+ "%@:%@ for scheme:%@.",
+ kGTMBundleURLClassKey, class, typeScheme];
+ [self addError:errAECorruptData
+ withDescription:errorString
+ toDescriptor:replyEvent];
+ }
+ } else {
+ NSString *errorString
+ = [NSString stringWithFormat:@"Unable to find handler for scheme %@.",
+ scheme];
+ [self addError:errAECorruptData
+ withDescription:errorString
+ toDescriptor:replyEvent];
+ }
+ return cls;
+}
+
+- (void)getUrl:(NSAppleEventDescriptor *)event
+withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
+ NSURL *url = [self extractURLFromEvent:event withReplyEvent:replyEvent];
+ if (!url) {
+ return;
+ }
+ NSString *scheme = [url scheme];
+ Class cls = [self getClassForScheme:scheme withReplyEvent:replyEvent];
+ if (!cls) {
+ return;
+ }
+ BOOL wasGood = [cls gtm_openURL:url];
+ if (!wasGood) {
+ NSString *errorString
+ = [NSString stringWithFormat:@"[%@ gtm_openURL:] failed to handle %@",
+ NSStringFromClass(cls), url];
+ [self addError:errAEEventNotHandled
+ withDescription:errorString
+ toDescriptor:replyEvent];
+ }
+}
+
+- (void)addError:(OSStatus)error
+ withDescription:(NSString*)string
+ toDescriptor:(NSAppleEventDescriptor *)desc {
+ NSAppleEventDescriptor *errorDesc = nil;
+ if (error != noErr) {
+ NSNumber *errNum = [NSNumber numberWithLong:error];
+ errorDesc = [errNum gtm_appleEventDescriptor];
+ [desc setParamDescriptor:errorDesc forKeyword:keyErrorNumber];
+ }
+ if (string) {
+ errorDesc = [string gtm_appleEventDescriptor];
+ [desc setParamDescriptor:errorDesc forKeyword:keyErrorString];
+ }
+}
+@end
+
diff --git a/Foundation/GTMStackTrace.m b/Foundation/GTMStackTrace.m
index 5dc1c0b..a7198dc 100644
--- a/Foundation/GTMStackTrace.m
+++ b/Foundation/GTMStackTrace.m
@@ -45,7 +45,7 @@ struct GTMClassDescription {
static struct GTMClassDescription *GTMClassDescriptions(int *total_count) {
int class_count = objc_getClassList(nil, 0);
struct GTMClassDescription *class_descs
- = calloc(class_count, sizeof(struct GTMClassDescription));
+ = calloc(class_count, sizeof(struct GTMClassDescription));
if (class_descs) {
Class *classes = calloc(class_count, sizeof(Class));
if (classes) {
@@ -61,8 +61,10 @@ static struct GTMClassDescription *GTMClassDescriptions(int *total_count) {
}
free(classes);
} else {
+ // COV_NF_START - Don't know how to force this in a unittest
free(class_descs);
class_count = 0;
+ // COV_NF_END
}
}
if (total_count) {