Mercurial > hg > rr-repo
diff sites/all/modules/rdfx/rdfx.terms.inc @ 4:ce11bbd8f642
added modules
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Thu, 19 Sep 2013 10:38:44 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sites/all/modules/rdfx/rdfx.terms.inc Thu Sep 19 10:38:44 2013 +0100 @@ -0,0 +1,622 @@ +<?php + +/** + * @file + * Functions for managing RDF Terms. + */ + +function rdfx_get_properties() { + $property_tids = _rdfx_get_terms('property'); + return $property_tids; +} + +function rdfx_get_classes() { + $class_tids = _rdfx_get_terms('class'); + return $class_tids; +} + +function _rdfx_get_terms($term_type) { + $term_types = rdfx_term_types(); + switch ($term_type) { + case 'property': + $types = array_keys($term_types['properties']['term_types']); + break; + case 'class': + $types = array_keys($term_types['classes']['term_types']); + break; + default: + $types = array_merge(array_keys($term_types['properties']['term_types']), array_keys($term_types['classes']['term_types'])); + } + $query = db_select('rdfx_term_types', 'rdftt') + ->fields('rdft', array('tid')) + ->condition('rdftt.type', $types, 'IN'); + $query->join('rdfx_terms', 'rdft', 'rdftt.tid = rdft.tid'); + $query->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid'); + $query->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid'); + $terms = $query->execute()->fetchCol(); + return $terms; +} + +/** + * Gets a list of all defined namespaces. + */ +function rdfx_get_namespaces() { + $rdf_namespaces = &drupal_static(__FUNCTION__); + if (empty($rdf_namespaces)) { + $rdf_namespaces = rdf_get_namespaces(); + } + return $rdf_namespaces; +} + +/** + * Implements hook_rdf_namespaces. + */ +function rdfx_rdf_namespaces() { + // Starts with some additionnal common namespaces which core does not include. + $ns_mappings = array( + 'owl' => 'http://www.w3.org/2002/07/owl#', + 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'rss' => 'http://purl.org/rss/1.0/', + // url() does not support empty fragment. + 'site' => url('ns', array('absolute' => TRUE)) . '#', + ); + + // Gets the custom namespaces stored in the database. + $query = db_select('rdfx_vocabulary_graphs', 'g'); + $query->fields('n', array('prefix', 'uri')); + $query->join('rdfx_namespaces', 'n', 'g.main_ns = n.nsid'); + $query->orderBy('n.prefix'); + $namespaces = $query->execute()->fetchAllKeyed(); + foreach ($namespaces as $prefix => $uri) { + $ns_mappings[$prefix] = $uri; + } + + return $ns_mappings; +} + +/** + * Saves the main namespace mapping for a vocabulary graph and the additional + * namespace mappings as defined in the document. + */ +function _rdfx_save_vocabulary($main_ns, $main_ns_prefix, $vocabulary) { + $current_time = REQUEST_TIME; + // If the vocabulary URI matches the main_ns of a vocabulary source, then + // this is an update to that record. Otherwise, this is a newly imported + // source. + $gid = rdfx_get_gid($main_ns); + + // If there is an existing vocabulary, make sure that the main_ns is in the + // namespaces array and that the user defined mapping is the last in the + // array so the prefix reflects the user definition. Also change the + // vocabulary graph updated date. + if ($gid) { + $vocabulary['namespaces'][$main_ns_prefix] = $main_ns; + db_update('rdfx_vocabulary_graphs') + ->fields(array('date_updated' => $current_time,)) + ->execute(); + } + + // If this is a new vocabulary, create a graph with a main namespace and + // add the additional namespaces. + else { + // @todo If the vocab URI isn't used in any terms, don't add it to ns table. + // This may happen where multiple files are defining a vocabulary. + + // @todo This should be handled as a transaction in case there is an error + // in the middle. If there is an error, then there will be an SQL error + // when the user retries the import. + + // Insert this namespace to get the nsid. The vocabulary_source entry will + // point to this nsid for the main_ns. We temporarily insert 0 for the gid, + // then update when we have the real gid. + $nsid = db_insert('rdfx_namespaces') + ->fields(array('uri' => $main_ns, 'prefix' => $main_ns_prefix, 'gid' => '0')) + ->execute(); + $gid = db_insert('rdfx_vocabulary_graphs') + ->fields(array( + 'main_ns' => $nsid, + 'date_created' => $current_time, + 'date_updated' => $current_time,)) + ->execute(); + db_update('rdfx_namespaces') + ->condition('nsid', $nsid) + ->fields(array('gid' => $gid)) + ->execute(); + } + // Insert/update the vocabulary title. + if (count($vocabulary['title']) > 0) { + foreach ($vocabulary['title'] as $langcode => $title) { + $query = db_merge('rdfx_vocabulary_details') + ->key(array('gid' => $gid, 'language' => $langcode)) + ->fields(array('language' => $langcode, 'label' => $title)); + $status = $query->execute(); + } + } + + // Insert/update the vocabulary description. + if (count($vocabulary['description']) > 0) { + foreach ($vocabulary['description'] as $langcode => $description) { + $query = db_merge('rdfx_vocabulary_details') + ->key(array('gid' => $gid, 'language' => $langcode)) + ->fields(array('language' => $langcode, 'description' => $description)); + $status = $query->execute(); + } + } + + // Insert/update the other namespace mappings used in this vocabulary graph. + if (count($vocabulary['namespaces']) > 0) { + foreach ($vocabulary['namespaces'] as $prefix => $namespace) { + if ($namespace != $main_ns) { + $query = db_merge('rdfx_namespaces') + ->key(array('gid' => $gid, 'uri' => $namespace)) + ->fields(array('uri' => $namespace, 'prefix' => $prefix, 'gid' => $gid)) + ->updateFields(array('prefix' => $prefix)); + $status = $query->execute(); + } + } + } + + $nsids = rdfx_get_nsids($main_ns); + return $nsids; +} + +/** + * Saves vocabulary terms. + */ +function rdfx_save_terms($vocabulary_uri, $prefix, $vocabulary) { + $nsids = _rdfx_save_vocabulary($vocabulary_uri, $prefix, $vocabulary); + foreach ($vocabulary['terms'] as $term_type => $terms) { + foreach ($terms as $term_uri => $term_description) { + list($term_ns, $term_local_name) = rdfx_split_uri($term_uri); + if (isset($nsids[$term_ns])) { + $nsid = $nsids[$term_ns]; + } + else { + // If the namespace wasn't mapped to a prefix in the source graph, we + // didn't save it to the namespaces table, so we need to add an entry. + + // @todo For the prefix value, we save the URI... should this be changed? + $gid = rdfx_get_gid($vocabulary_uri); + $nsid = db_insert('rdfx_namespaces') + ->fields(array('uri' => $term_ns, 'prefix' => $term_ns, 'gid' => $gid)) + ->execute(); + $nsids[$term_ns] = $nsid; + } + + // Get the tid of this term, saving to {rdfx_terms} if not already there. + $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(':nsid' => $nsid, ':localname' => $term_local_name))->fetchField(); + if ($tid == NULL) { + $tid = db_insert('rdfx_terms') + ->fields(array('nsid', 'local_name')) + ->values(array( + 'nsid' => $nsid, + 'local_name' => $term_local_name, + )) + ->execute(); + } + + // Add the current type to this term in {rdfx_term_types}. + db_merge('rdfx_term_types') + ->key(array('tid' => $tid, 'type' => $term_type)) + ->fields(array( + 'tid' => $tid, + 'type' => $term_type, + )) + ->execute(); + + // Add label and comment to {rdfx_term_details}. + $term_details = array(); + if (isset($term_description['label'])) { + foreach ($term_description['label'] as $lang => $text) { + $term_details[$lang]['label'] = $text; + } + } + if (isset($term_description['comment'])) { + foreach ($term_description['comment'] as $lang => $text) { + $term_details[$lang]['comment'] = $text; + } + } + if (!empty($term_details)) { + foreach ($term_details as $lang => $details) { + db_merge('rdfx_term_details') + ->key(array('tid' => $tid, 'language' => $lang)) + ->fields(array( + 'tid' => $tid, + 'language' => $lang, + 'label' => isset($details['label']) ? $details['label'] : NULL, + 'comment' => isset($details['comment']) ? $details['comment'] : NULL, + )) + ->execute(); + } + } + + // Add relationships to their respective tables. This is handled as a + // complicated set of loops to reduce code duplication. To add a new + // relationship, just add the array key that is used in + // $types['properties']['description'] to define the relationship, and + // then add the name of the table that stores the relationship. + $relationships = array( + 'domain' => 'rdfx_term_domains', + 'range' => 'rdfx_term_ranges', + ); + foreach ($relationships as $relationship => $table_name) { + if (isset($term_description[$relationship])) { + foreach ($term_description[$relationship] as $related_term) { + $related_term_tid = rdfx_get_tid($related_term, $vocabulary_uri); + if ($related_term_tid) { + db_merge($table_name) + ->key(array('tid' => $tid, $relationship . '_tid' => $related_term_tid)) + ->fields(array( + 'tid' => $tid, + $relationship . '_tid' => $related_term_tid, + )) + ->execute(); + } + } + } + } + } + } + // @todo Add a hook that passes the $vocabulary and the $model. +} + +/** + * Returns metadata about term types defined by rdf modules. + * + * If your module needs to determine what term types are being supplied by + * other modules, call this function. Querying rdfx database tables directly + * for this information is discouraged. Any additional term types should be + * added through the corresponding alter hook. + * + * Three major bins of data are stored: tags, value_types, and functions. Each + * entry in these bins is keyed by the value stored in the actual VotingAPI + * tables, and contains an array with (minimally) 'name' and 'description' keys. + * Modules can add extra keys to their entries if desired. + * + * This metadata can be modified or expanded using hook_rdfx_term_types_alter(). + * + * @return + * An array of metadata defined by RDFx Terms and altered by rdf modules. + * + * @see hook_rdfx_term_types_alter() + * + * Modeled on VotingAPI votingapi_metadata. + */ +function rdfx_term_types($reset = FALSE) { + static $types; + if ($reset || !isset($types)) { + $types['classes']['term_types'] = array(); + $types['properties']['term_types'] = array(); + + $term_details = ''; + + // @todo Should the inference consider subProp and subClass relationships + // as well. ie. should all OWL classes also have the type RDFS Class + + // @todo Switch to drupal cache + $types['classes']['term_types'] = array( + 'rdfs_class' => array( + 'uri' => 'http://www.w3.org/2000/01/rdf-schema#Class', + 'inference' => array( + 'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array( + 'subject', + 'object', + ), + 'http://www.w3.org/2000/01/rdf-schema#domain' => array( + 'object', + ), + 'http://www.w3.org/2000/01/rdf-schema#range' => array( + 'object', + ), + ), + ), + 'owl_class' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#Class', + 'inference' => array( + 'http://www.w3.org/2002/07/owl#equivalentClass' => array( + 'subject', + 'object', + ), + 'http://www.w3.org/2002/07/owl#disjointWith' => array( + 'subject', + 'object', + ), + ), + ), + ); + + $types['properties']['term_types'] = array ( + 'rdf_property' => array( + 'uri' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property', + 'inference' => array( + 'http://www.w3.org/2000/01/rdf-schema#domain' => array( + 'subject', + ), + 'http://www.w3.org/2000/01/rdf-schema#range' => array( + 'subject', + ), + 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array( + 'subject', + 'object', + ), + 'http://www.w3.org/2002/07/owl#equivalentProperty' => array( + 'subject', + 'object', + ), + 'http://www.w3.org/2002/07/owl#inverseOf' => array( + 'subject', + 'object', + ), + ), + ), + 'owl_property_datatype' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#DatatypeProperty', + 'inference' => array( + ), + ), + 'owl_property_object' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#ObjectProperty', + 'inference' => array( + ), + ), + 'owl_property_functional' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#FunctionalProperty', + 'inference' => array( + ), + ), + 'owl_property_inverse_functional' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#InverseFunctionalProperty', + 'inference' => array( + ), + ), + 'owl_property_symmetric' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#SymmetricProperty', + 'inference' => array( + ), + ), + 'owl_property_asymmetric' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#AsymmetricProperty', + 'inference' => array( + ), + ), + 'owl_property_annotation' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#AnnotationProperty', + 'inference' => array( + ), + ), + 'owl_property_reflexive' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#ReflexiveProperty', + 'inference' => array( + ), + ), + 'owl_property_irreflexive' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#IrreflexiveProperty', + 'inference' => array( + ), + ), + 'owl_property_transitive' => array( + 'uri' => 'http://www.w3.org/2002/07/owl#TransitiveProperty', + 'inference' => array( + ), + ), + ); + + $types['classes']['description'] = array( + 'superclass' => array( + 'http://www.w3.org/2000/01/rdf-schema#subClassOf' => array( + 'object', + ), + ), + 'disjoint' => array( + 'http://www.w3.org/2002/07/owl#disjointWith' => array( + 'object', + ), + ), + ); + + $types['properties']['description'] = array( + 'domain' => array( + 'http://www.w3.org/2000/01/rdf-schema#domain' => array( + 'object', + ), + ), + 'range' => array( + 'http://www.w3.org/2000/01/rdf-schema#range' => array( + 'object', + ), + ), + 'superproperty' => array( + 'http://www.w3.org/2000/01/rdf-schema#subPropertyOf' => array( + 'object', + ), + ), + 'inverse' => array( + 'http://www.w3.org/2002/07/owl#inverseOf' => array( + 'object', + ), + ), + ); + drupal_alter('rdfx_term_types', $types); + } + + return $types; +} + +/** + * Splits a URI into namespace and localpart. + */ +function rdfx_split_uri ($uri) { + $parts = ARC2::splitURI($uri); + return $parts; +} + +function rdfx_get_tid($term_uri, $graph_main_ns) { + $nsids = rdfx_get_nsids($graph_main_ns); + list($term_ns, $term_local_name) = rdfx_split_uri($term_uri); + if (isset($nsids[$term_ns])) { + $tid = db_query("SELECT tid FROM {rdfx_terms} WHERE nsid = :nsid AND local_name = :localname", array(':nsid' => $nsids[$term_ns], ':localname' => $term_local_name))->fetchField(); + return $tid; + } + else { + return NULL; + } +} + +function rdfx_get_gid($main_ns) { + $gids = db_select('rdfx_namespaces', 'rdfns', array()); + $gids->join('rdfx_vocabulary_graphs', 'rdfvg', 'rdfvg.main_ns = rdfns.nsid'); + $gids + ->fields('rdfns', array('gid')) + ->condition('rdfns.uri', $main_ns); + // @todo There should only be one result if there is a matching vocab source. + // However, perhaps we should test to make sure and throw an error? + $gid = $gids->execute()->fetchField(); + return $gid; +} + +function rdfx_get_nsids($main_ns) { + $gid = rdfx_get_gid($main_ns); + $nsids = db_query("SELECT uri, nsid FROM {rdfx_namespaces} WHERE gid = :gid", array(':gid' => $gid))->fetchAllKeyed(); + return $nsids; +} + +function rdfx_curie($tid) { + $query = db_select('rdfx_terms', 'rdft') + ->fields('rdft', array('local_name')) + ->fields('rdfns', array('prefix')) + ->condition('rdft.tid', $tid, '='); + $query->join('rdfx_namespaces', 'rdfns', 'rdfns.nsid = rdft.nsid'); + $result = $query->execute()->fetch(); + $curie = $result->prefix . ':' . $result->local_name; + return $curie; +} + +function _rdfx_get_term_details($tid, $langcode = 'und') { + $query_language = db_query("SELECT language FROM {rdfx_term_details} WHERE tid = :tid", array(':tid' => $tid)); + $languages = $query_language->fetchCol(); + if (!in_array($langcode, $languages)) { + if (in_array('und', $languages)) { + $langcode = 'und'; + } + elseif (in_array('en', $languages)) { + $langcode = 'en'; + } + else { + return; + } + } + $query = db_select('rdfx_term_details', 'rdfd') + ->fields('rdfd', array('label', 'comment')) + ->condition('rdfd.tid', $tid, '=') + ->condition('language', $langcode, '='); + $details = $query->execute()->fetch(); + return $details; +} + +/** + * Queries a set of triples for classes and properties, and builds + * an associative array describing the vocabulary and any + * classes and properties found. + * + * @param array $model An ARC2-style array of triples an RDFS vocabulary or OWL ontology + * @param array $namespaces Associative array of namespaces parsed from the RDF file + * @param string $ns_prefix Namespace prefix for the vocabulary + * @param string $ns_uri Only terms in this namespace will be considered + * @return array Array describing the vocabulary, its classes and properties. + */ +function _rdfx_extract_schema(&$model, $namespaces, $ns_prefix, $ns_uri) { + $vocabulary_details = _rdfx_get_vocabulary_details($model, $ns_uri); + $terms = _rdfx_fetch_terms($model); + $vocabulary = array( + 'uri' => $ns_uri, + 'title' => $vocabulary_details['title'], + 'description' => $vocabulary_details['description'], + 'terms' => $terms, + 'namespaces' => $namespaces, + ); + return $vocabulary; +} + +function _rdfx_fetch_terms(&$model) { + $terms = array(); + $term_uris = array(); + + // Retrieve the queries for term retrieval. This may have been modified by + // other modules. + $term_type_groups = rdfx_term_types(); + + foreach($term_type_groups as $term_type_group => $group) { + foreach ($group['term_types'] as $term_type => $term) { + $query = array( + array('?', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', $term['uri']), + ); + foreach ($term['inference'] as $inference_uri => $query_types) { + foreach ($query_types as $query_type) { + switch ($query_type) { + case 'subject': + $query[] = array('?', $inference_uri, null); + break; + case 'object': + $query[] = array(null, $inference_uri, '?'); + break; + } + } + } + $term_uris[$term_type] = _rdfx_query_find_uris($model, $query); + + // Add term details and various relationships for each term, as defined + // in rdfx_term_types() and altered by hook_rdfx_term_types_alter(). + $query_x = array(); + foreach ($term_uris[$term_type] as $term_uri) { + + $terms[$term_type][$term_uri] = _evoc_query_for_term_description($model, $term_uri); + foreach ($group['description'] as $property => $queries) { + foreach ($queries as $predicate_uri => $query_types) { + foreach ($query_types as $query_type) { + switch ($query_type) { + case 'subject': + $query_x[$term_uri][$property][] = array('?', $predicate_uri, $term_uri); + break; + case 'object': + $query_x[$term_uri][$property][] = array($term_uri, $predicate_uri, '?'); + break; + } + } + } + $terms[$term_type][$term_uri][$property] = _rdfx_query_find_uris($model, $query_x[$term_uri][$property]); + } + } + } + } + return $terms; +} + +function _rdfx_get_vocabulary_details(&$model, $ns_uri) { + $query_predicates = array( + 'title' => array( + 'http://www.w3.org/2000/01/rdf-schema#label', + 'http://purl.org/dc/elements/1.1/title', + 'http://purl.org/dc/terms/title', + ), + 'description' => array( + 'http://www.w3.org/2000/01/rdf-schema#comment', + 'http://purl.org/dc/elements/1.1/description', + 'http://purl.org/dc/terms/description', + ), + ); + + if (substr($ns_uri, -1) == '#') { + $uri = substr($ns_uri, 0, -1); + } + + foreach ($query_predicates as $query_element => $predicates) { + foreach ($predicates as $predicate) { + $queries[$query_element][] = array($ns_uri, $predicate, '?'); +// if ($uri !== NULL) { +// $queries[$query_element][] = array($uri, $predicate, '?'); +// } + } + $details[$query_element] = _rdfx_query_find_literal($model, $queries[$query_element]); + } + return $details; +}