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

Last change on this file since 312 was 312, checked in by jkraaijeveld, 13 years ago
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.