/**
  * Calling this method will transfer all instances of $class from the statements table
  * to specific optimized relational tables.
  * 
  * During optimization, the current user has all privileges on the persistent memory. At
  * the end of the process, the old privileges will be set back.
  * 
  * The $options array can contain the following key => values (all booleans):
  * 
  * - recursive: compile the target class and its subclasses (default: false).
  * - append: append data to the existing optimized table if it already exists (default: false).
  * - rmSources: remove the triples in the statement table after transfer (default: true).
  *
  * @access public
  * @author Bertrand Chevrier, <*****@*****.**>
  * @param  \core_kernel_classes_Class class
  * @param  array options
  * @return boolean Will return true if it succeeds, false otherwise.
  */
 public function hardify(\core_kernel_classes_Class $class, $options = array())
 {
     $returnValue = (bool) false;
     $oldUpdatableModels = core_kernel_persistence_smoothsql_SmoothModel::getUpdatableModelIds();
     try {
         // Give access to all models during hardification.
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds(self::getAllModelIds());
         $classLabel = $class->getLabel();
         \common_Logger::i("Hardifying class {$classLabel}", array("GENERIS"));
         if (defined("DEBUG_PERSISTENCE") && DEBUG_PERSISTENCE) {
             if (in_array($class->getUri(), self::$debug_tables)) {
                 return;
             }
             \common_Logger::d('hardify ' . $class->getUri());
             self::$debug_tables[] = $class->getUri();
             $countStatement = $this->countStatements();
         }
         if (in_array($class->getUri(), self::$blackList)) {
             return $returnValue;
         }
         // ENTER IN SMOOTH SQL MODE
         PersistenceProxy::forceMode(PERSISTENCE_SMOOTH);
         //recursive will hardify the class and it's subclasses in the same table!
         isset($options['recursive']) ? $recursive = $options['recursive'] : ($recursive = false);
         //createForeigns will hardify the class that are range of the properties
         isset($options['createForeigns']) ? $createForeigns = $options['createForeigns'] : ($createForeigns = false);
         //check if we append the data in case the hard table exists or truncate the table and add the new rows
         isset($options['append']) ? $append = $options['append'] : ($append = false);
         //if true, the instances of the class will  be removed from the statements table!
         isset($options['rmSources']) ? $rmSources = (bool) $options['rmSources'] : ($rmSources = false);
         //if defined, we took all the properties of the class and it's parents till the topclass
         isset($options['topclass']) ? $topclass = $options['topclass'] : ($topclass = new \core_kernel_classes_Class(RDFS_RESOURCE));
         //if defined, compile the additional properties
         isset($options['additionalProperties']) ? $additionalProperties = $options['additionalProperties'] : ($additionalProperties = array());
         //if defined, reference the additional class to the table
         isset($options['referencesAllTypes']) ? $referencesAllTypes = $options['referencesAllTypes'] : ($referencesAllTypes = false);
         $tableName = '_' . Utils::getShortName($class);
         $myTableMgr = new TableManager($tableName);
         $referencer = ResourceReferencer::singleton();
         //get the table columns from the class properties
         $columns = array();
         $ps = new PropertySwitcher($class);
         $properties = $ps->getProperties($additionalProperties);
         $columns = $ps->getTableColumns($additionalProperties, self::$blackList);
         //init the count value in hardened classes:
         if (isset($this->hardenedClasses[$class->getUri()])) {
             PersistenceProxy::restoreImplementation();
             return true;
             //already being compiled
         } else {
             $this->hardenedClasses[$class->getUri()] = 0;
         }
         if (!$append || $append && !$myTableMgr->exists()) {
             //create the table
             if ($myTableMgr->exists()) {
                 $myTableMgr->remove();
             }
             $myTableMgr->create($columns);
             //reference the class
             $referencer->referenceClass($class, array("topclass" => $topclass, "additionalProperties" => $additionalProperties));
             if ($referencesAllTypes) {
                 $referencer->referenceInstanceTypes($class);
             }
         }
         //insert the resources
         $startIndex = 0;
         $instancePackSize = 100;
         $instances = $class->getInstances(false, array('offset' => $startIndex, 'limit' => $instancePackSize));
         $count = count($instances);
         $notDeletedInstances = array();
         do {
             //reset timeout:
             //set_time_limit(30);
             \helpers_TimeOutHelper::setTimeOutLimit(\helpers_TimeOutHelper::MEDIUM);
             $rows = array();
             foreach ($instances as $index => $resource) {
                 if ($referencer->isResourceReferenced($resource)) {
                     PersistenceProxy::forceMode(PERSISTENCE_HARD);
                     $resource->delete();
                     PersistenceProxy::restoreImplementation();
                 }
                 $row = array('uri' => $resource->getUri());
                 foreach ($properties as $property) {
                     $propValue = $resource->getOnePropertyValue($property);
                     $row[Utils::getShortName($property)] = $propValue;
                 }
                 $rows[] = $row;
             }
             $rowMgr = new RowManager($tableName, $columns);
             $rowMgr->insertRows($rows);
             foreach ($instances as $resource) {
                 $referencer->referenceResource($resource, $tableName, null, true);
                 if ($rmSources) {
                     //remove exported resources in smooth sql, if required:
                     // Be carefull, the resource can still exist even if
                     // delete returns true. Indeed, modelIds can be mixed between
                     // multiple models and only a part of the triples that consitute
                     // the resource might have been deleted.
                     if (!$resource->delete() || $resource->exists()) {
                         //@TODO : modified resource::delete() because resource not in local modelId cannot be deleted
                         $notDeletedInstances[] = $resource->getUri();
                         $startIndex++;
                     }
                 }
             }
             if (!$rmSources) {
                 //increment start index only if not removed
                 $startIndex += $instancePackSize;
             }
             //record hardened instances number
             if (isset($this->hardenedClasses[$class->getUri()])) {
                 $this->hardenedClasses[$class->getUri()] += $count;
             } else {
                 $this->hardenedClasses[$class->getUri()] = $count;
             }
             //update instance array and count value
             $instances = $class->getInstances(false, array('offset' => $startIndex, 'limit' => $instancePackSize));
             foreach ($notDeletedInstances as $uri) {
                 unset($instances[$uri]);
             }
             $count = count($instances);
             \helpers_TimeOutHelper::reset();
         } while ($count > 0);
         $returnValue = true;
         // Treat subclasses of the current class
         if ($recursive) {
             foreach ($class->getSubClasses(true) as $subClass) {
                 $returnValue = $this->hardify($subClass, array_merge($options, array('recursive' => false, 'append' => true)));
             }
         }
         //reset cache:
         $referencer->clearCaches();
         // EXIT SMOOTH SQL MODE
         PersistenceProxy::restoreImplementation();
         if (defined("DEBUG_PERSISTENCE") && DEBUG_PERSISTENCE) {
             $this->unhardify($class, array_merge($options, array('recursive' => false, 'removeForeigns' => false)));
             \common_Logger::d('unhardened result statements ' . $this->countStatements() . ' / ' . $countStatement);
         }
         // Give the normal rights on models to the session.
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds($oldUpdatableModels);
     } catch (Exception $e) {
         \common_Logger::e('An error occured during hardification: ' . $e->getMessage());
         core_kernel_persistence_smoothsql_SmoothModel::forceUpdatableModelIds($oldUpdatableModels);
     }
     return (bool) $returnValue;
 }
 /**
  * Test the referencer on properties, using the file caching mode
  * (it's the default caching mode for the properties)
  * @see oat\generisHard\models\hardapi\ResourceReferencer
  */
 public function testPropertyReferencer()
 {
     $referencer = ResourceReferencer::singleton();
     $this->assertIsA($referencer, 'oat\\generisHard\\models\\hardapi\\ResourceReferencer');
     $referencer->setPropertyCache(ResourceReferencer::CACHE_FILE);
     $referencer->clearCaches();
     $class = new core_kernel_classes_Class(CLASS_GENERIS_USER);
     $table = '_' . Utils::getShortName($class);
     // this part simulates a hardifying of the Userclass
     $myUserTblMgr = new TableManager($table);
     $this->assertFalse($myUserTblMgr->exists());
     $this->assertTrue($myUserTblMgr->create(array(array('name' => '05label'), array('name' => '05comment'), array('name' => '07login'), array('name' => '07password'), array('name' => '07userMail'), array('name' => '07userFirstName'), array('name' => '07userLastName'))));
     $this->assertTrue($myUserTblMgr->exists());
     $referencer->referenceClass($class);
     $this->assertTrue($referencer->isClassReferenced($class));
     // test start on the cache containing the simulated data
     // in case of a  fallback to the real sata (class_to_table) the tests fail
     $labelProperty = new core_kernel_classes_Property(RDFS_LABEL);
     $this->assertTrue($referencer->isPropertyReferenced($labelProperty));
     $commentProperty = new core_kernel_classes_Property(RDFS_COMMENT);
     $this->assertTrue($referencer->isPropertyReferenced($commentProperty));
     $loginProperty = new core_kernel_classes_Property(PROPERTY_USER_LOGIN);
     $this->assertTrue($referencer->isPropertyReferenced($loginProperty));
     $passwordProperty = new core_kernel_classes_Property(PROPERTY_USER_PASSWORD);
     $this->assertTrue($referencer->isPropertyReferenced($passwordProperty));
     $firstNameProperty = new core_kernel_classes_Property(PROPERTY_USER_FIRSTNAME);
     foreach ($referencer->propertyLocation($firstNameProperty) as $foundTable) {
         $this->assertEquals($foundTable, $table);
     }
     $this->assertTrue($myUserTblMgr->exists());
     $referencer->unReferenceClass($class);
     $this->assertFalse($referencer->isClassReferenced($class));
     $this->assertFalse($myUserTblMgr->exists());
     // Testing the cache...
     $cache = common_cache_FileCache::singleton();
     $serial = 'hard-api-property';
     $this->assertTrue($cache->has($serial));
     try {
         $cacheContent = $cache->get($serial);
         $this->assertTrue(is_array($cacheContent));
         $this->assertTrue(count($cacheContent) > 0);
         $this->assertTrue(array_key_exists(RDFS_LABEL, $cacheContent));
         $this->assertTrue(array_key_exists(PROPERTY_USER_LOGIN, $cacheContent));
     } catch (common_cache_Exception $e) {
         $this->fail('Cannot access hard-api-property cache.');
     }
     //clear the cache
     $cache->remove($serial);
     $this->assertFalse($cache->has($serial));
 }