From df1c14ea9a7fa14fb55ced79966130f88f7db8eb Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Thu, 1 Nov 2012 12:17:21 +0000
Subject: [PATCH] Added pruning of potentially outdated information.

SVN: 27444
---
 .../BisKit/Classes/CISDOBIpadEntity.h         |  2 +
 .../BisKit/Classes/CISDOBIpadEntity.m         |  2 +
 .../BisKit/Classes/CISDOBIpadServiceManager.h |  1 +
 .../BisKit/Classes/CISDOBIpadServiceManager.m | 74 +++++++++++++------
 .../contents                                  |  7 +-
 5 files changed, 60 insertions(+), 26 deletions(-)

diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.h b/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.h
index 7376be327e7..5a93a3d26d5 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.h
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.h
@@ -41,6 +41,8 @@
 @property (nonatomic, retain) NSString * permId;
 @property (nonatomic, retain) NSString * refconJson;
 @property (readonly) id refcon;
+@property (nonatomic, retain) NSString * serverUrlString;
+@property (nonatomic, retain) NSDate * lastUpdateDate;
 
 // Potentially nil properties
 @property (nonatomic, retain) NSString * summaryHeader;
diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.m b/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.m
index e356f825160..0a4c2d1a85c 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.m
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadEntity.m
@@ -45,6 +45,8 @@ id ObjectFromJsonData(NSString *jsonData, NSError **error)
 @dynamic childrenPermIdsJson;
 @dynamic propertiesJson;
 @dynamic rootLevel;
+@dynamic serverUrlString;
+@dynamic lastUpdateDate;
 
 @synthesize image;
 
diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
index b1a5743c8cd..d6d4a095b73 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
@@ -58,6 +58,7 @@
 // Local Actions -- actions that do not require a network connection
 - (NSArray *)allIpadEntitiesOrError:(NSError **)error;
 - (NSArray *)entitiesByPermId:(NSArray *)permIds error:(NSError **)error;
+- (NSArray *)entitiesNotUpdatedSince:(NSDate *)date error:(NSError **)error;
 
 - (NSFetchRequest *)entityFetchRequest;
 - (NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest error:(NSError **)error;
diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
index b6ddd1cd29b..c4c85041a64 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
@@ -61,25 +61,6 @@ static NSManagedObjectContext* GetDatabaseManagedObjectContext(NSURL* storeUrl,
 	return moc;
 }
 
-static BOOL SynchEntityWithManagedObjectContext(CISDOBIpadRawEntity *rawEntity, NSManagedObjectModel *model, NSManagedObjectContext *moc, NSError **error)
-{
-    // Create new entities in the moc, and store them.
-    CISDOBIpadEntity *entity;
-    NSDictionary *fetchVariables = [NSDictionary dictionaryWithObject: [NSArray arrayWithObject: rawEntity.permId] forKey: @"PERM_IDS"];
-    NSFetchRequest *request = [model fetchRequestFromTemplateWithName: @"EntitiesByPermIds" substitutionVariables: fetchVariables];
-    NSArray *matchedEntities = [moc executeFetchRequest: request error: error];
-    if (!matchedEntities) return NO;
-    if ([matchedEntities count] > 0) {
-        entity = [matchedEntities objectAtIndex: 0];
-        [entity updateFromRawEntity: rawEntity];
-    } else {
-        entity = [NSEntityDescription insertNewObjectForEntityForName: @"CISDOBIpadEntity" inManagedObjectContext: moc];
-        [entity initializeFromRawEntity: rawEntity];
-    }
-    return YES;
-}
-
-
 
 @implementation CISDOBIpadServiceManager
 
@@ -101,25 +82,57 @@ static BOOL SynchEntityWithManagedObjectContext(CISDOBIpadRawEntity *rawEntity,
     return self;
 }
 
-- (BOOL)syncEntities:(NSArray *)rawEntities error:(NSError **)error
+- (BOOL)synchEntity:(CISDOBIpadRawEntity *)rawEntity lastUpdateDate:(NSDate *)date error:(NSError **)error
 {
+{
+    // Create new entities in the moc, and store them.
+    CISDOBIpadEntity *entity;
+    NSArray *matchedEntities = [self entitiesByPermId: [NSArray arrayWithObject: rawEntity.permId] error: error];
+    if (!matchedEntities) return NO;
+    if ([matchedEntities count] > 0) {
+        entity = [matchedEntities objectAtIndex: 0];
+        [entity updateFromRawEntity: rawEntity];
+    } else {
+        entity = [NSEntityDescription insertNewObjectForEntityForName: @"CISDOBIpadEntity" inManagedObjectContext: self.managedObjectContext];
+        [entity initializeFromRawEntity: rawEntity];
+    }
+    entity.lastUpdateDate = date;
+    entity.serverUrlString =  [((CISDOBLiveConnection *)(self.service.connection)).url absoluteString];
+    
+    return YES;
+}
+}
+
+- (BOOL)syncEntities:(NSArray *)rawEntities pruning:(BOOL)prune error:(NSError **)error
+{
+    NSDate *lastUpdateDate = [NSDate date];
     BOOL success;
     for (CISDOBIpadRawEntity *rawEntity in rawEntities) {
-        success = SynchEntityWithManagedObjectContext(rawEntity, self.managedObjectModel, self.managedObjectContext, error);
+        success = [self synchEntity: rawEntity lastUpdateDate: lastUpdateDate error: error];
         if (!success) return NO;
     }
+    // If pruning is requested, remove entities that cannot be reached from the server result set.
+    // TODO : we should treat the intial results as a root set and trace out to do a gc, but the simpler implementation is just to remove everything that is not mentioned
+    if (prune) {
+        // Remove all entities that were not mentioned
+        NSArray *entitiesToDelete = [self entitiesNotUpdatedSince: lastUpdateDate error: error];
+        for (CISDOBIpadEntity *entity in entitiesToDelete) {
+            [self.managedObjectContext deleteObject: entity];
+        }
+    }
+    
     success = [self.managedObjectContext save: error];
     return success;
 }
 
-- (CISDOBIpadServiceManagerCall *)managerCallWrappingServiceCall:(CISDOBAsyncCall *)serviceCall
+- (CISDOBIpadServiceManagerCall *)managerCallWrappingServiceCall:(CISDOBAsyncCall *)serviceCall pruning:(BOOL)prune
 {
     CISDOBIpadServiceManagerCall *managerCall = [[CISDOBIpadServiceManagerCall alloc] initWithServiceManager: self serviceCall: serviceCall];
     
     serviceCall.success = ^(id result) {
         // Update the cache
         NSError *error;
-        BOOL didSync = [self syncEntities: result error: &error];
+        BOOL didSync = [self syncEntities: result pruning: prune error: &error];
         if (!didSync) {
             serviceCall.fail(error);
         } else if (managerCall.success) {
@@ -132,6 +145,11 @@ static BOOL SynchEntityWithManagedObjectContext(CISDOBIpadRawEntity *rawEntity,
     return managerCall;
 }
 
+- (CISDOBIpadServiceManagerCall *)managerCallWrappingServiceCall:(CISDOBAsyncCall *)serviceCall
+{
+    return [self managerCallWrappingServiceCall: serviceCall pruning: NO];
+}
+
 - (CISDOBAsyncCall *)loginUser:(NSString *)user password:(NSString *)password
 {
     return [self.service loginUser: user password: password];
@@ -140,7 +158,8 @@ static BOOL SynchEntityWithManagedObjectContext(CISDOBIpadRawEntity *rawEntity,
 - (CISDOBAsyncCall *)retrieveRootLevelEntities
 {
     CISDOBAsyncCall *call = [self.service listRootLevelEntities];
-    return [self managerCallWrappingServiceCall: call];
+        // get rid of entities not mentioned in the original call
+    return [self managerCallWrappingServiceCall: call pruning: YES];
 }
 
 - (CISDOBAsyncCall *)drillOnEntity:(CISDOBIpadEntity *)entity
@@ -168,6 +187,13 @@ static BOOL SynchEntityWithManagedObjectContext(CISDOBIpadRawEntity *rawEntity,
     return [self executeFetchRequest: request error: error];
 }
 
+- (NSArray *)entitiesNotUpdatedSince:(NSDate *)date error:(NSError **)error
+{
+    NSDictionary *fetchVariables = [NSDictionary dictionaryWithObject: date forKey: @"LAST_UPDATE_DATE"];
+    NSFetchRequest *request = [self.managedObjectModel fetchRequestFromTemplateWithName: @"EntitiesNotUpdatedSince" substitutionVariables: fetchVariables];
+    return [self executeFetchRequest: request error: error];
+}
+
 - (NSFetchRequest *)entityFetchRequest
 {
 	NSFetchRequest *request = [[NSFetchRequest alloc] init];
diff --git a/openbis-ipad/BisKit/Classes/persistent-data-model.xcdatamodeld/persistent-data-model.xcdatamodel/contents b/openbis-ipad/BisKit/Classes/persistent-data-model.xcdatamodeld/persistent-data-model.xcdatamodel/contents
index 50b6c58dc1e..156f3a1b456 100644
--- a/openbis-ipad/BisKit/Classes/persistent-data-model.xcdatamodeld/persistent-data-model.xcdatamodel/contents
+++ b/openbis-ipad/BisKit/Classes/persistent-data-model.xcdatamodeld/persistent-data-model.xcdatamodel/contents
@@ -6,19 +6,22 @@
         <attribute name="childrenPermIdsJson" optional="YES" attributeType="String" indexed="YES" syncable="YES"/>
         <attribute name="identifier" optional="YES" attributeType="String" indexed="YES" syncable="YES"/>
         <attribute name="imageUrl" optional="YES" attributeType="String" syncable="YES"/>
+        <attribute name="lastUpdateDate" optional="YES" attributeType="Date" indexed="YES" syncable="YES"/>
         <attribute name="permId" optional="YES" attributeType="String" indexed="YES" syncable="YES"/>
         <attribute name="properties" optional="YES" transient="YES" syncable="YES"/>
         <attribute name="propertiesJson" optional="YES" attributeType="String" syncable="YES"/>
         <attribute name="refcon" optional="YES" transient="YES" syncable="YES"/>
         <attribute name="refconJson" optional="YES" attributeType="String" syncable="YES"/>
-        <attribute name="rootLevel" optional="YES" attributeType="Boolean" syncable="YES"/>
+        <attribute name="rootLevel" optional="YES" attributeType="Boolean" indexed="YES" syncable="YES"/>
+        <attribute name="serverUrlString" optional="YES" attributeType="String" syncable="YES"/>
         <attribute name="summary" optional="YES" attributeType="String" syncable="YES"/>
         <attribute name="summaryHeader" optional="YES" attributeType="String" syncable="YES"/>
     </entity>
     <fetchRequest name="EntitiesByPermIds" entity="CISDOBIpadEntity" predicateString="permId IN $PERM_IDS" fetchBatchSize="20"/>
+    <fetchRequest name="EntitiesNotUpdatedSince" entity="CISDOBIpadEntity" predicateString="lastUpdateDate &lt; $LAST_UPDATE_DATE"/>
     <fetchRequest name="EntityAndChildren" entity="CISDOBIpadEntity" predicateString="SELF == $ENTITY OR permId IN $CHILDREN" fetchBatchSize="20"/>
     <fetchRequest name="RootEntities" entity="CISDOBIpadEntity" predicateString="rootLevel == 0" fetchBatchSize="20"/>
     <elements>
-        <element name="CISDOBIpadEntity" positionX="0" positionY="0" width="128" height="240"/>
+        <element name="CISDOBIpadEntity" positionX="0" positionY="0" width="128" height="270"/>
     </elements>
 </model>
\ No newline at end of file
-- 
GitLab