/**
     * Short description of method unReferenceClass
     *
     * @access public
     * @author Joel Bout, <*****@*****.**>
     * @param  Class class
     * @return boolean
     */
    public function unReferenceClass(\core_kernel_classes_Class $class)
    {
        $returnValue = (bool) false;
        if ($this->isClassReferenced($class)) {
            $tableName = '_' . Utils::getShortName($class);
            //need to instanciate table manager before unreferencing otherwise, the "remove table" will fail
            $tm = new TableManager($tableName);
            // Delete reference of the class in classs_to_table, resource_has_class, resource_to_table
            $dbWrapper = \core_kernel_classes_DbWrapper::singleton();
            // Remove references of the resources in the resource has class table
            $queries = array();
            $queries[] = 'DELETE 
				FROM "resource_has_class" 
				WHERE "resource_has_class"."resource_id" 
					IN (SELECT "resource_to_table"."id" FROM "resource_to_table" WHERE "resource_to_table"."table" = \'' . $tableName . '\' );';
            // Remove reference of the class in the additional properties tables
            $queries[] = 'DELETE 
				FROM "class_additional_properties"
				WHERE "class_id" 
					IN (SELECT "class_to_table"."id" FROM "class_to_table" WHERE "class_to_table"."table" = \'' . $tableName . '\' );';
            // Remove resferences of the resources int resource to table table
            $queries[] = 'DELETE FROM "resource_to_table" WHERE "resource_to_table"."table" = \'' . $tableName . '\';';
            // Remove reference of the class in the class to table table
            $queries[] = 'DELETE FROM "class_to_table" WHERE "class_to_table"."table" = \'' . $tableName . '\';';
            $returnValue = true;
            try {
                foreach ($queries as $query) {
                    $result = $dbWrapper->exec($query);
                    if ($result === false) {
                        $returnValue = false;
                    }
                }
                if ($returnValue !== false) {
                    // delete table associated to the class
                    $tm->remove();
                    // remove class from the cache
                    if ($this->cacheModes['class'] == self::CACHE_MEMORY && is_array(self::$_classes)) {
                        foreach (self::$_classes as $index => $aClass) {
                            if ($aClass['uri'] == $class->getUri()) {
                                unset(self::$_classes[$index]);
                            }
                        }
                    }
                }
                ClassProxy::$ressourcesDelegatedTo = array();
                ResourceProxy::$ressourcesDelegatedTo = array();
                PropertyProxy::$ressourcesDelegatedTo = array();
            } catch (\PDOException $e) {
                throw new Exception("Unable to unreference class {$class->getUri()} : " . $e->getMessage());
            }
        }
        return (bool) $returnValue;
    }
 /**
  * Short description of method getRdfTriples
  *
  * @access public
  * @author Joel Bout, <*****@*****.**>
  * @param  Resource resource
  * @return \core_kernel_classes_ContainerCollection
  */
 public function getRdfTriples(\core_kernel_classes_Resource $resource)
 {
     $returnValue = null;
     $returnValue = new \core_kernel_classes_ContainerCollection(new \common_Object(__METHOD__));
     $referencer = ResourceReferencer::singleton();
     $tableName = $referencer->resourceLocation($resource);
     if (!empty($tableName)) {
         try {
             $tblmgr = new TableManager($tableName);
             $propertiesTableName = $tblmgr->getPropertiesTable();
             $dbWrapper = \core_kernel_classes_DbWrapper::singleton();
             // We get the triples for cardinality = multiple or lg dependent properties
             // as usual...
             $quotedUri = $dbWrapper->quote($resource->getUri());
             $propsQuery = 'SELECT "b"."id", "b"."uri", "p"."property_uri" AS "property_uri", COALESCE("p"."property_value", "p"."property_foreign_uri") as "property_value", "p"."l_language"  FROM "' . $tableName . '" "b" ';
             $propsQuery .= 'INNER JOIN "' . $propertiesTableName . '" "p" ON ("b"."id" = "p"."instance_id") WHERE "b"."uri" = ' . $quotedUri;
             $propertyColumns = $tblmgr->getPropertyColumns();
             $baseQuery = '';
             if (!empty($propertyColumns)) {
                 // But if we have properties as columns in the 'base table' we
                 // have to be crafty...
                 $baseQueries = array();
                 foreach ($propertyColumns as $k => $pC) {
                     $quotedPropUri = $dbWrapper->quote($pC);
                     $baseQueries[] = 'SELECT "b"."id", "b"."uri", ' . $quotedPropUri . ' AS "property_uri", "b"."' . $k . '" AS "property_value", \'\' AS "l_language" FROM "' . $tableName . '" "b" WHERE "b"."uri" = ' . $quotedUri . ' AND "b"."' . $k . '" IS NOT NULL';
                 }
                 $baseQuery = implode(' UNION ', $baseQueries);
             }
             $query = $propsQuery . ' UNION ' . $baseQuery . ' ORDER BY "property_uri"';
             try {
                 $result = $dbWrapper->query($query);
                 while ($row = $result->fetch()) {
                     if ($row['property_value'] != null) {
                         $triple = new \core_kernel_classes_Triple();
                         $triple->subject = $row['uri'];
                         $triple->predicate = $row['property_uri'];
                         $triple->object = $row['property_value'];
                         $triple->lg = $row['l_language'];
                         $returnValue->add($triple);
                     }
                 }
                 // In hard mode, the rdf:type given to resources is defined by
                 // 'the table' their are belonging to. In this case, we need to
                 // manually add these triples to the end result.
                 $types = $resource->getTypes();
                 foreach ($types as $class) {
                     $triple = new \core_kernel_classes_Triple();
                     $triple->subject = $resource->getUri();
                     $triple->predicate = RDF_TYPE;
                     $triple->object = $class->getUri();
                     $triple->lg = '';
                     $returnValue->add($triple);
                 }
             } catch (\PDOException $e) {
                 $uri = $resource->getUri();
                 throw new Exception("Unable to retrieve RDF triples of resource '{$uri}': " . $e->getMessage());
             }
         } catch (HardapiException $e) {
             throw new Exception("Unable to access data from table '{$tableName}: " . $e->getMessage());
         }
     }
     return $returnValue;
 }
 /**
  * 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;
 }
 /**
  * (non-PHPdoc)
  * @see core_kernel_persistence_ClassInterface::createProperty()
  */
 public function createProperty(\core_kernel_classes_Class $resource, $label = '', $comment = '', $isLgDependent = false)
 {
     $returnValue = null;
     // First we reference the property in smooth mode because meta models always remain there.
     $smoothReturnValue = \core_kernel_persistence_smoothsql_Class::singleton()->createProperty($resource, $label, $comment, $isLgDependent);
     if ($smoothReturnValue) {
         $property = new \core_kernel_classes_Property($resource->getUri());
         $column = array('name' => HardapiUtils::getShortName($smoothReturnValue));
         $column['multi'] = $isLgDependent ? true : false;
         // If no range set, we assume it is a literal.
         $column['foreign'] = false;
         // We do not know the range yet.
         $referencer = ResourceReferencer::singleton();
         $classLocations = $referencer->classLocations($resource);
         foreach ($classLocations as $loc) {
             $tblmgr = new TableManager($loc['table']);
             $tblmgr->addColumn($column);
         }
         $returnValue = $smoothReturnValue;
         $referencer->clearCaches();
     } else {
         $uri = $resource->getUri();
         throw new Exception("An error occured when creating property in smooth mode '{$uri}' before handling the hard sql aspect.");
     }
     return $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));
 }
 /**
  * Change a multi-valued property to a single-valued one.
  *
  * @access public
  * @author Jerome Bogaerts, <*****@*****.**>
  * @param  Resource property The property to modifiy.
  * @param  int batchSize Data must be transfered from the properties table to a given column. This parameter indicates the size of each pack of data transfered from the properties table to the column.
  * @return void
  */
 public static function multipleToScalar(\core_kernel_classes_Resource $property, $batchSize = 100)
 {
     $referencer = ResourceReferencer::singleton();
     $dbWrapper = \core_kernel_classes_DbWrapper::singleton();
     $propertyDescription = self::propertyDescriptor($property);
     $propertyLocations = $referencer->propertyLocation($property);
     $propName = $propertyDescription['name'];
     $propUri = $property->getUri();
     $propRanges = array();
     foreach ($propertyDescription['range'] as $range) {
         // If no range provided, we assume it is a Literal.
         $propRanges[] = !empty($range) ? $range->getUri() : RDFS_LITERAL;
     }
     $offset = 0;
     foreach ($propertyLocations as $tblname) {
         $tblmgr = new TableManager($tblname);
         if ($tblmgr->exists()) {
             // Reset offset.
             $offset = 0;
             try {
                 // We go from multiple to single.
                 $toDelete = array();
                 // will contain ids of rows to delete in the 'properties table' after data transfer.
                 // Add a column to the base table to receive single value.
                 $baseTableName = str_replace('props', '', $tblname);
                 $tblmgr->setName($baseTableName);
                 $shortName = self::getShortName($property);
                 $columnAdded = $tblmgr->addColumn(array('name' => $shortName, 'multi' => false));
                 if ($columnAdded == true) {
                     // Now get the values in the props table. Group by instance ID in order to get only
                     // one value to put in the target column.
                     do {
                         $hasResult = false;
                         $retrievePropertyValue = empty($propRanges) || in_array(RDFS_LITERAL, $propRanges) ? true : false;
                         $sql = 'SELECT "a"."id", "a"."instance_id", "a"."property_value", "a"."property_foreign_uri" FROM "' . $tblname . '" "a" ';
                         $sql .= 'RIGHT JOIN (SELECT "instance_id", MIN("id") AS "id" FROM "' . $tblname . '" WHERE "property_uri" = ? ';
                         $sql .= 'GROUP BY "instance_id") AS "b" ON ("a"."id" = "b"."id")';
                         $sql = $dbWrapper->limitStatement($sql, $batchSize, $offset);
                         $result = $dbWrapper->query($sql, array($propUri));
                         // prepare the update statement.
                         $sql = 'UPDATE "' . $baseTableName . '" SET "' . $shortName . '" = ? WHERE "id" = ?';
                         while ($row = $result->fetch()) {
                             // Transfer to the 'base table'.
                             $hasResult = true;
                             $propertyValue = $retrievePropertyValue == true ? $row['property_value'] : $row['property_foreign_uri'];
                             $dbWrapper->exec($sql, array($propertyValue, $row['instance_id']));
                             $toDelete[] = $row['id'];
                         }
                         $offset += $batchSize;
                     } while ($hasResult === true);
                     $inData = implode(',', $toDelete);
                     $sql = 'DELETE FROM "' . $tblname . '" WHERE "id" IN (' . $inData . ')';
                     if ($dbWrapper->exec($sql) == 0) {
                         // If an error occured or no rows removed, we
                         // have a problem.
                         $msg = "Cannot set multiplicity of Property '{$propUri}' because data transfered to the 'base table' could not be deleted";
                         throw new Exception($msg);
                     }
                 } else {
                     $msg = "Cannot set multiplicity of Property '{$propUri}' because the corresponding 'base table' column could not be created.";
                     throw new Exception($msg);
                 }
             } catch (\PDOException $e) {
                 $msg = "Cannot set multiplicity of Property '{$propUri}': " . $e->getMessage();
                 throw new Exception($msg);
             }
         } else {
             $msg = "Cannot set multiplicity of Property '{$propUri}' because the corresponding database location '{$tblname}' does not exist.";
             throw new Exception($msg);
         }
     }
     $referencer->clearCaches();
 }