aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/MCPKit/MCPEntrepriseKit
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/MCPKit/MCPEntrepriseKit')
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.h106
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.m466
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.h47
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.m186
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+Private.h45
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.h93
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.m372
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.h36
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.m34
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.h74
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.m183
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.h35
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.m57
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.h84
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.m230
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.h126
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m1337
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation+Private.h46
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.h112
-rw-r--r--Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.m486
20 files changed, 4155 insertions, 0 deletions
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.h
new file mode 100644
index 00000000..1a474346
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.h
@@ -0,0 +1,106 @@
+//
+// $Id: MCPAttribute.h 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPAttribute.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+@class MCPModel;
+@class MCPClassDescription;
+@class MCPRelation;
+@class MCPJoin;
+
+@interface MCPAttribute : NSObject < NSCoding >
+{
+@protected
+ MCPClassDescription *classDescription; // ClassDescription of which the attribute is attribute
+ NSString *name; // Name of the attribute (Obj-C side)
+ Class valueClass; // Class used by the attribute (or NULL if the internal type is not an object)
+ NSString *internalType; // Name of the class, or type used for the class definition
+ NSString *externalName; // Name of the corresponding column in the DB
+ NSString *externalType; // Type used to store the attribute (in the DB)
+ unsigned int width; // Width (for storing by the DB)
+ BOOL allowsNull; // Attribute can be null
+ BOOL autoGenerated; // Attribute is auto generated by the DB
+ BOOL isPartOfKey; // Attribute is part of theprimary key of the class description
+ BOOL isPartOfIdentity; // Attribute is part of the idclass description of the class description
+ BOOL hasAccessor; // Does this attribute have an accessor
+ id defaultValue; // Default value of the attribute
+ NSMutableArray *joins; // An array of the joins using this attribute
+}
+
+#pragma mark Class methods
++ (void) initialize;
+
+#pragma mark Life cycle
+- (id) initForClassDescription:(MCPClassDescription *) iClassDescription withName:(NSString *) iName;
+- (void) dealloc;
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder;
+- (void) encodeWithCoder:(NSCoder *) encoder;
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName;
+- (void) setValueClass:(Class) iValueClass;
+- (void) setInternalType:(NSString *) iInternalType;
+- (void) setExternalType:(NSString *) iExternalType;
+- (void) setExternalName:(NSString *) iExternalName;
+- (void) setWidth:(unsigned int) iWidth;
+- (void) setAllowsNull:(BOOL) iAllowsNull;
+- (void) setAutoGenerated:(BOOL) iAutoGenerated;
+- (void) setIsPartOfKey:(BOOL) iIsPartOfKey;
+- (void) setIsPartOfIdentity:(BOOL) iIsPartOfIdentity;
+- (void) setHasAccessor:(BOOL) iHasAccessor;
+- (void) setDefaultValue:(id) iDefaultValue;
+- (void) insertObject:(MCPJoin *) iJoin inJoinsAtIndex:(unsigned int) index;
+- (void) removeObjectFromJoinsAtIndex:(unsigned int) index;
+//- (void) addRelation:(MCPRelation *) iRelation;
+//- (void) removeRelation:(MCPRelation *) iRelation;
+
+#pragma mark Getters
+- (MCPClassDescription *) classDescription;
+- (NSString *) name;
+- (Class) valueClass;
+- (NSString *) valueClassName;
+- (NSString *) internalType;
+- (NSString *) externalName;
+- (NSString *) externalType;
+- (unsigned int) width;
+- (BOOL) allowsNull;
+- (BOOL) autoGenerated;
+- (BOOL) isPartOfKey;
+- (BOOL) isPartOfIdentity;
+- (BOOL) hasAccessor;
+- (id) defaultValue;
+- (unsigned int) countOfJoins;
+- (MCPJoin *) objectInJoinsAtIndex:(unsigned int) index;
+- (unsigned int) indexOfJoinIdenticalTo:(id) iJoin;
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.m
new file mode 100644
index 00000000..34b50284
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPAttribute.m
@@ -0,0 +1,466 @@
+//
+// $Id: MCPAttribute.m 927 2009-06-24 10:53:07Z stuart02 $
+//
+// MCPAttribute.m
+// MCPkit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPAttribute.h"
+
+#import "MCPEntrepriseNotifications.h"
+
+#import "MCPModel.h"
+#import "MCPClassDescription.h"
+#import "MCPRelation.h"
+#import "MCPJoin.h"
+
+static NSArray *MCPRecognisedInternalType;
+
+@interface MCPAttribute (Private)
+
+- (void)setValueClassName:(NSString *) iClassName;
+
+@end
+
+@implementation MCPAttribute
+
+#pragma mark Class methods
++ (void) initialize
+{
+ if (self == [MCPAttribute class]) {
+ [self setVersion:010101]; // Ma.Mi.Re -> MaMiRe
+ MCPRecognisedInternalType = [[NSArray alloc] initWithObjects:@"NSCalendarDate", @"NSData", @"NSNumber", @"NSString", nil];
+ [self setKeys:[NSArray arrayWithObject:@"internalType"] triggerChangeNotificationsForDependentKey:@"valueClassName"];
+ [self setKeys:[NSArray arrayWithObject:@"valueClassName"] triggerChangeNotificationsForDependentKey:@"internalType"];
+ }
+ return;
+}
+
+
+#pragma mark Life cycle
+- (id) initForClassDescription:(MCPClassDescription *) iClassDescription withName:(NSString *) iName
+{
+ self = [super init];
+ {
+ classDescription = iClassDescription;
+ [self setName:iName];
+// relations = (NSMutableArray *)(CFArrayCreateMutable (kCFAllocatorDefault, 0, NULL));
+ joins = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+// NSArray *theRelations;
+// unsigned int i;
+
+ [name release];
+ [internalType release];
+ [externalName release];
+ [externalType release];
+ [defaultValue release];
+/*
+ while ([relations count]) {
+ [(MCPRelation *)[relations objectAtIndex:0] unjoinAttribute:self];
+ }
+// By now relation should be empty anyway...
+ [relations release];
+ */
+ while ([joins count]) {
+ [[self objectInJoinsAtIndex:0] invalidate];
+ }
+ // By now the joins array should be empty
+ [joins release];
+ [super dealloc];
+}
+
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder
+{
+ self = [super init];
+ if ((self) && ([decoder allowsKeyedCoding])) {
+ NSString *theClassName = [decoder decodeObjectForKey:@"MCPvalueClassName"];
+
+ classDescription = [decoder decodeObjectForKey:@"MCPclassDescription"];
+ [self setName:[decoder decodeObjectForKey:@"MCPname"]];
+ if (theClassName) {
+ [self setValueClass:NSClassFromString(theClassName)];
+ }
+ [self setInternalType:[decoder decodeObjectForKey:@"MCPinternalType"]];
+ [self setExternalName:[decoder decodeObjectForKey:@"MCPexternalName"]];
+ [self setExternalType:[decoder decodeObjectForKey:@"MCPexternalType"]];
+ [self setWidth:(unsigned int)[decoder decodeInt32ForKey:@"MCPwidth"]];
+ [self setAllowsNull:[decoder decodeBoolForKey:@"MCPallowsNull"]];
+ [self setAutoGenerated:[decoder decodeBoolForKey:@"MCPautoGenerated"]];
+ [self setIsPartOfKey:[decoder decodeBoolForKey:@"MCPisPartOfKey"]];
+ [self setIsPartOfIdentity:[decoder decodeBoolForKey:@"MCPisPartOfIdentity"]];
+ [self setHasAccessor:[decoder decodeBoolForKey:@"MCPhasAccessor"]];
+ [self setDefaultValue:[decoder decodeObjectForKey:@"MCPdefaultValue"]];
+// Not sure that the next line is working (getting an array holding weak references), hence doing the thing expelcitly:
+// relations = [[decoder decodeObjectForKey:@"MCPrelations"] retain];
+// relations = (NSMutableArray *)(CFArrayCreateMutable (kCFAllocatorDefault, 0, NULL));
+// [relations addObjectsFromArray:[decoder decodeObjectForKey:@"MCPrelations"]];
+ joins = [[NSMutableArray alloc] init]; // Will be filled in when the relations are read in.
+ }
+ else {
+ NSLog(@"For some reason, unable to decode MCPAttribute from the coder!!!");
+ }
+// NSLog(@"MAKING a new object : %@", self);
+ return self;
+}
+
+- (void) encodeWithCoder:(NSCoder *) encoder
+{
+ NSString *theValueClassName;
+
+ if (! [encoder allowsKeyedCoding]) {
+ NSLog(@"In MCPAttribute -encodeWithCoder : Unable to encode to a non-keyed encoder!!, will not perform encoding!!");
+ return;
+ }
+// theValueClassName = (valueClass) ? [valueClass className] : nil;
+ theValueClassName = (valueClass) ? NSStringFromClass(valueClass) : nil;
+ [encoder encodeObject:[self classDescription] forKey:@"MCPclassDescription"];
+ [encoder encodeObject:[self name] forKey:@"MCPname"];
+ if (theValueClassName) {
+ [encoder encodeObject:theValueClassName forKey:@"MCPvalueClassName"];
+ }
+ [encoder encodeObject:[self internalType] forKey:@"MCPinternalType"];
+ [encoder encodeObject:[self externalName] forKey:@"MCPexternalName"];
+ [encoder encodeObject:[self externalType] forKey:@"MCPexternalType"];
+ [encoder encodeInt32:(int32_t)[self width] forKey:@"MCPwidth"];
+ [encoder encodeBool:[self allowsNull] forKey:@"MCPallowsNull"];
+ [encoder encodeBool:[self autoGenerated] forKey:@"MCPautoGenerated"];
+ [encoder encodeBool:[self isPartOfKey] forKey:@"MCPisPartOfKey"];
+ [encoder encodeBool:[self isPartOfIdentity] forKey:@"MCPisPartOfIdentity"];
+ [encoder encodeBool:[self hasAccessor] forKey:@"MCPhasAccessor"];
+ [encoder encodeObject:[self defaultValue] forKey:@"MCPdefaultValue"];
+// [encoder encodeObject:relations forKey:@"MCPrelation"];
+ // We don't have to save the joins here ... the joins are saving there attributes.
+ // The links are recreated when the joins are decoded.
+}
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName
+{
+ if (iName != name) {
+ [name release];
+ name = [iName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setValueClass:(Class) iValueClass
+{
+ if (iValueClass != valueClass) {
+ valueClass = iValueClass;
+ if (valueClass) { // Not nil : set the internalType accrodingly.
+ // [internalType release];
+ // internalType = [[valueClass className] copy];
+// [self setValue:[NSString stringWithString:[valueClass className]] forKey:@"internalType"];
+ [self setValue:[NSString stringWithString:NSStringFromClass(valueClass)] forKey:@"internalType"];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setInternalType:(NSString *) iInternalType
+{
+ if (iInternalType != internalType) {
+ [internalType release];
+ internalType = [iInternalType retain];
+ if ([MCPRecognisedInternalType containsObject:internalType]) {
+ [self setValueClass:NSClassFromString(internalType)];
+// By itself does NOT provide observers the update.
+// but see setKeys:triggerChangeNotificationsForDependentKey... (in +initialize).
+ }
+ else {
+ [self setValueClass:nil];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setExternalType:(NSString *) iExternalType
+{
+ if (iExternalType != externalType) {
+ [externalType release];
+ externalType = [iExternalType retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setExternalName:(NSString *) iExternalName
+{
+ if (iExternalName != externalName) {
+ [externalName release];
+ externalName = [iExternalName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setWidth:(unsigned int) iWidth
+{
+ if (iWidth != width) {
+ width = iWidth;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setAllowsNull:(BOOL) iAllowsNull
+{
+ if (iAllowsNull != allowsNull) {
+ allowsNull = iAllowsNull;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setAutoGenerated:(BOOL) iAutoGenerated
+{
+ if (iAutoGenerated != autoGenerated) {
+ autoGenerated = iAutoGenerated;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setIsPartOfKey:(BOOL) iIsPartOfKey
+{
+ if (iIsPartOfKey != isPartOfKey) {
+ isPartOfKey = iIsPartOfKey;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setIsPartOfIdentity:(BOOL) iIsPartOfIdentity
+{
+ if (iIsPartOfIdentity != isPartOfIdentity) {
+ isPartOfIdentity = iIsPartOfIdentity;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setHasAccessor:(BOOL) iHasAccessor
+{
+ if (iHasAccessor != hasAccessor) {
+ hasAccessor = iHasAccessor;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) setDefaultValue:(id) iDefaultValue
+{
+ if (iDefaultValue != defaultValue) {
+ [defaultValue release];
+ defaultValue = [iDefaultValue retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[classDescription model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:classDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPAttributeChangedNotification object:self];
+ }
+}
+
+- (void) insertObject:(MCPJoin *) iJoin inJoinsAtIndex:(unsigned int) index
+{
+ [joins insertObject:iJoin atIndex:index];
+}
+
+- (void) removeObjectFromJoinsAtIndex:(unsigned int) index
+{
+ [joins removeObjectAtIndex:index];
+}
+
+/*
+- (void) addRelation:(MCPRelation *) iRelation
+{
+// Following implementation make sure that a given relation is only added once... but I don't see the reason for that to be true.
+ /* if (NSNotFound == [relations indexOfObjectIdenticalTo:iRelation]) {
+ [relations addObject:iRelation];
+ }
+ *//*
+ [relations addObject:iRelation];
+}
+
+- (void) removeRelation:(MCPRelation *) iRelation
+{
+// Following implementation needs only one reference to a given relation to be working properly (not true)
+// [relations removeObjectIdenticalTo:iRelation];
+ unsigned int i;
+
+ i = [relations indexOfObjectIdenticalTo:iRelation];
+ if (NSNotFound != i) {
+ [relations removeObjectAtIndex:i];
+ }
+// If the relation is there more than once, remove it only once.
+}
+*/
+
+#pragma mark Getters
+- (MCPClassDescription *) classDescription
+{
+ return classDescription;
+}
+
+- (NSString *) name
+{
+ return name;
+}
+
+- (Class) valueClass
+{
+ return valueClass;
+}
+
+- (NSString *) valueClassName
+{
+ return NSStringFromClass(valueClass);
+}
+
+- (NSString *) internalType
+{
+ return internalType;
+}
+
+- (NSString *) externalName
+{
+ return externalName;
+}
+
+- (NSString *) externalType
+{
+ return externalType;
+}
+
+- (unsigned int) width
+{
+ return width;
+}
+
+- (BOOL) allowsNull
+{
+ return allowsNull;
+}
+
+- (BOOL) autoGenerated
+{
+ return autoGenerated;
+}
+
+- (BOOL) isPartOfKey
+{
+ return isPartOfKey;
+}
+
+- (BOOL) isPartOfIdentity
+{
+ return isPartOfIdentity;
+}
+
+- (BOOL) hasAccessor
+{
+ return hasAccessor;
+}
+
+- (id) defaultValue
+{
+ return defaultValue;
+}
+
+- (unsigned int) countOfJoins
+{
+ return [joins count];
+}
+
+- (MCPJoin *) objectInJoinsAtIndex:(unsigned int) index
+{
+ return (MCPJoin *)((NSNotFound != index) ? [joins objectAtIndex:index] : nil);
+}
+
+- (unsigned int) indexOfJoinIdenticalTo:(id) iJoin
+{
+ return [joins indexOfObjectIdenticalTo:iJoin];
+}
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject
+// Equal to another attribute, if they have the same name and same class description.
+// Equal to a string (NSString), if the name of the attribute is equal to the string.
+{
+ if ([iObject isKindOfClass:[MCPAttribute class]]) {
+ MCPAttribute *theAttribute = (MCPAttribute *) iObject;
+
+ return ([name isEqualToString:[theAttribute name]]) && ([classDescription isEqual:[theAttribute classDescription]]);
+ }
+ if ([iObject isKindOfClass:[NSString class]]) {
+ return [name isEqualToString:(NSString *)iObject];
+ }
+ return NO;
+}
+
+#pragma mark For debugging the retain counting
+- (id) retain
+{
+ [super retain];
+ return self;
+}
+
+- (void) release
+{
+ [super release];
+ return;
+}
+
+@end
+
+@implementation MCPAttribute (Private)
+
+- (void)setValueClassName:(NSString *) iClassName
+{
+ if (NSClassFromString(iClassName) != valueClass) {
+ [self setValueClass:NSClassFromString(iClassName)];
+ }
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.h
new file mode 100644
index 00000000..adc66141
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.h
@@ -0,0 +1,47 @@
+//
+// $Id: MCPClassDescription+MCPEntreprise.h 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPClassDescription+MCPEntreprise.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 01/11/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+#import "MCPClassDescription.h"
+
+@interface MCPClassDescription (MCPEntreprise)
+
+#pragma mark Pseudo getters (for NSClassDescription overload)
+- (NSArray *) attributeKeys;
+- (NSString *) inverseRelationshipKey:(NSString *) relationshipKey;
+- (NSArray *) toManyRelationshipKeys;
+- (NSArray *) toOneRelationshipKeys;
+
+#pragma mark Specifics for MCPObject
+- (NSArray *) primaryKeyAttributes;
+- (NSArray *) identityAttributes;
+- (MCPAttribute *) attributeWithName: (NSString *) iName;
+- (MCPRelation *) relationWithName:(NSString *) iRelationName;
+- (BOOL) singleIntAutoGenKey;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.m
new file mode 100644
index 00000000..4c912a1f
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+MCPEntreprise.m
@@ -0,0 +1,186 @@
+//
+// $Id: MCPClassDescription+MCPEntreprise.m 927 2009-06-24 10:53:07Z stuart02 $
+//
+// MCPClassDescription+MCPEntreprise.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 01/11/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPClassDescription+MCPEntreprise.h"
+
+#import "MCPAttribute.h"
+#import "MCPRelation.h"
+
+@implementation MCPClassDescription (MCPEntreprise)
+
+#pragma mark Pseudo getters (for NSClassDescription overload)
+- (NSArray *) attributeKeys
+{
+ NSArray *theRet;
+ NSMutableArray *theKeys =[[NSMutableArray alloc] init];
+ unsigned int i;
+
+ for (i=0; i != [self countOfAttributes]; ++i) {
+ [theKeys insertObject:[(MCPAttribute *)[self objectInAttributesAtIndex:i] name] atIndex:i];
+ }
+ theRet = [NSArray arrayWithArray:theKeys];
+ [theKeys release];
+ return theRet;
+}
+
+- (NSString *) inverseRelationshipKey:(NSString *) relationshipKey
+{
+ unsigned int index = [self indexOfRelation:relationshipKey];
+
+ if (NSNotFound != index) {
+ MCPRelation *theRelation;
+
+ theRelation = (MCPRelation *)[self objectInRelationsAtIndex:index];
+ return [[theRelation inverseRelation] name];
+ }
+ return nil;
+}
+
+- (NSArray *) toManyRelationshipKeys
+{
+ NSArray *theRet;
+ NSMutableArray *theToManyRel = [[NSMutableArray alloc] init];
+ unsigned int i, j;
+
+ j=0;
+ for (i=0; i != [self countOfRelations]; ++i) {
+ MCPRelation *theRelation = (MCPRelation *)[self objectInRelationsAtIndex:i];
+
+ if ([theRelation isToMany]) {
+ [theToManyRel insertObject:[theRelation name] atIndex:j];
+ ++j;
+ }
+ }
+ theRet = [NSArray arrayWithArray:theToManyRel];
+ [theToManyRel release];
+ return theRet;
+}
+
+- (NSArray *) toOneRelationshipKeys;
+{
+ NSArray *theRet;
+ NSMutableArray *theToOneRel = [[NSMutableArray alloc] init];
+ unsigned int i, j;
+
+ j=0;
+ for (i=0; i != [self countOfRelations]; ++i) {
+ MCPRelation *theRelation = (MCPRelation *)[self objectInRelationsAtIndex:i];
+
+ if (! [theRelation isToMany]) {
+ [theToOneRel insertObject:[theRelation name] atIndex:j];
+ ++j;
+ }
+ }
+ theRet = [NSArray arrayWithArray:theToOneRel];
+ [theToOneRel release];
+ return theRet;
+}
+
+#pragma mark Specifics for MCPObject
+- (NSArray *) primaryKeyAttributes
+{
+ NSMutableArray *theRet = [NSMutableArray array];
+ unsigned int i, j;
+
+ j = 0;
+ for (i=0; i != [self countOfAttributes]; ++i) {
+ MCPAttribute *theAttribute = (MCPAttribute *)[self objectInAttributesAtIndex:i];
+
+ if ([theAttribute isPartOfKey]) {
+ [theRet insertObject:theAttribute atIndex:j];
+ ++j;
+ }
+ }
+ return (NSArray *)theRet;
+}
+
+- (NSArray *) identityAttributes
+{
+ NSMutableArray *theRet = [NSMutableArray array];
+ unsigned int i, j;
+
+ j = 0;
+ for (i=0; i != [self countOfAttributes]; ++i) {
+ MCPAttribute *theAttribute = (MCPAttribute *)[self objectInAttributesAtIndex:i];
+
+ if ([theAttribute isPartOfIdentity]) {
+ [theRet insertObject:theAttribute atIndex:j];
+ ++j;
+ }
+ }
+ return (NSArray *)theRet;
+}
+
+- (MCPAttribute *) attributeWithName: (NSString *) iName
+{
+// This type of implementation is NOT working : most likely the isEqual method is called on iName rather than on the objects of the array
+/*
+ unsigned int index = [self indexOfAttribute:iName];
+
+ return (NSNotFound != index) ? (MCPAttribute *)[self objectInAttributesAtIndex:index] : nil ;
+*/
+ unsigned int i;
+
+ for (i = 0; [attributes count] != i; ++i) {
+ if ([[(MCPAttribute *)[attributes objectAtIndex:i] name] isEqualToString:iName]) {
+ return (MCPAttribute *)[attributes objectAtIndex:i];
+ }
+ }
+ return nil;
+}
+
+- (MCPRelation *) relationWithName:(NSString *) iRelationName
+{
+// This type of implementation is NOT working : most likely the isEqual method is called on iName rather than on the objects of the array
+/* unsigned int index = [relations indexOfObject:iRelationName];
+
+ return (NSNotFound != index) ? (MCPRelation *)[relations objectAtIndex:index] : nil;
+*/
+ unsigned int i;
+
+ for (i = 0; [relations count] != i; ++i) {
+ if ([[(MCPRelation *)[relations objectAtIndex:i] name] isEqualToString:iRelationName]) {
+ return (MCPRelation *)[relations objectAtIndex:i];
+ }
+ }
+ return nil;
+}
+
+- (BOOL) singleIntAutoGenKey
+{
+ NSArray *theKeys = [self primaryKeyAttributes];
+
+ if (1 == [theKeys count]) {
+ MCPAttribute *theSingleKey = (MCPAttribute *)[theKeys objectAtIndex:0];
+
+ return [theSingleKey autoGenerated] && [[theSingleKey externalType] isEqualToString:@"INT"];
+ }
+ return NO;
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+Private.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+Private.h
new file mode 100644
index 00000000..46c8a626
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription+Private.h
@@ -0,0 +1,45 @@
+//
+// $Id: MCPClassDescription+Private.h 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPClassDescription+Private.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPClassDescription.h"
+
+@interface MCPClassDescription (Private)
+
+#pragma mark Setters
+- (void) setAttributes:(NSArray *) iAttributes;
+- (void) setRelations:(NSArray *) iRelations;
+- (void) insertObject:(MCPRelation *) iRelation inIncomingsAtIndex:(unsigned int) index;
+- (void) removeObjectFromIncomingsAtIndex:(unsigned int) index;
+
+#pragma mark Getters
+- (NSArray *) incomings;
+- (unsigned int) countOfIncomings;
+- (MCPRelation *) objectInIncomingsAtIndex:(unsigned int) index;
+- (unsigned int) indexOfIncoming:(id) iRelation;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.h
new file mode 100644
index 00000000..c5119797
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.h
@@ -0,0 +1,93 @@
+//
+// $Id: MCPClassDescription.h 927 2009-06-24 10:53:07Z stuart02 $
+//
+// MCPClassDescription.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+@class MCPModel;
+@class MCPAttribute;
+@class MCPRelation;
+@class MCPRelation;
+
+@interface MCPClassDescription : NSClassDescription <NSCoding>
+{
+@protected
+ MCPModel *model; // The model where we stand
+ NSString *name; // Name of the class (can not use className, as it is already used by NSObject).
+ NSString *externalName; // Name of the table for storage
+ NSMutableArray *attributes; // array of the attributes of the class description
+ NSMutableArray *relations; // array of the relations of the class description (both origin and destination)
+ NSMutableArray *incomings; // array if the INCOMMING relation (just to be sure we are able to invalidate those if necessary)
+ Class representedClass; // the class object that the description represents.
+}
+
+// This correspond to the method singleIntAutoGenKey in the category MCPEntreprise... which name should I change...
+
+#pragma mark Class methods
++ (void) initialize;
+
+#pragma mark Life cycle
+- (id) initInModel:(MCPModel *) iModel withName:(NSString *) iName;
+- (void) dealloc;
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder;
+- (void) encodeWithCoder:(NSCoder *) encoder;
+
+#pragma mark Making new attributes and relations
+- (MCPAttribute *) addNewAttributeWithName:(NSString *) iName inPosition:(int) index;
+- (MCPRelation *) addNewRelationTo:(MCPClassDescription *) iTo name:(NSString *) iName inPostion:(int) index;
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName;
+- (void) setExternalName:(NSString *) iExternalName;
+- (void) insertObject:(MCPAttribute *) iAttribute inAttributesAtIndex:(unsigned int) index;
+- (void) removeObjectFromAttributesAtIndex:(unsigned int) index;
+- (void) insertObject:(MCPRelation *) iRelation inRelationsAtIndex:(unsigned int) index;
+- (void) removeObjectFromRelationsAtIndex:(unsigned int) index;
+
+#pragma mark Getters
+- (MCPModel *) model;
+- (NSString *) name;
+- (NSString *) externalName;
+- (NSArray *) attributes;
+- (unsigned int) countOfAttributes;
+- (MCPAttribute *) objectInAttributesAtIndex:(unsigned int) index;
+- (unsigned int) indexOfAttribute:(id) iAttribute;
+- (NSArray *) relations;
+- (unsigned int) countOfRelations;
+- (MCPRelation *) objectInRelationsAtIndex:(unsigned int) index;
+- (unsigned int) indexOfRelation:(id) iRelation;
+- (Class) representedClass;
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject;
+
+#pragma mark Output for logging
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.m
new file mode 100644
index 00000000..fcd79e01
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPClassDescription.m
@@ -0,0 +1,372 @@
+//
+// $Id: MCPClassDescription.m 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPClassDescription.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPClassDescription.h"
+#import "MCPClassDescription+Private.h"
+
+#import "MCPEntrepriseNotifications.h"
+
+#import "MCPModel.h"
+#import "MCPAttribute.h"
+#import "MCPRelation.h"
+
+@implementation MCPClassDescription
+
+#pragma mark Class methods
++ (void) initialize
+{
+ if (self = [MCPClassDescription class]) {
+ [self setVersion:010101]; // Major.Minor.Revision -> MaMiRe
+ }
+ return;
+}
+
+#pragma mark Life cycle
+- (id) initInModel:(MCPModel *) iModel withName:(NSString *) iName
+{
+ self = [super init];
+ if (self) {
+ model = iModel;
+ [self setName:iName];
+ attributes = [[NSMutableArray alloc] init];
+ relations = [[NSMutableArray alloc] init];
+ incomings = [[NSMutableArray alloc] init];
+ representedClass = nil;
+ // NSLog(@"MAKING a new object : %@", self);
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+// NSArray *theRelations;
+// unsigned int i;
+
+ [name release];
+ [externalName release];
+ [attributes release];
+ while ([relations count]) {
+ [(MCPRelation *)[relations objectAtIndex:0] invalidateRelation];
+ }
+ [relations release];
+ while ([incomings count]) {
+ [(MCPRelation *)[incomings objectAtIndex:0] invalidateRelation];
+ }
+ [incomings release];
+ [super dealloc];
+}
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder
+{
+ self = [super init];
+ if ((self) && ([decoder allowsKeyedCoding])) {
+ model = [decoder decodeObjectForKey:@"MCPmodel"];
+// NSLog(@"in MCPClassDescription initWithCoder, model = %@ (pointer = %p)", model, model);
+ [self setName:[decoder decodeObjectForKey:@"MCPname"]];
+ [self setExternalName:[decoder decodeObjectForKey:@"MCPexternalName"]];
+ [self setAttributes:[decoder decodeObjectForKey:@"MCPattributes"]];
+// [self setRelations:[decoder decodeObjectForKey:@"MCPrelations"]];
+ relations = [[NSMutableArray alloc] init];
+ incomings = [[NSMutableArray alloc] init];
+ representedClass = nil;
+ [decoder decodeObjectForKey:@"MCPrelations"]; // The relation get linked properly while initted.
+ }
+ else {
+ NSLog(@"For some reason, unable to decode MCPClassDescription from the coder!!!");
+ }
+
+ return self;
+}
+
+- (void) encodeWithCoder:(NSCoder *) encoder
+{
+ if (! [encoder allowsKeyedCoding]) {
+ NSLog(@"In MCPClassDescription -encodeWithCoder : Unable to encode to a non-keyed encoder!!, will not perform encoding!!");
+ return;
+ }
+// [encoder encodeObject:[self model] forKey:@"MCPmodel"];
+ [encoder encodeConditionalObject:[self model] forKey:@"MCPmodel"];
+ [encoder encodeObject:[self name] forKey:@"MCPname"];
+ [encoder encodeObject:[self externalName] forKey:@"MCPexternalName"];
+ [encoder encodeObject:[self attributes] forKey:@"MCPattributes"];
+ [encoder encodeObject:[self relations] forKey:@"MCPrelations"];
+ [encoder encodeObject:@"1.1.1" forKey:@"MCPversion"];
+ return;
+}
+
+#pragma mark Making new attributes and relations
+- (MCPAttribute *) addNewAttributeWithName:(NSString *) iName inPosition:(int) index
+{
+ MCPAttribute *theAttribute = [[MCPAttribute alloc] initForClassDescription:self withName:iName];
+
+// [self addAttribute:theAttribute];
+ [self insertObject:theAttribute inAttributesAtIndex:(index < 0) ? ([self countOfAttributes] + index + 1) : index];
+ [theAttribute release];
+ return theAttribute;
+}
+
+- (MCPRelation *) addNewRelationTo:(MCPClassDescription *) iTo name:(NSString *) iName inPostion:(int) index
+{
+ MCPRelation *theRelation = [[MCPRelation alloc] initWithName:iName from:self to:iTo];
+
+ [theRelation release];
+ return theRelation;
+}
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName
+{
+ if (iName != name) {
+ [name release];
+ name = [iName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+ representedClass = nil;
+ }
+}
+
+- (void) setExternalName:(NSString *) iExternalName
+{
+ if (iExternalName != externalName) {
+ [externalName release];
+ externalName = [iExternalName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+ }
+}
+
+- (void) insertObject:(MCPAttribute *) iAttribute inAttributesAtIndex:(unsigned int) index
+{
+ [attributes insertObject:iAttribute atIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+}
+
+- (void) removeObjectFromAttributesAtIndex:(unsigned int) index
+{
+ [attributes removeObjectAtIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+}
+
+- (void) insertObject:(MCPRelation *) iRelation inRelationsAtIndex:(unsigned int) index
+{
+ [relations insertObject:iRelation atIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+}
+
+- (void) removeObjectFromRelationsAtIndex:(unsigned int) index
+{
+ [relations removeObjectAtIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+}
+
+#pragma mark Getters
+- (MCPModel *) model
+{
+ return model;
+}
+
+- (NSString *) name
+{
+ return name;
+}
+
+- (NSString *) externalName
+{
+ return externalName;
+}
+
+- (NSArray *) attributes
+{
+ return [NSArray arrayWithArray:attributes];
+}
+
+- (unsigned int) countOfAttributes
+{
+ return [attributes count];
+}
+
+- (MCPAttribute *) objectInAttributesAtIndex:(unsigned int) index
+{
+ return (MCPAttribute *)((NSNotFound != index) ? [attributes objectAtIndex:index] : nil);
+}
+
+- (unsigned int) indexOfAttribute:(id) iAttribute
+{
+ return [attributes indexOfObject:iAttribute];
+}
+
+- (NSArray *) relations
+{
+ return [NSArray arrayWithArray:relations];
+}
+
+- (unsigned int) countOfRelations
+{
+ return [relations count];
+}
+
+- (MCPRelation *) objectInRelationsAtIndex:(unsigned int) index
+{
+ return (MCPRelation *)((NSNotFound != index) ? [relations objectAtIndex:index] : nil);
+}
+
+- (unsigned int) indexOfRelation:(id) iRelation
+{
+ return [relations indexOfObject:iRelation];
+}
+
+- (Class) representedClass
+{
+ if (representedClass) {
+ return representedClass;
+ }
+ representedClass = NSClassFromString(name);
+ return representedClass;
+}
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject
+// Equal to another class description if they have the same name.
+// Equal to a string if the string is equal to the className of the class description.
+{
+ if ([iObject isKindOfClass:[MCPClassDescription class]]) {
+ return [name isEqualToString:[(MCPClassDescription *)iObject name]];
+ }
+ if ([iObject isKindOfClass:[NSString class]]) {
+ return [name isEqualToString:(NSString *)iObject];
+ }
+ return NO;
+}
+
+/*
+- (NSString *) description
+{
+ return [NSString stringWithFormat:@"<MCPClassDescription for class named %@ : %p>", [self name], self];
+}
+
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale
+{
+ return [self description];
+}
+*/
+
+#pragma mark Output for logging
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale
+{
+ NSMutableString *theOutput = [NSMutableString string];
+ unsigned i;
+
+ [theOutput appendFormat:@"MCPClassDescription for class : %@ (table : %@)\n", [self name], [self externalName]];
+ for (i=0; [attributes count] != i; ++i) {
+ MCPAttribute *theAttribute = (MCPAttribute *) [attributes objectAtIndex:i];
+
+ [theOutput appendFormat:@"attribute %u, name = %@, column = %@. Allows null : %c\n", i, [theAttribute name], [theAttribute externalName], ([theAttribute allowsNull] ? 'Y' : 'N')];
+ }
+ return theOutput;
+}
+
+#pragma mark For debugging the retain counting
+- (id) retain
+{
+ [super retain];
+ return self;
+}
+
+- (void) release
+{
+ [super release];
+ return;
+}
+
+@end
+
+@implementation MCPClassDescription (Private)
+
+#pragma mark Setters
+- (void) setAttributes:(NSArray *) iAttributes
+{
+ if (iAttributes != attributes) {
+ [attributes release];
+ attributes = [[NSMutableArray alloc] initWithArray:iAttributes];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+ }
+}
+
+- (void) setRelations:(NSArray *) iRelations
+{
+ if (iRelations != relations) {
+ [relations release];
+ relations = [[NSMutableArray alloc] initWithArray:iRelations];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:model];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:self];
+ }
+}
+
+- (void) insertObject:(MCPRelation *) iRelation inIncomingsAtIndex:(unsigned int) index
+{
+ if ([iRelation destination] == self) {
+ [incomings insertObject:iRelation atIndex:index];
+ }
+ else {
+ NSLog(@"in -[MCPClassDescription+Private insertObject:inIncomingsAtIndex:]. ERRROR : self is NOT the destination of the relation");
+ }
+}
+
+- (void) removeObjectFromIncomingsAtIndex:(unsigned int) index
+{
+ [incomings removeObjectAtIndex:index];
+}
+
+#pragma mark Getters
+- (NSArray *) incomings
+{
+ return [NSArray arrayWithArray:incomings];
+}
+
+- (unsigned int) countOfIncomings
+{
+ return [incomings count];
+}
+
+- (MCPRelation *) objectInIncomingsAtIndex:(unsigned int) index
+{
+ return (MCPRelation *)[incomings objectAtIndex:index];
+}
+
+- (unsigned int) indexOfIncoming:(id) iRelation
+{
+ return [incomings indexOfObject:iRelation];
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.h
new file mode 100644
index 00000000..496a448c
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.h
@@ -0,0 +1,36 @@
+//
+// $Id: MCPEntrepriseNotifications.h 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPEnterpriseNotifications.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+#pragma mark Name for notification
+
+extern NSString *MCPModelChangedNotification;
+extern NSString *MCPClassDescriptionChangedNotification;
+extern NSString *MCPAttributeChangedNotification;
+extern NSString *MCPRelationChangedNotification;
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.m
new file mode 100644
index 00000000..63e580ad
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPEntrepriseNotifications.m
@@ -0,0 +1,34 @@
+//
+// $Id: MCPEntrepriseNotifications.m 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPEnterpriseNotifications.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+NSString *MCPModelChangedNotification = @"Model has changed";
+NSString *MCPClassDescriptionChangedNotification = @"ClassDescription has changed";
+NSString *MCPAttributeChangedNotification = @"Attribute has changed";
+NSString *MCPRelationChangedNotification = @"Relation has changed";
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.h
new file mode 100644
index 00000000..c3882fee
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.h
@@ -0,0 +1,74 @@
+//
+// $Id: MCPJoin.h 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPJoin.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 18/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+@class MCPModel;
+@class MCPClassDescription;
+@class MCPAttribute;
+@class MCPRelation;
+@class MCPRelation;
+
+@interface MCPJoin : NSObject <NSCoding>
+{
+@protected
+ // Note that NONE of these attributes are retained!!!
+ // Instead all these objects are notified of the existence of the join
+ // and are responsible to invalidate/delete it if necessary.
+ MCPRelation *relation;
+ MCPAttribute *origin;
+ MCPAttribute *destination;
+}
+
+#pragma mark Class methods
++ (void) initialize;
+
+#pragma mark Life cycle
+- (id) initForRelation:(MCPRelation *) iRelation from:(MCPAttribute *) iOrigin to:(MCPAttribute *) iDestination;
+- (void) invalidate;
+- (void) dealloc;
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder;
+- (void) encodeWithCoder:(NSCoder *) encoder;
+
+#pragma mark Setters
+// No setter for relation : should be set at init time!
+- (void) setOrigin:(MCPAttribute *) iOrigin;
+- (void) setDestination:(MCPAttribute *) iDestination;
+
+#pragma mark Getters
+- (MCPRelation *) relation;
+- (MCPAttribute *) origin;
+- (MCPAttribute *) destination;
+- (unsigned int) index;
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.m
new file mode 100644
index 00000000..cf202196
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPJoin.m
@@ -0,0 +1,183 @@
+//
+// $Id: MCPJoin.m 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPJoin.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 18/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPJoin.h"
+
+#import "MCPModel.h"
+#import "MCPClassDescription.h"
+#import "MCPAttribute.h"
+#import "MCPRelation.h"
+#import "MCPRelation.h"
+
+@implementation MCPJoin
+
+#pragma mark Class methods
++ (void) initialize
+{
+ if (self = [MCPJoin class]) {
+ [self setVersion:010101]; // Ma.Mi.Re -> MaMiRe
+ }
+ return;
+}
+
+#pragma mark Life cycle
+- (id) initForRelation:(MCPRelation *) iRelation from:(MCPAttribute *) iOrigin to:(MCPAttribute *) iDestination;
+{
+ self = [super init];
+ if (self) {
+ relation = iRelation;
+ [self setOrigin:iOrigin];
+ [self setDestination:iDestination];
+ }
+ return self;
+}
+
+- (void) invalidate
+{
+ [self retain];
+ NSLog(@"Enterring -[MCPJoin invalidate], retain count is %u (after retaining : should be 4)", [self retainCount]);
+ [origin removeObjectFromJoinsAtIndex:[origin indexOfJoinIdenticalTo:self]];
+ [destination removeObjectFromJoinsAtIndex:[destination indexOfJoinIdenticalTo:self]];
+ [relation removeObjectFromJoinsAtIndex:[relation indexOfJoinIdenticalTo:self]];
+ NSLog(@"Enterring -[MCPJoin invalidate], retain count is %u (before releasing : should be 1)", [self retainCount]);
+ [self release];
+ return;
+}
+
+- (void) dealloc
+{
+ // Nothing to release, because the attributes are NOT retained.
+ [super dealloc];
+}
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder
+{
+ self = [super init];
+ if ((self) && ([decoder allowsKeyedCoding])) {
+ relation = [decoder decodeObjectForKey:@"MCPrelation"];
+ [self setOrigin:[decoder decodeObjectForKey:@"MCPorigin"]];
+ [self setDestination:[decoder decodeObjectForKey:@"MCPdestination"]];
+ }
+ else {
+ NSLog(@"For some reason, unable to decode MCPJoin from the coder!!!");
+ }
+ return self;
+}
+
+- (void) encodeWithCoder:(NSCoder *) encoder
+{
+ if (! [encoder allowsKeyedCoding]) {
+ NSLog(@"In MCPJoin -encodeWithCoder : Unable to encode to a non-keyed encoder!!, will not perform encoding!!");
+ return;
+ }
+ [encoder encodeObject:[self relation] forKey:@"MCPrelation"];
+ [encoder encodeObject:[self origin] forKey:@"MCPorigin"];
+ [encoder encodeObject:[self destination] forKey:@"MCPdestination"];
+}
+
+#pragma mark Setters
+- (void) setOrigin:(MCPAttribute *) iOrigin
+{
+ if (origin != iOrigin) {
+ if (origin) {
+ [origin removeObjectFromJoinsAtIndex:[origin indexOfJoinIdenticalTo:self]];
+ }
+ origin = iOrigin;
+ if (origin) {
+ [origin insertObject:self inJoinsAtIndex:[origin countOfJoins]];
+ }
+ }
+}
+
+- (void) setDestination:(MCPAttribute *) iDestination
+{
+ if (destination != iDestination) {
+ if (destination) {
+ [destination removeObjectFromJoinsAtIndex:[destination indexOfJoinIdenticalTo:self]];
+ }
+ destination = iDestination;
+ if (destination) {
+ [destination insertObject:self inJoinsAtIndex:[destination countOfJoins]];
+ }
+ }
+}
+
+#pragma mark Getters
+- (MCPRelation *) relation
+{
+ return relation;
+}
+
+- (MCPAttribute *) origin
+{
+ return origin;
+}
+
+- (MCPAttribute *) destination
+{
+ return destination;
+}
+
+- (unsigned int) index
+{
+ return [relation indexOfJoinIdenticalTo:self];
+}
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject
+{
+ if ([iObject isKindOfClass:[MCPJoin class]]) {
+ MCPJoin *theJoin = (MCPJoin *)iObject;
+
+ return ([relation isEqual:[theJoin relation]]) && ([origin isEqual:[theJoin origin]]) && ([destination isEqual:[theJoin destination]]);
+ }
+ if ([iObject isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *theDict = (NSDictionary *)iObject;
+
+ return ([relation isEqual:[theDict valueForKey:@"relation"]]) && ([origin isEqual:[theDict valueForKey:@"origin"]]) && ([destination isEqual:[theDict valueForKey:@"destination"]]);
+ }
+ return NO;
+}
+
+#pragma mark For debugging the retain counting
+- (id) retain
+{
+ [super retain];
+
+ return self;
+}
+
+- (void) release
+{
+ [super release];
+
+ return;
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.h
new file mode 100644
index 00000000..cc307752
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.h
@@ -0,0 +1,35 @@
+//
+// $Id: MCPModel+MCPEntreprise.h 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPModel+MCPEntreprise.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 01/11/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 <http://code.google.com/p/sequel-pro/>
+
+#import "MCPModel.h"
+
+@interface MCPModel (MCPEntreprise)
+
+- (void) registerAsClassDescriptionServer;
+- (void) registerDescriptionForClass:(NSNotification *) notification;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.m
new file mode 100644
index 00000000..cb9ebbaa
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel+MCPEntreprise.m
@@ -0,0 +1,57 @@
+//
+// $Id: MCPModel+MCPEntreprise.m 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPModel+MCPEntreprise.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 01/11/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPModel+MCPEntreprise.h"
+
+#import "MCPObject.h"
+
+@implementation MCPModel (MCPEntreprise)
+
+#pragma mark Work as a class description server
+
+- (void) registerAsClassDescriptionServer
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registerDescriptionForClass:) name:NSClassDescriptionNeededForClassNotification object:nil];
+}
+
+- (void) registerDescriptionForClass:(NSNotification *) notification
+{
+ Class theClass = [notification object];
+
+ if ([theClass isSubclassOfClass:[MCPObject class]]) {
+
+ NSString *theClassName = NSStringFromClass(theClass);
+ unsigned int index = [self indexOfClassDescription:theClassName];
+
+ if (NSNotFound != index) {
+ [NSClassDescription registerClassDescription:(NSClassDescription *)[self objectInClassDescriptionsAtIndex:index] forClass:theClass];
+ }
+ }
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.h
new file mode 100644
index 00000000..200b4a4b
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.h
@@ -0,0 +1,84 @@
+//
+// $Id: MCPModel.h 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPModel.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+@class MCPClassDescription;
+@class MCPAttribute;
+@class MCPRelation;
+
+@interface MCPModel : NSObject <NSCoding>
+{
+@protected
+ NSString *name; // Name of the model ... useless.
+ NSMutableArray *classDescriptions; // Order of the class descriptions in the model.
+ BOOL usesInnoDBTables; // The database should use InnoDB tables.
+ // Might add a string holding définition of tables.
+ // Might also add some sort of template for generated files (at least the header).
+}
+
+#pragma mark Class methods
++ (void) initialize;
+
+#pragma mark Life cycle
+- (id) initWithName:(NSString *) iName;
+- (void) dealloc;
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder;
+- (void) encodeWithCoder:(NSCoder *) encoder;
+
+#pragma mark Making new class description
+- (MCPClassDescription *) addNewClassDescriptionWithName:(NSString *) iName inPosition:(int) index;
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName;
+- (void) setClassDescriptions:(NSArray *) iClassDescriptions;
+- (void) insertObject:(MCPClassDescription *) iClassDescription inClassDescriptionsAtIndex:(unsigned int) index;
+- (void) removeObjectFromClassDescriptionsAtIndex:(unsigned int) index;
+- (void) setUsesInnoDBTables:(BOOL) iUsesInnoDB;
+
+// Deprecated : non KVC
+//- (void) removeClassDescription:(MCPClassDescription *) iClassDescription;
+//- (void) addClassDescription:(MCPClassDescription *) iClassDescription;
+
+#pragma mark Getters
+- (NSString *) name;
+- (NSArray *) classDescriptions;
+- (unsigned int) countOfClassDescriptions;
+- (MCPClassDescription *) objectInClassDescriptionsAtIndex:(unsigned int) index;
+- (unsigned int) indexOfClassDescription:(id) iClassDescription;
+- (BOOL) usesInnoDBTables;
+
+// Deprecated : non KVC
+//- (MCPClassDescription *) classDescriptionWithClassName:(NSString *) iClassDescriptionClassName;
+
+#pragma mark Output for logging
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.m
new file mode 100644
index 00000000..8f9393f2
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPModel.m
@@ -0,0 +1,230 @@
+//
+// $Id: MCPModel.m 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPModel.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 09/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPModel.h"
+
+#import "MCPEntrepriseNotifications.h"
+
+#import "MCPClassDescription.h"
+#import "MCPAttribute.h"
+#import "MCPRelation.h"
+
+@implementation MCPModel
+
+#pragma mark Class methods
++ (void) initialize
+{
+ if (self = [MCPModel class]) {
+ [self setVersion:010101]; // Ma.Mi.Re -> MaMiRe
+ }
+ return;
+}
+
+#pragma mark Life cycle
+- (id) initWithName:(NSString *) iName
+{
+ self = [super init];
+ if (self) {
+ [self setName:iName];
+ classDescriptions = [[NSMutableArray alloc] init];
+ }
+
+ return self;
+}
+
+- (void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [name release];
+ [classDescriptions release];
+ [super dealloc];
+}
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder
+{
+ self = [super init];
+ if ((self) && ([decoder allowsKeyedCoding])) {
+ [self setName:[decoder decodeObjectForKey:@"MCPname"]];
+ [self setClassDescriptions:[decoder decodeObjectForKey:@"MCPclassDescriptions"]];
+ [self setUsesInnoDBTables:[decoder decodeBoolForKey:@"MCPusesInnoDBTables"]];
+ }
+ else {
+ NSLog(@"For some reason, unable to decode MCPModel from the coder!!!");
+ }
+
+ return self;
+}
+
+- (void) encodeWithCoder:(NSCoder *) encoder
+{
+ if (! [encoder allowsKeyedCoding]) {
+ NSLog(@"In MCPModel -encodeWithCoder : Unable to encode to a non-keyed encoder!!, will not perform encoding!!");
+ return;
+ }
+// [super encodeWithCoder:encoder];
+ [encoder encodeObject:[self name] forKey:@"MCPname"];
+ [encoder encodeObject:[self classDescriptions] forKey:@"MCPclassDescriptions"];
+ [encoder encodeBool:[self usesInnoDBTables] forKey:@"MCPusesInnoDBTables"];
+ [encoder encodeObject:@"1.1.1" forKey:@"MCPversion"];
+ return;
+}
+
+#pragma mark Making new class description
+- (MCPClassDescription *) addNewClassDescriptionWithName:(NSString *) iName inPosition:(int) index;
+{
+ MCPClassDescription *theClassDescription = [[MCPClassDescription alloc] initInModel:self withName:iName];
+
+// [self addClassDescription:theClassDescription];
+ [self insertObject:theClassDescription inClassDescriptionsAtIndex:(index < 0) ? ([self countOfClassDescriptions] + index + 1) : index];
+ [theClassDescription release];
+ return theClassDescription;
+}
+
+#pragma mark Setters
+- (void) setName:(NSString *) iName
+{
+ if (iName != name) {
+ [name release];
+ name = [iName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+ }
+}
+
+- (void) setClassDescriptions:(NSArray *) iClassDescriptions
+{
+ if (iClassDescriptions != classDescriptions) {
+ [classDescriptions release];
+ classDescriptions = [[NSMutableArray alloc] initWithArray:iClassDescriptions];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+ }
+}
+
+- (void) insertObject:(MCPClassDescription *) iClassDescription inClassDescriptionsAtIndex:(unsigned int) index
+{
+ [classDescriptions insertObject:iClassDescription atIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+}
+
+- (void) removeObjectFromClassDescriptionsAtIndex:(unsigned int) index
+{
+ [classDescriptions removeObjectAtIndex:index];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+}
+
+- (void) setUsesInnoDBTables:(BOOL) iUsesInnoDB
+{
+ usesInnoDBTables = iUsesInnoDB;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+}
+
+// Deprecated : non KVC
+/*
+- (void) removeClassDescription:(MCPClassDescription *) iClassDescription
+{
+ [classDescriptions removeObject:iClassDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+}
+
+ - (void) addClassDescription:(MCPClassDescription *) iClassDescription
+ {
+ [classDescriptions addObject:iClassDescription];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:self];
+ }
+
+ */
+
+#pragma mark Getters
+- (NSString *) name
+{
+ return name;
+// return [NSString stringWithString:name];
+}
+
+- (NSArray *) classDescriptions
+{
+ return [NSArray arrayWithArray:classDescriptions];
+}
+
+- (unsigned int) countOfClassDescriptions
+{
+ return [classDescriptions count];
+}
+
+- (MCPClassDescription *) objectInClassDescriptionsAtIndex:(unsigned int) index
+{
+ return (MCPClassDescription *)((NSNotFound != index) ? [classDescriptions objectAtIndex:index] : nil);
+}
+
+- (MCPClassDescription *) classDescriptionWithClassName:(NSString *) iClassDescriptionClassName
+{
+// Given the implementation of isEqual: for the MCPClassDescription, one should be able to use NSArray method directly:
+ /* unsigned int i;
+
+ for (i=0; ([classDescriptions count] != i) && (! [iClassDescriptionClassName isEqualToString:[(MCPClassDescription *) [classDescriptions objectAtIndex:i] className]]); ++i ) {
+ }
+ return (i == [classDescriptions count]) ? nil : (MCPClassDescription *)[classDescriptions objectAtIndex:i];
+ */
+ unsigned int theIndex = [classDescriptions indexOfObject:iClassDescriptionClassName];
+ return (NSNotFound == theIndex) ? nil : [classDescriptions objectAtIndex:theIndex];
+}
+
+- (unsigned int) indexOfClassDescription:(id) iClassDescription
+{
+ return [classDescriptions indexOfObject:iClassDescription];
+}
+
+- (BOOL) usesInnoDBTables
+{
+ return usesInnoDBTables;
+}
+
+// Deprecated : non KVC
+
+#pragma mark Output for logging
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale
+{
+ return [NSString stringWithFormat:@"<MCPModel with name %@ : %p>", [self name], self];
+}
+
+#pragma mark For debugging the retain counting
+- (id) retain
+{
+ [super retain];
+
+ return self;
+}
+
+- (void) release
+{
+ [super release];
+
+ return;
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.h
new file mode 100644
index 00000000..7e6aa3b3
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.h
@@ -0,0 +1,126 @@
+//
+// $Id: MCPObject.h 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPObject.h
+// 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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+/*" Possible return code on some operations of the database interaction. "*/
+typedef enum {
+ MCPDBReturnUnknown = 0, /*"Unknown state, should not happen."*/
+ MCPDBReturnDeleted = 1, /*"The entry have been successfuly deleted from DB."*/
+ MCPDBReturnUsed = 2, /*"The entry can not be removed, because some entries are still connected to it (some delete restrict/inhibit delete)."*/
+ MCPDBReturnNone = 3, /*"No entry exist with this Id."*/
+ MCPDBReturnNew = 4, /*"The entry was indeed new and inserted in the database."*/
+ MCPDBReturnUpdated = 5, /*"The entry was updated in the DB."*/
+ MCPDBReturnIncompleteKey = 6, /*"Part of the primary key is missing, action not taken."*/
+ MCPDBReturnMultiple = 7, /*"Multiple rows are found with a query supposed to return at most one row."*/
+ MCPDBReturnNoIdentity = 8, /*"The object does not have attributes that defines identity."*/
+ MCPDBReturnNoKey = 9, /*"There is no primary key defined for this entity."*/
+ MCPDBReturnNoConnection = 10, /*"The MCPObject is not having a connection."*/
+ MCPDBReturnWrongRelationOrigin = 11, /*"Looking for a relation not which origin is not of the specified class."*/
+ MCPDBReturnWrongRelationCardinality = 12, /*"Using a method assuming a cardinality of the relation while the relation as the other one."*/
+ MCPDBReturnNoSuchRelation = 13, /*"There is no relation with such a name starting from this class."*/
+ MCPDBReturnNotTarget = 14, /*"Tried to remove an object from a relation, while the objects does NOT belong to the relation."*/
+ MCPDBReturnOK = 100 /*"Everything went OK."*/
+} MCPDBReturnCode;
+
+@class MCPConnection;
+@class MCPClassDescription;
+@class MCPRelation;
+
+@interface MCPObject : NSObject {
+ MCPClassDescription *classDescription;
+ MCPConnection *connection;
+}
+
+#pragma mark Life of the Object
+/*" Life of the object "*/
+- (id) init;
+- (id) initWithDictionary:(NSDictionary *) dictionary;
+
+- (void) dealloc;
+
+- (void) setAttributesToDefault;
+
+#pragma mark Accessors
+/*" Accessor(s) "*/
+- (MCPClassDescription *) classDescription;
+- (MCPConnection *) connection;
+
+- (void) setConnection:(MCPConnection *) iConnection;
+
+#pragma mark Database interface
+/*" Database interface "*/
+- (id) readFromDBRow:(NSDictionary *) iDictionary withTableName:(NSString *) iTableName;
+- (MCPDBReturnCode) setPrimaryKey:(id) iDictionary andFetchFromDB:(MCPConnection *) iConnection;
+//- (MCPDBReturnCode) setPrimaryKey:(NSDictionary *) iDictionary andFetchFromDB:(MCPConnection *) iConnection;
+- (NSDictionary *) checkDBId; // the returned dictionary contains a MCPDBReturnCode key with the return code.
+- (NSDictionary *) saveInDB; // the returned dictionary contains a MCPDBReturnCode key with the return code.
+- (MCPDBReturnCode) getAutoGenerated;
+- (MCPDBReturnCode) updateInDB;
+- (MCPDBReturnCode) deleteInDB;
++ (MCPDBReturnCode) deleteInDBUsingConnection:(MCPConnection *) iConnection withId:(id) iId;
+
+#pragma mark Handling relations
+/*" Handling realtions "*/
+- (id) getTargetOfRelation:(MCPRelation *) iRelation;
+- (id) getTargetOfRelationNamed:(NSString *) iRelationName;
+- (MCPDBReturnCode) setTarget:(id) iTarget forRelation:(MCPRelation *) iRelation;
+- (MCPDBReturnCode) setTarget:(id) iTarget forRelationNamed:(NSString *) iRelationName;
+- (unsigned int) countTargetForRelation:(MCPRelation *) iRelation;
+- (unsigned int) countTargetForRelationNamed:(NSString *) iRelationName;
+- (MCPObject *) getTargetOfRelation:(MCPRelation *) iRelation atIndex:(unsigned int) iIndex;
+- (MCPObject *) getTargetOfRelationNamed:(NSString *) iRelationName atIndex:(unsigned int) iIndex;
+- (MCPDBReturnCode) addTarget:(MCPObject *) iTarget toRelation:(MCPRelation *) iRelation;
+- (MCPDBReturnCode) addTarget:(MCPObject *) iTarget toRelationNamed:(NSString *) iRelationName;
+- (MCPDBReturnCode) removeTarget:(MCPObject *) iTarget toRelation:(MCPRelation *) iRelation;
+- (MCPDBReturnCode) removeTarget:(MCPObject *) iTarget toRelationNamed:(NSString *) iRelationName;
+- (MCPDBReturnCode) removeTargetToRelation:(MCPRelation *) iRelation atIndex:(unsigned int) iIndex;
+- (MCPDBReturnCode) removeTargetToRelationNamed:(NSString *) iRelationName atIndex:(unsigned int) iIndex;
+- (unsigned int) indexOfTarget:(MCPObject *) iTarget inRelation:(MCPRelation *) iRelation;
+- (unsigned int) indexOfTarget:(MCPObject *) iTarget inRelationNamed:(NSString *) iRelationName;
+
+#pragma mark Utilities
+/*" Utility methods "*/
+- (id) defaultValueForKey:(NSString *) iKey;
+- (NSDictionary *) primaryKey;
+
+/*" Testing equality (VERY important for relation management)"*/
+- (BOOL) isEqual:(id) iObject;
+
+#pragma mark Output
+/*" Output : "*/
+- (NSString *) description;
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale;
+
+#pragma mark Ordering the array for relations
+- (NSString *) orderSQLForClassDescription:(MCPClassDescription *) iClassDescription;
+
+/*" Anti-crash method... "*/
+- (void) setNilValueForKey:(NSString *) iKey;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m
new file mode 100644
index 00000000..d3145039
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPObject.m
@@ -0,0 +1,1337 @@
+//
+// $Id: MCPObject.m 927 2009-06-24 10:53:07Z stuart02 $
+//
+// 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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPObject.h"
+
+#import <MCPKit/MCPKit.h>
+
+#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) {
+ unsigned int 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];
+ unsigned 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;
+ unsigned 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]];
+ unsigned 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];
+ unsigned 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;
+ unsigned int i;
+
+ if (! theConnection) {
+ return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:MCPDBReturnNoConnection], @"MCPDBReturnCode"];
+ }
+ if (! [theKeyAttr count]) { // There is no primary key for this object.
+ [theKeys setObject:[NSNumber numberWithInt:MCPDBReturnNoKey] forKey:@"MCPDBReturnCode"];
+ return [NSDictionary dictionaryWithDictionary:theKeys];
+ }
+ if (! [theIdAttr count]) { // Identity is not defined for this object.
+ [theKeys setObject:[NSNumber numberWithInt: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 %u))", [(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 %i results were found, will take the first one.");
+ [theKeys setObject:[NSNumber numberWithInt:MCPDBReturnMultiple] forKey:@"MCPDBReturnCode"];
+ }
+ else {
+ [theKeys setObject:[NSNumber numberWithInt: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 numberWithInt: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;
+ int theCheckCode = [(NSNumber *)[theCheckReturn objectForKey:@"MCPDBReturnCode"] intValue];
+ unsigned i;
+ unsigned 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 numberWithInt: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 numberWithInt: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 : %i", 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 : %i !!!", 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 numberWithInt: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];
+ unsigned i,j;
+ NSMutableString *theQuery;
+ int theCheckCode;
+ MCPResult *theResult;
+ NSDictionary *theRow;
+ MCPConnection *theConnection = [self connection];
+
+ if (! theConnection) {
+ return MCPDBReturnNoConnection;
+ }
+
+ if (0 != [[classDescription identityAttributes] count]) {
+ theCheckCode = [(NSNumber *)[[self checkDBId] objectForKey:@"MCPDBReturnCode"] intValue];
+ 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];
+ int theCheckCode;
+ unsigned 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];
+ int theIdCheckCode = [(NSNumber *) [theKeyCheck objectForKey:@"MCPDBReturnCode"] intValue];
+
+ 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];
+ unsigned i;
+// NSMutableDictionary *theKeyValue = [NSMutableDictionary dictionary];
+ int 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 intValue]))) {
+ theCheckCode = MCPDBReturnIncompleteKey;
+ break;
+ }
+ }
+// If incomplete, try to find the rest, and perform again:
+ if (MCPDBReturnIncompleteKey == theCheckCode) {
+ if (MCPDBReturnOK == [(NSNumber *)[[self checkDBId] objectForKey:@"MCPDBReturnCode"] intValue]) {
+ 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];
+ unsigned 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;
+ unsigned int 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)."*/
+{
+ unsigned int 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;
+}
+
+- (unsigned int) countTargetForRelation:(MCPRelation *) iRelation
+{
+ NSMutableString *theQuery;
+ unsigned int 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(*) 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(*)"] unsignedIntValue];
+}
+
+- (unsigned int) countTargetForRelationNamed:(NSString *) iRelationName
+{
+ MCPRelation *theRelation = [classDescription relationWithName:iRelationName];
+ return (theRelation) ? [self countTargetForRelation:theRelation] : 0;
+}
+
+
+- (MCPObject *) getTargetOfRelation:(MCPRelation *) iRelation atIndex:(unsigned int) 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;
+ unsigned int 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 %u", 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:(unsigned int) 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;
+ unsigned int 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"] unsignedIntValue];
+}
+
+- (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;
+ unsigned int 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"] unsignedIntValue];
+ }
+ 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:(unsigned int) 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:(unsigned int) iIndex
+{
+ MCPRelation *theRelation = [classDescription relationWithName:iRelationName];
+ return (theRelation) ? [self removeTargetToRelation:theRelation atIndex:iIndex] : MCPDBReturnNoSuchRelation;
+}
+
+- (unsigned int) 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;
+ unsigned int 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;
+}
+
+- (unsigned int) 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 numberWithInt: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];
+ unsigned 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;
+ unsigned int 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];
+ unsigned int 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 %u : 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];
+ unsigned int 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 numberWithInt:0] forKey:iKey];
+}
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation+Private.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation+Private.h
new file mode 100644
index 00000000..c8683aab
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation+Private.h
@@ -0,0 +1,46 @@
+//
+// $Id: MCPRelation+Private.h 482 2009-04-05 01:38:48Z stuart02 $
+//
+// MCPRelation+Private.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 11/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPRelation.h"
+
+@interface MCPRelation (Private)
+
+#pragma mark Making some work
+- (void) invalidateJoins; // Check that the joins are realistics.
+
+#pragma mark Setters
+- (void) setOrigin:(MCPClassDescription *) iOrigin;
+//- (void) setJoins:(NSArray *) iJoins;
+
+#pragma mark Getters
+- (MCPModel *) model;
+
+#pragma mark Fro the controller layer and the UI
+- (void) addNewDefaultJoin;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.h b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.h
new file mode 100644
index 00000000..43ff027a
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.h
@@ -0,0 +1,112 @@
+//
+// $Id: MCPRelation.h 545 2009-04-10 14:49:45Z stuart02 $
+//
+// MCPRelation.h
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 11/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Foundation/Foundation.h>
+
+@class MCPModel;
+@class MCPClassDescription;
+@class MCPAttribute;
+@class MCPJoin;
+
+typedef enum {
+ OnDeleteNullify = 1,
+ OnDeleteDeny = 2,
+ OnDeleteCascade = 3,
+ OnDeleteDefault = 4,
+ OnDeleteNoAction = 5
+} MCPRelationDeleteRule;
+
+@interface MCPRelation : NSObject <NSCoding>
+{
+@protected
+ NSString *name; // Name of the relation
+ MCPRelationDeleteRule deleteRule; // Delete rule : what to do of the destination when origin is deleted
+ MCPRelation *inverseRelation; // The inverse relation (or nil if no inverse present)
+ MCPClassDescription *origin; // The class description from which the relation originate
+ MCPClassDescription *destination; // The class description to which the relation arrives
+ NSMutableArray *joins; // Joining attributes (array of MCPJoin)
+ BOOL isToMany; // Is the relation to many (or to one)
+ BOOL isMandatory; // Is the relation mandatory for the class description (origin)
+ BOOL ownsDestination; // The origin class description owns the destination class description(ies)
+}
+
+#pragma mark Class methods
++ (void) initialize;
+
++ (NSArray *) existingDeleteRules;
+- (NSArray *) existingDeleteRules;
+
+#pragma mark Life cycle
+- (id) initWithName:(NSString *) iName from:(MCPClassDescription *) iFrom to:(MCPClassDescription *) iTo;
+- (void) invalidateRelation;
+- (void) dealloc;
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder;
+- (void) encodeWithCoder:(NSCoder *) encoder;
+
+#pragma mark Managing joins
+//- (MCPJoin *) addNewJoin;
+- (MCPJoin *) addJoinFrom:(MCPAttribute *) iFrom to:(MCPAttribute *) iTo;
+- (void) removeJoinFrom:(MCPAttribute *) iFrom to:(MCPAttribute *) iTo;
+//- (void) unjoinAttribute:(MCPAttribute *) iAttribute;
+
+#pragma mark Setters
+- (void) setDestination:(MCPClassDescription *) iDestination;
+- (void) setName:(NSString *) iName;
+- (void) setDeleteRule:(MCPRelationDeleteRule) iDeleteRule;
+- (void) setInverseRelation:(MCPRelation *) iInverseRelation;
+- (void) insertObject:(MCPJoin *) iJoin inJoinsAtIndex:(unsigned int) index;
+- (void) removeObjectFromJoinsAtIndex:(unsigned int) index;
+- (void) setIsToMany:(BOOL) iIsToMany;
+- (void) setIsMandatory:(BOOL) iIsMandatory;
+- (void) setOwnsDestintation:(BOOL) iOwnsDestination;
+
+#pragma mark Getters
+- (NSString *) name;
+- (MCPRelationDeleteRule) deleteRule;
+- (MCPRelation *) inverseRelation;
+- (MCPClassDescription *) origin;
+- (MCPClassDescription *) destination;
+- (NSArray *) joins;
+- (unsigned int) countOfJoins;
+- (MCPJoin *) objectInJoinsAtIndex:(unsigned int) index;
+- (unsigned int) indexOfJoinIdenticalTo:(id) iJoin;
+- (BOOL) isToMany;
+- (BOOL) isMandatory;
+- (BOOL) ownsDestination;
+
+#pragma mark Some Usefull methods
+- (MCPAttribute *) destinationAttributeForOrigin:(MCPAttribute *) iFrom;
+- (MCPAttribute *) originAttributeForDestination:(MCPAttribute *) iTo;
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject;
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale;
+
+@end
diff --git a/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.m b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.m
new file mode 100644
index 00000000..57186fdc
--- /dev/null
+++ b/Frameworks/MCPKit/MCPEntrepriseKit/MCPRelation.m
@@ -0,0 +1,486 @@
+//
+// $Id: MCPRelation.m 927 2009-06-24 10:53:07Z stuart02 $
+//
+// MCPRelation.m
+// MCPKit
+//
+// Created by Serge Cohen (serge.cohen@m4x.org) on 11/08/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 <http://mysql-cocoa.sourceforge.net/>
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "MCPRelation.h"
+#import "MCPRelation+Private.h"
+
+#import "MCPEntrepriseNotifications.h"
+
+#import "MCPModel.h"
+#import "MCPClassDescription.h"
+#import "MCPClassDescription+Private.h"
+#import "MCPAttribute.h"
+
+#import "MCPJoin.h"
+
+static NSArray *MCPexistingDeleteRules;
+
+@implementation MCPRelation
+
+#pragma mark Class methods
++ (void) initialize
+{
+ if (self = [MCPRelation class]) {
+ NSMutableArray *theExistingDeleteRules = [[NSMutableArray alloc] init];
+
+ [self setVersion:010101]; // Ma.Mi.Re -> MaMiRe
+
+ [theExistingDeleteRules addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:OnDeleteNullify], @"tag", @"Nullify", @"name", nil]];
+ [theExistingDeleteRules addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:OnDeleteDeny], @"tag", @"Deny", @"name", nil]];
+ [theExistingDeleteRules addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:OnDeleteCascade], @"tag", @"Cascade", @"name", nil]];
+ [theExistingDeleteRules addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:OnDeleteDefault], @"tag", @"Default", @"name", nil]];
+ [theExistingDeleteRules addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:OnDeleteNoAction], @"tag", @"No Action", @"name", nil]];
+ MCPexistingDeleteRules = [[NSArray alloc] initWithArray:theExistingDeleteRules];
+ [theExistingDeleteRules release];
+ }
+ return;
+}
+
++ (NSArray *) existingDeleteRules
+{
+ return MCPexistingDeleteRules;
+}
+
+- (NSArray *) existingDeleteRules
+{
+ return [MCPRelation existingDeleteRules];
+}
+
+
+#pragma mark Life cycle
+- (id) initWithName:(NSString *) iName from:(MCPClassDescription *) iFrom to:(MCPClassDescription *) iTo
+{
+ self = [super init];
+ if (self) {
+ [self setName:iName];
+ [self setOrigin:iFrom];
+ [self setDestination:iTo];
+ joins = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void) invalidateRelation
+{
+ [self retain]; // To be sure not to be released before the end of the method
+ [self invalidateJoins]; // Remove each of the joins (so that attributes get notified)
+/*
+ [origin removeObjectFromRelationsAtIndex:[[origin relations] indexOfObjectIdenticalTo:self]];
+ origin = nil;
+ [destination removeObjectFromIncomingsAtIndex:[[destination incomings] indexOfObjectIdenticalTo:self]];
+ destination = nil;
+*/
+ [self setOrigin:nil];
+ [self setDestination:nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ [self release];
+}
+
+- (void) dealloc
+{
+ [joins release]; // Should be empty by now...
+ [name release];
+// The inverse relation don't have an inverse relation any more...
+// [inverseRelation setInverseRelation:nil];
+ [self setInverseRelation:nil];
+// Other are weak references.
+ [super dealloc];
+}
+
+#pragma mark NSCoding protocol
+- (id) initWithCoder:(NSCoder *) decoder
+{
+ self = [super init];
+ if ((self) && ([decoder allowsKeyedCoding])) {
+ [self setName:[decoder decodeObjectForKey:@"MCPname"]];
+ [self setDeleteRule:(MCPRelationDeleteRule)[decoder decodeInt32ForKey:@"MCPdeleteRule"]];
+ if ([decoder containsValueForKey:@"MCPinverseRelation"]) {
+ [self setInverseRelation:[decoder decodeObjectForKey:@"MCPinverseRelation"]];
+ }
+ else {
+ [self setInverseRelation:nil];
+ }
+ [self setOrigin:[decoder decodeObjectForKey:@"MCPorigin"]];
+ [self setDestination:[decoder decodeObjectForKey:@"MCPdestination"]];
+// [self setJoins:[decoder decodeObjectForKey:@"MCPjoins"]];
+ joins = [[NSMutableArray alloc] initWithArray:[decoder decodeObjectForKey:@"MCPjoins"]];
+ [self setIsToMany:[decoder decodeBoolForKey:@"MCPisToMany"]];
+ [self setIsMandatory:[decoder decodeBoolForKey:@"MCPisMandatory"]];
+ [self setOwnsDestintation:[decoder decodeBoolForKey:@"MCPownsDestination"]];
+ }
+
+ return self;
+}
+
+- (void) encodeWithCoder:(NSCoder *) encoder
+{
+ if (! [encoder allowsKeyedCoding]) {
+ NSLog(@"In MCPRelation -encodeWithCoder : Unable to encode to a non-keyed encoder!!, will not perform encoding!!");
+ return;
+ }
+ [encoder encodeObject:[self name] forKey:@"MCPname"];
+ [encoder encodeInt32:(int32_t)[self deleteRule] forKey:@"MCPdeleteRule"];
+ if ([self inverseRelation]) {
+ [encoder encodeObject:[self inverseRelation] forKey:@"MCPinverseRelation"];
+ }
+ [encoder encodeObject:[self origin] forKey:@"MCPorigin"];
+ [encoder encodeObject:[self destination] forKey:@"MCPdestination"];
+ [encoder encodeObject:[self joins] forKey:@"MCPjoins"];
+ [encoder encodeBool:[self isToMany] forKey:@"MCPisToMany"];
+ [encoder encodeBool:[self isMandatory] forKey:@"MCPisMandatory"];
+ [encoder encodeBool:[self ownsDestination] forKey:@"MCPownsDestination"];
+}
+
+#pragma mark Making new joins
+/*
+- (MCPJoin *) addNewJoin // Usefull for the interface, to be able to create a new join by just using a binding.
+{
+ [self addJoinFrom:[origin objectInAttributesAtIndex:0] to:[destination objectInAttributesAtIndex:0]];
+}
+*/
+
+- (MCPJoin *) addJoinFrom:(MCPAttribute *) iFrom to:(MCPAttribute *) iTo
+{
+ MCPJoin *theJoin;
+
+ if ([iFrom classDescription] != [self origin]) {
+ NSLog(@"Tried to make a join starting from an attribute (%@) that does NOT belong to the origin class description (%@)! Will not perform the link", iFrom, [self origin]);
+ return nil;
+ }
+ if ([iTo classDescription] != [self destination]) {
+ NSLog(@"Tried to make a join arriving to an attribute (%@) that does NOT belong to the destination class description (%@)! Will not perform the link", iTo, [self destination]);
+ return nil;
+ }
+ theJoin = [[MCPJoin alloc] initForRelation:self from:iFrom to:iTo];
+// theJoin = [[MCPJoin alloc] initFrom:iFrom to:iTo];
+// [joins addObject:theJoin];
+ [joins insertObject:theJoin atIndex:[joins count]];
+ [theJoin release];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ return theJoin;
+}
+
+- (void) removeJoinFrom:(MCPAttribute *) iFrom to:(MCPAttribute *) iTo
+{
+ NSDictionary *theJoinDict = [[NSDictionary alloc] initWithObjectsAndKeys:self, @"relation", iFrom, @"origin", iTo, @"destination", nil];
+ unsigned int i = [joins indexOfObject:theJoinDict];
+
+ if (NSNotFound != i) {
+ [[self objectInJoinsAtIndex:i] invalidate];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+ [theJoinDict release];
+}
+
+/*
+- (void) unjoinAttribute:(MCPAttribute *) iAttribute
+{
+ unsigned int i = 0;
+
+#warning HAVE to rewrite this code!!!
+ if ([[iAttribute classDescription] isEqual:origin]) {
+ for (i=0; ([joins count] != i) && ([iAttribute isEqual:[(MCPJoin *)[joins objectAtIndex:i] origin]]); ++i) {
+ }
+ }
+ if ([[iAttribute classDescription] isEqual:destination]) {
+ for (i=0; ([joins count] != i) && ([iAttribute isEqual:[(MCPJoin *)[joins objectAtIndex:i] destination]]); ++i) {
+ }
+ }
+ if ((0 == i) || ([joins count] == i)) { // No joins found using this attribute.
+ return;
+ }
+ [self removeJoinFrom:[(MCPJoin *)[joins objectAtIndex:i] origin] to:[(MCPJoin *)[joins objectAtIndex:i] destination]];
+}
+*/
+
+#pragma mark Setters
+- (void) setDestination:(MCPClassDescription *) iDestination
+{
+ if (iDestination != destination) {
+ [destination removeObjectFromIncomingsAtIndex:[[destination incomings] indexOfObjectIdenticalTo:self]];
+ destination = iDestination;
+ [destination insertObject:self inIncomingsAtIndex:0];
+ [self invalidateJoins];
+ }
+}
+
+
+- (void) setName:(NSString *) iName
+{
+ if (iName != name) {
+ [name release];
+ name = [iName retain];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+- (void) setDeleteRule:(MCPRelationDeleteRule) iDeleteRule
+{
+ if (iDeleteRule != deleteRule) { // Don't do the notification for nothing!!!
+ deleteRule = iDeleteRule;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+- (void) setInverseRelation:(MCPRelation *) iInverseRelation
+{
+ if (iInverseRelation != inverseRelation) {
+ [inverseRelation release];
+ inverseRelation = [iInverseRelation retain];
+ [inverseRelation setInverseRelation:self];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+- (void) insertObject:(MCPJoin *) iJoin inJoinsAtIndex:(unsigned int) index
+{
+ [joins insertObject:iJoin atIndex:index];
+}
+
+- (void) removeObjectFromJoinsAtIndex:(unsigned int) index
+{
+ [joins removeObjectAtIndex:index];
+}
+
+- (void) setIsToMany:(BOOL) iIsToMany
+{
+ if (iIsToMany != isToMany) { // Don't do the notification for nothing!!!
+ isToMany = iIsToMany;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+- (void) setIsMandatory:(BOOL) iIsMandatory
+{
+ if (iIsMandatory != isMandatory) { // Don't do the notification for nothing!!!
+ isMandatory = iIsMandatory;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+- (void) setOwnsDestintation:(BOOL) iOwnsDestination
+{
+ if (iOwnsDestination != ownsDestination) { // Don't do the notification for nothing!!!
+ ownsDestination = iOwnsDestination;
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPModelChangedNotification object:[origin model]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPClassDescriptionChangedNotification object:origin];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+
+#pragma mark Getters
+- (NSString *) name
+{
+ return name;
+}
+
+- (MCPRelationDeleteRule) deleteRule
+{
+ return deleteRule;
+}
+
+- (MCPRelation *) inverseRelation
+{
+ return inverseRelation;
+}
+
+- (MCPClassDescription *) origin
+{
+ return origin;
+}
+
+- (MCPClassDescription *) destination
+{
+ return destination;
+}
+
+- (NSArray *) joins
+{
+ return [NSArray arrayWithArray:joins];
+}
+
+- (unsigned int) countOfJoins
+{
+ return [joins count];
+}
+
+- (MCPJoin *) objectInJoinsAtIndex:(unsigned int) index
+{
+ return (MCPJoin *)((NSNotFound != index) ? [joins objectAtIndex:index] : nil);
+}
+
+- (unsigned int) indexOfJoinIdenticalTo:(id) iJoin
+{
+ return [joins indexOfObjectIdenticalTo:iJoin];
+}
+
+- (BOOL) isToMany
+{
+ return isToMany;
+}
+
+- (BOOL) isMandatory
+{
+ return isMandatory;
+}
+
+- (BOOL) ownsDestination
+{
+ return ownsDestination;
+}
+
+#pragma mark Some Usefull methods
+
+- (MCPAttribute *) destinationAttributeForOrigin:(MCPAttribute *) iFrom
+{
+ unsigned int i;
+
+ for (i=0; ([joins count] != i) && ([[(MCPJoin *)[joins objectAtIndex:i] origin] isEqual:iFrom]); ++i) {
+ }
+ return ([joins count] == i) ? nil : [(MCPJoin *)[joins objectAtIndex:i] destination];
+}
+
+- (MCPAttribute *) originAttributeForDestination:(MCPAttribute *) iTo
+{
+ unsigned int i;
+
+ for (i=0; ([joins count] != i) && ([[(MCPJoin *)[joins objectAtIndex:i] destination] isEqual:iTo]); ++i) {
+ }
+ return ([joins count] == i) ? nil : [(MCPJoin *)[joins objectAtIndex:i] origin];
+}
+
+
+#pragma mark Some general methods:
+- (BOOL) isEqual:(id) iObject
+// Equal to another relation, if they have the same name and same origin and destination class descriptions (they have the same names).
+// Equal to a string (NSString), if the name of the relation is equal to the string.
+{
+ if ([iObject isKindOfClass:[MCPRelation class]]) {
+ MCPRelation *theRelation = (MCPRelation *) iObject;
+
+ return ([name isEqualToString:[theRelation name]]) && ([[self origin] isEqual:[theRelation origin]]) && ([[self destination] isEqual:[theRelation destination]]);
+ }
+ if ([iObject isKindOfClass:[NSString class]]) {
+ return [name isEqualToString:(NSString *)iObject];
+ }
+ return NO;
+}
+
+- (NSString *) descriptionWithLocale:(NSDictionary *) locale
+{
+ NSMutableString *theRet = [NSMutableString stringWithFormat:@"MCPRelation named %@, going from %@ to %@. Joins :\n", name, [origin name], [destination name]];
+ unsigned int i;
+
+ for (i = 0; [joins count] != i; ++i) {
+ MCPJoin *tmpJoin = (MCPJoin *)[joins objectAtIndex:i];
+ [theRet appendFormat:@"\t\t%@ == %@\n", [[tmpJoin origin] name], [[tmpJoin destination] name]];
+ }
+ return theRet;
+}
+
+
+#pragma mark For debugging the retain counting
+- (id) retain
+{
+ [super retain];
+ return self;
+}
+
+- (void) release
+{
+ [super release];
+ return;
+}
+
+@end
+
+@implementation MCPRelation (Private)
+
+#pragma mark Making some work
+- (void) invalidateJoins
+{
+ while ([joins count]) {
+ [[self objectInJoinsAtIndex:0] invalidate];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+}
+
+#pragma mark Setters
+- (void) setOrigin:(MCPClassDescription *) iOrigin
+{
+ if (iOrigin != origin) {
+ [origin removeObjectFromRelationsAtIndex:[[origin relations] indexOfObjectIdenticalTo:self]];
+ origin = iOrigin;
+ [origin insertObject:self inRelationsAtIndex:[origin countOfRelations]];
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ [self invalidateJoins];
+ }
+}
+
+/*
+- (void) setJoins:(NSArray *) iJoins
+{
+ if (iJoins != joins) {
+ unsigned int i;
+ if (joins) {
+ [self invalidateJoins];
+ }
+ else {
+ joins = [[NSMutableArray alloc] init];
+ }
+ for (i=0; [iJoins count] != i; ++i) {
+ [self addJoinFrom:[(MCPJoin *)[iJoins objectAtIndex:i] origin] to:[(MCPJoin *)[iJoins objectAtIndex:i] destination]];
+ }
+ [[NSNotificationCenter defaultCenter] postNotificationName:MCPRelationChangedNotification object:self];
+ }
+}
+*/
+
+#pragma mark Getters
+- (MCPModel *) model
+{
+ return [origin model];
+}
+
+#pragma mark Fro the controller layer and the UI
+- (void) addNewDefaultJoin // Usefull for the interface, to be able to create a new join by just using a binding.
+{
+ [self addJoinFrom:[origin objectInAttributesAtIndex:0] to:[destination objectInAttributesAtIndex:0]];
+}
+
+@end