Location: PHPKode > scripts > RDF API > rdf-api/model/Model.php
<?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






?>
Return current item: RDF API