*
Using [ ] for blank nodes, or _: if necessary
* Literal datatype- and xmlLanguageTag support
*
*
* Un-supported N3 Features include:
*
*
*
* @author Gunnar AA. Grimnes
* @author Daniel Westphal
* @author Christian Weiske
* @version $Id: N3Serializer.php 556 2008-01-22 10:52:48Z fusel2k $
* @package syntax
* @access public
**/
define('MAGIC_STRING', '~~~');
class N3Serializer extends Object
{
var $debug = false;
var $prefixes = array();
var $noPrefixes = array();
var $done; // keeps track of already serialized resources
var $resourcetext;
var $resourcetext_taken;
var $model;
var $res;
var $anon;
var $styleCompress = false;
var $stylePretty = false;
var $styleNest = false;
/**
* Constructor
*
* @access public
*/
function N3Serializer()
{
$this->debug = false;
}
/**
* Adds a new namespace prefix to use.
* Unknown namespaces will become ns0, ns1 etc.
* @access public
* @param string $s
* @returns void
**/
function addNSPrefix($ns, $prefix)
{
$this->prefixes[$ns] = $prefix;
}
/**
* Clears all previously set namespace prefixes
*/
function clearNSPrefixes()
{
$this->prefixes = array();
}
/**
* Add a namespace that shall not get shortened by using a prefix.
*
* @param string $ns Namespace URI like "http://example.com/"
*/
function addNoNSPrefix($ns)
{
$this->noPrefixes[$ns] = true;
}
/**
* Clears all previously set noNamespace prefixes
*/
function clearNoNSPrefixes()
{
$this->noPrefixes = array();
}
/**
* Serializes a model to N3 syntax.
*
* @param object Model $model
* @return string
* @access public
*/
function &serialize(&$m)
{
if (is_a($m, 'DbModel')) {
$m=$m->getMemModel();
}
$this->reset();
$this->model = $m;
$this->res = "";
// copy default namespaces
global $default_prefixes;
foreach($default_prefixes as $prefix => $namespace) {
$this->addNSPrefix($namespace,$prefix);
}
$nps= $this->model->getParsedNamespaces();
if ($nps!=false) {
foreach ($nps as $uri => $prefix){
$this->addNSPrefix($uri,$prefix);
}
}
$namespaces = array();
$count = array();
$resources = array();
foreach ($this->model->triples as $t) {
$s = $t->getSubject();
if (is_a($s, 'Resource')) {
$namespaces[$s->getNamespace()] = 1;
}
$p = $t->getPredicate();
if (is_a($p, 'Resource')) {
$namespaces[$p->getNamespace()] = 1;
}
$o = $t->getObject();
if (is_a($o, 'Resource')) {
$namespaces[$o->getNamespace()] = 1;
}
$uri = $s->getURI();
if (isset($count[$uri])) {
$count[$uri]++;
} else {
$count[$uri] = 0;
$resources[$uri] = $s;
}
if ($this->styleNest && is_a($s, 'BlankNode')) {
//make sure blank nodes are sorted *after* normal nodes
//so that they can be included
$count[$uri] -= 0.00001;
}
}
if (!HIDE_ADVERTISE) {
$this->res .= '# Generated by N3Serializer.php from RDF RAP.'.LINEFEED
.'# http://www.wiwiss.fu-berlin.de/suhl/bizer/rdfapi/index.html'
.LINEFEED.LINEFEED;
}
$this->doNamespaces($namespaces);
$this->res .= LINEFEED.LINEFEED;
arsort($count);
foreach ( $count as $k => $v) {
$this->doResource($resources[$k]);
// $this->res.=" .\n";
}
//make all replacements
do {
$bReplacements = false;
foreach ($this->resourcetext as $r => $t) {
if (preg_match_all('/'.MAGIC_STRING.'([^ ]+)'.MAGIC_STRING.'/', $t, $ms, PREG_SET_ORDER)) {
foreach ($ms as $mseach) {
$rp = $this->resourcetext[$mseach[1]];
$this->resourcetext[$r] = preg_replace('/'.MAGIC_STRING.$mseach[1].MAGIC_STRING.'/', $rp, $t);
$bReplacements = true;
}
}
}
} while ($bReplacements);
//after all replacements took place, put the lines out
$c = 0;
foreach ($this->resourcetext as $r => $t) {
if ($this->debug) {
$this->res .= $c . ': ';
}
if (!(isset($this->resourcetext_taken[$r]) && $this->resourcetext_taken[$r]>0)) {
$this->res .= $t . ' .' . LINEFEED;
if ($this->stylePretty) {
$this->res .= LINEFEED;
}
} else if ($this->debug ) {
$this->res.=' Skipping : '.$t.LINEFEED;
}
$c++;
}
// $max=-1111;
// $maxkey="";
// foreach ($count as $k=>$c) {
// if ( $c>$max) { $maxkey=$k; $max=$c; }
// }
// if ($this->debug) {
// print "$maxkey is subject of most triples! ($max) \n";
// }
return $this->res;
}//function &serialize(&$m)
/**
* Serializes a model and saves it into a file.
* Returns FALSE if the model couldn't be saved to the file.
*
* @param object MemModel $model
* @param string $filename
* @return boolean
* @access public
*/
function saveAs(&$model, $filename)
{
// serialize model
$n3 = $this->serialize($model);
// write serialized model to file
$file_handle = @fopen($filename, 'w');
if ($file_handle) {
fwrite($file_handle, $n3);
fclose($file_handle);
return true;
} else {
return false;
};
}
/**
* Set to true, if the N3 serializer should try to compress the blank node
* syntax using [] whereever possible.
*/
function setCompress($compress)
{
$this->styleCompress = $compress;
}
/**
* Enables pretty printing in semicolon delimited sentences.
*/
function setPrettyPrint($prettyPrint)
{
$this->stylePretty = $prettyPrint;
}
/**
* Enables nesting of blank nodes with [] if
* compression is activated via @see setCompress
*/
function setNest($nest)
{
$this->styleNest = $nest;
}
/* ==================== Private Methods from here ==================== */
/**
* Readies this object for serializing another model
* @access private
* @param void
* @returns void
**/
function reset()
{
$this->anon = 0;
$this->done = array();
$this->resourcetext_taken = array();
$this->resourcetext = array();
$this->res = '';
$this->model = null;
}
/**
* Makes ns0, ns1 etc. prefixes for unknown prefixes.
* Outputs @prefix lines.
* @access private
* @param array $n
* @returns void
**/
function doNamespaces(&$n)
{
$c = 0;
foreach ($n as $ns => $nonsense) {
if (!$ns || isset($this->noPrefixes[$ns])) {
continue;
}
if (isset($this->prefixes[$ns])) {
$p = $this->prefixes[$ns];
} else {
$p = 'ns' . $c;
$this->prefixes[$ns] = $p;
$c++;
}
$this->res .= "@prefix $p: <".$ns.'> .'.LINEFEED;
}
}
/**
* Fill in $resourcetext for a single resource.
* Will recurse into Objects of triples, but should never look ? (really?)
* @param object Resource $r
* @returns boolean
* @access private
**/
function doResource(&$r, $bEmbedded = false, $strIndent = ' ')
{
//var_dump($r->getURI());
$ts = $this->model->find($r, null, null);
if (count($ts->triples) == 0) {
if ($bEmbedded) {
$this->resourcetext[$r->getURI()] = '_:' . $r->getLabel();
}
return;
}
$out = '';
if (isset($this->done[$r->getURI()]) && $this->done[$r->getURI()]) {
if (!$this->styleNest && is_a($r, 'BlankNode')) {
if ($this->resourcetext_taken[$r->getURI()] == 1) {
//Oh bother, we must use the _:blah construct.
$a = $this->resourcetext[$r->getURI()];
$this->resourcetext[$r->getURI()]='_:anon'.$this->anon;
$this->resourcetext['_:anon'.$this->anon]=$this->fixAnon($a, '_:anon'.$this->anon);
$this->resourcetext_taken[$r->getURI()]=2;
$this->anon++;
}
}
return false;
}
$this->done[$r->getURI()] = true;
$compress = false;
if (is_a($r, 'Resource')) {
if (is_a($r, 'BlankNode')) {
//test if this blanknode is referenced somewhere
$rbn = $this->model->find(null, null, $r);
$compress = (N3SER_BNODE_SHORT || $this->styleCompress)
&& (
count($rbn->triples) == 0
|| (count($rbn->triples) == 1 && $bEmbedded)
);
if ($compress) {
$out.='[';
} else {
$out.='_:'.$r->getLabel();
}
} else {
$this->doURI($r, $out);
}
}
usort($ts->triples, 'statementsorter');
$lastp = '';
$out .= ' ';
foreach ($ts->triples as $t) {
$p = $t->getPredicate();
if ($p === $lastp) {
$out .= ' , ';
} else {
if ($lastp!='') {
if ($this->stylePretty) {
$out .= ";\n" . $strIndent;
} else {
$out .= ' ; ';
}
}
$this->doURI($p, $out);
$lastp = $p;
}
$out .= ' ';
$o = $t->getObject();
if (is_a($o, 'Literal')) {
$l = $o->getLabel();
if (strpos($l, LINEFEED) === false) {
$long = false;
} else {
$long = true;
}
//try to be intelligent
$quoteSingle = strpos($l, '\'') !== false;
$quoteDouble = strpos($l, '"') !== false;
if ($quoteSingle && !$quoteDouble) {
$quoteChar = $long ? '"""' : '"';
} else if ($quoteDouble && !$quoteSingle) {
$quoteChar = $long ? '\'\'\'' : '\'';
} else if ($quoteDouble && $quoteSingle) {
//both quotation chars inside
$quoteChar = $long ? '"""' : '"';
$l = addslashes($l);
} else {
//no quotation chars
$quoteChar = $long ? '"""' : '"';
}
$out .= $quoteChar . $l . $quoteChar;
if ( $o->getLanguage()!='' ) {
$out.='@'.$o->getLanguage();
}
if ( $o->getDatatype()!='' ) {
$out.='^^<'.$o->getDatatype().'>';
}
}
if (is_a($o, 'Resource')) {
if ($this->debug) {
print 'Doing object: '.$o->getURI().LINEFEED;
}
if (is_a($o, 'BlankNode')) {
if ($this->styleNest && $this->styleCompress
&& !isset($this->done[$o->getURI()])
) {
$this->doResource($o, true, $strIndent . ' ');
$out .= MAGIC_STRING . $o->getURI() . MAGIC_STRING;
//$out .= $this->resourcetext[$o->getURI()];
$this->resourcetext_taken[$o->getURI()] = 1;
} else {
$out .= '_:'.$o->getLabel();
}
} else {
$this->doURI($o, $out);
}
}
}
if ($compress) {
$out .= ' ]';
};
$this->resourcetext[$r->getURI()]=$out;
return true;
}//function doResource(&$r)
/**
* Format a single URI
* @param string $s
* @return void
* @access private
**/
function doURI(&$r, &$out)
{
if ($r->getURI() == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
$out .= 'a';
return;
}
$ns = $r->getNamespace();
if ($ns != '' && !isset($this->noPrefixes[$ns])) {
$out .= $this->prefixes[$ns].':'.$r->getLocalName();
} else {
//Will this ever happen? It does, now.
$out .= '<' . $r->getURI() . '>';
}
}
/**
* Fix the resourcetext for a blanknode where the _: construct was used
* @param string $s
* @param string $a
* @access private
* @return void
**/
function fixAnon($t,$a)
{
$t = preg_replace("/( \] $|^\[ )/", '', $t);
return $a . $t;
}
}
?>