function getLoadedOntologies($structwsf, $queryExtension = NULL)
{
    $ontologyRead = new OntologyReadQuery($structwsf);
    $getLoadedOntologiesFunction = new GetLoadedOntologiesFunction();
    $getLoadedOntologiesFunction->modeDescriptions();
    $ontologyRead->getLoadedOntologies($getLoadedOntologiesFunction)->send($queryExtension !== NULL ? $queryExtension : NULL);
    if ($ontologyRead->isSuccessful()) {
        $resultset = $ontologyRead->getResultset()->getResultset();
        $ontologies = array('local' => array(), 'reference' => array(), 'admin' => array());
        foreach ($resultset['unspecified'] as $uri => $ontology) {
            $onto = array('uri' => '', 'label' => '', 'modified' => false);
            $ontologyType = 'local';
            if (isset($ontology['http://purl.org/ontology/sco#ontologyType'])) {
                switch ($ontology['http://purl.org/ontology/sco#ontologyType'][0]['uri']) {
                    case "http://purl.org/ontology/sco#referenceOntology":
                        $ontologyType = 'reference';
                        break;
                    case "http://purl.org/ontology/sco#administrativeOntology":
                        $ontologyType = 'admin';
                        break;
                    case "http://purl.org/ontology/sco#localOntology":
                        $ontologyType = 'local';
                        break;
                }
            }
            $onto['uri'] = $uri;
            $onto['label'] = $ontology['prefLabel'];
            if (isset($ontology['http://purl.org/ontology/wsf#ontologyModified'])) {
                $onto['modified'] = TRUE;
            }
            array_push($ontologies[$ontologyType], $onto);
        }
        return $ontologies;
    } else {
        $debugFile = md5(microtime()) . '.error';
        file_put_contents('/tmp/' . $debugFile, var_export($ontologyRead, TRUE));
        @cecho('Can\'t get loaded ontologies. ' . $ontologyRead->getStatusMessage() . $ontologyRead->getStatusMessageDescription() . "\nDebug file: /tmp/{$debugFile}\n", 'RED');
        exit(1);
    }
}
    public function run()
    {
        cecho("\n\n");
        cecho("Data validation test: " . $this->description . "...\n\n", 'LIGHT_BLUE');
        // First, get the list of all the possible custom datatypes and their possible base datatype
        $customDatatypes = $this->getCustomDatatypes();
        // Check for universal restriction on Datatype Properties
        $sparql = new SparqlQuery($this->network);
        $from = '';
        foreach ($this->checkOnDatasets as $dataset) {
            $from .= 'from <' . $dataset . '> ';
        }
        foreach ($this->checkUsingOntologies as $ontology) {
            $from .= 'from named <' . $ontology . '> ';
        }
        $sparql->mime("application/sparql-results+json")->query('select distinct ?restriction ?class ?onProperty ?dataRange
                      ' . $from . '
                      where
                      {
                        ?s a ?class.

                        graph ?g {
                            ?class <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?restriction .
                            ?restriction a <http://www.w3.org/2002/07/owl#Restriction> ;
                                         <http://www.w3.org/2002/07/owl#onProperty> ?onProperty ;
                                         <http://www.w3.org/2002/07/owl#allValuesFrom> ?dataRange .
                                         
                            ?onProperty a <http://www.w3.org/2002/07/owl#DatatypeProperty> .
                        }
                      }')->send();
        if ($sparql->isSuccessful()) {
            $results = json_decode($sparql->getResultset(), TRUE);
            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                cecho("Here is the list of all the OWL universal restrictions defined for the classes currently used in the target datasets applied on datatype properties:\n\n", 'LIGHT_BLUE');
                $universals = array();
                foreach ($results['results']['bindings'] as $result) {
                    $class = $result['class']['value'];
                    $dataRange = $result['dataRange']['value'];
                    $onProperty = $result['onProperty']['value'];
                    cecho('  -> Record of type "' . $class . '" has an universal restriction  when using datatype property "' . $onProperty . '" with value type "' . $dataRange . '"' . "\n", 'LIGHT_BLUE');
                    if (!isset($universals[$class])) {
                        $universals[$class] = array();
                    }
                    $universals[$class][] = array('onProperty' => $onProperty, 'dataRange' => $dataRange);
                }
                cecho("\n\n");
                foreach ($universals as $class => $univs) {
                    foreach ($univs as $universal) {
                        $sparql = new SparqlQuery($this->network);
                        $from = '';
                        foreach ($this->checkOnDatasets as $dataset) {
                            $from .= 'from <' . $dataset . '> ';
                        }
                        foreach ($this->checkUsingOntologies as $ontology) {
                            $from .= 'from <' . $ontology . '> ';
                        }
                        $datatypeFilter = '';
                        // Here, if rdfs:Literal is used, we have to filter for xsd:string also since it is the default
                        // used by Virtuoso...
                        if (!empty($universal['dataRange'])) {
                            if ($universal['dataRange'] == 'http://www.w3.org/2000/01/rdf-schema#Literal') {
                                $datatypeFilter = 'filter((str(datatype(?value)) != \'http://www.w3.org/2000/01/rdf-schema#Literal\' && str(datatype(?value)) != \'http://www.w3.org/2001/XMLSchema#string\') && ?onProperty in (<' . $universal['onProperty'] . '>))';
                            } else {
                                if (!empty($customDatatypes[$universal['dataRange']])) {
                                    if ($customDatatypes[$universal['dataRange']] === 'http://www.w3.org/2001/XMLSchema#anySimpleType') {
                                        $datatypeFilter = 'filter((str(datatype(?value)) = \'' . $universal['dataRange'] . '\' || str(datatype(?value)) = \'' . $customDatatypes[$universal['dataRange']] . '\' || str(datatype(?value)) = \'http://www.w3.org/2001/XMLSchema#string\') && ?onProperty in (<' . $universal['onProperty'] . '>))';
                                    } else {
                                        $datatypeFilter = 'filter((str(datatype(?value)) != \'' . $universal['dataRange'] . '\' && str(datatype(?value)) != \'' . $customDatatypes[$universal['dataRange']] . '\') && ?onProperty in (<' . $universal['onProperty'] . '>))';
                                    }
                                } else {
                                    $datatypeFilter = 'filter((str(datatype(?value)) != \'' . $universal['dataRange'] . '\') && ?onProperty in (<' . $universal['onProperty'] . '>))';
                                }
                            }
                        }
                        // With this query, we make sure that if we have a universal restriction for that property and class
                        // that all the values uses the specified datatype. Otherwise, we return the ones that doesn't.
                        $sparql->mime("application/sparql-results+json")->query('select ?s
                              ' . $from . '
                              where
                              {
                                ?s a <' . $class . '> .
                                ?s ?onProperty ?value .

                                ' . $datatypeFilter . '
                              }
                             ')->send();
                        if ($sparql->isSuccessful()) {
                            $results = json_decode($sparql->getResultset(), TRUE);
                            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                                foreach ($results['results']['bindings'] as $result) {
                                    $subject = $result['s']['value'];
                                    cecho('  -> record: ' . $subject . "\n", 'LIGHT_RED');
                                    cecho('     -> property: ' . $universal['onProperty'] . "\n", 'LIGHT_RED');
                                    $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-100', 'type' => 'error', 'invalidRecordURI' => $subject, 'invalidPropertyURI' => $universal['onProperty'], 'dataRange' => $universal['dataRange']);
                                }
                            }
                        } else {
                            cecho("We couldn't get the number of properties per record from the structWSF instance\n", 'YELLOW');
                            // Error: can't get the number of properties used per record
                            $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-51', 'type' => 'warning');
                        }
                        // Now let's make sure that there is at least one triple where the value belongs
                        // to the defined datatype
                        $values = array();
                        if (!empty($universal['dataRange'])) {
                            if ($universal['dataRange'] == 'http://www.w3.org/2000/01/rdf-schema#Literal') {
                                $datatypeFilter = 'filter(str(datatype(?value)) = \'http://www.w3.org/2000/01/rdf-schema#Literal\' || str(datatype(?value)) = \'http://www.w3.org/2001/XMLSchema#string\')';
                            } else {
                                if (!empty($customDatatypes[$universal['dataRange']])) {
                                    if ($customDatatypes[$universal['dataRange']] === 'http://www.w3.org/2001/XMLSchema#anySimpleType') {
                                        $datatypeFilter = 'filter(str(datatype(?value)) = \'' . $universal['dataRange'] . '\' || str(datatype(?value)) = \'' . $customDatatypes[$universal['dataRange']] . '\'  || str(datatype(?value)) = \'http://www.w3.org/2001/XMLSchema#string\')';
                                    } else {
                                        $datatypeFilter = 'filter(str(datatype(?value)) = \'' . $universal['dataRange'] . '\' || str(datatype(?value)) = \'' . $customDatatypes[$universal['dataRange']] . '\' )';
                                    }
                                } else {
                                    $datatypeFilter = 'filter(str(datatype(?value)) = \'' . $universal['dataRange'] . '\')';
                                }
                            }
                        }
                        $sparql = new SparqlQuery($this->network);
                        $from = '';
                        foreach ($this->checkOnDatasets as $dataset) {
                            $from .= 'from <' . $dataset . '> ';
                        }
                        $sparql->mime("application/sparql-results+json")->query('select distinct ?value ?s
                              ' . $from . '
                              where
                              {
                                ?s a <' . $class . '> ;
                                   <' . $universal['onProperty'] . '> ?value.
                                ' . $datatypeFilter . '
                              }')->send();
                        if ($sparql->isSuccessful()) {
                            $results = json_decode($sparql->getResultset(), TRUE);
                            $values = array();
                            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                                foreach ($results['results']['bindings'] as $result) {
                                    $value = $result['value']['value'];
                                    $s = '';
                                    if (isset($result['s'])) {
                                        $s = $result['s']['value'];
                                    }
                                    $values[] = array('value' => $value, 'type' => $universal['dataRange'], 'affectedRecord' => $s);
                                }
                            }
                            // For each value/type(s), we do validate that the range is valid
                            // We just need to get one triple where the value comply with the defined datatype
                            // to have this check validated
                            foreach ($values as $value) {
                                // First, check if we have a type defined for the value. If not, then we infer it is rdfs:Literal
                                if (empty($value['type'])) {
                                    $value['type'] = array('http://www.w3.org/2000/01/rdf-schema#Literal');
                                }
                                // If then match, then we make sure that the value is valid according to the
                                // internal Check datatype validation tests
                                $datatypeValidationError = FALSE;
                                switch ($value['type']) {
                                    case "http://www.w3.org/2001/XMLSchema#anySimpleType":
                                        if (!$this->validateAnySimpleType($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#base64Binary":
                                        if (!$this->validateBase64Binary($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#boolean":
                                        if (!$this->validateBoolean($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#byte":
                                        if (!$this->validateByte($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#dateTimeStamp":
                                        if (!$this->validateDateTimeStampISO8601($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#dateTime":
                                        if (!$this->validateDateTimeISO8601($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#decimal":
                                        if (!$this->validateDecimal($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#double":
                                        if (!$this->validateDouble($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#float":
                                        if (!$this->validateFloat($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#hexBinary":
                                        if (!$this->validateHexBinary($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#int":
                                        if (!$this->validateInt($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#integer":
                                        if (!$this->validateInteger($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#language":
                                        if (!$this->validateLanguage($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#long":
                                        if (!$this->validateLong($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#Name":
                                        if (!$this->validateName($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#NCName":
                                        if (!$this->validateNCName($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#negativeInteger":
                                        if (!$this->validateNegativeInteger($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#NMTOKEN":
                                        if (!$this->validateNMTOKEN($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger":
                                        if (!$this->validateNonNegativeInteger($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger":
                                        if (!$this->validateNonPositiveInteger($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#normalizedString":
                                        if (!$this->validateNormalizedString($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral":
                                        if (!$this->validatePlainLiteral($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#positiveInteger":
                                        if (!$this->validatePositiveInteger($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#short":
                                        if (!$this->validateShort($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#string":
                                        if (!$this->validateString($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#token":
                                        if (!$this->validateToken($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#unsignedByte":
                                        if (!$this->validateUnsignedByte($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#unsignedInt":
                                        if (!$this->validateUnsignedInt($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#unsignedLong":
                                        if (!$this->validateUnsignedLong($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#unsignedShort":
                                        if (!$this->validateUnsignedShort($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral":
                                        if (!$this->validateXMLLiteral($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    case "http://www.w3.org/2001/XMLSchema#anyURI":
                                        if (!$this->validateAnyURI($value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                    default:
                                        // Custom type, try to validate it according to the
                                        // description of that custom datatype within the
                                        // ontology
                                        if (!$this->validateCustomDatatype($value['type'], $value['value'])) {
                                            $datatypeValidationError = TRUE;
                                        }
                                        break;
                                }
                                if ($datatypeValidationError === TRUE) {
                                    cecho('  -> Couldn\'t validate that this value: "' . $value['value'] . '" belong to the datatype "' . $universal['dataRange'] . '"' . "\n", 'LIGHT_RED');
                                    cecho('     -> Affected record: "' . $value['affectedRecord'] . '"' . "\n", 'YELLOW');
                                    // If it doesn't match, then we report an error directly
                                    $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-102', 'type' => 'error', 'datatypeProperty' => $universal['onProperty'], 'expectedDatatype' => $universal['dataRange'], 'invalidValue' => $value['value'], 'affectedRecord' => $value['affectedRecord']);
                                }
                            }
                        } else {
                            cecho("We couldn't get the list of values for the {$datatypePropety} property\n", 'YELLOW');
                            $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-54', 'type' => 'warning');
                        }
                    }
                }
                if (count($this->errors) <= 0) {
                    cecho("\n\n  All records respects the universal restrictions cardinality specified in the ontologies...\n\n\n", 'LIGHT_GREEN');
                }
            } else {
                cecho("No classes have any universal restriction cardinality defined in any ontologies. Move on to the next check...\n\n\n", 'LIGHT_GREEN');
            }
        } else {
            cecho("We couldn't get the list of universal restriction from the structWSF instance\n", 'YELLOW');
            // Error: can't get the list of retrictions
            $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-50', 'type' => 'warning');
        }
        // Check for universal restriction on Object Properties
        $sparql = new SparqlQuery($this->network);
        $from = '';
        foreach ($this->checkOnDatasets as $dataset) {
            $from .= 'from <' . $dataset . '> ';
        }
        foreach ($this->checkUsingOntologies as $ontology) {
            $from .= 'from named <' . $ontology . '> ';
        }
        $sparql->mime("application/sparql-results+json")->query('select distinct ?restriction ?class ?onProperty ?classExpression
                      ' . $from . '
                      where
                      {
                        ?s a ?class.

                        graph ?g {
                            ?class <http://www.w3.org/2000/01/rdf-schema#subClassOf> ?restriction .
                            ?restriction a <http://www.w3.org/2002/07/owl#Restriction> ;
                                         <http://www.w3.org/2002/07/owl#onProperty> ?onProperty ;
                                         <http://www.w3.org/2002/07/owl#allValuesFrom> ?classExpression .                                            
                                         
                            ?onProperty a <http://www.w3.org/2002/07/owl#ObjectProperty> .
                        }
                      }')->send();
        if ($sparql->isSuccessful()) {
            $results = json_decode($sparql->getResultset(), TRUE);
            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                cecho("Here is the list of all the OWL universal restrictions defined for the classes currently used in the target datasets applied on object properties:\n\n", 'LIGHT_BLUE');
                $universals = array();
                foreach ($results['results']['bindings'] as $result) {
                    $class = $result['class']['value'];
                    $onProperty = $result['onProperty']['value'];
                    $classExpression = $result['classExpression']['value'];
                    cecho('  -> Record of type "' . $class . '" have an universal restriction when using object property "' . $onProperty . '"' . "\n", 'LIGHT_BLUE');
                    if (!isset($universals[$class])) {
                        $universals[$class] = array();
                    }
                    $universals[$class][] = array('onProperty' => $onProperty, 'classExpression' => $classExpression);
                }
                cecho("\n\n");
                foreach ($universals as $class => $univs) {
                    foreach ($univs as $universal) {
                        $sparql = new SparqlQuery($this->network);
                        $from = '';
                        foreach ($this->checkOnDatasets as $dataset) {
                            $from .= 'from <' . $dataset . '> ';
                        }
                        foreach ($this->checkUsingOntologies as $ontology) {
                            $from .= 'from <' . $ontology . '> ';
                        }
                        $classExpressionFilter = '';
                        if (!empty($universal['classExpression']) && $universal['classExpression'] != 'http://www.w3.org/2002/07/owl#Thing') {
                            $subClasses = array($universal['classExpression']);
                            // Get all the classes that belong to the class expression defined in this restriction
                            $getSubClassesFunction = new GetSubClassesFunction();
                            $getSubClassesFunction->getClassesUris()->allSubClasses()->uri($universal['classExpression']);
                            $ontologyRead = new OntologyReadQuery($this->network);
                            $ontologyRead->ontology($this->getClassOntology($universal['classExpression']))->getSubClasses($getSubClassesFunction)->enableReasoner()->mime('resultset')->send();
                            if ($ontologyRead->isSuccessful()) {
                                $resultset = $ontologyRead->getResultset()->getResultset();
                                $subClasses = array_merge($subClasses, array_keys($resultset['unspecified']));
                                if (!empty($subClasses)) {
                                    $filter = '';
                                    foreach ($subClasses as $subClass) {
                                        $filter .= ' str(?value_type) = "' . $subClass . '" ||';
                                    }
                                    $classExpressionFilter .= " filter(" . trim(trim($filter, '||')) . ") \n";
                                }
                            } else {
                                cecho("We couldn't get sub-classes of class expression from the structWSF instance\n", 'YELLOW');
                                // Error: can't get the subclasses of a target ontology
                                $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-53', 'type' => 'warning');
                            }
                        }
                        $sparql->mime("application/sparql-results+json")->query('select ?s
                              ' . $from . '
                              where
                              {
                                ?s a <' . $class . '> .

                                ?s <' . $universal['onProperty'] . '> ?value .

                                filter not exists { 
                                  ?value a ?value_type .
                                  ' . $classExpressionFilter . '
                                }                              
                              }
                             ')->send();
                        if ($sparql->isSuccessful()) {
                            $results = json_decode($sparql->getResultset(), TRUE);
                            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                                foreach ($results['results']['bindings'] as $result) {
                                    $subject = $result['s']['value'];
                                    cecho('  -> record: ' . $subject . "\n", 'LIGHT_RED');
                                    cecho('     -> property: ' . $universal['onProperty'] . "\n", 'LIGHT_RED');
                                    $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-101', 'type' => 'error', 'invalidRecordURI' => $subject, 'invalidPropertyURI' => $universal['onProperty'], 'classExpression' => $universal['classExpression']);
                                }
                            }
                        } else {
                            cecho("We couldn't check if the universal restriction was respected from the structWSF instance\n", 'YELLOW');
                            $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-53', 'type' => 'warning');
                        }
                    }
                    if (count($this->errors) <= 0) {
                        cecho("\n\n  All records respects the universal restrictions specified in the ontologies...\n\n\n", 'LIGHT_GREEN');
                    }
                }
            }
        } else {
            cecho("We couldn't get the number of object properties used per record from the structWSF instance\n", 'YELLOW');
            // Error: can't get the number of object properties used per record
            $this->errors[] = array('id' => 'OWL-RESTRICTION-ONLY-52', 'type' => 'warning');
        }
    }
    public function run()
    {
        cecho("\n\n");
        cecho("Data validation test: " . $this->description . "...\n\n", 'LIGHT_BLUE');
        $sparql = new SparqlQuery($this->network);
        $from = '';
        foreach ($this->checkOnDatasets as $dataset) {
            $from .= 'from named <' . $dataset . '> ';
        }
        foreach ($this->checkUsingOntologies as $ontology) {
            $from .= 'from <' . $ontology . '> ';
        }
        // Get the list of all the object properties used within the datasets
        $sparql->mime("application/sparql-results+json")->query('select distinct ?p ?range
                      ' . $from . '
                      where
                      {
                        graph ?g {
                          ?s ?p ?o .
                          filter(isIRI(?o))                          
                        } 
                        
                        optional
                        {                                                          
                          ?p <http://www.w3.org/2000/01/rdf-schema#range> ?range .
                        }
                        
                        filter(str(?p) != "http://purl.org/dc/terms/isPartOf" && str(?p) != "http://www.w3.org/1999/02/22-rdf-syntax-ns#value" && str(?p) != "http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
                      }')->send();
        if ($sparql->isSuccessful()) {
            $results = json_decode($sparql->getResultset(), TRUE);
            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                $objectProperties = array();
                $thereAreEmptyRanges = FALSE;
                foreach ($results['results']['bindings'] as $result) {
                    $objectProperty = $result['p']['value'];
                    $objectProperties[$objectProperty] = '';
                    if (isset($result['range'])) {
                        $objectProperties[$objectProperty] = $result['range']['value'];
                    } else {
                        $thereAreEmptyRanges = TRUE;
                    }
                }
                // Display warnings
                if ($thereAreEmptyRanges) {
                    cecho("The following object properties are used to describe records, but their range is not specified in the ontologies. owl:Thing is assumed as the range, but you may want to define it further and re-run this check:\n", 'YELLOW');
                    foreach ($objectProperties as $objectProperty => $range) {
                        if (empty($range)) {
                            cecho('  -> object property: ' . $objectProperty . "\n", 'YELLOW');
                            $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-50', 'type' => 'warning', 'objectProperty' => $objectProperty);
                        }
                    }
                }
                // Now, for each object properties that have a range defined,
                // we:
                //
                //  (a) List all values used for a given property
                //  (b) For each of these values, make sure they comply with what is defined in the ontology as
                //      the range of the
                foreach ($objectProperties as $objectProperty => $range) {
                    // If the range is empty, we consider it owl:Thing.
                    // If the range is owl:Thing, then we simply skip this check since everything is an owl:Thing
                    if (!empty($range) && $range != 'http://www.w3.org/2002/07/owl#Thing') {
                        $values = array();
                        $sparql = new SparqlQuery($this->network);
                        $from = '';
                        foreach ($this->checkOnDatasets as $dataset) {
                            $from .= 'from <' . $dataset . '> ';
                            $from .= 'from named <' . $dataset . '> ';
                        }
                        foreach ($this->checkUsingOntologies as $ontology) {
                            $from .= 'from <' . $ontology . '> ';
                        }
                        $sparql->mime("application/sparql-results+json")->query('select distinct ?value ?value_type
                              ' . $from . '
                              where
                              {
                                graph ?g {
                                  ?s <' . $objectProperty . '> ?value.
                                  filter(isIRI(?value))
                                }

                                optional
                                {
                                  ?value a ?value_type
                                }
                              }')->send();
                        if ($sparql->isSuccessful()) {
                            // Create the array of object-values/types
                            $results = json_decode($sparql->getResultset(), TRUE);
                            if (isset($results['results']['bindings']) && count($results['results']['bindings']) > 0) {
                                foreach ($results['results']['bindings'] as $result) {
                                    $value = $result['value']['value'];
                                    $type = '';
                                    if (isset($result['value_type'])) {
                                        $type = $result['value_type']['value'];
                                    }
                                    if (!isset($values[$value])) {
                                        $values[$value] = array();
                                    }
                                    if (!empty($type) && !in_array($type, $values[$value])) {
                                        $values[$value][] = $type;
                                    }
                                }
                            }
                            // For each value/type(s), we do validate that the range is valid
                            foreach ($values as $value => $types) {
                                // First, check if we have a type defined for the value. If not, then we infer it is owl:Thing
                                if (empty($types)) {
                                    $types = array('http://www.w3.org/2002/07/owl#Thing');
                                }
                                // Then, check if the $range and the $types directly match
                                if (in_array($range, $types)) {
                                    continue;
                                } else {
                                    // If they are not, then we check in the ontology to see if the range is not
                                    // one of the super class of one of the type(s)
                                    $superClasses = array();
                                    foreach ($types as $type) {
                                        $ontologyURI = $this->getTypeOntology($type);
                                        if ($ontologyURI !== FALSE) {
                                            $ontologyRead = new OntologyReadQuery($this->network);
                                            $getSuperClassesFunc = new GetSuperClassesFunction();
                                            $getSuperClassesFunc->allSuperClasses()->getClassesUris()->uri($type);
                                            $ontologyRead->enableReasoner()->ontology($ontologyURI)->getSuperClasses($getSuperClassesFunc)->mime('resultset')->send();
                                            if ($ontologyRead->isSuccessful()) {
                                                $scs = $ontologyRead->getResultset()->getResultset();
                                                // If empty, then there is no super-classes
                                                if (!empty($scs)) {
                                                    $scs = $scs[key($scs)];
                                                    foreach ($scs as $superClass => $description) {
                                                        if (!in_array($superClass, $superClasses)) {
                                                            $superClasses[] = $superClass;
                                                        }
                                                    }
                                                }
                                            } else {
                                                cecho("We couldn't get the list of super-classes of a target type from the structWSF instance\n", 'YELLOW');
                                                // Log a warning
                                                // Can't get the super classes of the target type
                                                $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-51', 'type' => 'warning', 'objectProperty' => $objectProperty);
                                            }
                                        } else {
                                            cecho("We couldn't find the ontology where the {$type} is defined on the structWSF instance\n", 'YELLOW');
                                            // Log a warning
                                            // Can't find ontology where the type $type is defined
                                            $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-52', 'type' => 'warning', 'objectProperty' => $objectProperty);
                                        }
                                    }
                                    $rangeMatch = FALSE;
                                    foreach ($superClasses as $superClass) {
                                        if ($superClass == $range) {
                                            $rangeMatch = TRUE;
                                            break;
                                        }
                                    }
                                    if (!$rangeMatch) {
                                        // Log an error
                                        // Couldn't match one of the super classe with the specified range
                                        cecho('  -> Object property "' . $objectProperty . '" doesn\'t match range "' . $range . '" for value "' . $value . '"' . "\n", 'LIGHT_RED');
                                        $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-100', 'type' => 'error', 'objectProperty' => $objectProperty, 'definedRange' => $range, 'value' => $value, 'valueTypes' => $types, 'valueSuperTypes' => $superClasses, 'affectedRecords' => $this->getAffectedRecords($objectProperty, $value));
                                    }
                                }
                            }
                        } else {
                            cecho("We couldn't get the range of the {$objectProperty} object property from the structWSF instance\n", 'YELLOW');
                            $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-54', 'type' => 'warning');
                        }
                    }
                }
            }
        } else {
            cecho("We couldn't get the list of object properties from the structWSF instance\n", 'YELLOW');
            $this->errors[] = array('id' => 'OBJECT-PROPERTIES-RANGE-53', 'type' => 'warning');
        }
    }
function generateStructures($folder, $structwsf, $queryExtension = NULL)
{
    include_once 'getLoadedOntologies.php';
    cecho("Generating derivate ontological structures...\n", 'CYAN');
    $ontologiesClustered = getLoadedOntologies($structwsf);
    $ontologies = array();
    $ontologies = array_merge($ontologies, $ontologiesClustered['local'], $ontologiesClustered['reference'], $ontologiesClustered['admin']);
    // Generate the ironXML schemas
    foreach ($ontologies as $ontology) {
        cecho("Generating ironXML schema of the " . $ontology['label'] . " ontology...\n", 'CYAN');
        $ontologyRead = new OntologyReadQuery($structwsf);
        $ontologyRead->ontology($ontology['uri'])->getIronXMLSchema()->send($queryExtension !== NULL ? $queryExtension : NULL);
        if ($ontologyRead->isSuccessful()) {
            $resultset = $ontologyRead->getResultset()->getResultset();
            $ironXML = $resultset['unspecified'][$ontology['uri']]['http://purl.org/ontology/wsf#serializedIronXMLSchema']['0']['value'];
            cecho("Generated...\n", 'CYAN');
            $filename = substr($ontology['uri'], strripos($ontology['uri'], "/") + 1);
            $filename = substr($filename, 0, strripos($filename, "."));
            file_put_contents(rtrim($folder, '/') . '/' . $filename . '.xml', $ironXML);
            cecho("Saved to: " . rtrim($folder, '/') . '/' . $filename . '.xml' . "\n", 'CYAN');
        } else {
            $debugFile = md5(microtime()) . '.error';
            file_put_contents('/tmp/' . $debugFile, var_export($ontologyRead, TRUE));
            @cecho('Can\'t get the ironXML schema structure for this ontology: ' . $ontology . '. ' . $ontologyRead->getStatusMessage() . $ontologyRead->getStatusMessageDescription() . "\nDebug file: /tmp/{$debugFile}\n", 'RED');
            continue;
        }
    }
    // Generate the ironJSON schemas
    foreach ($ontologies as $ontology) {
        cecho("Generating ironJSON schema of the " . $ontology['label'] . " ontology...\n", 'CYAN');
        $ontologyRead = new OntologyReadQuery($structwsf);
        $ontologyRead->ontology($ontology['uri'])->getIronJsonSchema()->send($queryExtension !== NULL ? $queryExtension : NULL);
        if ($ontologyRead->isSuccessful()) {
            $resultset = $ontologyRead->getResultset()->getResultset();
            $ironJSON = $resultset['unspecified'][$ontology['uri']]['http://purl.org/ontology/wsf#serializedIronJSONSchema']['0']['value'];
            cecho("Generated...\n", 'CYAN');
            $filename = substr($ontology['uri'], strripos($ontology['uri'], "/") + 1);
            $filename = substr($filename, 0, strripos($filename, "."));
            file_put_contents(rtrim($folder, '/') . '/' . $filename . '.json', $ironJSON);
            cecho("Saved to: " . rtrim($folder, '/') . '/' . $filename . '.json' . "\n", 'CYAN');
            // Create the pre-existing JS script to load that schema in a Schema object.
            $ironJSONSchema = 'var ' . $filename . '_schema_srz = ' . $ironJSON . ";";
            $ironJSONSchema .= 'var ' . $filename . '_schema = new Schema(' . $filename . '_schema_srz);';
            // Special handling: make sure to convert all "%5C" characters into "\". This has to be done because
            // of the way the ProcessorXML.php (so, the DOMDocument API) works, and how the xml-encoding currently
            // works. Enventually, we should use the simplexml API in the processorXML script to properly
            // manage the encoding, and decoding of the XML data, *AND* of the HTML entities (it is the HTML
            // entities that are strangely manipulated in the DOMDocument API).
            $ironJSONSchema = str_replace("%5C", '\\', $ironJSONSchema);
            file_put_contents(rtrim($folder, "/") . "/" . $filename . ".js", $ironJSONSchema);
            cecho("Saved to: " . rtrim($folder, '/') . '/' . $filename . '.js' . "\n", 'CYAN');
        } else {
            $debugFile = md5(microtime()) . '.error';
            file_put_contents('/tmp/' . $debugFile, var_export($ontologyRead, TRUE));
            @cecho('Can\'t get the ironJSON schema structure for this ontology: ' . $ontology . '. ' . $ontologyRead->getStatusMessage() . $ontologyRead->getStatusMessageDescription() . "\nDebug file: /tmp/{$debugFile}\n", 'RED');
            continue;
        }
    }
    // Create the JS schema file.
    $schemaJS = ' /* List of attribute URIs that are generally used for labeling entities in different ontologies */
                var prefLabelAttributes = [
                  "http://www.w3.org/2004/02/skos/core#prefLabel",
                  "http://purl.org/ontology/iron#prefLabel",
                  "http://umbel.org/umbel#prefLabel",
                  "http://purl.org/dc/terms/title",
                  "http://purl.org/dc/elements/1.1/title",
                  "http://xmlns.com/foaf/0.1/name",
                  "http://xmlns.com/foaf/0.1/givenName",
                  "http://xmlns.com/foaf/0.1/family_name",
                  "http://www.geonames.org/ontology#name",
                  "http://www.w3.org/2000/01/rdf-schema#label"
                ];

                var altLabelAttributes = [
                  "http://www.w3.org/2004/02/skos/core#altLabel",
                  "http://purl.org/ontology/iron#altLabel",
                  "http://umbel.org/umbel#altLabel",
                ];

                var descriptionAttributes = [
                  "http://purl.org/ontology/iron#description",
                  "http://www.w3.org/2000/01/rdf-schema#comment",
                  "http://purl.org/dc/terms/description",
                  "http://purl.org/dc/elements/1.1/description",
                  "http://www.w3.org/2004/02/skos/core#definition"
                ];


                function Schema(sjson)
                {
                  // Define all prefixes of this resultset
                  this.prefixes = sjson.schema.prefixList;

                  // Unprefixize all URIs of this Schema
                  var resultsetJsonText = JSON.stringify(sjson);

                  for(var prefix in this.prefixes)
                  {
                    if(this.prefixes.hasOwnProperty(prefix))
                    {
                      var pattern = new RegExp(prefix+"_", "igm");
                      resultsetJsonText = resultsetJsonText.replace(pattern, this.prefixes[prefix]);
                    }
                  }

                  sjson = JSON.parse(resultsetJsonText);

                  this.attributes = sjson.schema.attributeList;

                  this.types = sjson.schema.typeList;

                  // Extend all attributes of this schema with additional functions
                  for(var i = 0; i < this.attributes.length; i++)
                  {
                    this.attributes[i].prefixes = this.prefixes;
                  }

                  // Extend all types of this schema with additional functions
                  for(var i = 0; i < this.types.length; i++)
                  {
                    this.types[i].prefixes = this.prefixes;
                  }
                }';
    file_put_contents(rtrim($folder, "/") . "/schema.js", $schemaJS);
    // Generate PHP serialized classes hierarchy
    cecho("Generating PHP serialized classes hierarchy structure file...\n", 'CYAN');
    $ontologyRead = new OntologyReadQuery($structwsf);
    $ontologyRead->getSerializedClassHierarchy()->send($queryExtension !== NULL ? $queryExtension : NULL);
    if ($ontologyRead->isSuccessful()) {
        $resultset = $ontologyRead->getResultset()->getResultset();
        $ironXML = $resultset['unspecified'][""]['http://purl.org/ontology/wsf#serializedClassHierarchy']['0']['value'];
        cecho("Generated...\n", 'CYAN');
        file_put_contents(rtrim($folder, '/') . '/classHierarchySerialized.srz', $ironXML);
        cecho("Saved to: " . rtrim($folder, '/') . '/classHierarchySerialized.srz' . "\n", 'CYAN');
    } else {
        $debugFile = md5(microtime()) . '.error';
        file_put_contents('/tmp/' . $debugFile, var_export($ontologyRead, TRUE));
        @cecho('Can\'t get the PHP serialized classes hierarchy structure file' . $ontologyRead->getStatusMessage() . $ontologyRead->getStatusMessageDescription() . "\nDebug file: /tmp/{$debugFile}\n", 'RED');
    }
    // Generate PHP serialized properties hierarchy
    cecho("Generating PHP serialized properties hierarchy structure file...\n", 'CYAN');
    $ontologyRead = new OntologyReadQuery($structwsf);
    $ontologyRead->getSerializedPropertyHierarchy()->send($queryExtension !== NULL ? $queryExtension : NULL);
    if ($ontologyRead->isSuccessful()) {
        $resultset = $ontologyRead->getResultset()->getResultset();
        $ironXML = $resultset['unspecified'][""]['http://purl.org/ontology/wsf#serializedPropertyHierarchy']['0']['value'];
        cecho("Generated...\n", 'CYAN');
        file_put_contents(rtrim($folder, '/') . '/propertyHierarchySerialized.srz', $ironXML);
        cecho("Saved to: " . rtrim($folder, '/') . '/propertyHierarchySerialized.srz' . "\n", 'CYAN');
    } else {
        $debugFile = md5(microtime()) . '.error';
        file_put_contents('/tmp/' . $debugFile, var_export($ontologyRead, TRUE));
        @cecho('Can\'t get the PHP serialized properties hierarchy structure file' . $ontologyRead->getStatusMessage() . $ontologyRead->getStatusMessageDescription() . "\nDebug file: /tmp/{$debugFile}\n", 'RED');
    }
    cecho("All structures generates!\n", 'CYAN');
}