/* DBConnection.m
 *
 * (C) Copyright 1996, 1997 by VHF Computer GmbH
 * Author:  Georg Fleischmann
 *
 * created:    .12.96
 * modified: 27.01.97 23.04.97
 */

#import "DBConnection.h"
#import "VHFGenericRecord.h"
#import "types.h"
#import "dbCommon.h"

static EOModel	*mainModel, *geoModel;

@interface DBConnection(PrivateMethods)
@end


@implementation DBConnection

/* if we don't get a name for a model we load Company.eomodel
 */
+ (void)initClassWithModel:(NSString*)modelName
{
    mainModel = [self getModelFromName:(modelName) ? modelName : @"CompanyOpenBase"];
    //geoModel = [self getModelFromName:@"Geo"];
}

+ (EOModel*)mainModel
{
    return mainModel;
}

+ (EOModel*)geoModel
{
    return geoModel;
}

/* get model
 * this let us create multiple concurrent transaction sessions
 *
 * created:  27.01.97
 * modified: 10.02.97
 */
+ (EOModel*)getModelFromName:(NSString*)modelName
{
    return [[EOModel alloc] initWithContentsOfFile:modelName];
}

/* get database context
 * this let us create multiple concurrent transaction sessions (not all databases allow this)
 *
 * created:  20.04.97
 * modified: 
 */
+ (EODatabaseContext*)getContextFromModel:(EOModel*)model
{   EOAdaptor		*eoAdaptor;
    EODatabase		*db;
    EODatabaseContext	*context;

    if( !model )
    {	NSLog(@"%@ model = nil", NSStringFromSelector(_cmd));
        return nil;
    }
    if( !(db=[[[EODatabase alloc] initWithModel:model] autorelease]) )
    {	NSLog(@"%@ is unable to initWithModel:%@", NSStringFromSelector(_cmd), [model name]);
        return nil;
    }
    if( !(context=[[[EODatabaseContext alloc] initWithDatabase:db] autorelease]) )
    {	NSLog(@"%@ is unable to get context for:%@", NSStringFromSelector(_cmd), [model name]);
        return nil;
    }
    if( !(eoAdaptor = [[context adaptorContext] adaptor]) )
    {	NSLog(@"%@ is unable to get adaptor for:%@", NSStringFromSelector(_cmd), [model name]);
        return nil;
    }
    [eoAdaptor assertConnectionDictionaryIsValid];

    return context;
}

/* get database channel from database context
 * this let us create multiple channels for a context (for example to update an object within a fetch)
 *
 * created:  27.01.97
 * modified: 
 */
+ (EODatabaseChannel*)getChannelFromContext:(EODatabaseContext*)context
{   EODatabaseChannel	*channel;

    if( !(channel = [[[EODatabaseChannel alloc] initWithDatabaseContext:context] autorelease]) )
    {	NSLog(@"%@ is unable to get channel from context: '%@'", NSStringFromSelector(_cmd), [context description]);
        return nil;
    }
    NS_DURING
        [[channel adaptorChannel] openChannel];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER

    return channel;
}

/* get entity from model
 *
 * created:    .12.96
 * modified: 27.01.97
 */
+ (EOEntity*)getEntity:(NSString*)entityName
{
    return [[EOModelGroup defaultGroup] entityNamed:entityName];
}

+ (NSNumber*)getUniqueKeyFor:(EOEntity*)theEntity
{   NSString		*qString;
    EOQualifier		*qualifier;
    NSNumber		*max;
    EOGenericRecord	*tableMax = nil;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*tables;

    // Construct the qualifier for our name
    qString = [NSString stringWithFormat:@"entity_name='%@'", [theEntity externalName]];
    qualifier = [EOQualifier qualifierWithQualifierFormat:qString];

    // Select the object and check for the presence of our name and "max_key"
    tables = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:@"unique_key" qualifier:qualifier sortOrderings:nil]];
    if( [tables count] )
        tableMax = [tables objectAtIndex:0];
    else
    {   EOEntity	*entity = [self getEntity:@"unique_key"];

        tableMax = [[[EOGenericRecord alloc] initWithEditingContext:editingContext classDescription:[entity classDescriptionForInstances] globalID:[entity globalIDForRow:nil]] autorelease];
        [editingContext insertObject:tableMax];
        [tableMax takeValue:[NSNumber numberWithInt:0] forKey:@"max_key"];
    }

    // Was there an entry for our table name?
    if(!tableMax)
    {	NSLog(@"Unique key was unable to fetch EntityName = %@", [theEntity externalName]);
        return 0;
    }

    // Then there should be a value for "max_key" for our table
    max = [tableMax valueForKey:@"max_key"];
    if(!max)
    {	NSLog(@"Unique key method nextKey could not fine MaxKey");
        return 0;
    }

    max = [NSNumber numberWithInt:[[tableMax valueForKey:@"max_key"] intValue]+1];
    [tableMax takeValue:max forKey:@"max_key"];
    [editingContext saveChanges];
    [editingContext invalidateAllObjects];

    return max;
}


- init
{
    return [super init];
}

- (EOModel*)mainModel
{
    return mainModel;
}

- (EOModel*)geoModel
{
    return geoModel;
}

/* get entity from model
 *
 * created:    .12.96
 * modified: 27.01.97
 */
- (EOEntity*)getEntity:(NSString*)entityName
{
    return [[self class] getEntity:entityName];
}

- (NSNumber*)getUniqueKeyFor:(EOEntity*)theEntity
{
    return [[self class] getUniqueKeyFor:theEntity];
}

/* created:  27.11.96
 * modified:
 *
 * get unique binary key (1, 2, 4, 8, 16, 32) for 'category_id'
 */
- (NSNumber*)getUniqueBinaryKey:(NSString*)entityName
{   unsigned long	u, uSet = 0;
    EOEditingContext	*editingContext = [EOEditingContext new];
    VHFGenericRecord	*obj;
    NSArray		*objects;
    NSEnumerator	*enumerator;

    objects = [self getObjectsFromEntityName:entityName where:@"" context:editingContext];
    enumerator = [objects objectEnumerator];
    while( obj = [enumerator nextObject] )
        uSet |= [[obj valueForKey:@"category_id"] unsignedIntValue];

    for(u=1; u<=0x10000000; u*=2)
    {	if( !(uSet&u) )
        return [NSNumber numberWithInt:u];
    }

    [editingContext release];

    return nil;
}

/* modified: 01.11.96 14.01.97 08.02.97 15.06.97
 */
- (NSString*)getState:(NSNumber*)stateId language:(NSString*)language
{   NSString		*qString;
    EOQualifier		*qualifier;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*states;

    qString = [NSString stringWithFormat:@"state_id=%@", stateId];
    qualifier = [EOQualifier qualifierWithQualifierFormat:qString];
    states = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:@"state" qualifier:qualifier sortOrderings:nil]];

    if( ![states count] )
    {	NSLog(@"Sorry, state %d not in database\n", stateId);
        return nil;
    }

    return [self getLocalValueFor:[states objectAtIndex:0] name:@"state" language:language];
}

#if 0
- (NSArray*)getRegionsForState:(int)stateId language:(NSString*)language addString:(NSString*)string
{   NSString		*qString;
    EOQualifier		*qualifier;
    EOGenericRecord	*myObject;
    EODatabaseContext	*context;
    EODatabaseChannel	*channel;
    EOEntity		*entity;
    NSString		*region;
    NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
    NSArray		*sortArray;

    if( !(context = [self getContextFromModel:[self geoModel]]) )
        return nil;
    if( !(channel = [self getChannelFromContext:context]) )
        return nil;
    if( !(entity = [self getEntity:@"region" fromModel:[self geoModel]]) )
        return nil;

    if(string)
        [dict setObject:LocalString(languageTable, string) forKey:@"0"];

    // Construct the qualifier for our name
    qString = [NSString stringWithFormat:@"state_id=%i", stateId];
    qualifier = [EOQualifier qualifierWithQualifierFormat:qString];

    // Select the object and check for the presence of our name and "MaxKey"
#warning EOF1xTo2 beginTransaction (now an EOAdaptorContext method) may generate an exception on error
    (([[context adaptorContext] beginTransaction], YES), YES);
#warning EOF1xTo2 the new edition of this method requires an EOEditingContext (consider using objectsWithFetchSpecification:editingContext: instead)
    [channel selectObjectsDescribedByQualifier:qualifier fetchOrder:nil];
    while( myObject=[channel fetchObject] )
    {	NSNumber *regionId = [myObject valueForKey:@"region_id"];
        region = [self getLocalValueFor:myObject name:@"region" language:language];

        [dict setObject:region forKey:[regionId stringValue]];
    }

    [channel cancelFetch];
#warning EOF1xTo2 commitTransaction (now an EOAdaptorContext method) may generate an exception on error
    (([[context adaptorContext] commitTransaction], YES), YES);
    [[channel adaptorChannel] closeChannel];
    [channel closeChannel];

    sortArray = [self sortDict:dict];

    return sortArray;
}
#endif

- (NSArray*)getCountriesForState:(int)stateId language:(NSString*)language addString:(NSString*)string
{   EOQualifier		*qualifier;
    NSString		*tmpStr;
    NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
    NSArray		*sortArray;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*countries;
    int			i;

    if(string)
        [dict setObject:LocalString(languageTable, string) forKey:@"0"];

    qualifier = [EOQualifier qualifierWithQualifierFormat:[NSString stringWithFormat:@"state_id=%i", stateId]];
    countries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:@"country" qualifier:qualifier sortOrderings:nil]];

    for( i=0; i<[countries count]; i++ )
    {   VHFGenericRecord	*country = [countries objectAtIndex:i];
        NSNumber		*countryId = [country valueForKey:@"country_id"];

        tmpStr = [self getLocalValueFor:country name:@"country" language:language];
        [dict setObject:tmpStr forKey:[countryId stringValue]];
    }

    sortArray = [self sortDict:dict];

    return sortArray;
}


/* substitute all umlauts inside 'source' with 'to'
 *
 * created:  15.11.96
 * modified: 30.07.97 13.08.97
 */
- (NSString*)replaceUmlautIn:(NSString*)string with:(NSString*)subst
{   int		i, len, t, lenTo;
    const char	*source, *to;
    char	*target;
    NSString	*ret;

    source = [string cString];
    len = strlen(source);
    to = [subst cString];
    lenTo = strlen(to);
    if( !(target = malloc(len*lenTo+1)) )
         return 0;
    for(i=0, t=0; i<len; i++)
    {
        if( index("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 *?", source[i]) )
            target[t++] = source[i];
        else
        {   strcpy(target+t, to);
            t += lenTo;
        }
    }
    target[t] = 0;
    ret = [NSString stringWithCString:target];
    free(target);
    return ret;
}

/* get name of city for a given city id and zip
 *
 * created:  15.07.97
 * modified: 
 */
- (VHFGenericRecord*)getObjectForCity:(NSString*)city withZip:(NSString*)zip
{   NSString		*string;
    EOQualifier		*qualifier;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*cities;
    VHFGenericRecord	*cityObj = nil;

    if( city && [city length] )
    {   string = [self replaceUmlautIn:city with:@"?"];
        string = [NSString stringWithFormat: @"zip<='%@' AND zip_max>='%@' AND (city_de like '%@' OR city_gb like '%@')", zip, zip, string, string];
    }
    else
        string = [NSString stringWithFormat: @"zip<='%@' AND zip_max>='%@'", zip, zip];
    qualifier = [EOQualifier qualifierWithQualifierFormat:string];
    cities = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:@"city" qualifier:qualifier sortOrderings:nil]];
    if( [cities count] )
        cityObj = [cities objectAtIndex:0];
    return cityObj;
}

/* get name of city for a given city id and zip
 *
 * created:  14.11.96
 * modified: 07.02.97
 */
- (NSNumber*)getCityIdForCity:(NSString*)city withZip:(NSString*)zip
{   NSString		*string;
    EOQualifier		*qualifier;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*cities;
    VHFGenericRecord	*cityObj = nil;

    if( city && [city length] )
    {   string = [self replaceUmlautIn:city with:@"?"];
        string = [NSString stringWithFormat: @"zip<='%@' AND zip_max>='%@' AND (city_de like '%@' OR city_gb like '%@')", zip, zip, string, string];
    }
    else
        string = [NSString stringWithFormat: @"zip<='%@' AND zip_max>='%@'", zip, zip];
    qualifier = [EOQualifier qualifierWithQualifierFormat:string];
    cities = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:@"city" qualifier:qualifier sortOrderings:nil]];
    if( [cities count] )
        cityObj = [cities objectAtIndex:0];
    if(cityObj)
        return [cityObj valueForKey:@"city_id"];

    return nil;
}





/* get all entries from database and return a dictionary (keys/values)
 *
 * created:  01.12.96
 * modified: 30.01.97 26.04.97 15.06.97
 */
- (NSDictionary*)getDictFromEntity:(NSString*)entityName key:(NSString*)key value:(NSString*)val addString:(NSString*)string
{   EOQualifier		*qualifier;
    NSString		*keyVal;
    NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*entries;
    NSEnumerator	*enumerator;
    VHFGenericRecord	*entry;

    if(string)
        [dict setObject:LocalString(languageTable, string) forKey:@"0"];

    qualifier = [EOQualifier qualifierWithQualifierFormat:@""];
    entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil]];
    enumerator = [entries objectEnumerator];
    while( entry = [enumerator nextObject] )
    {   NSNumber	*keyId = [entry valueForKey:key];

        keyVal = [entry htmlStringForKey:val];
        if( ![keyVal isEqual:[EONull null]] )
            [dict setObject:keyVal forKey:[keyId stringValue]];
    }

    return dict;
}

/* created:  10.12.96
 * modified: 
 * get all entries from database and sort them into an array with arrays of key/value pairs
 */
- (NSArray*)addEntriesFromEntity:(NSString*)entityName key:(NSString*)key value:(NSString*)val to:(NSArray*)array
{   NSDictionary	*dict;
    NSArray		*keys, *vals;
    int			i, cnt;

    dict = [self getDictFromEntity:entityName key:key value:val addString:nil];
    keys = [dict allKeys];
    vals = [dict allValues];
    for(i=0, cnt=[keys count]; i<cnt; i++)
    {
        [(NSMutableArray*)[array objectAtIndex:0] addObject:[keys objectAtIndex:i]];
        [(NSMutableArray*)[array objectAtIndex:1] addObject:[vals objectAtIndex:i]];
    }

    [self sortDictArray:array];

    return array;
}

/* created: 25.11.96 02.02.99
 * get all entries from database and sort them into an array with arrays of key/value pairs
 */
- (NSArray*)getEntriesFromEntity:(NSString*)entityName key:(NSString*)key value:(NSString*)val addString:(NSString*)string
{   //NSDictionary	*dict;
    //NSArray		*sortArray;

    //dict = [self getDictFromEntity:entityName key:key value:val addString:string];
    //sortArray = [self sortDict:dict];

    //return sortArray;

    return [self getEntriesFromEntity:entityName where:@"" key:key value:val addString:string];
}

/* created: 18.12.96
 * get all entries from database and sort them into an array with arrays of key/value pairs
 *
 * key		the key for identifying the entry in the dictionary (dict-array) -> "id"
 *		they are collected in array[0] of the return value
 * val		the key of the value to build the entries for the key -> "last_name"
 *
 * return value is an array consisting of keys [0] and values [1]:
 * key	value
 * "1"	"Fleischmann"
 * "2"	"Chan"
 */
- (NSArray*)getEntriesFromEntity:(NSString*)entityName where:(NSString*)where key:(NSString*)key value:(NSString*)val addString:(NSString*)string
{   NSArray *vals = [NSArray arrayWithObjects:val, nil], *prefixes = [NSArray arrayWithObjects:@"", nil];

    return [self getEntriesFromEntity:entityName where:where key:key values:vals prefixes:prefixes addString:string];
}

/* created: 18.12.96
 * get all entries from database and sort them into an array with arrays of key/value pairs
 *
 * key		the key for identifying the entry in the dictionary (dict-array) -> "id"
 *			they are collected in array[0] of the return value
 * vals		the keys of the values to build the entries for the key -> ["last_name", "first_name"]
 *			together with the prefixes they build the values in array[1] of the return value
 * prefixes	the prefixes added to each value -> ["", ", "]
 *
 * return value is an array consisting keys [0] and vlaues [1]:
 * keys	values
 * "1"	"Fleischmann, Georg"
 * "2"	"Chan, Cyntia"
 */
- (NSArray*)getEntriesFromEntity:(NSString*)entityName key:(NSString*)key values:(NSArray*)vals prefixes:(NSArray*)prefixes addString:(NSString*)string
{
    return [self getEntriesFromEntity:entityName where:@"" key:key values:vals prefixes:prefixes addString:string];
}

/* created:  30.11.96
 * modified: 18.12.96 15.06.97
 * get all entries from database and sort them into an array with arrays of key/value pairs
 *
 * key		the key for identifying the entry in the dictionary (dict-array) -> "id"
 *			they are collected in array[0] of the return value
 * vals		the keys of the values to build the entries for the key -> ["last_name", "first_name"]
 *			together with the prefixes they build the values in array[1] of the return value
 * prefixes	the prefixes added to each value -> ["", ", "]
 *
 * return value is an array consisting keys [0] and vlaues [1]:
 * keys	values
 * "1"	"Fleischmann, Georg"
 * "2"	"Chan, Cyntia"
 */
- (NSArray*)getEntriesFromEntity:(NSString*)entityName where:(NSString*)qString key:(NSString*)key values:(NSArray*)vals prefixes:(NSArray*)prefixes addString:(NSString*)string
{   int			cnt = [vals count];
    EOQualifier		*qualifier;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*entries;
    NSEnumerator	*enumerator;
    VHFGenericRecord	*entry;
    NSArray		*sortArray = [NSArray arrayWithObjects:[NSMutableArray array], [NSMutableArray array], nil];
    NSArray		*fetchOrder = nil;

    qualifier = [[[EOSQLQualifier alloc] initWithEntity:[self getEntity:entityName] qualifierFormat:qString] autorelease];
    //qualifier = [EOQualifier qualifierWithQualifierFormat:qString];
    if( [vals count] )
        fetchOrder = [NSArray arrayWithObject:[[[EOSortOrdering alloc] initWithKey:[vals objectAtIndex:0] selector:EOCompareAscending] autorelease]];
    entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:fetchOrder]];
    enumerator = [entries objectEnumerator];
    while( entry = [enumerator nextObject] )
    {	NSNumber	*keyId = [entry valueForKey:key];
        NSString	*keyVal = @"";
        int		i;

        /* build value for 'keyId' consiting of the values for the keys specified in 'vals' */
        for(i=0; i<cnt; i++)
        {   NSString	*val;

            keyVal = [keyVal stringByAppendingString:[prefixes objectAtIndex:i]];
            if(val = [entry htmlStringForKey:[vals objectAtIndex:i]])
                keyVal = [keyVal stringByAppendingFormat:@"%@",val];
            //else
            //    NSLog(@"getEntriesFromEntity, Can't get '%@' from object", [vals objectAtIndex:i]);
            if( i == cnt-1 && [prefixes count]>cnt )
                keyVal = [keyVal stringByAppendingString:[prefixes objectAtIndex:i+1]];
        }

        [[sortArray objectAtIndex:0] addObject:[keyId stringValue]];
        [[sortArray objectAtIndex:1] addObject:keyVal];
    }

    if(string)
    {   [[sortArray objectAtIndex:1] insertObject:LocalString(languageTable, string) atIndex:0];
        [[sortArray objectAtIndex:0] insertObject:@"0" atIndex:0];
    }

    return sortArray;
}

/* created:  08.10.96
 * modified: 
 * get all entries from database and sort them into an array with arrays of key/value pairs
 *
 * key		the key for identifying the entry in the dictionary (dict-array) -> "id"
 *			they are collected in array[0] of the return value
 * vals		the keys of the values to build the entries for the key -> ["last_name", "first_name"]
 *			together with the prefixes they build the values in array[1] of the return value
 * prefixes	the prefixes added to each value -> ["", ", "]
 *
 * return value is an array consisting keys [0] and vlaues [1]:
 * keys	values
 * "1"	"Fleischmann, Georg"
 * "2"	"Chan, Cyntia"
 */
- (NSArray*)getEntriesFromEntity:(NSString*)entityName where:(NSString*)qString key:(NSString*)key values:(NSArray*)vals prefixes:(NSArray*)prefixes addString:(NSString*)string language:(NSString*)language
{   int			cnt = [vals count];
    EOQualifier		*qualifier;
    EOEditingContext	*editingContext = [[EOEditingContext new] autorelease];
    NSArray		*entries;
    NSEnumerator	*enumerator;
    VHFGenericRecord	*entry;
    NSArray		*sortArray = [NSArray arrayWithObjects:[NSMutableArray array], [NSMutableArray array], nil];
    NSArray		*fetchOrder = nil;

    qualifier = [[[EOSQLQualifier alloc] initWithEntity:[self getEntity:entityName] qualifierFormat:qString] autorelease];
    //qualifier = [EOQualifier qualifierWithQualifierFormat:qString];
    if( [vals count] )
        fetchOrder = [NSArray arrayWithObject:[[[EOSortOrdering alloc] initWithKey:[vals objectAtIndex:0] selector:EOCompareAscending] autorelease]];
    entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:fetchOrder]];
    enumerator = [entries objectEnumerator];
    while( entry = [enumerator nextObject] )
    {	NSNumber	*keyId = [entry valueForKey:key];
        NSString	*keyVal = @"";
        int		i;

        /* build value for 'keyId' consiting of the values for the keys specified in 'vals' */
        for(i=0; i<cnt; i++)
        {   NSString	*val;

            keyVal = [keyVal stringByAppendingString:[prefixes objectAtIndex:i]];
            if(val = [entry htmlStringForKey:[vals objectAtIndex:i] language:language])
                keyVal = [keyVal stringByAppendingFormat:@"%@",val];
            else
                NSLog(@"getEntriesFromEntity, Can't get '%@' from object", [vals objectAtIndex:i]);
        }

        [[sortArray objectAtIndex:0] addObject:[keyId stringValue]];
        [[sortArray objectAtIndex:1] addObject:keyVal];
    }

    if(string)
    {   [[sortArray objectAtIndex:1] insertObject:LocalString(languageTable, string) atIndex:0];
        [[sortArray objectAtIndex:0] insertObject:@"0" atIndex:0];
    }

    return sortArray;
}


/* add prefix to all strings in array
 */
- (void)addPrefix:(NSString*)prefix toArray:(NSMutableArray*)array
{   int	i, cnt;

    if(!array)
        return;
    for(i=0, cnt=[array count]; i<cnt; i++)
        [array replaceObjectAtIndex:i withObject:[prefix stringByAppendingString:[array objectAtIndex:i]]];
}

- (void)addArray:(NSArray*)array1 toArray:(NSMutableArray*)array
{   int	i, cnt;

    if(!array1)
        return;
    for(i=0, cnt=[array1 count]; i<cnt; i++)
        [array addObject:[array1 objectAtIndex:i]];
}


/* sort dictionary in an array containing the keys and value arrays
 * you have the responsibility to release dict
 */
- (NSArray*)sortDict:(NSDictionary*)dict
{   NSMutableArray	*keys, *vals;
    int			i, j, cnt;

    keys = [[[dict allKeys] mutableCopy] autorelease];
    vals = [[[dict allValues] mutableCopy] autorelease];

    for(i=0, cnt=[keys count]; i<cnt-1; i++)
    {	NSString	*val = [[vals objectAtIndex:i] description];

        for(j=i+1; j<cnt; j++)
        {
            if( [val compare:[[vals objectAtIndex:j] description]] == NSOrderedDescending )
            {	NSString	*key = [keys objectAtIndex:i];

                [keys replaceObjectAtIndex:i withObject:[keys objectAtIndex:j]];
                [keys replaceObjectAtIndex:j withObject:key];
                [vals replaceObjectAtIndex:i withObject:[vals objectAtIndex:j]];
                [vals replaceObjectAtIndex:j withObject:val];
                val = [[vals objectAtIndex:i] description];
            }
        }
    }

    return [NSArray arrayWithObjects:keys, vals, nil];
}

/* sort dict array containing the keys and value arrays
 */
+ (NSArray*)sortDictArray:(NSArray*)array
{   NSMutableArray	*keys, *vals;
    int			i, j, cnt;

    keys = [array objectAtIndex:0];
    vals = [array objectAtIndex:1];
    for(i=0, cnt=[keys count]; i<cnt-1; i++)
    {	NSString	*val = [vals objectAtIndex:i];
        for(j=i+1; j<cnt; j++)
        {   if( [val compare:[vals objectAtIndex:j]] == NSOrderedDescending )
            {	NSString	*key = [keys objectAtIndex:i];
                [keys replaceObjectAtIndex:i withObject:[keys objectAtIndex:j]];
                [keys replaceObjectAtIndex:j withObject:key];
                [vals replaceObjectAtIndex:i withObject:[vals objectAtIndex:j]];
                [vals replaceObjectAtIndex:j withObject:val];
                val = [vals objectAtIndex:i];
            }
        }
    }

    return array;
}
- (NSArray*)sortDictArray:(NSArray*)array
{
    return [[self class] sortDictArray:array];
}

/* modified: 14.01.97
 * build localized string: "region", "de" -> "region_de"
 * return object: "Hochhaus"
 */
- (NSString*)getLocalValueFor:theObject name:(NSString*)name language:(NSString*)language
{   NSString *value;

    value = [theObject valueForKey:[NSString stringWithFormat:@"%@_%@", name, language]];
    if(!value || [value isEqual:[EONull null]] || [value length]==0)
        value = [theObject valueForKey:[NSString stringWithFormat:@"%@_gb", name]];
    if(!value || [value isEqual:[EONull null]] || [value length]==0)
        value = [theObject valueForKey:[NSString stringWithFormat:@"%@_de", name]];
    if(!value || [value isEqual:[EONull null]])
        return nil;
    return value;
}

/* created:  29.04.97
 * modified: 15.06.97
 */
- (id)getSQLObjectFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{   EOQualifier		*qualifier;
    EOEditingContext	*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    NSArray		*entries;

    qualifier = [[[EOSQLQualifier alloc] initWithEntity:[self getEntity:entityName] qualifierFormat:where] autorelease];
    entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil]];
    if( [entries count] )
        return [entries objectAtIndex:0];
    return nil;
}

/* created:  29.04.97
 * modified: 15.06.97
 */
- (id)getObjectFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{
    return [[self class] getObjectFromEntityName:entityName where:where context:ec];
}

+ (id)getObjectFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{   EOQualifier		*qualifier;
    EOEditingContext	*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    NSArray		*entries = nil;

    NS_DURING
        qualifier = [EOQualifier qualifierWithQualifierFormat:where];
        entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil]];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER
    if( [entries count] )
        return [entries objectAtIndex:0];
    return nil;
}

/* created:  18.05.97
 * modified: 15.06.97
 */
- (NSArray*)getSQLObjectsFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{
    return [self getSQLObjectsFromEntityName:entityName where:where sort:nil context:ec];
}
- (NSArray*)getSQLObjectsFromEntityName:(NSString*)entityName where:(NSString*)where sort:(NSString*)sortKey context:(EOEditingContext*)ec
{   EOQualifier		*qualifier;
    EOEditingContext	*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    NSArray		*entries;
    NSArray		*sortOrder;

    sortOrder = (sortKey) ? [NSArray arrayWithObject:[EOSortOrdering sortOrderingWithKey:sortKey selector:EOCompareAscending]] : nil;

    qualifier = [[[EOSQLQualifier alloc] initWithEntity:[self getEntity:entityName] qualifierFormat:where] autorelease];
    entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:sortOrder]];
    return entries;
}
- (NSArray*)getObjectsFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{
    return [[self class] getObjectsFromEntityName:entityName where:where context:ec];
}
+ (NSArray*)getObjectsFromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{   EOQualifier		*qualifier;
    EOEditingContext	*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    NSArray		*entries;

    qualifier = [EOQualifier qualifierWithQualifierFormat:where];
    NS_DURING
        entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil]];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
        entries = nil;
    NS_ENDHANDLER
    return entries;
}

- (NSArray*)getObjectsFromEntityName:(NSString*)entityName where:(NSString*)where sort:(NSString*)sortKey context:(EOEditingContext*)ec
{   EOQualifier		*qualifier;
    EOEditingContext	*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    NSArray		*entries;
    NSArray		*sortOrder;

    sortOrder = (sortKey) ? [NSArray arrayWithObject:[[[EOSortOrdering alloc] initWithKey:sortKey selector:EOCompareAscending] autorelease]] : nil;

    qualifier = [EOQualifier qualifierWithQualifierFormat:where];
    NS_DURING
        entries = [editingContext objectsWithFetchSpecification:[EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:sortOrder]];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
        entries = nil;
    NS_ENDHANDLER
    return entries;
}

/* incremental fetch
 * don't forget to send [channel cancelFetch] behind last -fetchObjects:
 */
- (EODatabaseChannel*)startFetch:(NSString*)entityName where:(NSString*)where offset:(int)offset sort:(NSString*)sortKey context:(EOEditingContext*)ec
{   EOEntity			*entity = [self getEntity:entityName];
    //EODatabase			*db = [[[EODatabase alloc] initWithModel:[entity model]] autorelease];
    //EODatabaseContext		*context = [[[EODatabaseContext alloc] initWithDatabase:db] autorelease];
    //EODatabaseChannel		*channel = [[[EODatabaseChannel alloc] initWithDatabaseContext:context] autorelease];
    EOEditingContext		*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    EOObjectStoreCoordinator	*coordinator = [editingContext rootObjectStore];
    EODatabaseContext		*context;
    EODatabaseChannel		*channel;
    EOQualifier			*qualifier;
    NSArray			*sortOrder;
    EOFetchSpecification	*fetchSpec;
    VHFGenericRecord		*eo;
    int				i = 0, j;

    sortOrder = (sortKey) ? [NSArray arrayWithObject:[[[EOSortOrdering alloc] initWithKey:sortKey selector:EOCompareAscending] autorelease]] : nil;
    qualifier = [[[EOSQLQualifier alloc] initWithEntity:entity qualifierFormat:where] autorelease];
    fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:sortOrder];
    context = (EODatabaseContext*)[coordinator objectStoreForFetchSpecification:fetchSpec];
    channel = [context availableChannel];
    [[channel adaptorChannel] openChannel];

    NS_DURING
        [channel selectObjectsWithFetchSpecification:fetchSpec editingContext:editingContext];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER

    for( j=i; j<i+20 && j<offset; j++ )
    {   NSAutoreleasePool	*pool = [[NSAutoreleasePool alloc] init];

        while( i++<offset && (eo = [channel fetchObject]) )
            ;
        [pool release];
    }
    return channel;
}
- (NSArray*)fetchObjects:(int)count channel:(EODatabaseChannel*)channel
{   int			i = 0;
    NSMutableArray	*array = [NSMutableArray array];
    VHFGenericRecord	*eo;

    while( (eo = [channel fetchObject]) && i++<count )
        [array addObject:eo];
    if( ![array count] )
        return nil;
    return array;
}

/* get limitted number of objects
 * define an offset by giving a start value for the sort key in the where clause
 * sortKey: "client_id"
 * where:   "... AND client_id>1234", where 1234 is the value of the last client in the returned array
 */
- (NSArray*)getObjectsFromEntityName:(NSString*)entityName where:(NSString*)where sort:(NSString*)sortKey limit:(int)limit context:(EOEditingContext*)ec
{   EOEntity			*entity = [self getEntity:entityName];
    //EODatabase			*db = [[[EODatabase alloc] initWithModel:[entity model]] autorelease];
    //EODatabaseContext		*context = [[[EODatabaseContext alloc] initWithDatabase:db] autorelease];
    //EODatabaseChannel		*channel = [[[EODatabaseChannel alloc] initWithDatabaseContext:context] autorelease];
    EOEditingContext		*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    EOObjectStoreCoordinator	*coordinator = [editingContext rootObjectStore];
    EODatabaseContext		*context;
    EODatabaseChannel		*channel;
    EOQualifier			*qualifier;
    NSArray			*sortOrder;
    EOFetchSpecification	*fetchSpec;
    VHFGenericRecord		*eo;
    int				i = 0;
    NSMutableArray		*array = [NSMutableArray array];

    sortOrder = (sortKey) ? [NSArray arrayWithObject:[[[EOSortOrdering alloc] initWithKey:sortKey selector:EOCompareAscending] autorelease]] : nil;
    qualifier = [[[EOSQLQualifier alloc] initWithEntity:entity qualifierFormat:where] autorelease];
    fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:sortOrder];
    context = (EODatabaseContext*)[coordinator objectStoreForFetchSpecification:fetchSpec];
    channel = [context availableChannel];
    [[channel adaptorChannel] openChannel];

    NS_DURING
        [channel selectObjectsWithFetchSpecification:fetchSpec editingContext:editingContext];
    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER

    while( (eo = [channel fetchObject]) && i++<limit )
        [array addObject:eo];
    [channel cancelFetch];

    if( ![array count] )
        return nil;
    return array;
}

- (id)getAttribute:(NSString*)attributeName fromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{
    EOEntity			*entity = [self getEntity:entityName];
    EOQualifier			*qualifier;
    EOFetchSpecification	*fetchSpec;
    EOEditingContext		*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    EOObjectStoreCoordinator	*coordinator = [editingContext rootObjectStore];
    EODatabaseContext		*context;
    EODatabaseChannel		*dbChannel;
    EOAdaptorChannel		*channel;
    NSDictionary		*theRow;

    NS_DURING

        qualifier = [[[EOSQLQualifier alloc] initWithEntity:entity qualifierFormat:where] autorelease];
        fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil];
        context = (EODatabaseContext*)[coordinator objectStoreForFetchSpecification:fetchSpec];
        dbChannel = [context availableChannel];
        channel = [dbChannel adaptorChannel];

        if (![channel isOpen])
            [channel openChannel];

        [channel selectAttributes:[NSArray arrayWithObject:[entity attributeNamed:attributeName]] fetchSpecification:fetchSpec lock:NO entity:entity];

        theRow = [channel fetchRowWithZone:NULL];

    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER
    [dbChannel cancelFetch];

    return [theRow objectForKey:attributeName];
}

- (id)getAttributes:(NSArray*)attributeNames fromEntityName:(NSString*)entityName where:(NSString*)where context:(EOEditingContext*)ec
{
    EOEntity			*entity = [self getEntity:entityName];
    EOQualifier			*qualifier;
    EOFetchSpecification	*fetchSpec;
    EOEditingContext		*editingContext = (ec) ? ec : [[EOEditingContext new] autorelease];
    EOObjectStoreCoordinator	*coordinator = [editingContext rootObjectStore];
    EODatabaseContext		*context;
    EODatabaseChannel		*dbChannel;
    EOAdaptorChannel		*channel;
    NSDictionary		*theRow;
    NSMutableArray		*attributes = [NSMutableArray array];
    int				i, cnt;

    NS_DURING

        qualifier = [[[EOSQLQualifier alloc] initWithEntity:entity qualifierFormat:where] autorelease];
        fetchSpec = [EOFetchSpecification fetchSpecificationWithEntityName:entityName qualifier:qualifier sortOrderings:nil];
        context = (EODatabaseContext*)[coordinator objectStoreForFetchSpecification:fetchSpec];
        dbChannel = [context availableChannel];
        channel = [dbChannel adaptorChannel];

        for( i=0, cnt=[attributeNames count]; i<cnt; i++ )
            [attributes addObject:[entity attributeNamed:[attributeNames objectAtIndex:i]]];

        if (![channel isOpen])
            [channel openChannel];
        [channel selectAttributes:attributes fetchSpecification:fetchSpec lock:NO entity:entity];

        theRow = [channel fetchRowWithZone:NULL];

    NS_HANDLER
        NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    NS_ENDHANDLER
    [dbChannel cancelFetch];

    return theRow;
}

/* created:  19.06.97
 * modified: 
 */
+ (BOOL)deleteRowsWhere:(NSString*)where entityName:(NSString*)entityName
{   EODatabaseContext	*myContext;
    EODatabaseChannel	*myChannel;
    EOEntity		*entity;
    EOQualifier		*qualifier;

    if( !(entity = [self getEntity:entityName]) )
        return NO;
    if( !(myContext = [self getContextFromModel:[entity model]]) )
        return NO;
    if( !(myChannel = [self getChannelFromContext:myContext]) )
        return NO;

    qualifier = [EOQualifier qualifierWithQualifierFormat:where];
    [[myContext adaptorContext] beginTransaction];
    [[myChannel adaptorChannel] deleteRowsDescribedByQualifier:qualifier entity:entity];
    [[myContext adaptorContext] commitTransaction];

    return YES;
}

- (void)setAttribute:(NSString*)key inObjects:(NSArray*)objects to:(id)value
{   NSEnumerator	*enumerator = [objects objectEnumerator];
    VHFGenericRecord	*obj;

    while( obj = [enumerator nextObject] )
        [obj takeValue:value forKey:key];
}


/* we change umlaut of strings from HTML to internal format
 */
- (NSMutableArray*)translateArrayToHTML:(id)array
{   int	i, cnt;

    if( ![array isKindOfClass:[NSMutableArray class]] )
        array = [[array mutableCopy] autorelease];
    for( i=0, cnt=[array count]; i<cnt; i++ )
    {   id	val = [array objectAtIndex:i];

        if( [val isKindOfClass:[NSString class]] )
            [array replaceObjectAtIndex:i withObject:convertUmlautToHTML(val)];
    }
    return array;
}

/* we change umlaut of strings from HTML to internal format
 */
- (NSMutableDictionary*)translateDictionaryToHTML:(NSMutableDictionary*)dict
{
    if( [dict isKindOfClass:[NSMutableDictionary class]] )
    {   NSEnumerator	*enumerator = [dict keyEnumerator];
        id		key;

        while( key = [enumerator nextObject] )
        {
            if( [[dict valueForKey:key] isKindOfClass:[NSString class]] )
                [dict setObject:convertUmlautToHTML([dict valueForKey:key]) forKey:key];
        }
    }
    return dict;
}

/* we change umlaut of strings from HTML to internal format
 */
- (NSMutableDictionary*)translateDictionaryFromHTML:(NSMutableDictionary*)dict
{
    if( [dict isKindOfClass:[NSMutableDictionary class]] )
    {   NSEnumerator	*enumerator = [dict keyEnumerator];
        id		key;

        while( key = [enumerator nextObject] )
        {
            if( [[dict valueForKey:key] isKindOfClass:[NSString class]] )
                [dict setObject:convertUmlautFromHTML([dict valueForKey:key]) forKey:key];
        }
    }
    return dict;
}



/* format currency
 * format=0: 12000.7354 -> "12.000,74 DM"
 * format=1: 12000.7354 -> "$ 12,000.74"
 * format=2: 12000.7354 -> "DM 12.000,74"
 */
- (NSString*)formatCurrency:(NSNumber*)value currency:currency format:(int)format
{   NSString	*string = (!currency) ? @" DM" : [NSString stringWithFormat:@" %@", currency];

    switch(format)
    {
        default: return [self formatNumber:value res:2 prefix:@"" separator:@"," suffix:string];
        case 1:  return [self formatNumber:value res:2 prefix:string separator:@"." suffix:@""];
        case 2:  return [self formatNumber:value res:2 prefix:string separator:@"," suffix:@""];
    }
    return nil;
}

/* return formatted floating point number
 * 12.735999 -> "12,74 DM"
 */
- (NSString*)formatNumber:(NSNumber*)value res:(int)res prefix:(NSString*)prefix separator:(NSString*)separator suffix:(NSString*)suffix
{   NSString	*string;
    NSString	*format = [NSString stringWithFormat:@"%%@%%.%df%%@", res];

    string = [NSString stringWithFormat:format, prefix, (!value) ? 0.0 : [value floatValue], suffix];
    return replaceSubString(string, @".", separator);
}

/* return formatted floating point number
 * 12.735999 -> "12,74 DM"
 */
- (NSString*)formatFloat:(float)value res:(int)res prefix:(NSString*)prefix separator:(NSString*)separator suffix:(NSString*)suffix
{   NSString	*string;
    NSString	*format = [NSString stringWithFormat:@"%%@%%.%df%%@", res];

    string = [NSString stringWithFormat:format, prefix, value, suffix];
    return replaceSubString(string, @".", separator);
}




- (void)dealloc
{
    [super dealloc];
}

- (BOOL)adaptorChannel:channel shouldEvaluateExpression:(EOSQLExpression*)expression
{
    NSLog( [expression whereClauseString] );
    return YES;
}

@end
