<?php
// ----------------------------------------------------------------------------------
// Class: Model
// ----------------------------------------------------------------------------------
/**
* A model is a programming interface to an RDF graph.
* An RDF graph is a directed labeled graph, as described in http://www.w3.org/TR/rdf-mt/.
* It can be defined as a set of <S, P, O> triples, where P is a uriref, S is either
* a uriref or a blank node, and O is either a uriref, a blank node, or a literal.
*
* History:
* 09-10-2002 : First version of this class.
*
* @version V0.1
* @author Chris Bizer <hide@address.com>
*
* @package model
* @todo nothing
* @access public
*/
class Model extends Object {
/**
* Base_uri of the model
* Affects creating of new resources and serialization syntax.
* @var string
* @access private
*/
var $baseURI;
/**
* Triples of the model
* @var array
* @access private
*/
var $triples = array();
/**
* Constructor
* You can supply a base_uri
*
* @param string $baseURI
* @access public
*/
function model($baseURI = NULL) {
$this->setBaseURI($baseURI);
}
/**
* Set a base URI for the model.
* Affects creating of new resources and serialization syntax.
* If the URI doesn't end with # : or /, then a # is added to the URI.
* @param string $uri
* @access public
*/
function setBaseURI($uri) {
$c = substr($uri, strlen($uri)-1 ,1);
if (!($c=="#" || $c==":" || $c=="/" || $c=="\\"))
$uri .= "#";
$this->baseURI = $uri;
}
/**
* Returns current base URI setting.
* @access public
*/
function getBaseURI() {
return $this->baseURI;
}
/**
* Number of triples in the model
*
* @return integer
* @access public
*/
function size() {
return count($this->triples);
}
/**
* Checks if model is empty
*
* @return boolean
* @access public
*/
function isEmpty() {
if (count($this->triples) == 0) {
return TRUE;
} else {
return FALSE;
};
}
/**
* Adds a new triple to the model.
*
* @param object Statement $statement
* @access public
* @throws PhpError
*/
function add($statement) {
if (!is_a($statement, "Statement")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: add): Statement expected.";
trigger_error($errmsg, E_USER_ERROR);
}
if (!$this->contains($statement))
$this->triples[] = $statement;
}
/**
* Removes the triple from the model.
*
* @param object Statement $statement
* @access public
* @throws PhpError
*/
function remove($statement) {
if (!is_a($statement, "Statement")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: remove): Statement expected.";
trigger_error($errmsg, E_USER_ERROR);
}
foreach($this->triples as $key => $value) {
if ($this->matchStatement($value, $statement->subject(), $statement->predicate(),
$statement->object())) {
unset($this->triples[$key]);
break;
}
}
}
/**
* Short Dump of the Model.
*
* @access public
* @return string
*/
function toString() {
return "Model[baseURI=" . $this->getBaseURI() . "; size=" . $this->size() . "]";
}
/**
* Dumps of the Model including all triples.
*
* @access public
* @return string
*/
function toStringIncludingTriples() {
$dump = $this->toString() . chr(13);
foreach($this->triples as $key => $value) {
$dump .= $value->toString() . chr(13);
}
return $dump;
}
/**
* Tests if the model contains the given triple.
* TRUE if the triple belongs to the model;
* FALSE otherwise.
*
* @param object Statement $statement
* @return boolean
* @access public
*/
function contains(&$statement) {
foreach($this->triples as $key => $value) {
if ($value->equals($statement))
return TRUE;
}
return FALSE;
}
/**
* Determine if all of the statements in a model are also contained in this model.
* True if all of the statements in $model are also contained in this model and false otherwise.
*
* @param object Model $model
* @return boolean
* @access public
*/
function containsAll(&$model) {
$i = 0;
foreach($model->triples as $mkey => $modelStatement)
if($this->contains($modelStatement))
$i++;
if ($model->size() == $i)
return TRUE;
return FALSE;
}
/**
* Determine if any of the statements in a model are also contained in this model.
* True if any of the statements in $model are also contained in this model and false otherwise.
*
* @param object Model $model
* @return boolean
* @access public
*/
function containsAny(&$model) {
foreach($model->triples as $mkey => $modelStatement)
if($this->contains($modelStatement))
return TRUE;
return FALSE;
}
/**
* General method to search for triples.
* NULL input for any parameter will match anything.
* Example: $result = $m->find( NULL, NULL, $node );
* Finds all triples with $node as object.
*
* @param object Node $subject
* @param object Node $predicate
* @param object Node $object
* @return object Model
* @access public
* @throws PhpError
*/
function find($subject, $predicate, $object) {
if (
(!is_a($subject, "Node") && $subject != NULL) ||
(!is_a($predicate, "Node") && $predicate != NULL) ||
(!is_a($object, "Node") && $object != NULL)
) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: find): Parameters must be subclasses of Node or NULL";
trigger_error($errmsg, E_USER_ERROR);
}
$res = new Model($this->getBaseURI());
if($this->size() == 0)
return $res;
if($subject == NULL && $predicate == NULL && $object == NULL)
return $this;
foreach($this->triples as $key => $value) {
if ($this->matchStatement($value, $subject, $predicate, $object)) {
$res->add($value);
}
}
return $res;
}
/**
* Method to search for triples using Perl-style regular expressions.
* NULL input for any parameter will match anything.
* Example: $result = $m->find_regex( NULL, NULL, $regex );
* Finds all triples where the label of the object node matches the regular expression.
*
* @param string $subject_regex
* @param string $predicate_regex
* @param string $object_regex
* @return object Model
* @access public
*/
function findRegex($subject_regex, $predicate_regex, $object_regex) {
$res = new Model($this->getBaseURI());
if($this->size() == 0)
return $res;
if($subject_regex == NULL && $predicate_regex == NULL && $object_regex == NULL)
return $this;
foreach($this->triples as $key => $value) {
if (
($subject_regex == NULL || preg_match($subject_regex, $value->subj->getLabel())) &&
($predicate_regex == NULL || preg_match($predicate_regex, $value->pred->getLabel())) &&
($object_regex == NULL || preg_match($object_regex, $value->obj->getLabel()))
) $res->add($value);
}
return $res;
}
/**
* Returns all tripels of a certain vocabulary.
* $vocabulary is the namespace of the vocabulary inluding a # : / char at the end.
* e.g. http://www.w3.org/2000/01/rdf-schema#
*
* @param string $namespace
* @return object Model
* @access public
*/
function findVocabulary($vocabulary) {
if($this->size() == 0)
return $res;
if($vocabulary == NULL || $vocabulary == "")
return $this;
$res = new Model($this->getBaseURI());
foreach($this->triples as $key => $value) {
if (RDFUtil::getNamespace($value->getPredicate()) == $vocabulary)
$res->add($value);
}
return $res;
}
/**
* General method to replace nodes a model.
* NULL input for any parameter will match nothing.
* Example: $m->replace($node, NULL, $node, $replacement);
* Replaces all $node objects beeing subject or object in
* any triple of the model with the $needle node.
*
* @param object Node $subject
* @param object Node $predicate
* @param object Node $object
* @param object Node $replacement
* @access public
* @throws PhpError
*/
function replace($subject, $predicate, $object, $replacement) {
if (
(!is_a($replacement, "Node")) ||
(!is_a($subject, "Node") && $subject != NULL) ||
(!is_a($predicate, "Node") && $predicate != NULL) ||
(!is_a($object, "Node") && $object != NULL)
) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: replace): Parameters must be subclasses of Node or NULL";
trigger_error($errmsg, E_USER_ERROR);
}
if($this->size() == 0)
break;
foreach($this->triples as $key => $value) {
if ($this->triples[$key]->subj->equals($subject))
$this->triples[$key]->subj = $replacement;
if ($this->triples[$key]->pred->equals($predicate))
$this->triples[$key]->pred = $replacement;
if ($this->triples[$key]->obj->equals($object))
$this->triples[$key]->obj = $replacement;
}
}
/**
* Internal method that checks, if a statement matches a S, P, O or NULL combination.
* NULL input for any parameter will match anything.
*
* @param object Statement $statement
* @param object Node $subject
* @param object Node $predicate
* @param object Node $object
* @return boolean
* @access private
*/
function matchStatement($statement, $subject, $predicate, $object) {
if(($subject != NULL) AND !($statement->subj->equals($subject)))
return false;
if($predicate != NULL && !($statement->pred->equals($predicate)))
return false;
if($object != NULL && !($statement->obj->equals($object)))
return false;
return true;
}
/**
* Internal method, that returns a resource URI that is unique for the model.
* URIs are generated using the base_uri of the model, the prefix and a unique number.
*
* @todo speed this up !!!
* @param string $prefix
* @return string
* @access private
*/
function getUniqueResourceURI($prefix) {
$counter = 1;
while (true) {
$uri= $this->getBaseURI().$prefix.$counter;
$tempbNode= new BlankNode($uri);
$res1 = $this->find($tempbNode, NULL, NULL);
$res2 = $this->find(NULL, NULL, $tempbNode);
if ($res1->size()==0 && $res2->size()==0)
return $uri;
$counter++;
$resource= NULL;
}
}
/**
* Checks if two models are equal.
* Two models are equal if and only if the two RDF graphs they represent are isomorphic.
*
* @access public
* @param object model $that
* @return boolean
* @throws phpErrpr
*/
function equals(&$that) {
if (!is_a($that, "Model")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: equals): Model expected.";
trigger_error($errmsg, E_USER_ERROR);
}
if ($this->size() != $that->size())
return FALSE;
$i = 0;
foreach($that->triples as $key => $that_triple) {
if ($this->contains($that_triple))
$i++;
}
if ($this->size() == $i)
return TRUE;
return FALSE;
}
/**
* Returns a new model that is the set-union the model with another model.
* The result of taking the set-union of two or more RDF graphs (i.e. sets of triples)
* is another graph, which we will call the merge of the graphs.
* Each of the original graphs is a subgraph of the merged graph. Notice that when forming
* a merged graph, two occurrences of a given uriref or literal as nodes in two different
* graphs become a single node in the union graph (since by definition they are the same
* uriref or literal) but blank nodes are not 'merged' in this way; and arcs are of course
* never merged. In particular, this means that every blank node in a merged graph can be
* identified as coming from one particular graph in the original set of graphs.
*
* Notice that one does not, in general, obtain the merge of a set of graphs by concatenating
* their corresponding N-triples documents and constructing the graph described by the merged
* document, since if some of the documents use the same node identifiers, the merged document
* will describe a graph in which some of the blank nodes have been 'accidentally' merged.
* To merge Ntriples documents it is necessary to check if the same nodeID is used in two or
* more documents, and to replace it with a distinct nodeID in each of them, before merging the
* documents. (Not implemented yet !!!!!!!!!!!)
*
* @param object Model $model
* @return object Model
* @access public
* @throws phpErrpr
*
* @todo Handel blank nodes as described above !!!!!!!
*/
function & unite(&$model) {
if (!is_a($model, "Model")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: unite): Model expected.";
trigger_error($errmsg, E_USER_ERROR);
}
$res = $this;
foreach($model->triples as $key => $value) {
$res->add($value);
}
return $res;
}
/**
* Returns a new model that is the subtraction of another model from this model.
*
* @param object Model $model
* @return object Model
* @access public
* @throws phpErrpr
*/
function & subtract(&$model) {
if (!is_a($model, "Model")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: subtract): Model expected.";
trigger_error($errmsg, E_USER_ERROR);
}
$res = $this;
foreach($model->triples as $key => $value) {
$res->remove($value);
}
return $res;
}
/**
* Returns a new model containing all the statements which are in both this model and another.
*
* @param object Model $model
* @return object Model
* @access public
* @throws phpErrpr
*/
function & intersect(&$model) {
if (!is_a($model, "Model")) {
$errmsg = RDFAPI_ERROR . "(class: Model; method: unite): Model expected.";
trigger_error($errmsg, E_USER_ERROR);
}
$res = new Model($this->getBaseURI());
foreach($model->triples as $key => $value) {
if ($this->contains($value))
$res->add($value);
}
return $res;
}
/**
* Reifies the model.
* Returns a new model that contains the reifications of all statements of this model.
*
* @todo speed this up !!!!!!!
* @access public
* @return object model
*/
function & reify() {
$res = new Model($this->getBaseURI());
foreach($this->triples as $key => $statement) {
$pointer =& $statement->reify($res);
$res = $res->unite($pointer);
}
return $res;
}
/**
* Close the Model and free up resources held.
*
* @access public
*/
function close() {
unset( $baseURI );
unset( $triples );
}
} // end: model
?>