/* 
   NSCharacterSet.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <Foundation/common.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSData.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSException.h>

#include <Foundation/exceptions/GeneralExceptions.h>

#include "NSConcreteCharacterSet.h"

@implementation NSCharacterSet

// Cluster allocation

+ (id)allocWithZone:(NSZone*)zone
{
    return NSAllocateObject( (self == [NSCharacterSet class]) ? 
	[NSBitmapCharacterSet class] : self, 0, zone);
}

// Creating a shared Character Set from a standard file
// We should mmap only ONE file and have a the sets initialized
// with range subdata of the main file (range subdata of NSData must
// not copy its data but retain its master data ...
// In this case we need a dictionary to know the offset of each charset
// in the BIG data; this approach should be very efficient on systems
// that support mmap (in paged virtual memory systems)

static NSMutableDictionary* predefinedCharacterSets = nil;

+ (NSCharacterSet*)characterSetWithContentsOfFile:(NSString*)fileName
{
    NSCharacterSet* aSet;

    // TODO: worry about locking
    if (predefinedCharacterSets == nil)
	    predefinedCharacterSets = [[NSMutableDictionary alloc]
		    initWithCapacity:12];

    aSet = [predefinedCharacterSets objectForKey:fileName];
    
    if (aSet == nil) {
	const char* charSetsPathCString;
	NSMutableArray* charSetsPath;
	NSMutableArray* searchPaths
	    = [NSMutableArray arrayWithObject:@RESOURCES_PATH];
	id fullFilenamePath = nil;
	int i, count;
	id data = nil;

	if ((charSetsPathCString = getenv("LIB_FOUNDATION_RESOURCES_PATH"))) {
	    charSetsPath = [[[[NSString stringWithCString:charSetsPathCString]
			    componentsSeparatedByString:@":"]
			    mutableCopy]
			    autorelease];
	    [charSetsPath addObjectsFromArray:searchPaths];
	    searchPaths = charSetsPath;
	}

	for (i = 0, count = [searchPaths count]; i < count; i++) {
	    fullFilenamePath = [[[[searchPaths objectAtIndex:i]
				      stringByAppendingPathComponent:@"CharacterSets"]
				      stringByAppendingPathComponent:fileName]
				      stringByResolvingSymlinksInPath];
	    if(fullFilenamePath && [fullFilenamePath length])
		break;
	}
	if (fullFilenamePath)
	    data = [NSData dataWithContentsOfMappedFile:fullFilenamePath];
	
	if (!data)
	    THROW([[FileNotFoundException alloc] initWithFilename:fileName]);
	aSet = [[[NSBitmapCharacterSet alloc] 
		    initWithBitmapRepresentation:data]
		    autorelease];
	[predefinedCharacterSets setObject:aSet forKey:fileName];
    }
    
    return aSet;
}

// Creating a Standard Character Set 

+ (NSCharacterSet*)alphanumericCharacterSet
{
    return [self characterSetWithContentsOfFile:@"alphanumericCharacterSet.bitmap"];
}

+ (NSCharacterSet*)controlCharacterSet
{
    return [self characterSetWithContentsOfFile:@"controlCharacterSet.bitmap"];
}

+ (NSCharacterSet*)decimalDigitCharacterSet
{
    return [self characterSetWithContentsOfFile:@"decimalDigitCharacterSet.bitmap"];
}

+ (NSCharacterSet*)decomposableCharacterSet
{
    return [self characterSetWithContentsOfFile:@"decomposableCharacterSet.bitmap"];
}

+ (NSCharacterSet*)illegalCharacterSet
{
    return [self characterSetWithContentsOfFile:@"illegalCharacterSet.bitmap"];
}

+ (NSCharacterSet*)letterCharacterSet
{
    return [self characterSetWithContentsOfFile:@"letterCharacterSet.bitmap"];
}

+ (NSCharacterSet*)lowercaseLetterCharacterSet
{
    return [self characterSetWithContentsOfFile:@"lowercaseLetterCharacterSet.bitmap"];
}

+ (NSCharacterSet*)nonBaseCharacterSet
{
    return [self characterSetWithContentsOfFile:@"nonBaseCharacterSet.bitmap"];
}

+ (NSCharacterSet*)uppercaseLetterCharacterSet
{
    return [self characterSetWithContentsOfFile:@"uppercaseLetterCharacterSet.bitmap"];
}

+ (NSCharacterSet*)whitespaceAndNewlineCharacterSet
{
    return [self characterSetWithContentsOfFile:@"whitespaceAndNewlineCharacterSet.bitmap"];
}

+ (NSCharacterSet*)whitespaceCharacterSet
{
    return [self characterSetWithContentsOfFile:@"whitespaceCharacterSet.bitmap"];
}

+ (NSCharacterSet*)punctuationCharacterSet
{
    return [self characterSetWithContentsOfFile:@"punctuationCharacterSet.bitmap"];
}

+ (NSCharacterSet*)emptyCharacterSet
{
    return [self characterSetWithContentsOfFile:@"emptyCharacterSet.bitmap"];
}

// Creating a Custom Character Set 

+ (NSCharacterSet*)characterSetWithBitmapRepresentation:(NSData*)data
{
    return [[[NSBitmapCharacterSet alloc] 
		initWithBitmapRepresentation:data] autorelease];
}

+ (NSCharacterSet*)characterSetWithCharactersInString:(NSString*)aString
{
    char*	bytes = Calloc(1, BITMAPDATABYTES);
    id		data;
    int		i, count = [aString length];
    unichar	c;

    for (i = 0; i < count; i++) {
	    c = [aString characterAtIndex:i];
	    SETBIT(bytes, c);
    }

    data = [NSData dataWithBytesNoCopy:bytes 
	    length:BITMAPDATABYTES];
    return [self isKindOfClass:[NSCharacterSet class]]
		? [[[NSBitmapCharacterSet alloc] 
		    initWithBitmapRepresentation:data] autorelease]
		: [[[NSMutableBitmapCharacterSet alloc] 
		    initWithBitmapRepresentation:data] autorelease];
}

+ (NSCharacterSet*)characterSetWithRange:(NSRange)aRange
{
    return [[[NSRangeCharacterSet alloc] initWithRange:aRange] autorelease];
}

// Getting a Binary Representation 

- (NSData*)bitmapRepresentation
{
    [self subclassResponsibility:_cmd];
    return nil;
}

// Testing Set Membership 

- (BOOL)characterIsMember:(unichar)aCharacter
{
    [self subclassResponsibility:_cmd];
    return NO;
}

// Inverting a Character Set 

- (NSCharacterSet*)invertedSet
{
    [self subclassResponsibility:_cmd];
    return nil;
}

// NSCopying

- copyWithZone:(NSZone*)zone
{
    if (NSShouldRetainWithZone(self, zone))
	    return [self retain];
    else {
	    id data = [self bitmapRepresentation];
	    return [[NSCharacterSet alloc] initWithBitmapRepresentation:data];
    }
}

- mutableCopyWithZone:(NSZone*)zone
{
    id data = [self bitmapRepresentation];
    return [[NSMutableCharacterSet characterSetWithBitmapRepresentation:data]
	    retain];
}

// NSArchiving

- (Class)classForCoder
{
    return [NSCharacterSet class];
}

- (id)replacementObjectForCoder:(NSCoder*)coder
{
    if ([[self class] isKindOfClass:[NSMutableCharacterSet class]])
	return [super replacementObjectForCoder:coder];
    return self;
}

- initWithCoder:(NSCoder*)coder
{
    id data;
    id new;

    [coder decodeValueOfObjCType:@encode(id) at:&data];
    new = [[[self class] alloc] initWithBitmapRepresentation:data];
    [self dealloc];
    return new;
}

- (void)encodeWithCoder:(NSCoder*)coder
{
    id data = [self bitmapRepresentation];
    [coder encodeValueOfObjCType:@encode(id) at:&data];
}

@end /* NSCharacterSet */

@implementation NSMutableCharacterSet

// Cluster allocation

+ (id)allocWithZone:(NSZone*)zone
{
    return NSAllocateObject( (self == [NSMutableCharacterSet class]) ? 
	    [NSMutableBitmapCharacterSet class] : self, 0, zone);
}

// Creating a Custom Character Set 

+ (NSCharacterSet*)characterSetWithBitmapRepresentation:(NSData*)data
{
    return [[[self alloc] initWithBitmapRepresentation:data] autorelease];
}

// Adding and Removing Characters

- (void)addCharactersInRange:(NSRange)aRange
{
    [self subclassResponsibility:_cmd];
}

- (void)addCharactersInString:(NSString*)aString
{
    [self subclassResponsibility:_cmd];
}

- (void)removeCharactersInRange:(NSRange)aRange
{
    [self subclassResponsibility:_cmd];
}

- (void)removeCharactersInString:(NSString*)aString
{
    [self subclassResponsibility:_cmd];
}

// Combining Character Sets

- (void)formIntersectionWithCharacterSet:(NSCharacterSet*)otherSet
{
    [self subclassResponsibility:_cmd];
}

- (void)formUnionWithCharacterSet:(NSCharacterSet*)otherSet
{
    [self subclassResponsibility:_cmd];
}

// Inverting a Character Set

- (void)invert
{
    [self subclassResponsibility:_cmd];
}

// NSCopying

- copyWithZone:(NSZone*)zone
{
    id data = [self bitmapRepresentation];
    return [[NSCharacterSet characterSetWithBitmapRepresentation:data] retain];
}

// NSArchiving

- (Class)classForCoder
{
    return [NSMutableCharacterSet class];
}

@end /* NSMutableCharacterSet */

