From 05f1612cbb7e33cf9135a346fc2505cc0e87e853 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Thu, 23 Feb 2012 02:13:56 +0000 Subject: Warning: this branch commit is largely untested, and known to throw exceptions as database structure retrieval is currently missing! Further work on SPMySQLFramework integration: - Improve SPMySQL framework build settings including correct ppc builds and a Distribution configuration for the build distributions to match - Add new convenience querying and result methods to the framework - Amend Sequel Pro source to use the new SPMySQL.framework methods everywhere, replacing MCPKit methods where they differ and improving some functions - Remove MCPKit from the source - Fix a number of warnings on Release-style builds --- Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m | 1342 ------------------------ 1 file changed, 1342 deletions(-) delete mode 100644 Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m (limited to 'Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m') diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m deleted file mode 100644 index dba963c6..00000000 --- a/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m +++ /dev/null @@ -1,1342 +0,0 @@ -// -// $Id$ -// -// MCPObject.m -// MCPKit -// -// Created by Serge Cohen (serge.cohen@m4x.org) on 19/05/04. -// Copyright (c) 2004 Serge Cohen. All rights reserved. -// -// Forked by the Sequel Pro team (sequelpro.com), April 2009 -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// More info at -// More info at - -#import "MCPObject.h" - -#import - -#import "MCPClassDescription.h" -#import "MCPClassDescription+MCPEntreprise.h" -#import "MCPAttribute.h" -#import "MCPRelation.h" -#import "MCPJoin.h" - -@implementation MCPObject - -#pragma mark Life of the object -- (id) init -/*" Taking care of getting the class description for self (after passing the message up). "*/ -{ -// NSArray theAttributes; -// unsigned int i; - - if (self = [super init]) { - classDescription = [[NSClassDescription classDescriptionForClass:[self class]] retain]; - [self setAttributesToDefault]; -/* - theAttributes = [classDescription attributeKeys]; - for (i=0; [theAttributes count] != i; ++i) { // setting the attributtes with proper defaults. - NSString *theKey = (NSString *) [theAttributes objectAtIndex:i]; - - [self setValue:[self defaultValueForKey:theKey] forKey:theKey]; - } - */ - } - return self; -} - -- (id) initWithDictionary:(NSDictionary *) dictionary -/*" This method will use the class description to fetch in the dictionary the values of the attributes of the object.... - Should try to get this description a bit clearer."*/ -{ - self = [super init]; - if (self) { - NSUInteger i; - NSArray *attrArray; - - classDescription = [[NSClassDescription classDescriptionForClass:[self class]] retain]; - [self setAttributesToDefault]; - attrArray = [classDescription attributes]; - for (i=0; [attrArray count] != i; ++i) { - MCPAttribute *currentAttribute = (MCPAttribute *)[attrArray objectAtIndex:i]; - id currentValue = [dictionary objectForKey:[currentAttribute name]]; - - if (! currentValue) { - currentValue = [dictionary objectForKey:[currentAttribute externalName]]; - } - if (currentValue) { - [self setValue:currentValue forKey:[currentAttribute name]]; - } - } - } - return self; -} - -- (void) dealloc -/*" Deallocating the class description, then passes the message to super. "*/ -{ -// unsigned int i; -// NSArray *tmpAttributes = [classDescription attributes]; - -/* for (i=0; [tmpAttributes count] != i; ++i) { - MCPAttribute *tmpAttr = [tmpAttributes objectAtIndex:i]; - if ([tmpAttr valueClass]) { - [self setValue:nil forKey:[tmpAttr name]]; - } - } -*/ - [classDescription release]; - [connection release]; - [super dealloc]; -} - -- (void) setAttributesToDefault -/*" Set all the attributes to default values, except for auto-generated and primary key attributes, which are set to NULL. - - NOTE : !! In the current version the auto-generated and key are ALSO set to default values!!. - "*/ -{ - NSArray *theAttributes = [classDescription attributes]; -// NSArray *thePrimKeys = [classDescription primaryKeyAttributes]; - NSUInteger i; - - for (i=0; [theAttributes count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *) [theAttributes objectAtIndex:i]; - NSString *theKey = [theAttribute name]; - - if (! [theAttribute autoGenerated]) { - [self setValue:[self defaultValueForKey:theKey] forKey:theKey]; - } - else { // Auto-generated attribute ... set it to NULL: - [self setValue:[self defaultValueForKey:theKey] forKey:theKey]; - } - } -/* - for (i=0; [thePrimKeys count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *) [thePrimKeys objectAtIndex:i]; - if (! [theAttribute autoGenerated]) { - NSString *theKey = [theAttribute name]; - - [[self valueForKey:theKey] release]; - [self setValue:NULL forKey:theKey]; - } - } - */ - return; -} - -#pragma mark Accessor(s) -- (MCPClassDescription *) classDescription -{ - return classDescription; -} - -- (MCPConnection *) connection -{ - if ((! connection) || (! [connection checkConnection])) { - [self setConnection:nil]; - } - return connection; -} - -- (void) setConnection:(MCPConnection *) iConnection -{ - if (iConnection != connection) { - [connection release]; - connection = [iConnection retain]; - } -} - - - -#pragma mark Database interface -- (id) readFromDBRow:(NSDictionary *) iDictionary withTableName:(NSString *) iTableName -/*" Uses a query result row (described as a NSDictionary) to set the instance variables of self. If - the result contains columns from multiple tables, the iTableName can be used to specify the alias - used for the table name corresponding to the class. - -If iTableName == nil, the columns will be searched first without table name (column_name) and if - not found then with the table name in front (from the class description : table_name.column_name). - -Otherwise, the search will be performed in the following order : iTableName.column_name, column_name, - table_name.column_name. - "*/ -{ - NSArray *theAttributeKeys = [classDescription attributeKeys]; - NSArray *thePrefixArray; - NSUInteger i; - -// Depending on the value of iTableName, get the search order. - if ((nil == iTableName) || ([@"" isEqualToString:iTableName])) { - thePrefixArray = [NSArray arrayWithObjects:@"", [NSMutableString stringWithFormat:@"%@.", [classDescription externalName]], nil]; - } - else { - thePrefixArray = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%@.", iTableName], [NSMutableString stringWithFormat:@"%@.", [classDescription externalName]], @"", nil]; - } - for (i=0; [theAttributeKeys count] != i; ++i) { - id theValue = nil; - MCPAttribute *theAttribute = [classDescription attributeWithName:[theAttributeKeys objectAtIndex:i]]; - NSUInteger j; - - for (j=0; [thePrefixArray count] != j; ++j) { - if (theValue = [iDictionary objectForKey:[NSString stringWithFormat:@"%@%@", [thePrefixArray objectAtIndex:j], [theAttribute externalName]]]) { - break; - } - } - if (theValue) { - [self takeValue:theValue forKey:[theAttribute name]]; - } - } - return self; -} - - -//- (MCPDBReturnCode) setPrimaryKey:(NSDictionary *) iDictionary andFetchFromDB:(MCPConnection *) iConnection -- (MCPDBReturnCode) setPrimaryKey:(id) iDictionary andFetchFromDB:(MCPConnection *) iConnection -/*" This method is used to retrieve an object from the DB given its precise primary key. It will return self. - If the object is not found in the DB, then all instance variable are set to the default - (and autogenerated/primary-key attributes are set to null)."*/ -{ - BOOL missingKey = NO; - NSArray *theKeyAttr = [classDescription primaryKeyAttributes]; - NSUInteger i; - NSMutableString *query = [NSMutableString stringWithFormat:@"SELECT * FROM %@ WHERE ", [classDescription externalName]]; - MCPResult *result; - NSDictionary *row; - - [self setConnection:iConnection]; - if (! iConnection) { - return MCPDBReturnNoConnection; - } - for (i=0; [theKeyAttr count] != i; ++i) { - MCPAttribute *theAttr = [classDescription attributeWithName:[(MCPAttribute *)[theKeyAttr objectAtIndex:i] name]]; - id theKeyValue; - -// if (theKeyValue = [iDictionary objectForKey:[theAttr name]]) { - if (theKeyValue = [iDictionary valueForKey:[theAttr name]]) { - if (i != 0) { - [query appendString:@" and "]; - } -// Implies the iDictionary IS a dictionary: -// [query appendFormat:@"(%@ = %@)", [theAttr externalName], [iConnection quoteObject:[iDictionary objectForKey:[theAttr name]]]]; -// If the iDictionary is just an object complying with NSValueCodeing: - [query appendFormat:@"(%@ = %@)", [theAttr externalName], [iConnection quoteObject:theKeyValue]]; - } - else { // Part of the primary key is missing... look for the DB name of the attribute - if (theKeyValue = [iDictionary valueForKey:[theAttr externalName]]) { - if (i != 0) { - [query appendString:@" and "]; - } - [query appendFormat:@"(%@ = %@)", [theAttr externalName], [iConnection quoteObject:theKeyValue]]; - } - else { // Not able to find the value for this attribute !!! - missingKey = YES; - NSLog(@"Unable to find the value for attribute %@ of object of class %@, will make it default", [theAttr name], [self className]); - break; - } - } - } // Now the query is prepared... or a key part is missing: - if (missingKey) { - [self setAttributesToDefault]; - return MCPDBReturnIncompleteKey; - } - result = [iConnection queryString:query]; - if ([result numOfRows] == 0) { - [self setAttributesToDefault]; - return MCPDBReturnNone; - } - row = [result fetchRowAsDictionary]; - [self readFromDBRow:row withTableName:@""]; - if ([result numOfRows] != 1) { - NSLog(@"Got more than one row when querying : %@.... will take only the first one!!! that an IMPORTANT flaw in your data model!!!", query); - return MCPDBReturnMultiple; - } - return MCPDBReturnOK; -} - - -- (NSDictionary *) checkDBId -/*" Using the identity properties of the class, this method will check if self already exists in the DB, - in which case it will set the primary key attributes to match the DB entry. If the object is not present - in the DB, the primary key attributes are set to null if they are declared as aut-generated (untouched otherwise). - -The returned dictionary contains the values of the primary key attributes. It also contains one entry with - key MCPDBReturnCode, which contains the result of the operation (was the object in DB?). - -If the identityAttributes of the class description is empty, the entry will always be considered not to be - in the DB, AND the primary key attributes of the object will be left unchnaged and returned as they are at - call time."*/ -{ - NSArray *theIdAttr = [classDescription identityAttributes]; - NSMutableDictionary *theKeys = [NSMutableDictionary dictionary]; - NSArray *theKeyAttr = [classDescription primaryKeyAttributes]; - MCPConnection *theConnection = [self connection]; - MCPResult *theResult; - NSUInteger i; - - if (! theConnection) { - return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:MCPDBReturnNoConnection], @"MCPDBReturnCode", nil]; - } - if (! [theKeyAttr count]) { // There is no primary key for this object. - [theKeys setObject:[NSNumber numberWithInteger:MCPDBReturnNoKey] forKey:@"MCPDBReturnCode"]; - return [NSDictionary dictionaryWithDictionary:theKeys]; - } - if (! [theIdAttr count]) { // Identity is not defined for this object. - [theKeys setObject:[NSNumber numberWithInteger:MCPDBReturnNoIdentity] forKey:@"MCPDBReturnCode"]; - for (i=0; [theKeyAttr count] != i; ++i) { - NSString *theKey = [(MCPAttribute*)[theKeyAttr objectAtIndex:i] name]; - [theKeys setObject:[self valueForKey:theKey] forKey:theKey]; - } - return [NSDictionary dictionaryWithDictionary:theKeys]; - } -// Do the fetch in the DB. - NSMutableString *theQuery = [NSMutableString stringWithString:@"SELECT "]; - - for (i = 0; [theKeyAttr count] != i; ++i) { - if (i) { - [theQuery appendString:@", "]; - } - [theQuery appendString:[(MCPAttribute *)[theKeyAttr objectAtIndex:i] externalName]]; - } - [theQuery appendFormat:@" FROM %@ WHERE ", [classDescription externalName]]; - for (i = 0; [theIdAttr count] != i; ++i) { - if (i) { - [theQuery appendString:@" AND "]; - } -/* - if ([[(MCPAttribute *)[theIdAttr objectAtIndex:i] valueClass] isSubclassOfClass:[NSString class]]) { - if ([(MCPAttribute *)[theIdAttr objectAtIndex:i] width] != 0) { - [theQuery appendFormat:@"(BINARY %@ = SUBSTRING(%@ FROM 1 FOR %u)", [(MCPAttribute *)[theIdAttr objectAtIndex:i] externalName], [theConnection quoteObject:[self valueForKey:[(MCPAttribute *)[theIdAttr objectAtIndex:i] name]]], [(MCPAttribute *)[theIdAttr objectAtIndex:i] width]]; - } - else { - [theQuery appendFormat:@"(BINARY %@ = %@)", [(MCPAttribute *)[theIdAttr objectAtIndex:i] externalName], [theConnection quoteObject:[self valueForKey:[(MCPAttribute *)[theIdAttr objectAtIndex:i] name]]]]; - } - } - */ - if (([[(MCPAttribute *)[theIdAttr objectAtIndex:i] valueClass] isSubclassOfClass:[NSString class]]) && ([(MCPAttribute *)[theIdAttr objectAtIndex:i] width] != 0)) { - [theQuery appendFormat:@"(%@ = SUBSTRING(%@ FROM 1 FOR %ld))", [(MCPAttribute *)[theIdAttr objectAtIndex:i] externalName], [theConnection quoteObject:[self valueForKey:[(MCPAttribute *)[theIdAttr objectAtIndex:i] name]]], [(MCPAttribute *)[theIdAttr objectAtIndex:i] width]]; - } - else { - [theQuery appendFormat:@"(%@ = %@)", [(MCPAttribute *)[theIdAttr objectAtIndex:i] externalName], [theConnection quoteObject:[self valueForKey:[(MCPAttribute *)[theIdAttr objectAtIndex:i] name]]]]; - } - } - [theQuery appendString:@" ORDER BY "]; - for (i = 0; [theKeyAttr count] != i; ++i) { - if (i) { - [theQuery appendString:@", "]; - } - [theQuery appendString:[(MCPAttribute *)[theKeyAttr objectAtIndex:i] externalName]]; - } - theResult = [theConnection queryString:theQuery]; - if ([theResult numOfRows]) { // the object was found. - NSDictionary *theFirstRow = [theResult fetchRowAsDictionary]; - if ([theResult numOfRows] != 1) { - NSLog(@"in MCPObject -checkDBIdWithConnection: method.... not only one (as expected) but %ld results were found, will take the first one."); - [theKeys setObject:[NSNumber numberWithInteger:MCPDBReturnMultiple] forKey:@"MCPDBReturnCode"]; - } - else { - [theKeys setObject:[NSNumber numberWithInteger:MCPDBReturnOK] forKey:@"MCPDBReturnCode"]; - } - [theKeys addEntriesFromDictionary:theFirstRow]; -// Setting the value of self for the primary key to the one just found. - for (i=0; [theKeyAttr count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *) [theKeyAttr objectAtIndex:i]; - - [self setValue:[theFirstRow objectForKey:[theAttribute externalName]] forKey:[theAttribute name]]; - } - } - else { // Object not found in the DB. - for (i = 0; [theKeyAttr count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *)[theKeyAttr objectAtIndex:i]; - - if ([theAttribute autoGenerated]) { - [self setValue:[self defaultValueForKey:[theAttribute name]] forKey:[theAttribute name]]; - } - [theKeys setObject:[self valueForKey:[theAttribute name]] forKey:[theAttribute name]]; - } - [theKeys setObject:[NSNumber numberWithInteger:MCPDBReturnNone] forKey:@"MCPDBReturnCode"]; -// Setting the value of self for the primary key to the default values. -/* - for (i=0; [theKeyAttr count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *) [theKeyAttr objectAtIndex:i]; - - [self setValue:[self defaultValueForKey:[theAttribute name]] forKey:[theAttribute name]]; - } -*/ - } - return [NSDictionary dictionaryWithDictionary:theKeys]; -} - - -- (NSDictionary *) saveInDB -/*" First will use the checkDBIdWithConnection method to check if the entry is already in the database. If this is the case, - it will use the updateInDB... method to update the current DB instance. Otherwise it will make an insert into the database - to save the entry, than it makes a select to get the value of autogenerated column, and use them (if necessary) to return - the primary key (as NSDictionary). - - As for checkDBId... the returned dictionary also have a MCPDBReturnCode key with a NSNumber as value containing the - return code of the operation."*/ -{ - MCPConnection *theConnection = [self connection]; - NSDictionary *theCheckReturn = [self checkDBId]; - NSMutableDictionary *theRet; - NSInteger theCheckCode = [(NSNumber *)[theCheckReturn objectForKey:@"MCPDBReturnCode"] integerValue]; - NSUInteger i; - NSUInteger j; - NSMutableString *theQuery = [NSMutableString string]; - NSArray *theAttr = [classDescription attributes]; - - switch (theCheckCode) { - case MCPDBReturnNoIdentity : // For this object the identity is not set... so we try to save. - break; - - case MCPDBReturnOK : // Object already in the DB (found using checkDBId... method). - // NSLog(@"in saveUsingConnection: the entry already existed so will update the one with key : %@", theCheckReturn); - theCheckCode = [self updateInDB]; - theRet = [NSMutableDictionary dictionaryWithDictionary:[self primaryKey]]; - [theRet setObject:[NSNumber numberWithInteger:theCheckCode] forKey:@"MCPDBReturnCode"]; - return [NSDictionary dictionaryWithDictionary:theRet]; - break; - - case MCPDBReturnMultiple : // Multiple were found, the first one will be updated. - NSLog(@"Multiple entries found with the same identity ... will update the first one."); - theCheckCode = [self updateInDB]; - theRet = [NSMutableDictionary dictionaryWithDictionary:[self primaryKey]]; - [theRet setObject:[NSNumber numberWithInteger:theCheckCode] forKey:@"MCPDBReturnCode"]; - return [NSDictionary dictionaryWithDictionary:theRet]; - break; - - case MCPDBReturnNone : // The entry is not already in the DB, save it... - break; - - case MCPDBReturnNoConnection : // Self does not have a connection. - NSLog(@"Can not save when the connection is not working..."); - break; - - default : // We should not arrive here anyway. - NSLog(@"For some resons we got a unexpected result from checkDBId : %ld", theCheckCode); - break; - } - -// Generate the INSERT query: - [theQuery appendFormat:@"INSERT INTO %@ (", [classDescription externalName]]; - j = 0; - for (i=0; [theAttr count] != i; ++i) { - if (! [(MCPAttribute *)[theAttr objectAtIndex:i] autoGenerated]) { - if (j) { - [theQuery appendString:@", "]; - } - [theQuery appendString:[(MCPAttribute *)[theAttr objectAtIndex:i] externalName]]; - ++j; - } - } - [theQuery appendString:@") VALUES ("]; - j = 0; - for (i=0; [theAttr count] != i; ++i) { - if (! [(MCPAttribute *)[theAttr objectAtIndex:i] autoGenerated]) { - if (j) { - [theQuery appendString:@", "]; - } - [theQuery appendString:[theConnection quoteObject:[self valueForKey:[(MCPAttribute *)[theAttr objectAtIndex:i] name]]]]; - ++j; - } - } - [theQuery appendString:@")"]; -// Finished preparing the query. - -// Now we perform the query... - [theConnection queryString:theQuery]; - if (1 != (i = [theConnection affectedRows])) { // More than one row affected ... Should NEVER occure. - NSLog(@"Problem while saving a MCPObject to the database : number of inserted rows is : %ld !!!", i); - NSLog(@"Maybe there is an error : %@ ", [theConnection getLastErrorMessage]); - NSLog(@"The class of the object is : %@, and it's description is :\n%@", [self className], [self descriptionWithLocale:nil]); - theCheckCode = (i == 0) ? MCPDBReturnNone : MCPDBReturnMultiple; - } - else { - theCheckCode = MCPDBReturnOK; - } - -// Finally we get the primary key of the inserted object: - if ([classDescription singleIntAutoGenKey]) { - NSString *thePrimKey = [(MCPAttribute *) [[classDescription primaryKeyAttributes] objectAtIndex:0] name]; - - [self setValue:[NSNumber numberWithLongLong:[theConnection insertId]] forKey:thePrimKey]; - } - [self getAutoGenerated]; - theRet = [NSMutableDictionary dictionaryWithDictionary:[self primaryKey]]; - [theRet setObject:[NSNumber numberWithInteger:theCheckCode] forKey:@"MCPDBReturnCode"]; - - return [NSDictionary dictionaryWithDictionary:theRet]; -} - - - -- (MCPDBReturnCode) getAutoGenerated -/*" This method will use the Identity attributes of the object to retrieve the autogenerated -attributes from the database. - -If the identity is not defined for this class/entity, then it will try to use the primary key -to get the auto-generated values. - -Obviously this might generate a bug if one of the Identity attributes is autogenerated. -This will create a trouble if some object does not have an identity defined but still contains -some auto-generated attributes. -"*/ -{ - NSMutableArray *theAutoAttr = [NSMutableArray array]; - NSArray *theAttr = [classDescription attributes]; - NSArray *theKeyAttr = [classDescription primaryKeyAttributes]; - NSUInteger i,j; - NSMutableString *theQuery; - NSInteger theCheckCode; - MCPResult *theResult; - NSDictionary *theRow; - MCPConnection *theConnection = [self connection]; - - if (! theConnection) { - return MCPDBReturnNoConnection; - } - - if (0 != [[classDescription identityAttributes] count]) { - theCheckCode = [(NSNumber *)[[self checkDBId] objectForKey:@"MCPDBReturnCode"] integerValue]; - if ((MCPDBReturnOK != theCheckCode) && (MCPDBReturnMultiple != theCheckCode)) { - NSLog(@"Unable to get the primary key for the object, will abort now the fetch of autoGenerated attributes (left unchanged)!"); - return theCheckCode; - } - if (MCPDBReturnMultiple == theCheckCode) { - NSLog(@"Will get the autoGenerated values for the first entry...."); - } - } - j = 0; - for (i=0; [theAttr count] != i; ++i) { // generate the array with autoGenerated attributes. - if ([(MCPAttribute *)[theAttr objectAtIndex:i] autoGenerated]) { - [theAutoAttr insertObject:[theAttr objectAtIndex:i] atIndex:j]; - ++j; - } - } - if (0 == [theAutoAttr count]) { - return MCPDBReturnOK; - } - -// Make the query: - theQuery = [NSMutableString stringWithString:@"SELECT "]; - for (i=0; [theAutoAttr count] != i; ++i) { - if (i) { - [theQuery appendString:@", "]; - } - [theQuery appendString:[(MCPAttribute *)[theAutoAttr objectAtIndex:i] externalName]]; - } - [theQuery appendFormat:@" FROM %@ WHERE ", [classDescription externalName]]; -// Preparing the Where Clause: - for (i=0; [theKeyAttr count] != i; ++i) { - if (i) { - [theQuery appendString:@" AND "]; - } - [theQuery appendFormat:@"( %@ = %@ )", [(MCPAttribute*)[theKeyAttr objectAtIndex:i] externalName], [theConnection quoteObject:[self valueForKey:[(MCPAttribute*)[theKeyAttr objectAtIndex:i] name]]]]; - } -// Query is ready: - theResult = [theConnection queryString:theQuery]; - if (! theResult) { - NSLog(@"While fetching the auto-generated part of the object, got an error -a nil MCPResult-. Will report that as a MCPDBReturnNone."); - return MCPDBReturnNone; - } - if (0 == [theResult numOfRows]) { // The entry was not found in the DB. - if (0 == [[classDescription identityAttributes] count]) { - NSLog(@"It seems that the object is not in the DB... but the identity is not defined so there might be a trouble with having the proper ID."); - } - return MCPDBReturnNone; - } -// Getting the values from the DB select: - theRow = [theResult fetchRowAsDictionary]; - for (i=0; [theAutoAttr count] != i; ++i) { - [self setValue:[theRow objectForKey:[(MCPAttribute*)[theAutoAttr objectAtIndex:i] externalName]] forKey:[(MCPAttribute*)[theAutoAttr objectAtIndex:i] name]]; - } -// Returning the proper value: - if (1 != [theResult numOfRows]) { - NSLog(@"Multiple entries (or none : %llu), got the values for the first one...", [theResult numOfRows]); - return MCPDBReturnMultiple; - } - return MCPDBReturnOK; -} - - -- (MCPDBReturnCode) updateInDB -/*"This method will use the primary key value held by the object to modify the object that is saved in the DB. - - (NO YET IMPLEMENTED) If the primary key is not complete (some of the attributes are to null or 0) and the identityAttributes - (from class description) is not an empty list then it will perform a checkDBId... to try to retrieve the - missing part of the primary key. - "*/ -{ - NSArray *theAttributes = [classDescription attributes]; - NSArray *thePrimKeyAttributes = [classDescription primaryKeyAttributes]; - NSMutableString *theQuery = [NSMutableString string]; - NSInteger theCheckCode; - NSUInteger i, j; - MCPConnection *theConnection = [self connection]; - - if (! theConnection) { - return MCPDBReturnNoConnection; - } -// Generate the query - [theQuery appendFormat:@"UPDATE %@ SET ", [classDescription externalName]]; - // Prepare the value key pairs: - j=0; - for (i=0 ; [theAttributes count] != i; ++i) { - id theValue = [self valueForKey:[(MCPAttribute *)[theAttributes objectAtIndex:i] name]]; - - if ((theValue) && (! [(MCPAttribute *)[theAttributes objectAtIndex:i] autoGenerated])) { - if (j) { - [theQuery appendString:@", "]; - } - [theQuery appendFormat:@"%@ = %@ ", [(MCPAttribute *)[theAttributes objectAtIndex:i] externalName], [theConnection quoteObject:theValue]]; - ++j; - } - } - if (0 == j) { // Nothing has to be updated in this entry.... - NSLog(@"Tried to update a row from an object that does NOT contain any information!!"); - return MCPDBReturnNone; - } - // Prepare the WHERE clause: - [theQuery appendFormat:@" WHERE "]; - j = 0; - for (i=0; [thePrimKeyAttributes count] != i; ++i) { - id theValue = [self valueForKey:[(MCPAttribute *)[thePrimKeyAttributes objectAtIndex:i] name]]; - - if (j) { - [theQuery appendString:@" AND "]; - } - if (theValue) { - [theQuery appendFormat:@"%@ = %@", [(MCPAttribute *)[thePrimKeyAttributes objectAtIndex:i] externalName], [theConnection quoteObject:theValue]]; - ++j; - } - } -// Perform the update: - if (j == [thePrimKeyAttributes count]) { - [theConnection queryString:theQuery]; - i = [theConnection affectedRows]; - switch (i) { - case 0 : - theCheckCode = MCPDBReturnNone; - break; - case 1 : - theCheckCode = MCPDBReturnOK; - break; - default : - theCheckCode = MCPDBReturnMultiple; - break; - } - } - else { - NSDictionary *theKeyCheck = [self checkDBId]; - NSInteger theIdCheckCode = [(NSNumber *) [theKeyCheck objectForKey:@"MCPDBReturnCode"] integerValue]; - - if (MCPDBReturnOK == theIdCheckCode) { - theCheckCode = [self updateInDB]; - } - else { - theCheckCode = MCPDBReturnNoKey; - } - } -// Return proper code: - return theCheckCode; -} - - -- (MCPDBReturnCode) deleteInDB -/*" Uses the connection to delete the object from the DB. In this process the autoGenerated attributes are set to null. - If the primary key is not completely set, this method calls checkDBId... method to try to complete it - (if the identityAttributes of the class description is not empty)."*/ -{ - NSArray *theKeyAttributes = [classDescription primaryKeyAttributes]; - NSArray *theAttributes = [classDescription attributes]; - NSUInteger i; -// NSMutableDictionary *theKeyValue = [NSMutableDictionary dictionary]; - NSInteger theCheckCode = MCPDBReturnOK; - MCPConnection *theConnection = [self connection]; - - if (! theConnection) { - return MCPDBReturnNoConnection; - } -// Get the value of the primary key: - for (i=0; [theKeyAttributes count] != i; ++i) { - id theValue = [self valueForKey:[(MCPAttribute*) [theKeyAttributes objectAtIndex:i] name]]; - if ((! theValue) || ([theValue isKindOfClass:[NSNumber class]] && (0 == [theValue integerValue]))) { - theCheckCode = MCPDBReturnIncompleteKey; - break; - } - } -// If incomplete, try to find the rest, and perform again: - if (MCPDBReturnIncompleteKey == theCheckCode) { - if (MCPDBReturnOK == [(NSNumber *)[[self checkDBId] objectForKey:@"MCPDBReturnCode"] integerValue]) { - return [self deleteInDB]; - } - else { - return MCPDBReturnIncompleteKey; - } - } - -// Perform the deletion from the DB: - theCheckCode = [[self class] deleteInDBUsingConnection:theConnection withId:self]; - -// set the auto-generated attributes to proper values: - for (i=0 ; [theAttributes count] != i; ++i) { - MCPAttribute *theAttribute = (MCPAttribute *) [theAttributes objectAtIndex:i]; - if ([theAttribute autoGenerated]) { -// [self setValue:NULL forKey:[theAttribute name]]; - [self setValue:[self defaultValueForKey:[theAttribute name]] forKey:[theAttribute name]]; - } - } - -// Finished... just have to return the proper value: - return theCheckCode; -} - - -+ (MCPDBReturnCode) deleteInDBUsingConnection:(MCPConnection *) iConnection withId:(id) iId -/*" Uses the connection to remove from the DB the entry corresponding to the primary key id given by iId. - If any part of the primary key is missing, nothing is done."*/ -{ - NSMutableString *theQuery = [NSMutableString string]; - MCPClassDescription *theClassDescription = (MCPClassDescription *) [NSClassDescription classDescriptionForClass:[self class]]; - NSArray *theKeyAttributes = [theClassDescription primaryKeyAttributes]; - NSUInteger i; - - if (! [iConnection checkConnection]) { - return MCPDBReturnNoConnection; - } -// Generate the query: - [theQuery appendFormat:@"DELETE FROM %@ WHERE ", [theClassDescription externalName]]; -// Prepare the WHERE STATEMENT: - for (i=0; [theKeyAttributes count] != i; ++i) { - if (i) { - [theQuery appendString:@" AND "]; - } -// [theQuery appendFormat:@"( %@ = %@ )", [(MCPAttribute*)[theKeyAttributes objectAtIndex:i] externalName], [iConnection quoteObject:[iId objectForKey:[(MCPAttribute*)[theKeyAttributes objectAtIndex:i] name]]]]; - [theQuery appendFormat:@"( %@ = %@ )", [(MCPAttribute*)[theKeyAttributes objectAtIndex:i] externalName], [iConnection quoteObject:[iId valueForKey:[(MCPAttribute*)[theKeyAttributes objectAtIndex:i] name]]]]; - } -// Perform the query: - [iConnection queryString:theQuery]; -// Return the proper code: - i = [iConnection affectedRows]; - - if (1 < i) { - return MCPDBReturnMultiple; - } - if (0 == i) { - return MCPDBReturnNone; - } - return MCPDBReturnOK; -} - - -#pragma mark Handling realtions -- (id) getTargetOfRelation:(MCPRelation *) iRelation -/*" This method is using the information from iRelation to fetch the targe of the relation in the DB. - -If iRelation is flagged as a to-one, then the return type is the type of the target object (value might be nil, if the target was not found). -If iRelation is flagged as a to-many, then the return type is NSArray (and might be empty if no target were found). - -In any case this method is first checking that iRelation is starting from the class of self, and that self is connected to the DB. -Also the returned object is ALWAYS autoreleased before being returned. -"*/ -{ - NSMutableString *query; - MCPClassDescription *destinationDesc; - NSArray *joins; - NSArray *keys; - NSArray *ids; - MCPResult *result; - NSUInteger i; - NSDictionary *theRow; - id theRet; - - if (! iRelation) { - NSLog(@"Tried to get the target of a relation... but the relation object is nil"); - } - else { -// NSLog(@"Trying to get the target of the realtion : %@", [iRelation descriptionWithLocale:nil]); - } - if ((! [connection isConnected]) || (! [classDescription isEqual:[iRelation origin]])) { // Error condition. - return nil; - } -// Generating the query: - destinationDesc = [iRelation destination]; - keys = [destinationDesc primaryKeyAttributes]; - joins = [iRelation joins]; - ids = [destinationDesc identityAttributes]; - query = [[NSMutableString alloc] initWithString:@"SELECT "]; - for (i=0; [keys count] != i; ++i) { - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[keys objectAtIndex:i] externalName]]; - } - [query appendFormat:@" FROM %@ WHERE ", [destinationDesc externalName]]; - for (i=0; [joins count] != i; ++i) { - MCPJoin *theJoin = (MCPJoin *)[joins objectAtIndex:i]; - - if (i) { - [query appendString:@" and "]; - } - [query appendFormat:@"(%@ = %@)", [[theJoin destination] externalName], [connection quoteObject:[self valueForKey:[[theJoin origin] name]]]]; - } -// NSLog(@"in -[MCPObject getTargetOfRelation:%@]; query is %@...", [iRelation descriptionWithLocale:nil], query); - [query appendString:[self orderSQLForClassDescription:destinationDesc]]; -// NSLog(@"in -[MCPObject getTargetOfRelation:%@]; query is %@...", [iRelation descriptionWithLocale:nil], query); -/* - [query appendString:@" ORDER BY "]; - for (i=0; [ids count] != i; ++i) { // Generating the order : - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[ids objectAtIndex:i] externalName]]; - } -*/ - result = [connection queryString:query]; - [query release]; - -// Getting the results in proper objects: - if ([iRelation isToMany]) { // To-Many relation - NSMutableArray *theArrayRet = [[NSMutableArray alloc] init]; - - theRet = theArrayRet; - while (theRow = [result fetchRowAsDictionary]) { - MCPObject *theTarget = [[[destinationDesc representedClass] alloc] init]; - - [theTarget setPrimaryKey:theRow andFetchFromDB:connection]; - [theArrayRet insertObject:theTarget atIndex:[theArrayRet count]]; - [theTarget release]; - } - } - else { // To-One relation - theRow = [result fetchRowAsDictionary]; - if (theRow) { - theRet = [[[destinationDesc representedClass] alloc] init]; - [theRet setPrimaryKey:theRow andFetchFromDB:connection]; - } - else { - theRet = nil; - } - } - [theRet autorelease]; - return theRet; -} - -- (id) getTargetOfRelationNamed:(NSString *) iRelationName -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self getTargetOfRelation:theRelation] : nil; -} - -- (MCPDBReturnCode) setTarget:(id) iTarget forRelation:(MCPRelation *) iRelation -/*" This method will modify the DB content so that the corresponding relation is deleted. - If any value corresping to an attribute of self is modified in the DB, then it will update all attributes of self to reflect DB status. - -Obviuosly this method is taking care of only to-one relation. It will hence check that iRelation is a to-one relation starting from self. -Finally, you can use setTarget:nil forRelation:... to 'delete' a previously establlished relation (if the model and DB permit it)."*/ -{ - NSUInteger i; - MCPObject *oldTarget; - - if (! [connection isConnected]) { - return MCPDBReturnNoConnection; - } - if (! [classDescription isEqual:[iRelation origin]]) { - return MCPDBReturnWrongRelationOrigin; - } - if ([iRelation isToMany]) { - return MCPDBReturnWrongRelationCardinality; - } - - oldTarget = [self getTargetOfRelation:iRelation]; - if ((oldTarget) && ([iTarget isEqual:oldTarget])) { // No need to change the relation's target. - return MCPDBReturnOK; - } - for (i=0; [iRelation countOfJoins] != i; ++i) { - MCPJoin *theJoin = [iRelation objectInJoinsAtIndex:i]; - - if (([[theJoin origin] isPartOfKey]) && (![[theJoin destination] isPartOfKey])) { // Will change the destination... - [iTarget setValue:[self valueForKey:[[theJoin origin] name]] forKey:[[theJoin destination] name]]; - [oldTarget setValue:[oldTarget defaultValueForKey:[[theJoin destination] name]] forKey:[[theJoin destination] name]]; - } - else { // Will change the origin - [self setValue:[iTarget valueForKey:[[theJoin destination] name]] forKey:[[theJoin origin] name]]; - } - } - if ([iRelation ownsDestination]) { - [oldTarget deleteInDB]; - } - return MCPDBReturnOK; -} - -- (MCPDBReturnCode) setTarget:(id) iTarget forRelationNamed:(NSString *) iRelationName -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self setTarget:iTarget forRelation:theRelation] : MCPDBReturnNoSuchRelation; -} - -- (NSUInteger) countTargetForRelation:(MCPRelation *) iRelation -{ - NSMutableString *theQuery; - NSUInteger i; - NSArray *theJoinArray; - MCPResult *theResult; - NSDictionary *theRow; - - if ((! [connection isConnected]) || (! [classDescription isEqual:[iRelation origin]])){ - return 0; - } - theJoinArray = [iRelation joins]; - theQuery = [[NSMutableString alloc] initWithFormat:@"SELECT COUNT(1) FROM %@ WHERE ", [[iRelation destination] externalName]]; - for (i=0; [theJoinArray count] != i; ++i) { - MCPJoin *theJoin = (MCPJoin *)[theJoinArray objectAtIndex:i]; - if (i) { - [theQuery appendString:@" AND "]; - } - [theQuery appendFormat:@"( %@ = %@ )", [[theJoin destination] externalName], [connection quoteObject:[self valueForKey:[[theJoin origin] name]]]]; - } - theResult = [connection queryString:theQuery]; - [theQuery release]; - theRow = [theResult fetchRowAsDictionary]; - return [(NSNumber *)[theRow objectForKey:@"COUNT(1)"] unsignedIntegerValue]; -} - -- (NSUInteger) countTargetForRelationNamed:(NSString *) iRelationName -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self countTargetForRelation:theRelation] : 0; -} - - -- (MCPObject *) getTargetOfRelation:(MCPRelation *) iRelation atIndex:(NSUInteger) iIndex -/*" This method will return the specific object which is the iIndex'th object of th relation (after ordering the object using the destination identity). - -This method (like other assuming order in the relation targets) will be doubtfull if the class does NOT have a single identity attribute."*/ -{ - NSMutableString *query; - MCPClassDescription *destinationDesc; - NSArray *joins; - NSArray *keys; - NSArray *ids; - MCPResult *result; - NSUInteger i; - NSDictionary *theRow; - MCPObject *theRet; - - if (! iRelation) { - NSLog(@"Tried to get the target of a relation... but the relation object is nil"); - } - else { -// NSLog(@"Trying to get the target of the realtion : %@", [iRelation descriptionWithLocale:nil]); - } - if ((! [connection isConnected]) || (! [classDescription isEqual:[iRelation origin]])) { // Error condition. - return nil; - } - if (! [iRelation isToMany]) { - NSLog(@"Tried to use the -[MCPObject getTargetOfRelation:atIndex:] on a to-one relation... this does NOT works!!! You should use -[MCPObject getTargetOfRelation:] instead!!!"); - return [self getTargetOfRelation:iRelation]; - } -// Generating the query: - destinationDesc = [iRelation destination]; - keys = [destinationDesc primaryKeyAttributes]; - joins = [iRelation joins]; - ids = [destinationDesc identityAttributes]; - query = [[NSMutableString alloc] initWithString:@"SELECT "]; - for (i=0; [keys count] != i; ++i) { - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[keys objectAtIndex:i] externalName]]; - } - [query appendFormat:@" FROM %@ WHERE ", [destinationDesc externalName]]; - for (i=0; [joins count] != i; ++i) { - MCPJoin *theJoin = (MCPJoin *)[joins objectAtIndex:i]; - - if (i) { - [query appendString:@" and "]; - } - [query appendFormat:@"(%@ = %@)", [[theJoin destination] externalName], [connection quoteObject:[self valueForKey:[[theJoin origin] name]]]]; - } - [query appendString:[self orderSQLForClassDescription:destinationDesc]]; -/* - [query appendString:@" ORDER BY "]; - for (i=0; [ids count] != i; ++i) { // Generating the order : - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[ids objectAtIndex:i] externalName]]; - } -*/ - [query appendFormat:@" LIMIT 1 OFFSET %ld", iIndex]; - result = [connection queryString:query]; - [query release]; - -// Getting the results in proper objects: - theRow = [result fetchRowAsDictionary]; - if (theRow) { - theRet = (MCPObject *)[[[destinationDesc representedClass] alloc] init]; - [theRet setPrimaryKey:theRow andFetchFromDB:connection]; - [theRet autorelease]; - } - else { - theRet = nil; - } - return theRet; -} - -- (MCPObject *) getTargetOfRelationNamed:(NSString *) iRelationName atIndex:(NSUInteger) iIndex -/*"This is the equivalent of the getTargetOfRelation:atIndex:, but giving a relation name instead of the MCPRelation object itself."*/ -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self getTargetOfRelation:theRelation atIndex:iIndex]: nil; -} - -- (MCPDBReturnCode) addTarget:(MCPObject *) iTarget toRelation:(MCPRelation *) iRelation -/*" This method will modify the DB content so that the corresponding relation is added. -If any value corresping to an attribute of self i modified in the DB, then it will update all attributes of self to reflect DB status. - -Obviuosly this method is taking care of only to-many relation. It will hence check that iRelation is a to-many relation starting from self."*/ -{ - NSArray *joins; - NSUInteger i; - NSDictionary *saveReturn; - - if (! [connection isConnected]) { - return MCPDBReturnNoConnection; - } - if (! [classDescription isEqual:[iRelation origin]]) { - return MCPDBReturnWrongRelationOrigin; - } - if (! [iRelation isToMany]) { - return MCPDBReturnWrongRelationCardinality; - } - joins = [iRelation joins]; - for (i=0; [joins count] != i; ++i) { // Will change only values of iTarget, because it is a to-many relation. - MCPJoin *join = (MCPJoin *)[joins objectAtIndex:i]; - - [iTarget setValue:[self valueForKey:[[join origin] name]] forKey:[[join destination] name]]; - } - if (! [[iTarget connection] isConnected]) { - [iTarget setConnection:connection]; - } - saveReturn = [iTarget saveInDB]; - return (MCPDBReturnCode)[(NSNumber *)[saveReturn objectForKey:@"MCPDBReturnCode"] unsignedIntegerValue]; -} - -- (MCPDBReturnCode) addTarget:(MCPObject *) iTarget toRelationNamed:(NSString *) iRelationName -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self addTarget:iTarget toRelation:theRelation] : MCPDBReturnNoSuchRelation; -} - -- (MCPDBReturnCode) removeTarget:(MCPObject *) iTarget toRelation:(MCPRelation *) iRelation -/*" This method will modify the DB content so that the corresponding relation is removed. -If any value corresping to an attribute of self i modified in the DB, then it will update all attributes of self to reflect DB status. - -Obviuosly this method is taking care of only to-many relation. It will hence check that iRelation is a to-many relation starting from self."*/ -{ - NSArray *joins; - NSUInteger i; -// NSDictionary *saveReturn; - BOOL targetIsTarget = YES; - MCPDBReturnCode returnCode; - - if (! [connection isConnected]) { - return MCPDBReturnNoConnection; - } - if (! [classDescription isEqual:[iRelation origin]]) { - return MCPDBReturnWrongRelationOrigin; - } - if (! [iRelation isToMany]) { - return MCPDBReturnWrongRelationCardinality; - } - joins = [iRelation joins]; - for (i=0; [joins count] != i; ++i) { - MCPJoin *join = (MCPJoin *)[joins objectAtIndex:i]; - targetIsTarget = targetIsTarget && [[iTarget valueForKey:[[join destination] name]] isEqual:[self valueForKey:[[join origin] name]]]; - } - if (! targetIsTarget) { - return MCPDBReturnNotTarget; - } - if ([iRelation ownsDestination]) { // just delete the target from the DB. - returnCode = [iTarget deleteInDB]; - } - for (i=0; [joins count] != i; ++i) { // Put all the destination to default... - MCPJoin *join = (MCPJoin *)[joins objectAtIndex:i]; - - [iTarget setValue:[[join destination] defaultValue] forKey:[[join destination] name]]; - } - if (! [iRelation ownsDestination]) { - returnCode = [(NSNumber *)[[iTarget saveInDB] objectForKey:@"MCPDBReturnCode"] unsignedIntegerValue]; - } - return returnCode; -} - -- (MCPDBReturnCode) removeTarget:(MCPObject *) iTarget toRelationNamed:(NSString *) iRelationName -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self removeTarget:iTarget toRelation:theRelation] : MCPDBReturnNoSuchRelation; -} - -- (MCPDBReturnCode) removeTargetToRelation:(MCPRelation *) iRelation atIndex:(NSUInteger) iIndex -/*" This method will use an index t first query the object that it should remove from the relation, then uses the -[MCPObject removeTarget:toRelation:] - method to remove the object from the relation. If the index is out of bound (the returned object is nil), it will return MCPDBReturnNone, to signal - there was no object with this index in the relation."*/ -{ - MCPObject *target; - - if (! [connection isConnected]) { - return MCPDBReturnNoConnection; - } - if (! [classDescription isEqual:[iRelation origin]]) { - return MCPDBReturnWrongRelationOrigin; - } - if (! [iRelation isToMany]) { - return MCPDBReturnWrongRelationCardinality; - } - target = [self getTargetOfRelation:iRelation atIndex:iIndex]; - if (target) { - return [self removeTarget:target toRelation:iRelation]; - } - else { - return MCPDBReturnNone; - } -} - -- (MCPDBReturnCode) removeTargetToRelationNamed:(NSString *) iRelationName atIndex:(NSUInteger) iIndex -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self removeTargetToRelation:theRelation atIndex:iIndex] : MCPDBReturnNoSuchRelation; -} - -- (NSUInteger) indexOfTarget:(MCPObject *) iTarget inRelation:(MCPRelation *) iRelation -/*" Returns the index of the target object within the relation. Return NSNotFound if the objkect is NOT in the relation!!"*/ -{ - NSMutableString *query; - NSArray *joins; - NSArray *keys; - NSArray *ids; - NSUInteger i; - MCPResult *result; - NSDictionary *row; - NSMutableDictionary *targetKey; - BOOL targetIsTarget = YES; - - if ((! [connection isConnected]) || (! [classDescription isEqual:[iRelation origin]]) || (! [iRelation isToMany])) { // Checking the realtion object. - return NSNotFound; - } - query = [[NSMutableString alloc] initWithString:@"SELECT "]; - joins = [iRelation joins]; -// keys = [[iRelation destination] attributeKeys]; - keys = [[iRelation destination] primaryKeyAttributes]; - ids = [[iRelation destination] identityAttributes]; - for (i=0; [keys count] != i; ++i) { - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[keys objectAtIndex:i] externalName]]; - } - [query appendFormat:@"FROM %@ WHERE ", [[iRelation destination] externalName]]; - for (i=0; [joins count] != i; ++i) { - MCPJoin *join = (MCPJoin *)[joins objectAtIndex:i]; - - targetIsTarget = targetIsTarget && [[iTarget valueForKey:[[join destination] name]] isEqual:[self valueForKey:[[join origin] name]]]; - if (i) { - [query appendString:@" AND "]; - } - [query appendFormat:@"( %@ = %@ )", [[join destination] externalName], [connection quoteObject:[self valueForKey:[[join origin] name]]]]; - } - if (! targetIsTarget) { // Checking that iTarget belongs to the relation. - [query release]; - return NSNotFound; - } - [query appendString:[self orderSQLForClassDescription:[iRelation destination]]]; -/* - [query appendString:@" ORDER BY "]; - for (i=0; [ids count] != i; ++i) { - if (i) { - [query appendString:@", "]; - } - [query appendString:[(MCPAttribute *)[ids objectAtIndex:i] externalName]]; - } -*/ - targetKey = [[NSMutableDictionary alloc] init]; - for (i=0; [keys count] != i; ++i) { // Setting the targetKey to the row that should gives the target. - MCPAttribute *attribute = (MCPAttribute *)[keys objectAtIndex:i]; - - [targetKey setObject:[iTarget valueForKey:[attribute name]] forKey:[attribute externalName]]; - } - result = [connection queryString:query]; - [query release]; - i = 0; - while (row = [result fetchRowAsDictionary]) { - if ([targetKey isEqualToDictionary:row]) { // We have found the proper object return i (after cleaning up); - [targetKey release]; - return i; - } - ++i; - } - return NSNotFound; -} - -- (NSUInteger) indexOfTarget:(MCPObject *) iTarget inRelationNamed:(NSString *) iRelationName -/*" The equivalent of -[MCPObject indexOfTarget:inRelation:] but using relation name instead of a MCPRelation -object. Indeed after getting the MCPRelation object corresponding to the name, this method will only return -the result of the corresponding call to -[MCPObject indexOfTarget:inRelation:]."*/ -{ - MCPRelation *theRelation = [classDescription relationWithName:iRelationName]; - return (theRelation) ? [self indexOfTarget:iTarget inRelation:theRelation] : NSNotFound; -} - -#pragma mark Utility methods -- (id) defaultValueForKey:(NSString *) iKey -/*" This method will return the default value (object) for the given key."*/ -{ - MCPAttribute *theAttribute = [classDescription attributeWithName:iKey]; - - if ([theAttribute allowsNull]) { -// return [NSNull null]; - return nil; - } - else { - Class theAttrClass = [theAttribute valueClass]; - id theRet; - - if ([theAttrClass isSubclassOfClass:[NSNumber class]]) { - return [NSNumber numberWithInteger:0]; - } - theRet = [[[theAttrClass alloc] init] autorelease]; - if (nil == theRet) { - NSLog(@"in MCPObject defaultValueForKey:%@ , for object of class %@, (attribute of class %@) the return value will be nil!!!", iKey, [self className], theAttrClass); - } - return theRet; - } -} - - -- (NSDictionary *) primaryKey -/*" Returns a dictionary with the values of the primary key. "*/ -{ - NSMutableDictionary *theRet = [NSMutableDictionary dictionary]; - NSArray *theIdAttr = [classDescription primaryKeyAttributes]; - NSUInteger i; - - for (i=0; [theIdAttr count] != i; ++i) { - NSString *theKey = [(MCPAttribute *)[theIdAttr objectAtIndex:i] name]; - - if ([self valueForKey:theKey]) { - [theRet setObject:[self valueForKey:theKey] forKey:theKey]; - } - else { - [theRet setObject:[NSNull null] forKey:theKey]; - } - } - return [NSDictionary dictionaryWithDictionary:theRet]; -} - -#pragma mark Testing equality (VERY important for relation management) -- (BOOL) isEqual:(id) iObject -{ - NSArray *theIdAttr; - NSUInteger i; - BOOL theRet; - - if (self == iObject) { - return YES; - } - if ([self class] != [iObject class]) { - return NO; - } - theIdAttr = [classDescription identityAttributes]; - for (i = 0; [theIdAttr count] != i; ++i) { - MCPAttribute *theAttr = [theIdAttr objectAtIndex:i]; - - theRet = theRet && [[self valueForKey:[theAttr name]] isEqual:[iObject valueForKey:[theAttr name]]]; - } - return theRet; -} - - -#pragma mark Output -- (NSString *) description -{ - return [self descriptionWithLocale:nil]; -} - -- (NSString *) descriptionWithLocale:(NSDictionary *) locale -{ - NSMutableString *theOutput = [NSMutableString string]; - NSUInteger i; - NSArray *theAttributes = [classDescription attributes]; - BOOL trunc = [MCPConnection truncateLongField]; - - [theOutput appendFormat:@"MCPObject subclass : %@\n", [self className]]; - - for (i = 0; [theAttributes count] != i; ++i) - { - MCPAttribute *theAttribute = (MCPAttribute *) [theAttributes objectAtIndex:i]; - id theValue = [self valueForKey:[theAttribute name]]; - - if (trunc) { - if (([theValue isKindOfClass:[NSString class]]) && (kLengthOfTruncationForLog < [(NSString *)theValue length])) { - theValue = [theValue substringToIndex:kLengthOfTruncationForLog]; - } - else if (([theValue isKindOfClass:[NSData class]]) && (kLengthOfTruncationForLog < [(NSData *)theValue length])) { - theValue = [NSData dataWithBytes:[theValue bytes] length:kLengthOfTruncationForLog]; - } - } - - [theOutput appendFormat:@"\tAttribute %ld : name = %@, value = %@\n", i, [theAttribute name], theValue]; - } - - [theOutput appendString:@"\n"]; - - return theOutput; -} - -#pragma mark Ordering the array for relations -- (NSString *) orderSQLForClassDescription:(MCPClassDescription *) iClassDescription -{ - NSMutableArray *theAttributes = [[NSMutableArray alloc] initWithArray:[iClassDescription identityAttributes]]; - NSMutableString *theReturn = [NSMutableString string]; - NSUInteger i; - - for (i = 0; [[iClassDescription primaryKeyAttributes] count] != i; ++i) { - [theAttributes insertObject:[[iClassDescription primaryKeyAttributes] objectAtIndex:i] atIndex:[theAttributes count]]; - } - for (i = 0; [theAttributes count] != i; ++i) { - if (i) { - [theReturn appendString:@", "]; - } - else { - [theReturn appendString:@" ORDER BY "]; - } - [theReturn appendString:[(MCPAttribute *)[theAttributes objectAtIndex:i] externalName]]; - } - return theReturn; -} - -#pragma mark Anti-crash method... -- (void) setNilValueForKey:(NSString *) iKey -{ - NSLog(@"Try to set %@ to nil .... not possible, will set it to zero instead...", iKey); - [self setValue:[NSNumber numberWithInteger:0] forKey:iKey]; -} - -@end -- cgit v1.2.3