Copyright ã1993, 1994 by Ralph Zazula  All Rights Reserved.

Mon Jun 27 00:45:04 1994

RZBundle

Inherits From:	Object

Defined In:	RZBundle.h

Class Description

A subclass of NXBundle that provides on-demand loading of bundle code as classes are needed.  Instances of RZBundle examine the files class.table, depend.table and bundle.table found within thier bundle directories.  These files are in NXStringTable format and have the following content:

	class.table		A list of all classes contained within the receivers loadable code
	depend.table		A list of all classes referenced but not contained within the receivers loadable code
	bundle.table		User-defined key/value pairs (see -valueForString:)
	
The first of these two files can be automatically generated when the bundle is installed (e.g., "make install") by adding the following (slightly scary text) to the bottom of the bundle project Makefile.postamble file:

after_install::
	@($(set_bundle_ext) ; \
	echo "Creating: 'class.table' - class content file..."; \
	nm -o $(DSTROOT)$(INSTALLDIR)$(THING_TO_STRIP) | \
		grep "A .objc_class_name" | awk '{printf "%s %s\n",$$2,$$3}' | \
		sed s/"A .objc_class_name_"// | awk '{printf "\"%s\";\n",$$1}' > \
		$(DSTROOT)$(INSTALLDIR)/$(REL_BUNDLE_DIR:.bproj=).$$bundle_ext/class.table; \
	echo "Creating: 'depend.table' - class dependancies file..."; \
	nm -o $(DSTROOT)$(INSTALLDIR)$(THING_TO_STRIP) | \
		grep "U .objc_class_name" | awk '{printf "%s %s\n",$$2,$$3}' | \
		sed s/"U .objc_class_name_"// | awk '{printf "\"%s\";\n",$$1}' > \
		$(DSTROOT)$(INSTALLDIR)/$(REL_BUNDLE_DIR:.bproj=).$$bundle_ext/depend.table; \
	echo "Adding dynload macros to headers..."; \
	for i in $(CLASSES:.m=.h) none; do \
		if [ $$i = none ] ; then continue; fi; \
		echo $$i;\
		cat $$i | awk '{if("@interface"==$$1) { if(index($$2, ":")) \
		cName=substr($$2, 1, index($$2,":")-1); else cName=$$2; \
		printf "\n#define _%s objc_getClass(\"%s\")\n",cName,cName;}}' >> \
		$(H_INSTALLDIR)/$$i;\
	done)

The bundle.table file must be created manually and added to Other Resources in Project Builder.

This after_install script also adds a macro to the headers for each class in the bundle when they are installed.  This macro has the form:

	#define _MyClass objc_getClass("MyClass")

EXAMPLE USAGE

- appDidInit:sender
{
	char path[MAXPATHLEN+1];
	id list;
	id myInstance;
	
	/* Install dynaloading function and check standard directories for bundles */
	[RZBundle setup];
	
	/* Add bundles in a non-standard directory */
	[RZBundle addBundlesInPath:"/LocalLibrary/MyProject/OtherBundles"];
	
	/* ... */
	
	/* any of these methods will work to get an instance of "MyClass" */
	myInstance = [[@(MyClass) alloc] init];
	//myInstance = [[_MyClass alloc] init];
	//myInstance = [[objc_getClass("MyClass") alloc] init];
	//myInstance = [[[RZBundle classNamed:"MyClass"] alloc] init]
	
	/* ... */
	
	/* search for bundles that have the key-value pair "BundleType = MyTool;" */
	list = [RZBundle bundlesWithValue:"MyTool" forString:"BundleType"];
	if(list) {
		/* "list" now contains all of the Tool bundles... 
		 * might want to get some information about them...
		 */
		list = [list free];
	}
	
	/* ... */
	return self;
}

Method Types
	+ addBundlesInPath:
	+ addBundlesInPath:withExtension:
	+ bundleContainingClass:
	+ bundlesWithValue:forString:
	+ classNamed:
	+ doPreloadsFor:
	+ setup
	± classNamed:
	± classTable
	± containsClass:
	± dependTable
	± doPreloads
	± infoTable
	± principalClass
	± valueForString:

Class Methods

addBundlesInPath:
+ addBundlesInPath: (const char *) path

Creates RZBundle instances for all files FOO.bundle in the folder path. 

addBundlesInPath:withExtension:
+ addBundlesInPath: (const char *) path
	withExtension: (const char *) extension

 Creates RZBundle instances for all files FOO.extension in the folder path. 

bundleContainingClass:
+  (RZBundle *) bundleContainingClass: (const char *) className

Returns the first bundle found containing the class className or nil if no bundles currently initialized contain the class className. 

bundlesWithValue:forString:
+  (List *) bundlesWithValue: (const char *) value
	forString: (const char *) key

Returns a List of RZBundle instances whose bundle.table file contain the key/value pair: key = value. Returns nil if no bundles contain the key/value pair. The List should be freed by the caller. 

classNamed:
+ classNamed: (const char *) className

Searches all initialized bundles for one containing className. If a bundle is found, the receiver performs any preloading required and then returns the class for className. Returns Nil if the class className cannot be found, or if the preloading could not be performed. 

doPreloadsFor:
+  (BOOL) doPreloadsFor: (RZBundle *) bundle

Attempts to load any bundles containing classes required to load bundle. Returns YES on success (all external classes found) and NO on failure. Upon success, the code for bundle and the code of any required bundles will be loaded. Upon failure, the code for bundle will not be loaded but some or all of the prerequisite bundles may be loaded. 

setup
+ setup

Installs the class lookup error handling function for automatic bundle loading.  This method also registers bundles found in the following directories:

 		~/Library/Bundles
 		~/Developer/Bundles
 		/LocalLibrary/Bundles
 		/LocalDeveloper/Bundles
 		<Path to Executable>/<AppName>.app
 		<Path to Executable>/<AppName>.app/Bundles
 		/NextLibrary/Bundles
 		/NextDeveloper/Bundles
		
When looking up classes, they will be found in the first bundle that was registered with the given name.  E.g., a class "Foo" that appears in a bundle in ~/Library/Bundles will be found before a similarly named class found in any other bundle.

Instance Methods

classNamed:
± classNamed: (const char *) className

Overriden to perform dependency checking and bundle pre-loading before loading the code for the receiving bundle. 

classTable
±  (NXStringTable *) classTable

Returns the NXStringTable instance representing the class.table file for the reciever. This table has the names of the classes defined in the receiver. 

containsClass:
±  (BOOL) containsClass: (const char *) name

Returns YES if this bundle contains the class named name. This information is based on the contents of the file class.table in the receivers bundle. 

dependTable
±  (NXStringTable *) dependTable

Returns the NXStringTable instance representing the depend.table file for the reciever. This table has the names of the external classes required by the receiver. The class list may contain classes already present in the runtime system (i.e., it is not pruned against objc_lookUpClass()). 

doPreloads
±  (BOOL) doPreloads

Checks for any external class dependancies and tries to resolve them. Returns YES on success and NO on failure. Upon success, the recivers code and the code of any required bundles will be loaded. Upon failure, the receivers code will not be loaded but some or all of the prerequisite bundles may be loaded. 

infoTable
±  (NXStringTable *) infoTable

Returns the NXStringTable instance representing the bundle.table file for the reciever. This table has the user-defined key/value pairs. 

principalClass
± principalClass

Overriden to perform dependency checking and bundle pre-loading before loading the code for the receiving bundle. 

valueForString:
±  (const char *) valueForString: (const char *) s

Returns the string value for keyword s found in the resource bundle.table. Returns NULL if the keyword is not found.