source: Dev/trunk/rdfapi/model/Model.php @ 12

Last change on this file since 12 was 12, checked in by basvannuland, 14 years ago

Added RAP RDF API
Added RDF reader writer for save and load survey

File size: 19.2 KB
RevLine 
[12]1<?php
2require_once RDFAPI_INCLUDE_DIR . 'util/Object.php';
3
4// ----------------------------------------------------------------------------------
5// Class: Model
6// ----------------------------------------------------------------------------------
7
8/**
9 * Abstract superclass of MemModel and DbModel. A model is a programming interface to an RDF graph.
10 * An RDF graph is a directed labeled graph, as described in http://www.w3.org/TR/rdf-mt/.
11 * It can be defined as a set of <S, P, O> triples, where P is a uriref, S is either
12 * a uriref or a blank node, and O is either a uriref, a blank node, or a literal.
13 *
14 *
15 * @version  $Id: Model.php 569 2008-05-19 05:24:39Z p_frischmuth $
16 * @author Radoslaw Oldakowski <radol@gmx.de>
17 * @author Daniel Westphal <mail@d-westphal.de>
18 *
19 * @package model
20 * @access      public
21 */
22class Model extends Object
23{
24    /**
25    * Base URI of the Model.
26    * Affects creating of new resources and serialization syntax.
27    *
28    * @var     string
29    * @access   private
30    */
31    var $baseURI;
32
33    /**
34    * Number of the last assigned bNode.
35    *
36    * @var     integer
37    * @access   private
38    */
39    var $bNodeCount;
40
41    /**
42    *   SparqlParser so we can re-use it
43    *   @var Parser
44    */
45    var $queryParser = null;
46
47
48
49    /**
50    * Notice for people who are used to work with older versions of RAP.
51    *
52    * @throws  PHPError
53    * @access   public
54    */
55    function Model()
56    {
57
58        $errmsg  = 'Since RAP 0.6 the class for manipulating memory models has been renamed to MemModel.';
59        $errmsg .= '<br>Sorry for this inconvenience.<br>';
60
61        trigger_error($errmsg, E_USER_ERROR);
62    }
63
64
65
66    /**
67    * Return current baseURI.
68    *
69    * @return  string
70    * @access   public
71    */
72    function getBaseURI()
73    {
74        return $this->baseURI;
75    }
76
77
78
79    /**
80    * Load a model from a file containing RDF, N3, N-Triples or a xhtml document containing RDF.
81    * This function recognizes the suffix of the filename (.n3 or .rdf) and
82    * calls a suitable parser, if no $type is given as string ("rdf" "n3" "nt");
83    * If the model is not empty, the contents of the file is added to this DbModel.
84    *
85    * @param    string  $filename
86    * @param    string  $type
87    * @param   boolean $stream
88    * @access   public
89    */
90    function load($filename, $type = NULL, $stream=false)
91    {
92        if ((isset($type)) && ($type =='n3') OR ($type =='nt')) {
93            // Import Package Syntax
94            include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3);
95            $parser = new N3Parser();
96        }elseif ((isset($type)) && ($type =='rdf')) {
97            // Import Package Syntax
98            include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
99            $parser = new RdfParser();
100        }elseif ((isset($type)) && ($type =='grddl')) {
101            // Import Package Syntax
102            include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_GRDDL);
103            $parser = new GRDDLParser();
104        }elseif ((isset($type)) && ($type =='rss')) {
105            // Import Package Syntax
106            include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RSS);
107            $parser = new RssParser();
108        }else {
109            // create a parser according to the suffix of the filename
110            // if there is no suffix assume the file to be XML/RDF
111            preg_match("/\.([a-zA-Z0-9_]+)$/", $filename, $suffix);
112            if (isset($suffix[1]) && (strtolower($suffix[1]) == 'n3' OR strtolower($suffix[1]) == 'nt')){
113                // Import Package Syntax
114                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3);
115                $parser = new N3Parser();
116            }elseif (isset($suffix[1]) && (strtolower($suffix[1]) == 'htm' OR strtolower($suffix[1]) == 'html' OR strtolower($suffix[1]) == 'xhtml')){
117                    include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_GRDDL);
118                    $parser = new GRDDLParser();
119            }else{
120                // Import Package Syntax
121                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
122                $parser = new RdfParser();
123            }
124        };
125
126        if (($stream && $type=='rdf')||($stream && $type=='n3')) {
127                $temp=&$parser->generateModel($filename,false,$this);
128        } else{
129                $temp=&$parser->generateModel($filename);
130        }
131        $this->addModel($temp);
132        if($this->getBaseURI()== null)
133            $this->setBaseURI($temp->getBaseURI());
134    }
135
136        /**
137         * This method takes a string conatining data and adds the parsed data to this model.
138         *
139         * @param string $str The string containing the data to be parsed and loaded.
140         * @param type $type The type of the string, currently only 'json' is supported.
141         */
142        function loadFromString($str, $type) {
143               
144                switch ($type) {
145                        case 'json':
146                                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_JSON);
147                                $parser = new JsonParser();
148                                break;
149                        case 'n3':
150                        case 'nt':
151                                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_N3);
152                                $parser = new N3Parser();
153                                break;
154                        case 'rdf':
155                        case 'rdfxml':
156                        case 'xml':
157                                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RDF);
158                                $parser = new RdfParser();
159                                break;
160                        case 'grddl':
161                                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_GRDDL);
162                                $parser = new GRDDLParser();
163                                break;
164                        case 'rss':
165                                include_once(RDFAPI_INCLUDE_DIR.PACKAGE_SYNTAX_RSS);
166                                $parser = new RssParser();
167                                break;
168                        default:
169                                trigger_error('(class: Model; method: loadFromString): type ' . $type . 'is currently not supported',
170                                                E_USER_ERROR);
171                }
172               
173                if (($parser instanceof JsonParser) || ($parser instanceof N3Parser)) {
174                        $parser->generateModelFromString($str, $this);
175                } else {
176                        $temp = $parser->generateModel($str, false, $this);
177                       
178                        $this->addModel($temp);
179            if($this->getBaseURI() == null) {
180                $this->setBaseURI($temp->getBaseURI());
181            }
182                }
183        }
184
185    /**
186    * Adds a statement from another model to this model.
187    * If the statement to be added contains a blankNode with an identifier
188    * already existing in this model, a new blankNode is generated.
189    *
190    * @param    Object Statement   $statement
191    * @access   private
192    */
193    function _addStatementFromAnotherModel($statement, &$blankNodes_tmp)
194    {
195        $subject = $statement->getSubject();
196        $object = $statement->getObject();
197
198        if (is_a($subject, "BlankNode")) {
199            $label = $subject->getLabel();
200            if (!array_key_exists($label, $blankNodes_tmp))
201            {
202                if ($this->findFirstMatchingStatement($subject, NULL, NULL)
203                || $this->findFirstMatchingStatement(NULL, NULL, $subject))
204                {
205                $blankNodes_tmp[$label] = new BlankNode($this);
206                $statement->subj = $blankNodes_tmp[$label];
207                } else {
208                $blankNodes_tmp[$label] = $subject;
209                }
210            } else
211                $statement->subj = $blankNodes_tmp[$label];
212        }
213
214        if (is_a($object, "BlankNode")) {
215            $label = $object->getLabel();
216            if (!array_key_exists($label, $blankNodes_tmp))
217            {
218                if ($this->findFirstMatchingStatement($object, NULL, NULL)
219                || $this->findFirstMatchingStatement(NULL, NULL, $object))
220                {
221                $blankNodes_tmp[$label] = new BlankNode($this);
222                $statement->obj = $blankNodes_tmp[$label];
223                } else {
224                $blankNodes_tmp[$label] = $object;
225                }
226            } else
227                $statement->obj = $blankNodes_tmp[$label];
228        }
229        $this->add($statement);
230    }
231
232
233
234    /**
235    * Internal method, that returns a resource URI that is unique for the Model.
236    * URIs are generated using the base_uri of the DbModel, the prefix and a unique number.
237    * If no prefix is defined, the bNode prefix, defined in constants.php, is used.
238    *
239    * @param    string  $prefix
240    * @return   string
241    * @access   private
242    */
243    function getUniqueResourceURI($prefix = false)
244    {
245        static $bNodeCount;
246        if(!$bNodeCount)
247            $bNodeCount = 0;
248
249        if(!$prefix)
250            $prefix=BNODE_PREFIX;
251
252        return $prefix.++$bNodeCount;
253    }
254
255
256
257    /**
258    * Returns a ResModel with this model as baseModel. This is the same as
259    * ModelFactory::getResModelForBaseModel($this).
260    *
261    * @return   object  ResModel
262    * @access   public
263    */
264    function & getResModel()
265    {
266                $modelFactory = new ModelFactory();
267        return $modelFactory->getResModelForBaseModel($this);
268    }
269
270
271
272    /**
273    * Returns an OntModel with this model as baseModel.
274    * $vocabulary has to be one of the following constants (currently only one is supported):
275    * RDFS_VOCABULARY to select a RDFS Vocabulary.
276    *
277    * This is the same as ModelFactory::getOntModelForBaseModel($this, $vocabulary).
278    *
279    * @param   constant  $vocabulary
280    * @return   object  OntModel
281    * @access   public
282    */
283    function & getOntModel($vocabulary)
284    {
285                $modelFactory = new ModelFactory();
286        return $modelFactory->getOntModelForBaseModel($this, $vocabulary);
287    }
288
289
290
291    /**
292    * Searches for triples using find() and tracks forward blank nodes
293    * until the final objects in the retrieved subgraphs are all named resources.
294    * The method calls itself recursivly until the result is complete.
295    * NULL input for subject, predicate or object will match anything.
296    * Inputparameters are ignored for recursivly found statements.
297    * Returns a new MemModel or adds (without checking for duplicates)
298    * the found statements to a given MemModel.
299    * Returns an empty MemModel, if nothing is found.
300    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
301    * WARNING: This method can be slow with large models.
302    * NOTE:    Blank nodes are not renamed, they keep the same nodeIDs
303    *          as in the queried model!
304    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
305    *
306    * @author   Anton Koestlbacher <anton1@koestlbacher.de>
307    * @param    object Node     $subject
308    * @param    object Node     $predicate
309    * @param    object Node     $object
310    * @param    object MemModel $object
311    * @return   object MemModel
312    * @access   public
313    * @throws   PhpError
314    */
315    function findForward($subject, $predicate, $object, &$newModel = NULL)
316    {
317        if (!is_a($newModel, "MemModel"))
318        {
319            $newModel = New MemModel;
320        }
321
322        if (is_a($this, "DbModel"))
323        {
324            $model = $this;
325            $res   = $model->find($subject, $predicate, $object);
326            $it    = $res->getStatementIterator();
327        }
328        elseif (is_a($this, "MemModel")) {
329            $model = $this;
330            $it    = $model->findAsIterator($subject, $predicate, $object);
331        }
332        elseif (is_a($this, "ResModel")) {
333            $model = $this->model;
334            $it    = $model->findAsIterator($subject, $predicate, $object);
335        }
336
337        while ($it->hasNext())
338        {
339            $statement = $it->next();
340            $newModel->add($statement);
341            if (is_a($statement->object(),'BlankNode'))
342            {
343                $model->findForward($statement->object(), NULL, NULL, $newModel);
344            }
345        }
346        return $newModel;
347    }
348
349
350
351    /**
352    * Perform an RDQL query on this Model. Should work with all types of models.
353    * This method returns a MemModel containing the result statements.
354    * If $closure is set to TRUE, the result will additionally contain
355    * statements found by the findForward-method for blank nodes.
356    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
357    * WARNING: If called with $closure = TRUE this method
358    *          can be slow with large models.
359    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
360    *
361    * @author   Anton Kᅵtlbacher <anton1@koestlbacher.de>
362    * @author   code snippets taken from the RAP Netapi by Phil Dawes and Chris Bizer
363    * @access   public
364    * @param    string $queryString
365    * @param    boolean $closure
366    * @return   object MemModel
367    *
368    */
369    function & getMemModelByRDQL($queryString, $closure = FALSE)
370    {
371        require_once(RDFAPI_INCLUDE_DIR.PACKAGE_RDQL);
372        $parser = new RdqlParser();
373        $parsedQuery =& $parser->parseQuery($queryString);
374
375        // If there are variables used in the pattern but not
376        // in the select clause, add them to the select clause
377        foreach ($parsedQuery['patterns'] as $n => $pattern)
378        {
379            foreach ($pattern as $key => $val_1)
380            {
381                if ($val_1['value']{0}=='?')
382                {
383                    if (!in_array($val_1['value'],$parsedQuery['selectVars']))
384                    {
385                    array_push($parsedQuery['selectVars'],$val_1['value']);
386                    }
387                }
388            }
389        }
390
391        if (is_a($this, "DbModel"))
392        {
393            $engine = new RdqlDbEngine();
394            $model  = $this;
395        }
396        elseif (is_a($this, "MemModel"))
397        {
398            $engine = new RdqlMemEngine();
399            $model  = $this;
400        }
401        elseif (is_a($this, "ResModel"))
402        {
403            $engine = new RdqlMemEngine();
404            $model  = $this->model;
405        }
406
407        $res = $engine->queryModel($model,$parsedQuery,TRUE);
408        $rdqlIter = new RdqlResultIterator($res);
409        $newModel = new MemModel();
410
411        // Build statements from RdqlResultIterator
412        while ($rdqlIter->hasNext()) {
413            $result = $rdqlIter->next();
414            foreach ($parsedQuery['patterns'] as $n => $pattern)
415            {
416                if (substr($pattern['subject']['value'], 0, 1) == '?')
417                {
418                    $subj = $result[$pattern['subject']['value']];
419                }
420                else
421                {
422                    $subj = new Resource($pattern['subject']['value']);
423                }
424                if (substr($pattern['predicate']['value'], 0, 1) == '?')
425                {
426                    $pred = $result[$pattern['predicate']['value']];
427                }
428                else
429                {
430                    $pred = new Resource($pattern['predicate']['value']);
431                }
432
433                if (substr($pattern['object']['value'], 0, 1) == '?')
434                {
435                    $obj = $result[$pattern['object']['value']];
436                }
437                else
438                {
439                    if (isset($pattern['object']['is_literal']))
440                    {
441                        $obj = new Literal($pattern['object']['value']);
442                        $obj->setDatatype($pattern['object']['l_dtype']);
443                        $obj->setLanguage($pattern['object']['l_lang']);
444                    }
445                    else
446                    {
447                        $obj = new Resource($pattern['object']['value']);
448                    }
449                }
450
451                $statement = new Statement($subj,$pred,$obj);
452                $newModel->add($statement);
453
454                // findForward() Statements containing an eventually given blank node
455                // and add them to the result, if closure = true
456                if (is_a($statement->object(),'BlankNode') && $closure == True)
457                {
458                    $newModel = $model->findForward($statement->object(),NULL,NULL, $newModel);
459                }
460                if (is_a($statement->subject(),'BlankNode') && $closure == True)
461                {
462                    $newModel = $model->findForward($statement->subject(),NULL,NULL, $newModel);
463                }
464            }
465        }
466        return $newModel;
467    }
468
469
470
471    /**
472    * Alias for RDFUtil::visualiseGraph(&$model, $format, $short_prefix)
473    *
474    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
475    * Note: See RDFUtil for further Information.
476    * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
477    *
478    * @author   Anton Kᅵtlbacher <anton1@koestlbacher.de>
479    * @param    string  $format
480    * @param    boolean $short_prefix
481    * @return   string, binary
482    * @access   public
483    * @throws   PhpError
484    */
485    function visualize($format = "dot", $short_prefix = TRUE)
486    {
487                $rdfUtil = new RDFUtil();
488        return $rdfUtil->visualizeGraph($this, $format, $short_prefix);
489    }
490
491
492    /**
493    * Performs a SPARQL query against a model. The model is converted to
494    * an RDF Dataset. The result can be retrived in SPARQL Query Results XML Format or
495    * as an array containing the variables an their bindings.
496    *
497    * @param  string $query      the sparql query string
498    * @param  string $resultform the result form ('xml' for SPARQL Query Results XML Format)
499    * @return string/array
500    */
501    function sparqlQuery($query, $resultform = false)
502    {
503        list($engine, $dataset) = $this->_prepareSparql();
504        return $engine->queryModel(
505            $dataset,
506            $this->_parseSparqlQuery($query),
507            $resultform
508        );
509    }//function sparqlQuery($query,$resultform = false)
510
511
512
513    /**
514    *   Prepares a sparql query and returns a prepared statement
515    *   that can be executed with data later on.
516    *
517    *   @param string $query Sparql query to prepare.
518    *   @return SparqlEngine_PreparedStatement  prepared statement object
519    */
520    function sparqlPrepare($query)
521    {
522        list($engine, $dataset) = $this->_prepareSparql();
523        return $engine->prepare(
524            $dataset,
525            $this->_parseSparqlQuery($query)
526         );
527    }//function sparqlPrepare($query)
528
529
530
531    /**
532    *   Prepares everything for SparqlEngine-usage
533    *   Loads the files, creates instances for SparqlEngine and
534    *   Dataset...
535    *
536    *   @return array First value is the sparql engine, second the dataset
537    */
538    function _prepareSparql()
539    {
540        require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngine.php';
541        require_once RDFAPI_INCLUDE_DIR . 'dataset/DatasetMem.php';
542
543        $dataset = new DatasetMem();
544        $dataset->setDefaultGraph($this);
545
546                $sparqlEngine = SparqlEngine::getInstance();
547               
548        return array(
549            $sparqlEngine->factory($this),
550            $dataset
551        );
552    }//function _prepareSparql()
553
554
555
556    /**
557    *   Parses an query and returns the parsed form.
558    *   If the query is not a string but a Query object,
559    *   it will just be returned.
560    *
561    *   @param $query mixed String or Query object
562    *   @return Query query object
563    *   @throws Exception If $query is no string and no Query object
564    */
565    function _parseSparqlQuery($query)
566    {
567        if ($this->queryParser === null) {
568            require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlParser.php';
569            $this->queryParser = new SparqlParser();
570        }
571        return $this->queryParser->parse($query);
572    }//function _parseSparqlQuery($query)
573
574} // end: Model
575
576?>
Note: See TracBrowser for help on using the repository browser.