Logo Search packages:      
Sourcecode: adun.app version File versions  Download package

ULDatabaseIndex.m

/*
   Project: UL

   Copyright (C) 2005 Michael Johnston & Jordi Villa-Freixa

   Author: Michael Johnston

   Created: 2005-07-12 15:24:33 +0200 by michael johnston

   This application 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 application 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
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "ULFramework/ULDatabaseIndex.h"

@implementation ULDatabaseIndex

- (id) initWithDirectory: (NSString*) dir
{
      if(self = [super init])
      {
            index = [NSMutableDictionary  dictionaryWithCapacity: 1];
            [index retain];
            databaseDir = [dir retain];
            lastNumber = 0;
            objectInputReferences = [NSMutableDictionary new];
            objectOutputReferences = [NSMutableDictionary new];
      }     

      return self;
}

- (void) dealloc
{
      if(indexArray != nil)
            [indexArray release];

      [index release];
      [databaseDir release];
      [objectInputReferences release];
      [objectOutputReferences release];
      [super dealloc];
}

- (void) updateMetadataForObject: (id) object
{
      id metadata;
      NSString* ident;

      //search for the metadata dict for this object

      metadata = [index objectForKey: [object identification]];

      if(metadata == nil)
            [NSException raise: NSInvalidArgumentException
                  format: [NSString stringWithFormat: 
                  @"Database - Object %@ not present. Cannot update metadata", [object description]]];

      ident = [object identification];
      [index setObject: [[object allData] mutableCopy] forKey: ident];
      [[index objectForKey: ident] setObject: ident forKey: @"Identification"];
      [[index objectForKey: ident] setObject: NSStringFromClass([object class]) 
            forKey: @"Class"];
      [indexArray release];
      indexArray = [[index allValues] retain];
}

- (void) updateOutputReferencesForObject: (id) object
{
      NSString* ident;

      //check the object is actually in the index

      ident = [object identification];
      if([index objectForKey: ident] == nil)
            [NSException raise: NSInternalInconsistencyException
                  format: @"Object %@ is not present in the database index. Cannot add output references.",
                  [object metadata]];

      [objectOutputReferences setObject: [object outputReferences] 
            forKey: ident];
}

- (BOOL) objectInIndex: (id) object
{
      id ident;

      ident = [object identification];
      if([index objectForKey: ident] != nil)
            return YES;
      else
            return NO;
}

- (void) addObject: (id) object
{
      NSMutableDictionary* metadata;
      NSKeyedArchiver* archiver;
      id ident;
      NSMutableData* data = [NSMutableData new];

      //extract metadata to index   

      NSDebugLLog(@"ULDatabaseIndex", @"Extracting object metadata to index");
      metadata = [[object allData] mutableCopy];
      NSDebugLLog(@"ULDatabaseIndex", @"Metdata is %@", metadata);

      //save to the database

      ident = [object identification];
      [index setObject: metadata forKey: ident];
      //add the objects identification to its metadata so other objects can access it
      [[index objectForKey: ident] setObject: ident forKey: @"Identification"];
      NSDebugLLog(@"ULDatabaseIndex", @"Object Ident is %@", ident);
      
      //add the objects class to the metadata

      [[index objectForKey: ident] setObject: NSStringFromClass([object class]) 
            forKey: @"Class"];

      [indexArray release];
      indexArray = [[index allValues] retain];
      NSDebugLLog(@"ULDatabaseIndex", @"Saving %@ to database in %@", object, databaseDir);

      //extract references 

      [objectInputReferences setObject: [object inputReferences] forKey: ident];
      //There are no output references when an object is first added
      //since it must be saved before it can be used to generate data.

      archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: data];
      [archiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
      [archiver encodeObject: object forKey: @"root"];
      [archiver finishEncoding];

      [data writeToFile: [databaseDir stringByAppendingPathComponent: ident]
            atomically: NO];

      [archiver release];
      [data release];
}

- (void) removeObjectWithId: (id) ident
{
      NSString* filePath;
      NSFileManager* fileManager;
      id object, temp;

      object = [index objectForKey: ident];

      if(object != nil)
      {
            filePath = [databaseDir stringByAppendingPathComponent:
                        [object valueForKey: @"Identification"]];

            NSDebugLLog(@"ULDatabaseIndex", @"Removing object (%@) and file %@", 
                        object, 
                        filePath);

            fileManager = [NSFileManager defaultManager];   
            if([fileManager fileExistsAtPath: filePath])
            {
                  if([fileManager isDeletableFileAtPath: filePath])
                  {
                        [fileManager removeFileAtPath: filePath
                              handler: nil];

                        //remove object from index

                        [index removeObjectForKey: ident]; 
                        NSDebugMLLog(@"ULDatabaseIndex", @"Index is %@",
                              index);
                        [indexArray release];
                        indexArray = [[index allValues] retain];

                        //remove related input and output references

                        [objectOutputReferences removeObjectForKey: ident];
                        [objectInputReferences removeObjectForKey: ident];
                  }
                  else
                  {
                        NSWarnLog(@"Failed to remove object from database");
                        [NSException raise: NSInternalInconsistencyException
                              format: [NSString stringWithFormat: 
                              @"Unable to remove file %@ from database", filePath]];
                  }
            }     
            else
            {
                  //remove object from index

                  [index removeObjectForKey: ident];
                  NSDebugMLLog(@"ULDatabaseIndex", @"Index is     %@", index);
                  [indexArray release];
                  indexArray = [[index allValues] retain];
                  [NSException raise: NSInternalInconsistencyException
                        format: [NSString stringWithFormat: 
                        @"File %@ not present in database!. This is probably due to a bug - Please notify devs."]];
            }
      }
      else
      {
            [NSException raise: NSInternalInconsistencyException
                  format: [NSString stringWithFormat: 
                  @"Object with ident %@ not present in index!. This is probably due to a bug - Please notify devs.",
                  ident]];
      }

}

- (void) removeObjectsWithIds: (NSArray*) idents
{
      NSEnumerator* idEnum;
      id ident;

      idEnum = [idents objectEnumerator];
      while(ident = [idEnum nextObject])
            [self removeObjectWithId: ident];
}

//FIXME: Temporary implementation. Its too much to have
//to unarchive all the input objects to update them. However
//its tricky to synch the output references each time.
- (void) removeOutputReferenceToObjectWithId: (NSString*) identOne
            fromObjectWithId: (NSString*) identTwo
{
      NSString* temp;
      NSKeyedArchiver* archiver;
      NSMutableData* data = [NSMutableData new];
      id obj2;

      //An id can only appear once in an objects input and output references.
      
      temp = [databaseDir stringByAppendingPathComponent: identTwo];
      obj2 = [NSKeyedUnarchiver unarchiveObjectWithFile: temp];
      [obj2 removeOutputReferenceToObjectWithID: identOne];

      [objectOutputReferences setObject: [obj2 outputReferences] 
            forKey: identTwo];

      archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: data];
      [archiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
      [archiver encodeObject: obj2 forKey: @"root"];
      [archiver finishEncoding];

      [data writeToFile: temp atomically: NO];

      [archiver release];
      [data release];
}

- (id) unarchiveObjectWithId: (NSString*) ident
{
      NSString* temp;
      NSDictionary* indexMetadata;
      id object;

      temp = [databaseDir stringByAppendingPathComponent: ident];
      object = [NSKeyedUnarchiver unarchiveObjectWithFile: temp];

      //sync object metadata with current index metadata

      indexMetadata = [index objectForKey: ident];
      [object updateMetadata: indexMetadata];

      return object;
}

- (NSArray*) availableObjects
{
      return [[indexArray copy] autorelease];
}

- (NSDictionary*) metadataForObjectWithID: (NSString*) ident
{
      return [index objectForKey: ident];
}

- (NSArray*) outputReferencesForObjectWithID: (NSString*) ident
{
      return [objectOutputReferences objectForKey: ident];
}

- (NSArray*) inputReferencesForObjectWithID: (NSString*) ident
{
      return [objectInputReferences objectForKey: ident];
}

- (void) reindexAll
{
      NSWarnLog(@"Not implemented %@", NSStringFromSelector(_cmd));
}

/******************

NSCoding

******************/

/**
Updates v1.3 objects
*/

00316 - (void) _updateVersion
{
      NSString* dbName;
      id object;
      NSEnumerator* indexEnum;

      //assuming here that the index is always part of the local filesystem db
      //which is valid at the moment
      if((dbName = NSUserName()) == nil)
                  dbName = @"unknown";
            
      dbName = [NSString stringWithFormat: @"%@@localhost", dbName];

      //create new ivars      

      objectInputReferences = [NSMutableDictionary new];
      objectOutputReferences = [NSMutableDictionary new];

      //update entries without the key "database"
      indexEnum = [index objectEnumerator];
      while(object = [indexEnum nextObject])
            if([object objectForKey: @"Database"] == nil)
                  [object setObject: dbName forKey: @"Database"];
}

- (id) initWithCoder: (NSCoder*) decoder
{
      id lastSaveDate, date;

      if([decoder allowsKeyedCoding])
      {
            index = [[decoder decodeObjectForKey: @"Index"] retain];
            databaseDir = [[decoder decodeObjectForKey: @"DatabaseDir"] retain];
            lastSaveDate = [decoder decodeObjectForKey: @"SavedDate"];
            date = [NSCalendarDate calendarDate];
            if([lastSaveDate dayOfYear] == [date dayOfYear])
            {
                  if([lastSaveDate yearOfCommonEra] == [date yearOfCommonEra])
                        lastNumber = [decoder decodeIntForKey: @"LastIndex"];
            }           
            else
                  lastNumber = 0;   

            //handle update to new version of ULDatabaseIndex - (version 1.2 - 1.3+)

            if([decoder decodeObjectForKey: @"objectInputReferences"] != nil)
            {
                  objectInputReferences = [decoder decodeObjectForKey: @"objectInputReferences"];
                  objectOutputReferences = [decoder decodeObjectForKey: @"objectOutputReferences"];
                  [objectInputReferences retain];
                  [objectOutputReferences retain];
            }
            else
                  [self _updateVersion];
      }
      else
            [NSException raise: NSInvalidArgumentException
                  format: @"Database does not support non-keyed coding"];
      
      indexArray = [[index allValues] retain];
      NSDebugLLog(@"ULDatabaseIndex", @"Last Saved %@. Todays date %@. Last Index %d", lastSaveDate, date, lastNumber);

      return self;
}

- (void) encodeWithCoder: (NSCoder*) encoder
{
      if([encoder allowsKeyedCoding])
      {
            [encoder encodeInt: lastNumber forKey: @"LastIndex"];
            [encoder encodeObject: [NSCalendarDate calendarDate] forKey: @"SavedDate"];
            [encoder encodeObject: index forKey: @"Index"];
            [encoder encodeObject: databaseDir forKey: @"DatabaseDir"];
            [encoder encodeObject: objectInputReferences 
                  forKey: @"objectInputReferences"];
            [encoder encodeObject: objectOutputReferences 
                  forKey: @"objectOutputReferences"];
      }
      else
            [NSException raise: NSInvalidArgumentException
                  format: @"Database does not support non-keyed coding"];
}

@end

Generated by  Doxygen 1.6.0   Back to index