source: Dev/branches/rest-dojo-ui/server/rdfapi/syntax/N3Serializer.php @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 14.4 KB
Line 
1<?php
2
3// ----------------------------------------------------------------------------------
4// Class: N3Serializer
5// ----------------------------------------------------------------------------------
6
7/**
8 * PHP Notation3 Serializer
9 *
10 * This class serialises models to N3 Syntax.
11 *
12 * Supported N3 features:
13 * <ul>
14 *   <li>Using [ ] for blank nodes, or _: if necessary</li>
15 *   <li>Literal datatype- and xmlLanguageTag support</li>
16 * </ul>
17 *
18 * Un-supported N3 Features include:
19 * <ul>
20 *   <li>Reification</li>
21 * </ul>
22 *
23 *
24 * @author Gunnar AA. Grimnes <ggrimnes@csd.abdn.ac.uk>
25 * @author Daniel Westphal <mail@d-westphal.de>
26 * @author Christian Weiske <cweiske@cweiske.de>
27 * @version $Id: N3Serializer.php 556 2008-01-22 10:52:48Z fusel2k $
28 * @package syntax
29 * @access public
30 **/
31
32define('MAGIC_STRING', '~~~');
33
34class N3Serializer extends Object
35{
36
37    var $debug = false;
38
39    var $prefixes   = array();
40    var $noPrefixes = array();
41
42    var $done;  // keeps track of already serialized resources
43    var $resourcetext;
44    var $resourcetext_taken;
45    var $model;
46    var $res;
47    var $anon;
48
49    var $styleCompress = false;
50    var $stylePretty   = false;
51    var $styleNest     = false;
52
53    /**
54    * Constructor
55    *
56    * @access   public
57    */
58    function N3Serializer()
59    {
60        $this->debug = false;
61    }
62
63
64    /**
65    * Adds a new namespace prefix to use.
66    * Unknown namespaces will become ns0, ns1 etc.
67    * @access public
68    * @param string $s
69    * @returns void
70    **/
71    function addNSPrefix($ns, $prefix)
72    {
73        $this->prefixes[$ns] = $prefix;
74    }
75
76
77
78    /**
79    * Clears all previously set namespace prefixes
80    */
81    function clearNSPrefixes()
82    {
83        $this->prefixes = array();
84    }
85
86
87
88    /**
89    *   Add a namespace that shall not get shortened by using a prefix.
90    *
91    *   @param string $ns Namespace URI like "http://example.com/"
92    */
93    function addNoNSPrefix($ns)
94    {
95        $this->noPrefixes[$ns] = true;
96    }
97
98
99
100    /**
101    * Clears all previously set noNamespace prefixes
102    */
103    function clearNoNSPrefixes()
104    {
105        $this->noPrefixes = array();
106    }
107
108
109
110    /**
111    * Serializes a model to N3 syntax.
112    *
113    * @param     object Model $model
114    * @return    string
115    * @access    public
116    */
117    function &serialize(&$m)
118    {
119        if (is_a($m, 'DbModel')) {
120            $m=$m->getMemModel();
121        }
122
123        $this->reset();
124        $this->model = $m;
125        $this->res   = "";
126
127        // copy default namespaces
128        global $default_prefixes;
129        foreach($default_prefixes as $prefix => $namespace) {
130            $this->addNSPrefix($namespace,$prefix);
131        }
132
133        $nps= $this->model->getParsedNamespaces();
134        if ($nps!=false) {
135            foreach ($nps as $uri => $prefix){
136                $this->addNSPrefix($uri,$prefix);
137            }
138        }
139
140        $namespaces = array();
141        $count      = array();
142        $resources  = array();
143        foreach ($this->model->triples as $t) {
144            $s = $t->getSubject();
145            if (is_a($s, 'Resource')) {
146                $namespaces[$s->getNamespace()] = 1;
147            }
148            $p = $t->getPredicate();
149            if (is_a($p, 'Resource')) {
150                $namespaces[$p->getNamespace()] = 1;
151            }
152            $o = $t->getObject();
153            if (is_a($o, 'Resource')) {
154                $namespaces[$o->getNamespace()] = 1;
155            }
156            $uri = $s->getURI();
157
158            if (isset($count[$uri])) {
159                $count[$uri]++;
160            } else {
161                $count[$uri]     = 0;
162                $resources[$uri] = $s;
163            }
164
165            if ($this->styleNest && is_a($s, 'BlankNode')) {
166                //make sure blank nodes are sorted *after* normal nodes
167                //so that they can be included
168                $count[$uri] -= 0.00001;
169            }
170        }
171
172        if (!HIDE_ADVERTISE) {
173            $this->res .= '# Generated by N3Serializer.php from RDF RAP.'.LINEFEED
174                        .'# http://www.wiwiss.fu-berlin.de/suhl/bizer/rdfapi/index.html'
175                        .LINEFEED.LINEFEED;
176        }
177
178        $this->doNamespaces($namespaces);
179
180        $this->res .= LINEFEED.LINEFEED;
181
182        arsort($count);
183
184        foreach ( $count as $k => $v) {
185            $this->doResource($resources[$k]);
186            //      $this->res.=" .\n";
187        }
188
189        //make all replacements
190        do {
191            $bReplacements = false;
192            foreach ($this->resourcetext as $r => $t) {
193                if (preg_match_all('/'.MAGIC_STRING.'([^ ]+)'.MAGIC_STRING.'/', $t, $ms, PREG_SET_ORDER)) {
194                    foreach ($ms as $mseach) {
195                        $rp = $this->resourcetext[$mseach[1]];
196                        $this->resourcetext[$r] = preg_replace('/'.MAGIC_STRING.$mseach[1].MAGIC_STRING.'/', $rp, $t);
197                        $bReplacements = true;
198                    }
199                }
200
201            }
202        } while ($bReplacements);
203
204        //after all replacements took place, put the lines out
205        $c = 0;
206        foreach ($this->resourcetext as $r => $t) {
207            if ($this->debug) {
208                $this->res .= $c . ': ';
209            }
210            if (!(isset($this->resourcetext_taken[$r]) && $this->resourcetext_taken[$r]>0)) {
211                $this->res .= $t . ' .' . LINEFEED;
212                if ($this->stylePretty) {
213                    $this->res .= LINEFEED;
214                }
215            } else if ($this->debug ) {
216                $this->res.=' Skipping : '.$t.LINEFEED;
217            }
218            $c++;
219        }
220
221    //     $max=-1111;
222    //     $maxkey="";
223    //     foreach ($count as $k=>$c) {
224    //       if ( $c>$max) { $maxkey=$k; $max=$c; }
225    //     }
226
227    //     if ($this->debug) {
228    //       print "$maxkey is subject of most triples! ($max) \n";
229    //     }
230
231        return $this->res;
232    }//function &serialize(&$m)
233
234
235
236    /**
237    * Serializes a model and saves it into a file.
238    * Returns FALSE if the model couldn't be saved to the file.
239    *
240    * @param     object MemModel $model
241    * @param     string $filename
242    * @return    boolean
243    * @access    public
244    */
245    function saveAs(&$model, $filename)
246    {
247        // serialize model
248        $n3 = $this->serialize($model);
249
250        // write serialized model to file
251        $file_handle = @fopen($filename, 'w');
252        if ($file_handle) {
253            fwrite($file_handle, $n3);
254            fclose($file_handle);
255            return true;
256        } else {
257            return false;
258        };
259    }
260
261
262
263    /**
264    * Set to true, if the N3 serializer should try to compress the blank node
265    *  syntax using [] whereever possible.
266    */
267    function setCompress($compress)
268    {
269        $this->styleCompress = $compress;
270    }
271
272
273
274    /**
275    * Enables pretty printing in semicolon delimited sentences.
276    */
277    function setPrettyPrint($prettyPrint)
278    {
279        $this->stylePretty = $prettyPrint;
280    }
281
282
283
284    /**
285    * Enables nesting of blank nodes with [] if
286    * compression is activated via @see setCompress
287    */
288    function setNest($nest)
289    {
290        $this->styleNest = $nest;
291    }
292
293  /* ==================== Private Methods from here ==================== */
294
295
296    /**
297    * Readies this object for serializing another model
298    * @access private
299    * @param void
300    * @returns void
301    **/
302    function reset()
303    {
304        $this->anon               = 0;
305        $this->done               = array();
306        $this->resourcetext_taken = array();
307        $this->resourcetext       = array();
308        $this->res                = '';
309        $this->model              = null;
310    }
311
312
313
314    /**
315    * Makes ns0, ns1 etc. prefixes for unknown prefixes.
316    * Outputs @prefix lines.
317    * @access private
318    * @param array $n
319    * @returns void
320    **/
321    function doNamespaces(&$n)
322    {
323        $c = 0;
324        foreach ($n as $ns => $nonsense) {
325            if (!$ns || isset($this->noPrefixes[$ns])) {
326                continue;
327            }
328            if (isset($this->prefixes[$ns])) {
329                $p = $this->prefixes[$ns];
330            } else {
331                $p = 'ns' . $c;
332                $this->prefixes[$ns] = $p;
333                $c++;
334            }
335            $this->res .= "@prefix $p: <".$ns.'> .'.LINEFEED;
336        }
337    }
338
339
340
341    /**
342    * Fill in $resourcetext for a single resource.
343    * Will recurse into Objects of triples, but should never look ? (really?)
344    * @param object Resource $r
345    * @returns boolean
346    * @access private
347    **/
348    function doResource(&$r, $bEmbedded = false, $strIndent = '    ')
349    {
350        //var_dump($r->getURI());
351
352        $ts = $this->model->find($r, null, null);
353        if (count($ts->triples) == 0) {
354            if ($bEmbedded) {
355                $this->resourcetext[$r->getURI()] = '_:' . $r->getLabel();
356            }
357            return;
358        }
359
360        $out = '';
361
362        if (isset($this->done[$r->getURI()]) && $this->done[$r->getURI()]) {
363            if (!$this->styleNest && is_a($r, 'BlankNode')) {
364                if ($this->resourcetext_taken[$r->getURI()] == 1) {
365                    //Oh bother, we must use the _:blah construct.
366                    $a = $this->resourcetext[$r->getURI()];
367                    $this->resourcetext[$r->getURI()]='_:anon'.$this->anon;
368                    $this->resourcetext['_:anon'.$this->anon]=$this->fixAnon($a, '_:anon'.$this->anon);
369                    $this->resourcetext_taken[$r->getURI()]=2;
370                    $this->anon++;
371                }
372            }
373            return false;
374        }
375
376        $this->done[$r->getURI()] = true;
377        $compress = false;
378
379        if (is_a($r, 'Resource')) {
380            if (is_a($r, 'BlankNode')) {
381                //test if this blanknode is referenced somewhere
382                $rbn      = $this->model->find(null, null, $r);
383                $compress = (N3SER_BNODE_SHORT || $this->styleCompress)
384                            && (
385                                count($rbn->triples) == 0
386                                || (count($rbn->triples) == 1 && $bEmbedded)
387                            );
388                if ($compress) {
389                    $out.='[';
390                } else {
391                    $out.='_:'.$r->getLabel();
392                }
393            } else {
394                $this->doURI($r, $out);
395            }
396        }
397
398        usort($ts->triples, 'statementsorter');
399        $lastp = '';
400        $out  .= ' ';
401
402        foreach ($ts->triples as $t) {
403            $p = $t->getPredicate();
404
405            if ($p === $lastp) {
406                $out .= ' , ';
407            } else {
408                if ($lastp!='') {
409                    if ($this->stylePretty) {
410                        $out .= ";\n" . $strIndent;
411                    } else {
412                        $out .= ' ; ';
413                    }
414                }
415                $this->doURI($p, $out);
416                $lastp = $p;
417            }
418
419            $out .= ' ';
420
421            $o = $t->getObject();
422
423            if (is_a($o, 'Literal')) {
424                $l = $o->getLabel();
425                if (strpos($l, LINEFEED) === false) {
426                    $long = false;
427                } else {
428                    $long = true;
429                }
430
431                //try to be intelligent
432                $quoteSingle = strpos($l, '\'') !== false;
433                $quoteDouble = strpos($l, '"')  !== false;
434                if ($quoteSingle && !$quoteDouble) {
435                    $quoteChar = $long ? '"""' : '"';
436                } else if ($quoteDouble && !$quoteSingle) {
437                    $quoteChar = $long ? '\'\'\'' : '\'';
438                } else if ($quoteDouble && $quoteSingle) {
439                    //both quotation chars inside
440                    $quoteChar = $long ? '"""' : '"';
441                    $l = addslashes($l);
442                } else {
443                    //no quotation chars
444                    $quoteChar = $long ? '"""' : '"';
445                }
446                $out .= $quoteChar . $l . $quoteChar;
447
448                if ( $o->getLanguage()!='' ) {
449                    $out.='@'.$o->getLanguage();
450                }
451                if ( $o->getDatatype()!='' ) {
452                    $out.='^^<'.$o->getDatatype().'>';
453                }
454            }
455
456            if (is_a($o, 'Resource')) {
457                if ($this->debug) {
458                    print 'Doing object: '.$o->getURI().LINEFEED;
459                }
460                if (is_a($o, 'BlankNode')) {
461                    if ($this->styleNest && $this->styleCompress
462                     && !isset($this->done[$o->getURI()])
463                    ) {
464                        $this->doResource($o, true, $strIndent . '    ');
465                        $out .= MAGIC_STRING . $o->getURI() . MAGIC_STRING;
466                        //$out .= $this->resourcetext[$o->getURI()];
467                        $this->resourcetext_taken[$o->getURI()] = 1;
468                    } else {
469                        $out .= '_:'.$o->getLabel();
470                    }
471                } else {
472                    $this->doURI($o, $out);
473                }
474            }
475        }
476
477        if ($compress) {
478            $out .= ' ]';
479        };
480
481        $this->resourcetext[$r->getURI()]=$out;
482
483        return true;
484    }//function doResource(&$r)
485
486
487
488    /**
489    * Format a single URI
490    * @param string $s
491    * @return void
492    * @access private
493    **/
494    function doURI(&$r, &$out)
495    {
496        if ($r->getURI() == 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
497            $out .= 'a';
498            return;
499        }
500        $ns = $r->getNamespace();
501        if ($ns != '' && !isset($this->noPrefixes[$ns])) {
502            $out .= $this->prefixes[$ns].':'.$r->getLocalName();
503        } else {
504            //Will this ever happen? It does, now.
505            $out .= '<' . $r->getURI() . '>';
506        }
507    }
508
509
510
511    /**
512    * Fix the resourcetext for a blanknode where the _: construct was used
513    * @param string $s
514    * @param string $a
515    * @access private
516    * @return void
517    **/
518    function fixAnon($t,$a)
519    {
520        $t = preg_replace("/( \] $|^\[ )/", '', $t);
521        return $a . $t;
522    }
523
524}
525
526?>
Note: See TracBrowser for help on using the repository browser.