libs-steptalk/Frameworks/StepTalk/STEnvironment.m
Stefan Urbanek 5c5c430b4a Changed from lib to framework
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/steptalk/trunk@17436 72102866-910b-0410-8b05-ffd578937521
2003-08-06 17:21:45 +00:00

617 lines
14 KiB
Objective-C

/**
STEnvironment
Scripting environment
Copyright (c) 2002 Free Software Foundation
Written by: Stefan Urbanek <urbanek@host.sk>
Date: 2000
This file is part of the StepTalk project.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
<title>STEnvironment class reference</title>
*/
#import <StepTalk/STEnvironment.h>
#import <StepTalk/STClassInfo.h>
#import <StepTalk/STEnvironmentDescription.h>
#import <StepTalk/STExterns.h>
#import <StepTalk/STFunctions.h>
#import <StepTalk/STBundleInfo.h>
#import <StepTalk/STObjCRuntime.h>
#import <StepTalk/STObjectReference.h>
#import <StepTalk/STUndefinedObject.h>
#import <StepTalk/STScripting.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSDebug.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSException.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSInvocation.h>
@interface STEnvironment(STPrivateMethods)
- (STClassInfo *)findClassInfoForObject:(id)anObject;
@end
@implementation STEnvironment
/**
Creates and initialises scripting environment using standard description.
*/
+ (STEnvironment *)defaultScriptingEnvironment
{
NSString *name;
name = [STEnvironmentDescription defaultEnvironmentDescriptionName];
return [self environmentWithDescriptionName:name];
}
/**
Creates and initialises scripting environment using description with name
<var>descName</var>.
*/
+ environmentWithDescriptionName:(NSString *)descName
{
return AUTORELEASE([[self alloc] initWithDescriptionName:descName]);
}
/**
Creates and initialises scripting environment using environment description
<var>description</var>.
*/
+ environmentWithDescription:(STEnvironmentDescription *)aDescription
{
return AUTORELEASE([[self alloc] initWithDescription:aDescription]);
}
/**
Initialises scripting environment using default description.
<init />
*/
- initDefault
{
NSString *name;
name = [STEnvironmentDescription defaultEnvironmentDescriptionName];
return [self initWithDescriptionName:name];
}
/**
Initialises scripting environment using description with name
<var>descName</var>.
<init />
*/
- initWithDescriptionName:(NSString *)descName
{
return [self initWithDescription:
[STEnvironmentDescription descriptionWithName:descName]];
}
/**
Initialises scripting environment using scripting description
<var>aDescription</var>.
*/
- initWithDescription:(STEnvironmentDescription *)aDescription
{
NSEnumerator *enumerator;
NSString *name;
self = [super init];
defaultPool = [[NSMutableDictionary alloc] init];
infoCache = [[NSMutableDictionary alloc] init];
description = RETAIN(aDescription);
classes = [description classes];
/* Load modules */
enumerator = [[description modules] objectEnumerator];
while( (name = [enumerator nextObject]) )
{
[self loadModule:name];
}
/* Register finders */
enumerator = [[description objectFinders] objectEnumerator];
while( (name = [enumerator nextObject]) )
{
[self registerObjectFinderNamed:name];
}
RETAIN(description);
return self;
}
- (void)dealloc
{
RELEASE(defaultPool);
RELEASE(description);
RELEASE(infoCache);
RELEASE(objectFinders);
RELEASE(loadedBundles);
[super dealloc];
}
/**
Enable or disable full scripting. When full scripting is enabled,
you may send any message to any object.
*/
- (void)setFullScriptingEnabled:(BOOL)flag
{
fullScripting = flag;
}
/**
Returns YES if full scripting is enabled.
*/
- (BOOL)fullScriptingEnabled
{
return fullScripting;
}
/**
<p>
Enable or disable creation of unknown objects. Normally you get nil if you
request for non-existant object. If <var>flag</var> is YES
then by requesting non-existant object, name for that object is created
and it is set no STNil.
</p>
<p>
Note: This method will be probably removed (moved to Smalltalk language bundle).
</p>
*/
-(void)setCreatesUnknownObjects:(BOOL)flag
{
createsUnknownObjects = flag;
}
/**
Returns YES if unknown objects are being created.
*/
-(BOOL)createsUnknownObjects
{
return createsUnknownObjects;
}
/**
Add classes specified by the names in the <var>names</var> array.
This method is used internally to add classes provided by modules.
*/
- (void)addClassesWithNames:(NSArray *)names
{
[self addNamedObjectsFromDictionary:STClassDictionaryWithNames(names)];
}
/**
Load StepTalk module with the name <var>moduleName</var>. Modules are stored
in the Library/StepTalk/Modules directory.
*/
- (void) loadModule:(NSString *)moduleName
{
NSBundle *bundle;
bundle = [NSBundle stepTalkBundleWithName:moduleName];
[self includeBundle:bundle];
}
/**
Include scripting capabilities advertised by the framework with name
<ivar>frameworkName</ivar>. If the framework is already loaded, nothing
happens.
*/
- (BOOL)includeFramework:(NSString *)frameworkName
{
NSBundle *bundle;
bundle = [NSBundle bundleForFrameworkWithName:frameworkName];
if(!bundle)
{
return NO;
}
return [self includeBundle:bundle];
}
/**
Include scripting capabilities advertised by the bundle
<ivar>aBundle</ivar>. If the bundle is already loaded, nothing
happens.
*/
- (BOOL)includeBundle:(NSBundle *)aBundle
{
STBundleInfo *info;
/* Ignore already included bundles. */
if([loadedBundles containsObject:[aBundle bundlePath]])
{
NSDebugLog(@"Bundle '%@' already included.", [aBundle bundlePath]);
return;
}
info = [STBundleInfo infoForBundle:aBundle];
if(!info)
{
return NO;
}
[self addNamedObjectsFromDictionary:[info namedObjects]];
[self addClassesWithNames:[info publicClassNames]];
if(!loadedBundles)
{
loadedBundles = [[NSMutableArray alloc] init];
}
/* FIXME: is this sufficient? */
[loadedBundles addObject:[aBundle bundlePath]];
return YES;
}
/**
Returns a dictionary of all named objects in the environment.
*/
- (NSMutableDictionary *)objectDictionary
{
return defaultPool;
}
/* -----------------------------------------------------------------------
Objects
----------------------------------------------------------------------- */
/**
Register object <var>anObject</var> with name <var>objName</var>.
*/
- (void)setObject:(id)anObject
forName:(NSString *)objName
{
if(anObject)
{
[defaultPool setObject:anObject forKey:objName];
}
else
{
[defaultPool setObject:STNil forKey:objName];
}
}
/**
Remove object named <var>objName</var>.
*/
- (void)removeObjectWithName:(NSString *)objName
{
[defaultPool removeObjectForKey:objName];
}
/**
*/
- (void)addNamedObjectsFromDictionary:(NSDictionary *)dict
{
[defaultPool addEntriesFromDictionary:dict];
}
/**
Return object with name <var>objName</var>. If object is not found int the
object dictionary, then object finders are used to try to find the object.
If object is found by an object finder, then it is put into the object
dicitonary. If there is no object with given name, <var>nil</var> is
returned.
*/
- (id)objectWithName:(NSString *)objName
{
NSEnumerator *enumerator;
id obj;
id finder;
obj = [defaultPool objectForKey:objName];
if(!obj)
{
enumerator = [objectFinders objectEnumerator];
while( (finder = [enumerator nextObject]) )
{
obj = [finder objectWithName:objName];
if(obj)
{
[defaultPool setObject:obj forKey:objName];
break;
}
}
}
return obj;
}
- (STObjectReference *)objectReferenceForObjectWithName:(NSString *)name
{
STObjectReference *ref;
id pool = defaultPool;
if( ![self objectWithName:name] )
{
if([[self knownObjectNames] containsObject:name])
{
pool = nil;
}
else if(createsUnknownObjects)
{
[defaultPool setObject:STNil forKey:name];
}
}
ref = [STObjectReference alloc];
[ref initWithObjectName:name
pool:defaultPool];
return AUTORELEASE(ref);
}
/* FIXME: rewrite, it is too sloooooow */
- (STClassInfo *)findClassInfoForObject:(id)anObject
{
STClassInfo *info = nil;
NSString *className;
NSString *origName;
Class class;
if(!anObject)
{
anObject = STNil;
}
/* FIXME: temporary solution */
if( [anObject isProxy] )
{
NSDebugLog(@"FIXME: receiver is a distant object");
info = [classes objectForKey:@"NSProxy"];
if(!info)
{
return [classes objectForKey:@"All"];
}
return info;
}
if([anObject respondsToSelector:@selector(classForScripting)])
{
class = [anObject classForScripting];
}
else
{
class = [anObject class];
}
className = [anObject className];
if([anObject isClass])
{
origName = className = [className stringByAppendingString:@" class"];
NSDebugLLog(@"STSending",
@"Looking for class info '%@'...",
className);
info = [infoCache objectForKey:className];
if(info)
{
return info;
}
while( !(info = [classes objectForKey:className]) )
{
class = [class superclass];
if(!class)
{
break;
}
className = [[class className] stringByAppendingString:@" class"];
NSDebugLLog(@"STSending",
@" ... %@?",className);
}
}
else
{
origName = className;
NSDebugLLog(@"STSending",
@"Looking for class info '%@' (instance)...",
className);
info = [infoCache objectForKey:className];
if(info)
{
return info;
}
while( !(info = [classes objectForKey:className]) )
{
class = [class superclass];
if(!class)
{
break;
}
className = [class className];
NSDebugLLog(@"STSending",
@" ... %@?",className);
}
}
if(!info)
{
NSDebugLLog(@"STSending",
@"No class info '%@'",
className);
return nil;
}
NSDebugLLog(@"STSending",
@"Found class info '%@'",
className);
[infoCache setObject:info forKey:origName];
return info;
}
- (NSString *)translateSelector:(NSString *)aString forReceiver:(id)anObject
{
STClassInfo *class;
NSString *selector;
class = [self findClassInfoForObject:anObject];
NSDebugLLog(@"STSending",
@"Lookup selector '%@' class %@", aString, [class behaviourName]);
selector = [class translationForSelector:aString];
NSDebugLLog(@"STSending",
@"Found selector '%@'",selector);
#ifdef DEBUG
if(! [selector isEqualToString:aString])
{
NSDebugLLog(@"STSending",
@"using selector '%@' instead of '%@'",
selector,aString);
}
#endif
if(!selector && fullScripting )
{
NSDebugLLog(@"STSending",
@"using selector '%@' (full scriptig)",
aString);
selector = AUTORELEASE([aString copy]);
}
if(!selector)
{
[NSException raise:STScriptingException
format:@"Receiver of type %@ denies selector '%@'",
[anObject className],aString];
/* if exception is ignored, then try to use original selector */
selector = AUTORELEASE([aString copy]);
}
return selector;
}
- (NSArray *)knownObjectNames
{
NSMutableArray *array = [NSMutableArray array];
NSEnumerator *enumerator;
id finder;
[array addObjectsFromArray:[defaultPool allKeys]];
enumerator = [objectFinders objectEnumerator];
while( (finder = [enumerator nextObject]) )
{
[array addObjectsFromArray:[finder knownObjectNames]];
}
return [NSArray arrayWithArray:array];
}
/** Register object finder <var>finder</var> under the name <var>name</var> */
- (void)registerObjectFinder:(id)finder name:(NSString*)name
{
if(!objectFinders)
{
objectFinders = [[NSMutableDictionary alloc] init];
}
[objectFinders setObject:finder forKey:name];
}
/** Register object finder named <var>name</var>. This method will try to find
an object finder bundle in Library/StepTalk/Finders directories.
*/
- (void)registerObjectFinderNamed:(NSString *)name
{
NSBundle *bundle;
NSString *path;
id finder;
if([objectFinders objectForKey:name])
{
return;
}
path = STFindResource(name, @"Finders", @"bundle");
if(!path)
{
NSLog(@"Unknown object finder with name '%@'", name);
return;
}
NSDebugLog(@"Finder '%@'", path);
bundle = [NSBundle bundleWithPath:path];
if(!bundle)
{
NSLog(@"Unable to load object finder bundle '%@'", path);
return;
}
finder = [[[bundle principalClass] alloc] init];
if(!finder)
{
NSLog(@"Unable to create object finder from '%@'", path);
return;
}
[self registerObjectFinder:finder name:name];
}
/** Remove object finder with name <var>name</var> */
- (void)removeObjectFinderWithName:(NSString *)name
{
[objectFinders removeObjectForKey:name];
}
@end