PBDD.m


max21 Unternehmensgruppe
#import "Aprica.h"
//	Aprica2
//	copyright Pirmin Braun 1997-2007 - pirmin@pirmin.de
//	all Rights reserved;
@implementation PBDD
- (void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeObject:tables];
    [coder encodeObject:tableDict];
    return;
}
- (id)initWithCoder:(NSCoder *)coder;
{
    if(!([self init]))return nil;
    [tables addObjectsFromArray:[coder decodeObject]];
    [tableDict addEntriesFromDictionary:[coder decodeObject]];
    isConsistent=YES;
    return self;
}
- init;
{
    if(!(self = [super init]))return nil;
    tables = [[NSMutableArray alloc]initWithCapacity:100];
    tableDict = [[NSMutableDictionary alloc]initWithCapacity:200];
    isConsistent=NO;
    return self;
}
- (void)dealloc
{
    int i,j;
    [self shrink];
    for (i=0,j=[tables count];i<j;i++) {
        PBDDTable *t=[tables oai:i];
        [[t attributes] makeObjectsPerformSelector:@selector(setMyDD:) withObject: nil];
        [[t plainAttributes] makeObjectsPerformSelector:@selector(setMyDD:) withObject: nil];
        [t setMyDD:nil];
    }
    [tableDict removeAllObjects];
    [tables removeAllObjects];
    [tables release];
    [tableDict release];
    [super dealloc];
}
- (NSArray *)loadIDMFileDict:(NSString *)path;
{
    NSData *d = [NSData dataWithContentsOfFile: path];
    NSString *s = [[[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding] autorelease];
    NSArray *lines = [s componentsSeparatedByString: @"\n"];
    NSMutableDictionary *tp;
    NSMutableArray *aa;
    LMDN(tpmd);
    unsigned i,n,j,m;
    //Build Proportylist (tpmd) from idm file.
    for (i=0, n=[lines count]; i<n; i++) {
        NSString *val[4];
        NSString *line = [lines oai: i];
        NSArray  *fvals = [line componentsSeparatedByString: @"\t"];
        // ignore empty lines.
        if ([line length]==0) continue;
        // report but ignore unparsable lines.
        m = [fvals count];
        if (m!=4) {
            LOGS(([NSSWF @"corrupted line %i in idm file: %@\n'%@'",i,path,line]));
            continue;
        }
        // setup local variables for fast access.
        for (j=0; j<4; j++) {
            val[j] = [fvals oai: j];
        }
        // retrieve or create current table plist dictionary.
        tp = [tpmd ofk: val[0]];
        if (tp==nil) {
            tp = [NSMutableDictionary dictionaryWithCapacity: 5];
            [tpmd setObject: tp forKey: val[0]];
            [tp setObject:[NSMutableDictionary dictionary] forKey:@"_fastAttributeAccessDict"];
        }
        if ([val[1] isEqualToString: @"-"]) {
            // set table property
            [tp setObject: [val[3] stringByUnescapingCRTAB] forKey: val[2]];
        } else {
            NSMutableDictionary *ap;
            NSMutableDictionary *_fastAttributeAccessDict = [tp ofk:@"_fastAttributeAccessDict"];
            // set attribute property
            aa = [tp ofk: @"attributes"];
            if (aa==nil) {
                // the first object is a temporary dictionary for quick attribute access.
                aa = [NSMutableArray arrayWithCapacity:10];
                [tp setObject: aa forKey: @"attributes"];
            }
            ap = [_fastAttributeAccessDict ofk: val[1]];
            if (ap==nil) {
                ap = [NSMutableDictionary dictionaryWithCapacity: 10];
                [_fastAttributeAccessDict setObject: ap forKey: val[1]];
                [aa addObject: ap];
            }
            [ap setObject: [val[3] stringByUnescapingCRTAB] forKey: val[2]];
        }
    }
    return [tpmd allValues];
}
- (void)loadIDMFile:(NSString *)path;
{
    NSArray *tbls;
    PBDDTable *t;
    unsigned i,n;
    NSMutableDictionary *tp;
    // remove quick attribute access dictionaries.
    // PB: _fastAttributeAccessDict as tableproperty because there are tables without attributes that raised an exception
    tbls = [self loadIDMFileDict:path];
    for (i=0,n=[tbls count];i<n;i++) {
        [[tbls oai: i]removeObjectForKey:@"_fastAttributeAccessDict"];
    }
    // create real tables from property lists.
    for(i=0,n=[tbls count];i<n;i++){
        tp = [tbls oai:i];
        t = [[PBDDTable alloc]init];
        [t takeValuesFromDictionary:tp];
        [t setMyDD:self];
        [t convertDictionaries];
        [self addTable:t];
	[t release];
    }
}
- (void)refreshFromIDMFile;
{
    NSArray *tbls;
    PBDDTable *t;
    unsigned i,n;
    NSMutableDictionary *tp;
    NSString *path = [MANDANTPATH stringByAppendingPathComponent:@"Mandant.idm"];
    tbls = [self loadIDMFileDict:path];
    for (i=0,n=[tbls count];i<n;i++) {
        [[tbls oai: i]removeObjectForKey:@"_fastAttributeAccessDict"];
    }
    // refresh real tables from property lists.
    for(i=0,n=[tbls count];i<n;i++){
        tp = [tbls oai:i];
        t = [self tableNamed:[tp ofk:@"dbName"]];
        [t refreshAttributesFrom:[tp ofk:@"attributes"]];
    }
}
- (id)initFromPath:(NSString *)mp;
{
    NSString *p;
    NSFileManager *fm = [NSFileManager defaultManager];
    if (!(self=[self init])) return nil;
    [tables removeAllObjects];
    [tableDict removeAllObjects];
// load Mandant
    p = [mp stringByAppendingPathComponent:@"Mandant.idm"];
    if ([fm fileExistsAtPath: p]) {
        [self loadIDMFile: p];
    } else {
        LOGS(([NSSWF @"'%@' nicht gefunden.", p]));
    }
    
    [self reorg];
    return self;
}
- (void)reorg;
{
    [tables makeObjectsPerformSelector:@selector(clean)];
    [tables makeObjectsPerformSelector:@selector(getPKN)];
    [tables makeObjectsPerformSelector:@selector(consolidate)];
    isConsistent = YES;
}
- (void)setIsConsistent:(BOOL)yn;
{
    isConsistent = yn;
}
- (BOOL)isConsistent;{return isConsistent;}
- (NSMutableArray *)plainAttributes;
{
    NSMutableArray *ma = [NSMutableArray arrayWithCapacity:1000];
    int i,j;
    PBDDTable	*t;
    for(i=0,j=[tables count];i<j;i++){
        t = [tables oai:i];
        [ma addObjectsFromArray:[t plainAttributes]];
    }
    return ma;
}
- (NSMutableDictionary *)tableDict;
{
    return tableDict;
}
- (PBDDTable *)tableNamedCheap:(NSString *)value;
{
    if(!value)return nil;
    return [tableDict ofk:value];
}
- (PBDDTable *)tableNamed:(NSString *)value;
{
    PBDDTable	*t;
    if(!value)return nil;
    t = [tableDict ofk:value];
    if(!t){
        LOGS(([NSSWF @"Tabelle %@ nicht gefunden;",value]));
        // PRINTCURRENTSTACK;
        return nil;
    }else{
        return t;
    }
}
- (void)addTables:(NSArray *)a;
{
    int i,j=[a count];
    for(i=0;i<j;i++){
        [self addTable:[a oai:i]];
    }
}
- (BOOL)addTable:pbt;
{
    NSString *s = [pbt dbName];
    if(!FILLED(s))return NO;
    if([tableDict ofk:s])return NO;
    [tables addObject:pbt];
    [tableDict setSecureObject:pbt forKey:s];
    [pbt setMyDD:self];
    return YES;
}
- (BOOL)removeTable:pbt;
{
    NSString *s = [pbt dbName];
    if(!FILLED(s))return NO;
    [tables removeObject:pbt];
    [tableDict rofk:s];
    return YES;
}
//f keyValueCoding:
- (NSArray *)tables;
{
    return tables;
}
- (NSArray *)realTables;
{
// convenience 
   return [self tablesIncludingAbstract:NO];
}
- (NSArray *)tablesIncludingAbstract:(BOOL)yn;
{
    int i,j=[tables count];
    PBDDTable *t;
    NSMutableArray *ma;
    ma = [NSMutableArray arrayWithCapacity:j];
    for(i=0;i<j;i++){
        t = [tables oai:i];
        if(!yn && [[t type] isEqual:SC_Abstract])continue;
        [ma addObject:t];
    }
    return ma;
}
- (NSArray *)positionsTabellenForTablenamed:(NSString *)tn;
{
    //entitity, die eine relation auf die entity des eo hat ueber masterkey
    LMA;
    NSArray *a = [self tables];
    int i,j=[a count];
    for(i=0;i<j;i++){
        PBDDTable *t = [a oai:i];
        PBDDAttribute *pba;
        if([[t type] isEqual:SC_Abstract])continue;
        if((pba = [t plainAttrNamed:@"masterkey"]) && [[pba refdTableName] iE:tn]){
            [lma addObjectUniq:t];
        }
    }
    if(![lma count])return nil;
    [lma sortUsingKeyOrderArray:[NSArray soaFrom:@"guiName"]];
    return [lma valuesForKey:@"dbName"];
}
- (NSArray *)minimalAttributesFrom:(NSArray *)a;
{
    LMA;
    int i,j;
    NSString *s;
    for(i=0,j=[a count];i<j;i++){
        s = [a oai:i];
        [lma addObject:[self minimalAttributeNamed:s]];
    }
    return (NSArray *)lma;
}
- (PBDDAttribute *)minimalAttributeNamed:(NSString *)s;
{
    PBDDAttribute *pba;
    pba = [[PBDDAttribute alloc]init];
    [pba setDbName:s];
    [pba setLength:10];
    return [pba autorelease];
}
- (void)shrink;
{
//zur Laufzeit, wenn plainAttributes aufgerufen wurde, koennen die normalen Attributes der tables weggehauen werden;
//ebenso koennen die hidden tables weggehauen werden;
    PBDDTable *t;
    int j;
    for(j=[tables count]-1;j>=0;j--){
        t = [tables oai:j];
        [[t attributes]removeAllObjects];
    }
}
- (NSArray *)dbDifferencesToDDNew:(PBDD *)odb;
{
//ein modell mit diesem vgl.;
//ist anders als Modelmerge, da nur DB-Aspekte interessieren u. nicht z.B. guiNames
    //tables Existenz
    //Attribute Existenz
    //Attribute Typ
    //Attribute Laenge bzw. scale u. precision
    //Achtung! Info-String wird in BBCompareDB geparsed f. resolve-action; nicht leichtfertig aendern
    PBDDTable *t,*tdb;
    NSArray *ta = [self tablesIncludingAbstract:NO],*aa;
    NSArray *tadb,*aadb;
    NSString *s,*an;
    NSDictionary *error;
    static NSMutableArray *lma;
    static NSMutableArray *errors;
    PBDDAttribute *pba,*pbadb;
    int i,j,ii,jj,dt,dtdb,pbal,pbanak;
    NSAutoreleasePool *localPool=nil;
    if(!lma){
        lma = [[NSMutableArray alloc]initWithCapacity:100];
        errors = [[NSMutableArray alloc]initWithCapacity:100];
    }
    [lma removeAllObjects];
    [errors removeAllObjects];
//    LOGS(([NSSWF @"APP.utf8db = %@",[_APP utf8db]?@"J":@"N"]));
//tables in DB aber nicht in DD
    tadb = [odb tables];
    for(ii=0,jj=[tadb count];ii<jj;ii++){
        tdb = [tadb oai:ii];
        s = [tdb dbName];
        if([s hasSecurePrefix:@"query_"] || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
        if(!(t = [[self tableDict]objectForKey:s])){
            error = [NSDictionary dictionaryWithObjectsAndKeys:
                [NSSWF @"table- %@",s],@"info",
                EON,@"tableName",
                EON,@"attrName",
                @"1",@"isPK",
                nil,nil];
            [errors addObject:error];
        }else{
            [lma addObject:t]; //gemeinsame Tables
        }
    }
//tables in DD aber nicht in DB
    for(i=0,j=[ta count];i<j;i++){
        t = [ta oai:i];
        s = [t dbName];
        if([s hasSecurePrefix:@"query_"] || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
        if(![[odb tableDict]objectForKey:s]){
            error = [NSDictionary dictionaryWithObjectsAndKeys:
                [NSSWF @"table+ %@",s],@"info",
                s,@"tableName",
                EON,@"attrName",
                @"1",@"isPK",
                nil,nil];
            [errors addObject:error]; //im DD aber nicht in DB
        }
    }
    for(i=0,j=[lma count];i<j;i++){ //gemeinsame
        localPool = [[NSAutoreleasePool alloc]init];
        t = [lma oai:i];
        s = [t dbName];
        if([s hasSecurePrefix:@"query_"]  || [s hasSecurePrefix:@"keyword_"])continue; // recherche-Tabellen werden dynamisch angelegt
        tdb = [[odb tableDict]objectForKey:s];
        aadb = [tdb attributes];
//attr. in DB aber nicht in DD
        for(ii=0,jj=[aadb count];ii<jj;ii++){
            pbadb = [aadb oai:ii];
            an = [[pbadb dbName]lowercaseString];
            if(!(pba = [t plainAttrNamed:an]) || ![pba isDB]){
                error = [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSSWF @"tableAttr- %@ %@",s,an],@"info",
                    s,@"tableName",
                    @"",@"attrName",
                    @"1",@"isPK",
                    nil,nil];
                [errors addObject:error];
            }else{
//attr. mit unterschiedl. Typ
                dt = [pba dataTyp];
                dtdb = [pbadb dataTyp];
                pbal = [pba length];
                pbanak = [pba nak];
                if(dt == DT_MONEY){
                    pbal = 11;
                    pbanak = 2;
                    dt=DT_FLOAT;
                }
                if(dt == DT_BOOL)dt=DT_CHAR; //char(1) gibt's nimmer; mind. 3
                if(dtdb !=dt ){
                    error = [NSDictionary dictionaryWithObjectsAndKeys:
                        [NSSWF @"tableAttr_dt %@ %@ dddt=%i dbdt=%i",s,an,dt,dtdb],@"info",
                        s,@"tableName",
                        an,@"attrName",
                        ([pba isPK]?@"0":@"1"),@"isPK",
                        nil,nil];
                    [errors addObject:error];
                    continue;
                }
// attr. gleicher Typ aber unterschiedl. laenge
                if(dt == DT_INT || dt == DT_DATE || dt == DT_DATETIME)continue; //bei integer lange nicht checken; ist in DB immer 11; in Modell fuer Anzeige evt. begrenzt
                if(pbal > (255))pbal=(65535); //text length
                if((dt == DT_CHAR) && ([pbadb length]  != pbal)){ //   / (utf8db?3:1)
                    error = [NSDictionary dictionaryWithObjectsAndKeys:
                        [NSSWF @"tableAttrW %@ %@ ddW=%i dbW=%i",s,an,pbal,[pbadb length]],@"info",
                        s,@"tableName",
                        an,@"attrName",
                        ([pba isPK]?@"0":@"1"),@"isPK",
                        nil,nil];
                    [errors addObject:error];
                    continue;
                }
                if((dt == DT_FLOAT)){ // schliesst money mit ein
                    if(([pbadb length]  != pbal)){
                        error = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSSWF @"tableAttrW %@ %@ ddW=%i dbW=%i",s,an,pbal,[pbadb length]],@"info",
                            s,@"tableName",
                            an,@"attrName",
                            @"1",@"isPK",
                            nil,nil];
                        [errors addObject:error];
                        continue;
                    }
                    if([pbadb nak] != pbanak){
                        error = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSSWF @"tableAttr_nak %@ %@ ddnak=%i dbnak=%i",s,an,pbanak,[pbadb nak]],@"info",
                            s,@"tableName",
                            an,@"attrName",
                            @"1",@"isPK",
                            nil,nil];
                        [errors addObject:error];
                        continue;
                    }
                }
            }
        }
//attr. in DD aber nicht in DB
        aa = [t attributesDB];
        for(ii=0,jj=[aa count];ii<jj;ii++){
            pba = [aa objectAtIndex:ii];
            an = [[pba dbName]lowercaseString];
            if(![tdb attrNamed:an]){
                error = [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSSWF @"tableAttr+ %@ %@",s,an],@"info",
                    s,@"tableName",
                    an,@"attrName",
                    @"1",@"isPK",
                    nil,nil];
                [errors addObject:error];
            }
        }
        [localPool release];
    }
    [errors sortUsingKeyOrderArray: [NSArray soaFrom:@"isPK,info"]];
// LOGS([errors description]);
    return errors;
}
- (NSMutableString *)sqlStatmentsForChanges:(NSArray *)chgs;
{
    int i,j;
    NSDictionary *d;
    NSArray *a;
    NSString *info,*diff;
    NSMutableString *sqlS = [NSMutableString stringWithCapacity:1024];
    PBDDTable *t;
    PBDDAttribute *pba;
    NSString *k,*s;
    LMD;
    [sqlS setString:EON];
    for(i=0,j=[chgs count];i<j;i++){
        d = [chgs oai:i];
        info = [d ofk:@"info"];
        a = [info componentsSeparatedByString:@" "];
        if([a count] < 2)continue;
        diff = [a firstObject];
        if([diff iE:@"table-"]){
            // drop table
            [sqlS appendFormat:@"drop table %@;\n",[a oai:1]];
        }else if([diff iE:@"table+"]){
            // create table
            if(!(t = [self tableNamed:[a oai:1]]))continue;
            [sqlS appendString:[_APP sqlFormatCt:t]];
        }else if([diff iE:@"tableAttr-"]){
            // alter table drop ...
            k = [NSSWF @"alter table %@ drop ",[a oai:1]];
            s = [lmd ofk:k];
            if(!s){
                s = [a oai:2];
            }else{
                s = [s stringByAppendingFormat:@", drop %@",[a oai:2]];
            }
            [lmd setObject:s forKey:k];
        }else if([diff iE:@"tableAttr+"]){
            if(!(t = [self tableNamed:[a oai:1]]))continue;
            if(!(pba = [t plainAttrNamed:[a oai:2]]))continue;
            k = [NSSWF @"alter table %@ add ",[a oai:1]];
            s = [lmd ofk:k];
            if(!s){
                s = [_APP sqlFormatCANew:pba];
            }else{
                s = [NSSWF @"%@, add %@",s,[_APP sqlFormatCANew:pba]];
            }
            [lmd setObject:s forKey:k];
            
// to prevent NULL Values in DB
            k = [NSSWF @"update %@ set ",[a oai:1]];
            s = [lmd ofk:k];
            if(!s){
                s = [NSSWF @"%@ = '%@'",[pba dbName],[pba initValue]];
                [lmd setObject:s forKey:k];
            }else{
                s = [s stringByAppendingFormat:@", %@ = '%@'",[pba dbName],[pba initValue]];
                [lmd setObject:s forKey:k];
            }
        }else if([diff iE:@"tableAttrW"] || [diff iE:@"tableAttr_dt"] || [diff iE:@"tableAttr_nak"]){
            // alter table modify
            if(!(t = [self tableNamed:[a oai:1]]))continue;
            if(!(pba = [t plainAttrNamed:[a oai:2]]))continue;
            [sqlS appendFormat:@"alter table %@ modify %@;\n",[a oai:1],[_APP sqlFormatCANew:pba]];
        }
    }
    a = [[lmd allKeys]sortedArray];
    for(i=0,j=[a count];i<j;i++){
        k = [a oai:i]; // alter vor update
        [sqlS appendFormat:@"%@ %@;\n",k,[lmd ofk:k]];
    }
    return sqlS;
}
@end
Foto