/*
  Copyright (C) 2002-2005 SKYRIX Software AG

  This file is part of SOPE.

  SOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  SOPE 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 Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with SOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

#include "SoClass.h"
#include "SoClassSecurityInfo.h"
#include "common.h"

#if APPLE_RUNTIME || NeXT_RUNTIME
@interface NSObject(Miss)
- (void)subclassResponsibility:(SEL)cmd;
@end
#endif

@implementation SoClass

static BOOL debugOn = NO;

- (id)initWithSoSuperClass:(SoClass *)_soClass {
  if ((self = [super init])) {
    self->soSuperClass = [_soClass retain];
    self->slots        = [[NSMutableDictionary alloc] init];
  }
  return self;
}
- (id)init {
  return [self initWithSoSuperClass:nil];
}

- (void)dealloc {
  [self->security     release];
  [self->slots        release];
  [self->soSuperClass release];
  [super dealloc];
}

/* hierachy */

- (SoClass *)soSuperClass {
  return self->soSuperClass;
}

/* keys (traverse hierarchy) */

- (BOOL)hasKey:(NSString *)_key inContext:(id)_ctx {
  if ([self valueForSlot:_key] != nil)
    return YES;
  
  return [self->soSuperClass hasKey:_key inContext:_ctx];
}

- (id)lookupKey:(NSString *)_key inContext:(id)_ctx {
  id value;
  
  if ((value = [self valueForSlot:_key]))
    return value;
  
  return [self->soSuperClass lookupKey:_key inContext:_ctx];
}

- (NSArray *)allKeys {
  SoClass *soClass;
  NSMutableSet *keys;
  
  keys = [NSMutableSet setWithCapacity:64];
  for (soClass = self; soClass != nil; soClass = [soClass soSuperClass])
    [keys addObjectsFromArray:[soClass slotNames]];
  return [keys allObjects];
}

/* slots (only works on the exact class) */

- (void)setValue:(id)_value forSlot:(NSString *)_key {
  if (debugOn)
    [self logWithFormat:@"set value for slot '%@': %@", _key, _value];

  if ([_key length] == 0) {
    [self logWithFormat:@"attempt to set value for invalid slot '%@'", _key];
    return;
  }
  [self->slots setObject:(_value ? _value : [NSNull null]) forKey:_key];
}
- (id)valueForSlot:(NSString *)_key {
  id value;
  
  value = [self->slots objectForKey:_key];
  if (debugOn)
    [self logWithFormat:@"queried value for slot '%@': %@", _key, value];
  return value;
}
- (NSArray *)slotNames {
  return self->slots ?  [self->slots allKeys] : [NSArray array];
}

/* security */

- (SoClassSecurityInfo *)soClassSecurityInfo {
  if (self->security == nil)
    self->security = [[SoClassSecurityInfo alloc] initWithSoClass:self];
  return self->security;
}

- (NSException *)validateKey:(NSString *)_key inContext:(id)_ctx {
  /* 
     nil means: access fully granted 
     
     IMPORTANT: to properly support acquisition, this method must return
     nil on keys which should be acquired (since validateKey is called before
     the lookup is performed) !
  */
  NSString *r;
  
  r = [NSString stringWithFormat:@"tried to access private key %@", _key];
  return [NSException exceptionWithName:@"KeyDenied" reason:r userInfo:nil];
}

/* factory */

- (id)instantiateObject {
  [self subclassResponsibility:_cmd];
  return nil;
}

- (NSClassDescription *)soClassDescription {
  return nil;
}

- (NSString *)className {
  return nil;
}

/* NSCopying */

- (id)copyWithZone:(NSZone *)_zone {
  /* 
    This is required on OSX because the class is used as a dict-key in
    OFSFactoryRegistry.
  */
  return [self retain];
}

/* description */

- (NSString *)description {
  NSMutableString *ms;
  
  ms = [NSMutableString stringWithCapacity:64];
  [ms appendFormat:@"<0x%08X[%@]:", self,
        NSStringFromClass((Class)*(void**)self)];
  
  if (self->soSuperClass)
    [ms appendFormat:@" super=0x%08X", self->soSuperClass];
  else
    [ms appendString:@" root"];
  
  if ([self->slots count] > 0) {
    [ms appendFormat:@" slots=%@", 
	  [[self->slots allKeys] componentsJoinedByString:@","]];
  }
  
  [ms appendString:@">"];
  return ms;
}

@end /* SoClass */
