Location: PHPKode > scripts > RDF API > rdf-api/syntax/RdfSerializer.php
<?php


// ----------------------------------------------------------------------------------
// Class: RdfSerializer
// ----------------------------------------------------------------------------------

/**
 * An RDF seralizer. 
 * Seralizes models to RDF syntax. It supports the xml:base and xml:lang directive. 
 * You can choose between different output syntaxes by using the configuration methods 
 * or changing the configuration default values in constants.php.
 * This class is based on the java class edu.unika.aifb.rdf.api.syntax.RDFSerializer 
 * by Boris Motik.
 * 
 * History:
 * 10-03-2002                : Bug in collectNamespaces() fixed.
 * 09-15-2002                : First version of this class.
 *
 * @version  V0.1
 * @author Chris Bizer <hide@address.com>, Boris Motik <hide@address.com>
 *
 * @package syntax
 * @access	public
 *
 */  
class RdfSerializer extends Object {

	// configuration
	var $use_entities;
	var $use_attributes;
    var $sort_model;
    var $rdf_qnames;
	var $use_xml_declaration;
	
	// properties
    var  $m_defaultNamespaces  = array();
    var  $m_namespaces = array();
    var  $m_nextAutomaticPrefixIndex;
    var  $m_out;
    var  $m_baseURI;
    var  $m_statements = array();
    var  $m_currentSubject;
    var  $m_rdfIDElementText;
    var  $m_rdfAboutElementText;
    var  $m_rdfResourceElementText;
    var  $m_groupTypeStatement;
    var  $m_attributeStatements = array();
    var  $m_contentStatements = array();
    var  $rdf_qname_prefix;
	
   /**
    * Constructor
    *
	* @access	public
    */
    function RdfSerializer() {
        
		// default serializer configuration
		$this->use_entities = SER_USE_ENTITIES;
	    $this->use_attributes = SER_USE_ATTRIBUTES;
		$this->sort_model = SER_SORT_MODEL;
		$this->rdf_qnames = SER_RDF_QNAMES;
		$this->use_xml_declaration = SER_XML_DECLARATION;
	
		// add default namespaces
		$this->addNamespacePrefix(RDF_NAMESPACE_PREFIX, RDF_NAMESPACE_URI);
        $this->addNamespacePrefix(RDF_SCHEMA_PREFIX, RDF_SCHEMA_URI);
    }

  /**
   * Serializer congiguration: Sort Model
   * Flag if the serializer should sort the model by subject before serializing. 
   * TRUE makes the RDF code more compact.
   * TRUE is default. Default can be changed in constants.php.
   *
   * @param		boolean
   * @access	public
   */	
	function configSortModel($bool) {
		$this->sort_model = $bool;
	}	
	
  /**
   * Serializer congiguration: Use Entities
   * Flag if the serializer should use entities for URIs. 
   * TRUE makes the RDF code more compact.
   * FALSE is default. Default can be changed in constants.php.
   *
   * @param		boolean
   * @access	public
   */	
	function configUseEntities($bool) {
		$this->use_entities = $bool;
	}

  /**
   * Serializer congiguration: Use Attributes
   * Flag if the serializer should serialize triples as XML attributes where possible. 
   * TRUE makes the RDF code more compact.
   * FALSE is default. Default can be changed in constants.php.
   *
   * @param		boolean
   * @access	public
   */	
	function configUseAttributes($bool) {
		$this->use_attributes = $bool;
	}

 /**
   * Serializer congiguration: Use Qnames
   * Flag if the serializer should use qualified names for RDF reserved words. 
   * TRUE makes the RDF code more compact.
   * TRUE is default. Default can be changed in constants.php. 
   *
   * @param		boolean
   * @access	public
   */	
	function configUseQnames($bool) {
		$this->rdf_qnames = $bool;
	}
	
  /**
   * Serializer congiguration: Use XML Declaration
   * Flag if the serializer should start documents with the xml declaration 
   * <?xml version="1.0" encoding="UTF-8" ?>.
   * TRUE is default. Default can be changed in constants.php. 
   *
   * @param		boolean
   * @access	public
   */	
	function configUseXmlDeclaration($bool) {
		$this->use_xml_declaration = $bool;
	}
	
  /**
   * Adds a new prefix/namespace combination. 
   *
   * @param		String $prefix
   * @param		String $prefix
   * @access	public
   */	
    function addNamespacePrefix($prefix, $namespace) {
        $this->m_defaultNamespaces[$prefix] = $namespace;
    }

  /**
   * Serializes a model to RDF syntax. 
   * RDF syntax can be changed by config_use_attributes($boolean), config_use_entities($boolean),
   * config_sort_model($boolean).
   *
   * @param		object Model $model
   * @param		String $encoding
   * @return	string   
   * @access	public
   */	
    function & serialize(&$model, $encoding = DEFAULT_ENCODING) {
        
		  // define rdf prefix (qname or not)
		  if ($this->rdf_qnames)
		  		$this->rdf_qname_prefix = RDF_NAMESPACE_PREFIX . ":";
		  else		 	
				$this->rdf_qname_prefix = "";
				
		   // check if model is empty
		   if ($model->size() == 0) return "<". $this->rdf_qname_prefix . RDF_RDF ." />";
		
           // copy default namespaces 
		   foreach($this->m_defaultNamespaces as $prefix => $namespace)
		   		$this->m_namespaces[$prefix] = $namespace;

           // set base URI
		    if ($model->getBaseURI()==NULL)
                $this->m_baseURI="opaque:uri";     
            else
                $this->m_baseURI=$model->getBaseURI();
            
			// sort the array of statements
			// Todo: This is awfull. Write own sorting function. 
			if ($this->sort_model) {
				foreach($model->triples as $key => $statement) {
				   $stmkey = $statement->subj->getURI() . 
				   			 $statement->pred->getURI() . 
							 $statement->obj->getLabel();
			  	   $this->m_statements[$stmkey] = $statement;				   
				}
				ksort($this->m_statements); 
            } else {
				$this->m_statements = $model->triples;
			}
			// collects namespaces
            $this->m_nextAutomaticPrefixIndex=0;
            $this->collectNamespaces($model);
            			
			// start writing the contents
			if ($this->use_xml_declaration)
            	$this->m_out = "<?xml version='1.0' encoding='". $encoding ."'?>" . LINEFEED;
            
			// write entitie declarations
			if($this->use_entities) {
				$this->m_out .= "<!DOCTYPE " . $this->rdf_qname_prefix . 
				                 RDF_RDF." [" . LINEFEED;
            	$this->writeEntityDeclarations();
            	$this->m_out .= LINEFEED . "]>" . LINEFEED;
            }
			
            // start the RDF text
            $this->m_out .= "<" . $this->rdf_qname_prefix . RDF_RDF;
            
			// write the xml:base
            if ($model->getBaseURI() != NULL)
                $this->m_out .= LINEFEED. INDENTATION ."xml:base=\"".$model->getBaseURI()."\"";
            
			// write namespaces declarations
			$this->writeNamespaceDeclarations();
            $this->m_out .=">". LINEFEED;
            
			// write triples
			$this->writeDescriptions();
            
			$this->m_out .= LINEFEED;
            $this->m_out .="</" . $this->rdf_qname_prefix . RDF_RDF .">";
        
            $this->m_namespaces=null;
            $this->m_statements=null;
            $this->m_currentSubject=null;
            $this->m_groupTypeStatement=null;
            $this->m_attributeStatements=null;
            $this->m_contentStatements=null;
            $this->m_rdfResourceElementText=null;
            			
			return $this->m_out;
    }
  
   /**
    * @access	private
    */	    
    function writeEntityDeclarations() {
		foreach($this->m_namespaces as $prefix => $namespace) {
		    $this->m_out .= INDENTATION . "<!ENTITY ". $prefix . " '" . $namespace ."'>".LINEFEED;
        }
    }

   /**
    * @access	private
    */	
    function writeNamespaceDeclarations() {
        foreach($this->m_namespaces as $prefix => $namespace) {

			if ($prefix == RDF_NAMESPACE_PREFIX && !$this->rdf_qnames) {
			
				if($this->use_entities) {
	            	$this->m_out .= LINEFEED . INDENTATION .XML_NAMESPACE_DECLARATION_PREFIX .
	                                "=\"&" .$prefix.";\"";
				} else {
					$this->m_out .= LINEFEED . INDENTATION .XML_NAMESPACE_DECLARATION_PREFIX .
	                                "=\"" .$namespace."\"";
				}	
			} else {
			
				if($this->use_entities) {
	            	$this->m_out .= LINEFEED . INDENTATION .XML_NAMESPACE_DECLARATION_PREFIX .
	                                $prefix."=\"&" .$prefix.";\"";
				} else {
					$this->m_out .= LINEFEED . INDENTATION .XML_NAMESPACE_DECLARATION_PREFIX .
	                                $prefix."=\"" .$namespace."\"";
				}
			}		
        }
    }

   /**
    * @access	private
    */    
	function writeDescriptions()  {

		$this->m_groupTypeStatement = NULL;
        $this->m_attributeStatements = array();
        $this->m_contentStatements = array();
        $this->m_currentSubject = NULL;
 		
		foreach($this->m_statements as $key => $statement) {
			$subject = $statement->getSubject();
			$predicate = $statement->getPredicate();
			$object = $statement->getobject();
			
            // write Group and update current subject if nessesary
			if ($this->m_currentSubject==NULL || !$this->m_currentSubject->equals($subject)) {
                $this->writeGroup();
                $this->m_currentSubject=$subject;
            }
            
			// classify the statement
            if (($predicate->getURI() == RDF_NAMESPACE_URI.RDF_TYPE) && is_a($object, "Resource"))
                $this->m_groupTypeStatement = $statement;
            elseif ($this->canAbbreviateValue($object) && 
			        $this->use_attributes &&
					$this->checkForDoubleAttributes($predicate))
                
				$this->m_attributeStatements[] = $statement;
             else
                $this->m_contentStatements[] = $statement;
        }
		$this->writeGroup();
	}

   /**
    * @access	private
    */	
    function writeGroup() {
        if ($this->m_currentSubject==NULL || ($this->m_groupTypeStatement==NULL && (count($this->m_attributeStatements)==0) && (count($this->m_contentStatements)==0)))
            return;
        if ($this->m_groupTypeStatement!=NULL)
            $outerElementName=$this->getElementText($this->m_groupTypeStatement->obj->getURI());
        else
            $outerElementName = $this->rdf_qname_prefix . RDF_DESCRIPTION;
        $this->m_out .= LINEFEED . "<";
        $this->m_out .= $outerElementName;
        $this->m_out .= " ";
        $this->writeSubjectURI($this->m_currentSubject->getURI());
        
		// attribute Statements
		if ($this->use_attributes)
			 $this->writeAttributeStatements();
        
		if (count($this->m_contentStatements)==0)
            $this->m_out .= "/>" . LINEFEED;
        else {
            $this->m_out .= ">" . LINEFEED;
			
            // content statements
			$this->writeContentStatements();
            
			$this->m_out .= "</";
            $this->m_out .= $outerElementName;
            $this->m_out .= '>'. LINEFEED;
        }
        $this->m_groupTypeStatement = NULL;
        $this->m_attributeStatements = array();
        $this->m_contentStatements = array();
    }

   /**
    * @access	private
    */
	function checkForDoubleAttributes($predicate) {
		foreach($this->m_attributeStatements as $key => $statement) {
			if ($statement->pred->equals($predicate)) 
				return FALSE;
		}
		return TRUE;	 
	}
	
   /**
    * @access	private
    */
	function relativizeURI($uri) {
		$uri_namespace = RDFUtil::guessNamespace($uri);
		if ($uri_namespace == $this->m_baseURI) {
			return RDFUtil::guessName($uri);
		} else {
			return $uri;
		}			 
	}

   /**
    * @access	private
    */	
    function writeSubjectURI($currentSubjectURI) {     
	 		$relativizedURI = $this->relativizeURI($currentSubjectURI);  
			if (!($relativizedURI == $currentSubjectURI)) {
                $this->m_out .= $this->rdf_qname_prefix . RDF_ID;
                $this->m_out .= "=\"";
                $this->m_out .= $relativizedURI;
            } else {
                $this->m_out .= $this->rdf_qname_prefix . RDF_ABOUT;
                $this->m_out .= "=\"";
                $this->writeAbsoluteResourceReference($relativizedURI);
            }
            $this->m_out .= "\"";
     
    }
	
   /**
    * @access	private
    */    
	function writeAttributeStatements() {
        foreach($this->m_attributeStatements as $key => $statement) {
            $this->m_out .= LINEFEED;
            $this->m_out .= INDENTATION;
            $this->m_out .= $this->getElementText($statement->pred->getURI());
            $this->m_out .= "=";
            $value=$statement->obj->getLabel();
            $quote=$this->getValueQuoteType($value);
            $this->m_out .= $quote;
            $this->m_out .= RDFUtil::escapeValue($value);
            $this->m_out .= $quote;
        }
    }

   /**
    * @access	private
    */	
    function writeContentStatements()  {
        foreach($this->m_contentStatements as $key => $statement) {
            $this->m_out .= INDENTATION;
            $this->m_out .= '<';
            $predicateElementText=$this->getElementText($statement->pred->getURI());
            $this->m_out .= $predicateElementText;
			
            if (is_a($statement->obj, "Resource")) {
                $this->writeResourceReference($statement->obj->getURI());
                $this->m_out .= "/>" . LINEFEED;
            } else {
                if(is_a($statement->obj, "Literal") and $statement->obj->getLanguage()!= NULL)
				 $this->m_out .= " " . XML_NAMESPACE_PREFIX . ":" . XML_LANG . "=\"".$statement->obj->getLanguage()."\"";
				
				$this->m_out .= '>';
                $this->writeTextValue($statement->obj->getLabel());
                $this->m_out .= "</";
                $this->m_out .= $predicateElementText;
                $this->m_out .= '>' . LINEFEED;
            }
        }
    }
	
   /**
    * @access	private
    */    
	function writeResourceReference($rebaseURI)  {
            $this->m_out .= ' ';
            $this->m_out .= $this->rdf_qname_prefix . RDF_RESOURCE;
            $this->m_out .= "=\"";
            $relativizedURI = $this->relativizeURI($rebaseURI);
            if (!($relativizedURI == $rebaseURI))
                $this->m_out .= "#" . $relativizedURI;
            else
                $this->writeAbsoluteResourceReference($rebaseURI);
            $this->m_out .= "\"";
    }
    
   /**
    * @access	private
    */	
	function writeAbsoluteResourceReference($rebaseURI) {
        $namespace=RDFUtil::guessNamespace($rebaseURI);
        $localName=RDFUtil::guessName($rebaseURI);
        $text=$rebaseURI;
        if ($namespace!="" and $this->use_entities) {
	            $prefix= array_search($namespace, $this->m_namespaces);
                $text="&".$prefix.";".$localName;
        }
        $this->m_out .= $text;
    }

   /**
    * @access	private
    */	
    function writeTextValue($textValue) {
        if ($this->getValueQuoteType($textValue)==USE_CDATA)
            $this->writeEscapedCDATA($textValue);
        else
            $this->m_out .= RDFUtil::escapeValue($textValue);
    }

   /**
    * @access	private
    */	
    function writeEscapedCDATA($textValue) {
	    	$this->m_out .= "<![CDATA[" . $textValue . "]]>";
    }
	
    /**
    * @access	private
    */	
    function getValueQuoteType($textValue) {
        $quote=USE_ANY_QUOTE;
        $hasBreaks=FALSE;
        $whiteSpaceOnly=TRUE;
        for ($i=0; $i<strlen($textValue); $i++) {
            $c=$textValue{$i};
            if ($c==LINEFEED)
                $hasBreaks=TRUE;
            if ($c=="\"" || $c=="\'") {
                if ($quote==USE_ANY_QUOTE)
                    $quote=($c=="\"") ? "\'" : "\"";
                elseif ($c==$quote)
                    return USE_CDATA;
            }
            if (!($c == " "))
                $whiteSpaceOnly = FALSE;
        }
        if ($whiteSpaceOnly || $hasBreaks)
            return USE_CDATA;
        return $quote==USE_ANY_QUOTE ? '"' : $quote;
    }
	
   /**
    * @access	private
    */	
    function canAbbreviateValue($node) {
        if (is_a($node, "Literal")) {
            $value= $node->getLabel();
            if (strlen($value)<MAX_ALLOWED_ABBREVIATED_LENGTH) {
                $c=$this->getValueQuoteType($value);
                return $c=='"' || $c=='\'';
            }
        }
        return FALSE;
    }
   
   /**
    * @access	private
    */    
	function getElementText($elementName)  {
        $namespace=RDFUtil::guessNamespace($elementName);
        $localName=RDFUtil::guessName($elementName);
        if ($namespace=="")
            return $localName;
        $prefix=array_search($namespace, $this->m_namespaces);
		
        if ($prefix===FALSE) {
			$errmsg = RDFAPI_ERROR . "(class: Serializer; method: getElementText): Prefix for element '" . $elementName . "' cannot be found.";
			trigger_error($errmsg, E_USER_ERROR); 
		}
        if ($prefix != RDF_NAMESPACE_PREFIX) 		
		     return $prefix.":".$localName;
		else
			 return $this->rdf_qname_prefix . $localName;	 
    }
	
   /**
    * @access	private
    */	
    function collectNamespaces($model)  {
        foreach($model->triples as $key => $value) {
            $this->collectNamespace($value->getPredicate());
			if ($this->use_entities) {
			    $this->collectNamespace($value->getSubject());
				if(!is_a($value->getObject(), "Literal")) 
					  $this->collectNamespace($value->getObject());
			}	
        }
    }
	
   /**
    * @access	private
    */
    function collectNamespace($resource)  {
        $namespace=RDFUtil::getNamespace($resource);
        if (!in_array($namespace, $this->m_namespaces)) {
            $prefix = array_search( $namespace, $this->m_defaultNamespaces);
            if ($prefix===FALSE) 
                $prefix=$this->getNextNamespacePrefix();
            $this->m_namespaces[$prefix] = $namespace;
        }
    }

   /**
    * @access	private
    */	
    function getNextNamespacePrefix() {
        $this->m_nextAutomaticPrefixIndex++;
        return GENERAL_PREFIX_BASE . $this->m_nextAutomaticPrefixIndex;
    }

}
?>
Return current item: RDF API