view 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 source
<?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;
}