libs-gdl2/EOAccess/EOExpressionArray.m
Manuel Guesdon 04d6bb3aac * EOAccess/EOAdaptorChannel.m
call delegate -adaptorChannel:willPerformOperations:
	* EOAccess/EODatabaseContext.m:
	  propagate delegate to channel
	  fix objectsForSourceGlobalID:relationshipName:editingContext: 
	    isToManyToOne case
	* EOAdaptors/PostgreSQLAdaptor/PostgreSQLChannel.m:
	  call delegate methods
	  adding assert to catch bad dates
	  use adaptor -primaryKeySequenceNameFormat
	* EOAccess/EOrelationship.m
	  implement -isParentRelationship
	  verify/clean -isReciprocalToRelationship:
	  implement -qualifierWithSourceRow:
	  small fix on -removeJoin:
	  use batch faulting when -setNumberOfToManyFaultsToBatchFetch:
	  improve -setIsMandatory
	  fix exception message -validateValue:
	  clean -_intermediateAttributes
	  clean -isMultiHop
	  implement -primaryKeyForTargetRowFromSourceDBSnapshot:
	  implement -_setSourceToDestinationKeyMap:
	  implement -qualifierForDBSnapshot:
	  fix/implement -isToManyToOne
	  fix -foreignKeyInDestination
	  implement -isPropagatesPrimaryKeyPossible
	  implement -qualifierOmittingAuxiliaryQualifierWithSourceRow
	  implement -auxiliaryQualifier
	  implement -setAuxiliaryQualifier:
	  clean -_sourceRowToForeignKeyMapping
	  add comment in -_sourceAttributeNames
	  fix -joinForAttribute:
	  fix -_flushCache
	  fix -_stringFromDeleteRule:
	  implement -_rightSideKeyMap
	  implement -_leftSideKeyMap
	  implement -_substitutionRelationshipForRow:
	  fix -isFlattened
	  remove _componentRelationships
	* EOAccess/EORelationship.h
	  remove _componentRelationships
	* EOAccess/EOEntityPriv.[hm]
	  fix _hiddenRelationships return type
	* EOAccess/Entity.m:
	  implement -_mapAttribute:toDestinationAttributeInLastComponentOfRelationshipPath:
	  implement -_inverseRelationshipPathForPath:
	  implement -_relationshipPathHasIdenticalKeys:
	  rewrite & fix -_keyMapForRelationshipPath:
	  rewrite & fix -_keyMapForIdenticalKeyRelationshipPath:
	  implement -valueForSQLExpression:
	  fix -validateObjectForDelete:
	  implement -qualifierForDBSnapshot:
	  fix -_addAttributesToFetchForRelationshipPath:atts:
	  fix -_parsePropertyName:	
	  implement -fetchSpecificationNamed:
	  implement +externalNameForInternalName:separatorString:useAllCaps:
	  implement +nameForExternalName:separatorString:initialCaps:
	  implement -stringByMarkingUpcaseTransitionsWithDelimiter
	  implement -snapshotKeyForAttributeName:
	  implement -_flattenedAttNameToSnapshotKeyMapping
	  fix -_attributesToFetch
	  fix -_attributesToSave
	* EOAccess/Entity.h:
	  fix _inverseRelationshipPathForPath:
	  fix -_relationshipPathHasIdenticalKey:
	  add _flgas.isSingleTableEntity
	* EOAccess/EntityPriv.h:
	  fix -qualifierForDBSnapshot:
	* EOAccess/EOPrivate.[hm]
	  add GDL2_EORelationshipClass
	  add GDL2_EOEntityClass
	* EOAccess/EOModel.m:
	  cache [EOEntity class]
	  add -propertyListForEntity:name: to enable subclassing
	* EOAccess/EOAttribute.m
	  fix -isFlattened
	  implement -targetAttribute
	  implement -relationshipPath
	  fix -_setDefinitionWithoutFlushingCaches:
	  fix -_normalizeDefinition:path:
	  fix -isReadOnly
	  implement -_setOverrideForKeyEnum:
	  implement -_hasAnyOverrides
	  implement -_isKeyEnumOverriden:
	  implement -_prototypeKeys
	  fix -initWithPropertyList:owner:
	  fix -readFormat
	  fix -writeFormat
	  fix -scale
	  fix -precision
	  fix -width
	  fix -allowsNull
	  fix -isReadOnly
	  fix -valueClassName
	  fix -externalType
	  fix -valueType
	  implement -_setValuesFromTargetAttribute
	* EOAccess/EOAttribute.h
	  declare -targetAttribute
	  declare -relationshipPath
	* EOAccess/EOAttributePriv.h
	  declare EOAttributeProtoOverrideBits enum
	  fix method arguments
	  declare  _setValuesFromTargetAttribute()
	* EOAccess/EOExpressionArray.h
	  declare -_isPropertyPath
	* EOAccess/EOExpressionArray.m
	  implement -_isPropertyPath



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@37813 72102866-910b-0410-8b05-ffd578937521
2014-04-26 18:23:22 +00:00

559 lines
13 KiB
Objective-C

/**
EOExpressionArray.m <title>EOExpressionArray</title>
Copyright (C) 1996-2002,2003,2004,2005 Free Software Foundation, Inc.
Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
Date: September 1996
Author: Mirko Viviani <mirko.viviani@gmail.com>
Date: February 2000
$Revision$
$Date$
<abstract></abstract>
This file is part of the GNUstep Database Library.
<license>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 3 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</license>
**/
#include "config.h"
RCS_ID("$Id$")
#include <ctype.h>
#ifdef GNUSTEP
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSException.h>
#include <Foundation/NSString.h>
#include <Foundation/NSValue.h>
#else
#include <Foundation/Foundation.h>
#endif
#ifndef GNUSTEP
#include <GNUstepBase/GNUstep.h>
#include <GNUstepBase/GSObjCRuntime.h>
#include <GNUstepBase/NSDebug+GNUstepBase.h>
#endif
#define GSI_ARRAY_TYPES GSUNION_OBJ
#include <GNUstepBase/GSIArray.h>
#include <EOControl/EODebug.h>
#include <EOAccess/EOEntity.h>
#include <EOAccess/EOExpressionArray.h>
#include <EOAccess/EORelationship.h>
#include "EOPrivate.h"
static SEL eqSel;
@interface EOExpressionArray (PrivateExceptions)
- (void) _raiseRangeExceptionWithIndex:(unsigned) index from:(SEL)selector;
@end
@implementation EOExpressionArray
+ (void) initialize
{
eqSel = NSSelectorFromString(@"isEqual:");
}
+ (EOExpressionArray*)expressionArray
{
return [[self new] autorelease];
}
- (void)dealloc
{
DESTROY(_realAttribute); //TODO mettere nei metodi GC
// DESTROY(_definition);
DESTROY(_prefix);
DESTROY(_infix);
DESTROY(_suffix);
GSIArrayEmpty(_contents);
NSZoneFree([self zone], _contents);
[super dealloc];
}
- (id) init
{
self = [self initWithCapacity:0];
return self;
}
/* designated initializer */
- (id) initWithCapacity:(NSUInteger)capacity
{
self = [super init];
_contents = NSZoneMalloc([self zone], sizeof(GSIArray_t));
_contents = GSIArrayInitWithZoneAndCapacity(_contents, [self zone], capacity);
return self;
}
- (id) initWithObjects:(const id[])objects count:(NSUInteger)count
{
NSUInteger i;
self = [self initWithCapacity:count];
for (i = 0; i < count; i++)
GSIArrayAddItem(_contents, (GSIArrayItem)objects[i]);
return self;
}
+ (EOExpressionArray*)expressionArrayWithPrefix: (NSString *)prefix
infix: (NSString *)infix
suffix: (NSString *)suffix
{
return [[[self alloc]initWithPrefix: prefix
infix: infix
suffix: suffix] autorelease];
}
- (id)initWithPrefix: (NSString *)prefix
infix: (NSString *)infix
suffix: (NSString *)suffix
{
if ((self = [self init]))
{
ASSIGN(_prefix, prefix);
ASSIGN(_infix, infix);
ASSIGN(_suffix, suffix);
}
return self;
}
- (BOOL)referencesObject: (id)anObject
{
return [self indexOfObject: anObject] != NSNotFound;
}
- (NSString *)expressionValueForContext: (id<EOExpressionContext>)ctx
{
if (ctx && [self count]
&& [[self objectAtIndex: 0] isKindOfClass: GDL2_EORelationshipClass])
return [ctx expressionValueForAttributePath: self];
else
{
int i, count = [self count];
id result = [[NSMutableString new] autorelease];
SEL sel = @selector(appendString:);
IMP imp = [result methodForSelector: sel];
if (_prefix)
[result appendString:_prefix];
if (count)
{
(*imp)(result, sel, [[self objectAtIndex: 0]
expressionValueForContext: ctx]);
for (i = 1 ; i < count; i++)
{
if (_infix)
(*imp)(result, sel, _infix);
(*imp)(result, sel, [[self objectAtIndex: i]
expressionValueForContext: ctx]);
}
}
if(_suffix)
[result appendString: _suffix];
return result;
}
}
- (void)setPrefix: (NSString *)prefix
{
ASSIGN(_prefix, prefix);
}
- (void)setInfix: (NSString *)infix
{
ASSIGN(_infix, infix);
}
- (void)setSuffix: (NSString *)suffix
{
ASSIGN(_suffix, suffix);
}
- (NSString *)prefix
{
return _prefix;
}
- (NSString *)infix
{
return _infix;
}
- (NSString *)suffix
{
return _suffix;
}
- (NSString *)definition
{
// return _definition;
return [self valueForSQLExpression: nil];
}
- (BOOL)isFlattened
{
// return _flags.isFlattened;
return ([self count] > 1);
}
- (EOAttribute *)realAttribute
{
return _realAttribute;
}
/*
+ (EOExpressionArray *)parseExpression:(NSString *)expression
entity:(EOEntity *)entity
replacePropertyReferences:(BOOL)replacePropertyReferences
{
EOExpressionArray *array = nil;
const char *s = NULL;
const char *start=NULL;
id objectToken=nil;
array = [[EOExpressionArray new] autorelease];
s = [expression cString];
// ASSIGN(array->_definition, expression);
array->_flags.isFlattened = NO;
if([expression isNameOfARelationshipPath])
{
NSArray *defArray;
NSString *realAttributeName;
int count, i;
array->_flags.isFlattened = YES;
defArray = [expression componentsSeparatedByString:@"."];
count = [defArray count];
for(i = 0; i < count - 1; i++)
{
id relationshipName = [defArray objectAtIndex:i];
id relationship=nil;
relationship = [entity relationshipNamed:relationshipName];
if(!relationship)
[NSException raise:NSInvalidArgumentException
format:@"%@ -- %@ 0x%x: '%@' for entity '%@' is an invalid property",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
relationshipName,
entity];
// if([relationship isToMany])
// [NSException raise:NSInvalidArgumentException format:@"%@ -- %@ 0x%x: '%@' for entity '%@' must be a to one relationship",
//NSStringFromSelector(_cmd),
//NSStringFromClass([self class]),
//self,
//relationshipName,
//entity];
[array addObject:relationship];
entity = [relationship destinationEntity];
}
realAttributeName = [defArray lastObject];
ASSIGN(array->_realAttribute, [entity attributeNamed:realAttributeName]);
if(!array->_realAttribute)
ASSIGN(array->_realAttribute, [entity relationshipNamed:realAttributeName]);
if(!array->_realAttribute)
[NSException raise:NSInvalidArgumentException
format:@"%@ -- %@ 0x%x: '%@' for entity '%@' is an invalid property",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self,
realAttributeName,
entity];
[array addObject:array->_realAttribute];
}
else
{
//IN eoentity persedescr
}
//
return array;
}
*/
- (BOOL)_isPropertyPath
{
if ([self count]<=0)
return NO;
else
return [[self objectAtIndex:0] isKindOfClass:GDL2_EORelationshipClass];
}
- (NSString *)valueForSQLExpression: (EOSQLExpression*)sqlExpression
{
//TODO verify
NSMutableString *value = [NSMutableString string];
volatile int i;
int count;
NS_DURING //Just for debugging
{
count = [self count];
for(i = 0; i < count; i++)
{
id obj = [self objectAtIndex: i];
NSString *relValue;
relValue = [obj valueForSQLExpression: sqlExpression];
if (i > 0 && _infix)
[value appendString: _infix];
[value appendString: relValue];
}
}
NS_HANDLER
{
NSLog(@"exception in EOExpressionArray valueForSQLExpression: self=%p class=%@ i=%d", self, [self class], i);
NSLog(@"exception in EOExpressionArray valueForSQLExpression: self=%@ class=%@ i=%d", self, [self class], i);
NSLog(@"exception=%@", localException);
[localException raise];
}
NS_ENDHANDLER;
return value;
}
/* These are *this* subclasses responsibility */
- (NSUInteger) count
{
return GSIArrayCount(_contents);
}
- (id) objectAtIndex:(NSUInteger) index
{
if (index >= GSIArrayCount(_contents))
[self _raiseRangeExceptionWithIndex:index from:_cmd];
return GSIArrayItemAtIndex(_contents, index).obj;
}
- (void) addObject:(id)object
{
if (object == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil to an array"];
return;
}
GSIArrayAddItem(_contents, (GSIArrayItem)object);
}
- (void) replaceObjectAtIndex:(NSUInteger)index withObject:(id)object
{
if (object == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil to an array"];
return;
}
else if (index >= GSIArrayCount(_contents))
{
[self _raiseRangeExceptionWithIndex:index from:_cmd];
return;
}
GSIArraySetItemAtIndex(_contents, (GSIArrayItem)object, index);
}
- (void) insertObject:(id)object atIndex:(NSUInteger)index
{
if (object == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil to an array"];
return;
}
else if (index >= GSIArrayCount(_contents))
{
[self _raiseRangeExceptionWithIndex:index from:_cmd];
}
GSIArrayInsertItem(_contents, (GSIArrayItem)object, index);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
if (index >= GSIArrayCount(_contents))
{
[self _raiseRangeExceptionWithIndex:index from:_cmd];
}
GSIArrayRemoveItemAtIndex(_contents, index);
}
- (void) removeAllObjects
{
GSIArrayRemoveAllItems(_contents);
}
/* might as well also implement because we can do it faster */
- (id) lastObject
{
return GSIArrayLastItem(_contents).obj;
}
- (id) firstObject
{
if (GSIArrayCount(_contents) == 0)
return nil;
return GSIArrayItemAtIndex(_contents, 0).obj;
}
/* not only make it faster but work around for old buggy implementations of
* NSArray in gnustep with an extra release */
- (void) removeObject:(id)anObject
{
int index = GSIArrayCount(_contents);
BOOL (*eq)(id,SEL,id)
= (BOOL (*)(id, SEL, id))[anObject methodForSelector:eqSel];
/* iterate backwards, so that all objects equal to 'anObject'
* can safely be removed from the array while iterating. */
while (index-- > 0)
{
if ((*eq)(anObject, eqSel, GSIArrayItemAtIndex(_contents, index).obj))
{
GSIArrayRemoveItemAtIndex(_contents, index);
}
}
}
/* private methods. */
- (void) _raiseRangeExceptionWithIndex: (unsigned)index from: (SEL)sel
{
NSDictionary *info;
NSException *exception;
NSString *reason;
unsigned count = GSIArrayCount(_contents);
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt: index], @"Index",
[NSNumber numberWithUnsignedInt: count], @"Count",
self, @"Array", nil, nil];
reason = [NSString stringWithFormat: @"Index %d is out of range %d (in '%@')", index, count, NSStringFromSelector(sel)];
exception = [NSException exceptionWithName: NSRangeException
reason: reason
userInfo: info];
[exception raise];
}
@end /* EOExpressionArray */
@implementation NSObject (EOExpression)
- (NSString*)expressionValueForContext: (id<EOExpressionContext>)ctx
{
if ([self respondsToSelector: @selector(stringValue)])
return [(id)self stringValue];
else
return [self description];
}
@end
@implementation NSString (EOExpression)
/* Avoid returning the description in case of NSString because if the string
contains whitespaces it will be quoted. Particular adaptors have to override
-formatValue:forAttribute: and they have to quote with the specific
database character the returned string. */
- (NSString*)expressionValueForContext: (id<EOExpressionContext>)ctx
{
return self;
}
@end
@implementation NSString (EOAttributeTypeCheck)
- (BOOL)isNameOfARelationshipPath
{
const char *s = [self cString];
BOOL result = NO;
if (isalnum(*s) || *s == '@' || *s == '_' || *s == '#')
{
for (++s; *s; s++)
{
if (!isalnum(*s) && *s != '@' && *s != '_' && *s != '#' && *s != '$'
&& *s != '.')
return NO;
if (*s == '.')
result = YES;
}
}
return result;
}
@end