source: Dev/branches/rest-dojo-ui/server/rdfapi/sparql/SparqlEngine.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: 41.3 KB
Line 
1<?php
2require_once RDFAPI_INCLUDE_DIR . 'util/Object.php';
3require_once RDFAPI_INCLUDE_DIR . 'sparql/FilterFunctions.php';
4require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngine/ResultConverter.php';
5
6// ----------------------------------------------------------------------------------
7// Class: SparqlEngine
8// ----------------------------------------------------------------------------------
9
10/**
11* This engine executes SPARQL queries against an RDF Datatset.
12*
13* @version  $Id$
14* @author   Tobias Gauß <tobias.gauss@web.de>
15* @license http://www.gnu.org/licenses/lgpl.html LGPL
16*
17* @package sparql
18*/
19
20Class SparqlEngine extends Object{
21
22
23    /**
24    *   The query object.
25    *   @var Query
26    */
27    protected $query;
28
29    /**
30    *   The RDF Dataset.
31    *   @var Dataset
32    */
33    protected $dataset;
34
35        // Store the single instance of Database
36    protected static $instance;
37
38
39    /**
40    * Use SparqlEngine::factory() instead of this
41    * constructor.
42    */
43    protected function __construct()
44    {
45        //protected to prevent direct instantiation
46    }
47
48        public static function getInstance()
49    {
50        if (!self::$instance)
51        {
52            self::$instance = new SparqlEngine();
53        }
54
55        return self::$instance;
56    }
57
58    /**
59    * Creates a new instance of the SparqlEngine, depending on the
60    * given model. For example, if you pass a DbModel, you will
61    * get a SparqlEngine specialized on databases.
62    *
63    * @param Model $model   RDF model that uses the engine
64    */
65    public function factory($model = null)
66    {
67        if ($model !== null && $model instanceof DbModel) {
68            require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb.php';
69            return new SparqlEngineDb($model);
70        } else {
71            return new SparqlEngine();
72        }
73    }
74
75
76
77    /**
78    * The query engine's main method.
79    *
80    * @param  Dataset       $dataset    the RDF Dataset
81    * @param  mixed         $query      the parsed SPARQL query
82    * @param  String        $resultform the result form. If set to 'xml' the result will be
83    *                                   SPARQL Query Results XML Format as described in http://www.w3.org/TR/rdf-sparql-XMLres/ .
84    * @return Array/String  Type of the result depends on $resultform.
85    */
86    public function queryModel($dataset, Query $query, $resultform = false)
87    {
88        $this->query   = $query;
89        $this->dataset = $dataset;
90
91        if($this->query->isEmpty){
92            $vartable[0]['patternResult'] = null;
93            return SparqlEngine_ResultConverter::convertFromResult(
94                $vartable,
95                $this,
96                $resultform
97            );
98        }
99
100        $graphlist = $this->preselectGraphs();
101        /// match graph patterns against the RDF Dataset
102        $patternlist = $this->matchPatterns($graphlist);
103        // filter results- apply inner filters
104        $patternlist = $this->filterPatterns($patternlist,false);
105        // join pattern results
106        $vartable = $this->joinResults($patternlist);
107        // filter results- apply outer filters
108        $vartable = $this->filterPatterns($vartable,true);
109
110        if ($vartable[0]['patternResult'] != null) {
111            // sort vars (ORDER BY, LIMIT, OFFSET)
112            $vartable = $this->sortVars($vartable[0]['patternResult']);
113            $qrf      = $this->query->getResultForm();
114            if ($qrf == 'select' || $qrf == 'select distinct') {
115                $vars     = $this->query->getResultVars();
116                $vartable = $this->selectVars($vartable, $vars);
117                if ($qrf == 'select distinct') {
118                    $vartable = $this->distinct($vartable);
119                }
120            }
121        } else {
122            $vartable = null;
123        }
124
125        return SparqlEngine_ResultConverter::convertFromResult(
126            $vartable,
127            $this,
128            $resultform
129        );
130    }//public function queryModel($dataset, Query $query, $resultform = false)
131
132
133
134    /**
135    * Matches all graph Patterns against the dataset and generates an array which
136    * contains the result sets for every given GraphPattern.
137    *
138    * @param  Array      $graphlist   the graphlist which contains the names of the named
139    *                    graphs which has to be queried.
140    * @return Array
141    */
142    protected function matchPatterns($graphlist){
143        $patternlist = array();
144        // get the result part from the query
145        $resultPart = $this->query->getResultPart();
146        // for each GrapPattern in the result part
147        if($resultPart)
148        foreach($resultPart as $graphPattern){
149            $this->matchPattern($patternlist, $graphlist, $graphPattern);
150        }
151        return $patternlist;
152    }
153
154
155
156    /**
157    * Finds tuples that match one graph pattern.
158    *
159    * @param  Array        $patternlist list that contains the graphPatterns
160    * @param  array        $graphlist   the graphlist
161    * @param  GraphPattern $graphPattern the pattern which has to be matched
162    * @return void
163    */
164    protected function matchPattern(&$patternlist, $graphlist, &$graphPattern) {
165        // generate an empty result set
166        $finalRes = null;
167        // if the GraphPattern has triple patterns
168        if (count($graphPattern->getTriplePatterns()) > 0) {
169            // check if the pattern has a GRAPH clause and if this Iri is in $graphlist
170            $newGraphList = $this->_checkGraphs($graphPattern,$graphlist);
171            if($newGraphList){
172                $qt = $graphPattern->getTriplePatterns();
173                $resultSet = $this->findTuplesMatchingOnePattern($qt[0], $newGraphList);
174                for ($i=1; $i<count($qt); $i++) {
175                    $rs = $this->findTuplesMatchingOnePattern($qt[$i], $newGraphList);
176                    $resultSet = $this->joinTuples($resultSet, $rs);
177                    if(!$resultSet)
178                    break;
179                }
180                if($finalRes != null){
181                    $finalRes = $this->joinTuples($finalRes,$resultSet);
182                }else{
183                    $finalRes = $resultSet;
184                }
185            }
186        }
187        // dependencies between pattern results
188        $patternlist[$graphPattern->getId()]['hasOptional']     = 0;
189        $patternlist[$graphPattern->getId()]['hasUnion']        = 0;
190        $patternlist[$graphPattern->getId()]['patternResult']   = $finalRes;
191
192        $op = $graphPattern->getOptional();
193        $un = $graphPattern->getUnion();
194
195        $patternlist[$graphPattern->getId()]['optionalTo']      = $op;
196        if(is_int($op))
197        $patternlist[$op]['hasOptional']++;
198
199        $patternlist[$graphPattern->getId()]['unionWith']       = $un;
200        if(is_int($un))
201        $patternlist[$un]['hasUnion']++;
202
203        $constraint = $graphPattern->getConstraints();
204        if(count($constraint) > 0){
205            foreach($constraint as $constr){
206                if($constr->isOuterFilter()){
207                    $patternlist[$graphPattern->getId()]['outerFilter'][]          = $constr;
208                    $patternlist[$graphPattern->getId()]['innerFilter'][]          = null;
209                }else{
210                    $patternlist[$graphPattern->getId()]['innerFilter'][]          = $constr;
211                    $patternlist[$graphPattern->getId()]['outerFilter'][]          = null;
212                }
213            }
214        }else{
215            $patternlist[$graphPattern->getId()]['innerFilter']          = null;
216            $patternlist[$graphPattern->getId()]['outerFilter']          = null;
217        }
218    }
219
220
221    /**
222    * Finds Tuples matching one TriplePattern.
223    *
224    * @param  TriplePattern $pattern
225    * @param  Array         $graphlist
226    * @return Array
227    */
228    protected function findTuplesMatchingOnePattern($pattern, $graphlist){
229        $var = null;
230        $sub  = $pattern->getSubject();
231        $pred = $pattern->getPredicate();
232        $obj  = $pattern->getObject();
233
234        if(is_string($sub)||$sub instanceof BlankNode){
235            if(is_string($sub))
236            $var['sub'] = $sub;
237            $sub = null;
238        }
239        if(is_string($pred)||$pred instanceof BlankNode ){
240            if(is_string($pred))
241            $var['pred'] = $pred;
242            $pred = null;
243        }
244        if(is_string($obj)||$obj instanceof BlankNode){
245            if(is_string($obj))
246            $var['obj'] = $obj;
247            $obj = null;
248        }
249        $intBindings = $this->_buildIntBindings($var);
250        $k = 0;
251
252        $key = 0;
253        // search in named graphs
254        if($graphlist['var'][0] != null||$graphlist['list'][0] != null){
255            foreach($graphlist['list'] as $key => $graphnode){
256
257                // query the dataset
258                $it = $this->dataset->findInNamedGraphs($graphnode,$sub,$pred,$obj,false);
259                if($it->valid()){
260                    // add statements to the result list
261                    while($it->valid()){
262                        if($graphnode == null){
263                            $element = $it->current()->getStatement();
264                            $grname  = $it->current()->getGraphname();
265                        }else{
266                            if($it->current() instanceof Quad)
267                                $element = $it->current()->getStatement();
268                            else
269                                $element = $it->current();
270
271                            $grname  = $graphnode;
272                        }
273                        if($this->checkIntBindings($element,$intBindings)){
274                            $resmodel['trip'][$k]  = $element;
275                            $resmodel['graph'][$k] = $grname;
276                        //    $resmodel['graphvar'][$k] = $graphlist['var'][$key];
277                            $resmodel['graphvar'][$k] = $graphlist['var'][0];
278                            $k++;
279
280                        }
281                        $it->next();
282                    }
283                }
284
285            }
286        }
287        // search in the default graph
288        if($graphlist['list'][0] == null && $graphlist['var'][0] == null){
289
290
291            $gr = $this->dataset->getDefaultGraph();
292
293            $res = $gr->find($sub,$pred,$obj);
294
295            foreach($res->triples as $innerkey => $element){
296                if($this->checkIntBindings($element,$intBindings)){
297                        $resmodel['trip'][$k]  = $element;
298                        $resmodel['graph'][$k] = null;
299                        $resmodel['graphvar'][$k] = $graphlist['var'][$key];
300                        $k++;
301                    }
302            }
303        }
304        if($k == 0)
305        return false;
306        return $this->_buildResultSet($pattern,$resmodel);
307    }
308
309    /**
310    * Checks it there are internal bindings between variables.
311    *
312    * @param  Triple  $trip
313    * @param  Array   $intBindings
314    * @return boolean
315    */
316    protected function checkIntBindings($trip, $intBindings){
317        switch($intBindings){
318            case -1:
319            return true;
320            break;
321            case 0:
322            if($trip->subj != $trip->pred)
323            return false;
324            break;
325            case 1:
326            if(is_a($trip->obj,'Literal'))
327            return false;
328            if($trip->subj != $trip->obj)
329            return false;
330            break;
331            case 2:
332            if(is_a($trip->obj,'Literal'))
333            return false;
334            if($trip->pred != $trip->obj)
335            return false;
336            break;
337            case 3:
338            if(is_a($trip->obj,'Literal'))
339            return false;
340            if($trip->pred != $trip->obj || $trip->pred != $trip->subj )
341            return false;
342            break;
343        }
344        return true;
345    }
346
347
348    /**
349    * Perform an SQL-like inner join on two resultSets.
350    *
351    * @param   Array   &$finalRes
352    * @param   Array   &$res
353    * @return  Array
354    */
355    protected function joinTuples(&$finalRes, &$res) {
356
357        if (!$finalRes || !$res)
358        return array();
359
360        // find joint variables and new variables to be added to $finalRes
361        $jointVars = array();
362        $newVars = array();
363        $k = key($res);
364
365        foreach ($res[$k] as $varname => $node) {
366            if (array_key_exists($varname, $finalRes[0]))
367            $jointVars[] = $varname;
368            else
369            $newVars[] = $varname;
370        }
371
372        // eliminate rows of $finalRes in which the values of $jointVars do not have
373        // a corresponding row in $res.
374        foreach ($finalRes as $n => $fRes) {
375            foreach ($res as $i => $r) {
376                $ok = TRUE;
377                foreach ($jointVars as $j_varname)
378                if ($r[$j_varname] != $fRes[$j_varname]) {
379                    $ok = FALSE;
380                    break;
381                }
382                if ($ok)
383                break;
384            }
385            if (!$ok)
386            unset($finalRes[$n]);
387        }
388
389        // join $res and $finalRes
390        $joinedRes = array();
391        foreach ($res as $r) {
392            foreach ($finalRes as $n => $fRes) {
393                $ok = TRUE;
394                foreach ($jointVars as $j_varname)
395                if ($r[$j_varname] != $fRes[$j_varname]) {
396                    $ok = FALSE;
397                    break;
398                }
399                if ($ok) {
400                    $joinedRow = $finalRes[$n];
401                    foreach($newVars as $n_varname)
402                    $joinedRow[$n_varname] = $r[$n_varname];
403                    $joinedRes[] = $joinedRow;
404                }
405            }
406        }
407        return $joinedRes;
408    }
409
410
411    /**
412    * Joins OPTIONAL pattern results.
413    *
414    * @param   Array   &$finalRes
415    * @param   Array   &$res
416    * @return  Array    the joined Array
417    */
418    protected function joinOptionalTuples(&$finalRes, &$res) {
419
420        if(!$finalRes && !$res)
421        return array();
422
423        if(!$finalRes)
424        return $res;
425
426        if(!$res)
427        return $finalRes;
428
429        // find joint variables and new variables to be added to $finalRes
430        $jointVars = array();
431        $newVars = array();
432        $result = array();
433
434        $k = key($res);
435
436        foreach ($res[$k] as $varname => $node) {
437            if (array_key_exists($varname, $finalRes[0])){
438                $jointVars[] = $varname;
439            }else{
440                $newVars[] = $varname;
441            }
442        }
443        $joined = array();
444        $joinc = 0;
445        foreach($finalRes as $i =>$fRes){
446            foreach($res as $n =>$r){
447                $join = false;
448                foreach($jointVars as $j_varname){
449                    if($r[$j_varname]==$fRes[$j_varname]){
450                        $join = true;
451                        //break;
452                    }else{
453                        $join = false;
454                    }
455                }
456                if($join){
457                    $result[$joinc] = $fRes;
458                    foreach($newVars as $n_varname)
459                    $result[$joinc][$n_varname] = $r[$n_varname];
460                    $joined[]=$n;
461                    $joinc++;
462                }
463
464            }
465        }
466
467        $count = count($result);
468        foreach($res as $k =>$val){
469            if(!in_array($k,$joined)){
470                $result[$count] = $finalRes[0];
471                foreach($result[$count] as $varname => $varVal){
472                    $result[$count][$varname]='';
473                }
474
475                foreach($val as $varname2 => $varVal2){
476                    $result[$count][$varname2]=$varVal2;
477                }
478                $count++;
479            }
480        }
481        return $result;
482    }
483
484
485
486    /**
487    * Looks in from and from named part of the query and
488    * adds the graphs to the graphlist.
489    *
490    * @return Array
491    */
492    protected function preselectGraphs(){
493        $fromNamed = $this->query->getFromNamedPart();
494        if($fromNamed == null)
495        $fromNamed[] = null;
496        return $fromNamed;
497    }
498
499
500    /**
501    * Evaluates the GRPAH clause if there is one. Checks if
502    * the GRAPH clause contains an IRI, variable or nothing.
503    * Returns an array which contains the graphs that has to be matched.
504    *
505    * @param  GraphPattern $pattern
506    * @param  Array        $graphlist
507    * @return Array
508    */
509    protected function _checkGraphs(&$pattern,$graphlist){
510
511        $gr = $pattern->getGraphname();
512        if($gr instanceof Resource ){
513            if($graphlist[0]==null || in_array($gr,$graphlist)){
514                $newGraphList['list'][] = $gr;
515                $newGraphList['var'][]  = null;
516            }else{
517                return false;
518            }
519        }elseif (is_string($gr)){
520            $newGraphList['list'] = $graphlist;
521            $newGraphList['var'][]  = $gr;
522        }else{
523            $newGraphList['list'] = $graphlist;
524            $newGraphList['var'][]  = null;
525        }
526        return $newGraphList;
527    }
528
529    /**
530    * Marks triples with internal bindings.
531    * int bindings -1 :none 0:sub=pred 1:sub=obj 2:pred=obj 3:sub=pred=obj.
532    *
533    * @param  Array $var
534    * @return Array
535    */
536    protected function _buildIntBindings($var){
537        $intBindings = -1;
538        if(!$var)
539        return $intBindings;
540
541        if(isset($var['sub'])){
542            if(isset($var['pred']))
543            if($var['sub'] == $var['pred'])
544            $intBindings = 0;
545            if(isset($var['obj']))
546            if($var['sub'] == $var['obj']){
547                if( $intBindings == 0){
548                    $intBindings = 3;
549                }else{
550                    $intBindings = 1;
551                }
552            }
553        }
554        if(isset($var['pred'])){
555            if(isset($var['obj']))
556            if($var['pred']==$var['obj']&&$intBindings!=3)
557            $intBindings = 2;
558        }
559        return $intBindings;
560    }
561
562    /**
563    * Builds the resultset.
564    *
565    * @param  GraphPattern $pattern
566    * @param  Array        $resmodel
567    * @return Array
568    */
569    protected function _buildResultSet($pattern,$resmodel){
570        // determine variables and their corresponding values
571        $result = null;
572        if(is_string($pattern->getSubject())){
573            $n = 0;
574            foreach($resmodel['trip'] as $key => $triple){
575                if(isset($resmodel['graphvar'][$key]))
576                $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
577                $result[$n++][$pattern->getSubject()] = $triple->subj;
578            }
579        }
580        if(is_string($pattern->getPredicate())){
581            $n = 0;
582            foreach($resmodel['trip'] as $key => $triple){
583                if(isset($resmodel['graphvar'][$key]))
584                $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
585                $result[$n++][$pattern->getPredicate()] = $triple->pred;
586            }
587        }
588        if(is_string($pattern->getObject())){
589            $n = 0;
590            foreach($resmodel['trip'] as $key => $triple){
591                if(isset($resmodel['graphvar'][$key]))
592                $result[$n][$resmodel['graphvar'][$key]] = $resmodel['graph'][$key];
593                $result[$n++][$pattern->getObject()] = $triple->obj;
594            }
595        }
596        return $result;
597    }
598
599    /**
600    * Selects the result variables and builds a result table.
601    *
602    * @param  Array  $table the result table
603    * @param  Array  $vars the result variables
604    * @return Array
605    */
606    protected function selectVars($table,$vars){
607        if($vars[0]=='*')
608        $vars = $this->query->getAllVars();
609        $resTable = array();
610        $hits = 0;
611        foreach($table as $val){
612            foreach($vars as $var){
613                if(isset($val[(string)$var])){
614                    $resTable[$hits][(string)$var]=$val[(string)$var];
615                }else{
616                    $resTable[$hits][(string)$var]="";
617                }
618            }
619            $hits++;
620        }
621        return $resTable;
622    }
623
624    /**
625    * Joins the results of the different Graphpatterns.
626    *
627    * @param  Array $patternlist
628    * @return Array
629    */
630    protected function joinResults($patternlist){
631        $joined[0]['patternResult'] = null;
632        $joined[0]['outerFilter'] = null;
633
634        while(count($patternlist)>0){
635            foreach($patternlist as $key => $pattern){
636                if($pattern['hasOptional'] == 0 && $pattern['hasUnion'] == 0){
637                    if(is_int($pattern['optionalTo'])){
638                        $patternlist[$pattern['optionalTo']]['hasOptional']--;
639                        $patternlist[$pattern['optionalTo']]['patternResult'] = $this->joinOptionalTuples($pattern['patternResult'],$patternlist[$pattern['optionalTo']]['patternResult']);
640                        unset($patternlist[$key]);
641                        break;
642                    }
643                    else if(is_int($pattern['unionWith'])){
644                        $patternlist[$pattern['unionWith']]['hasUnion']--;
645                        foreach($pattern['patternResult'] as $value)
646                        array_push($patternlist[$pattern['unionWith']]['patternResult'],$value);
647                        unset($patternlist[$key]);
648                        break;
649                    }else{
650                        if($joined[0]['patternResult'] == null){
651                            $joined[0]['patternResult'] = $pattern['patternResult'];
652                            if($joined[0]['outerFilter'] == null )
653                            $joined[0]['outerFilter']  = $pattern['outerFilter'];
654                            unset($patternlist[$key]);
655                            break;
656                        }
657                    //    if($pattern['patternResult'] !=null ){
658                            $joined[0]['patternResult'] = $this->joinTuples($joined[0]['patternResult'],$pattern['patternResult']);
659                            $joined[0]['outerFilter']   = $pattern['outerFilter'];
660                            unset($patternlist[$key]);
661                            break;
662                    //    }
663                    }
664                }
665            }
666        }
667        return $joined;
668    }
669
670    /**
671    * Filters the pattern results.
672    *
673    * @param  Array   $patternlist list containing the results of the GraphPatterns
674    * @param  boolean $outer TRUE if its an outer filter FALSE if not
675    * @return Array   the filtered patternlist
676    */
677    protected function filterPatterns($patternlist,$outer){
678        if($outer)
679        $filter = 'outerFilter';
680        else
681        $filter = 'innerFilter';
682        foreach($patternlist as $patkey => $pattern){
683            // get constraints
684            $constraint = $pattern[$filter];
685
686            if(count($constraint)>0){
687                foreach($constraint as $constr){
688                    if($constr != null){
689                        // extract Vars and function calls
690                        $evalString = $constr->getExpression();
691                        preg_match_all("/\?.[^\s\)\,]*/",$evalString,$vars);
692                        preg_match_all("/bound\((.[^\)]*)\)/i",$evalString,$boundcalls);
693                        preg_match_all("/isuri\((.[^\)]*)\)/i",$evalString,$isUricalls);
694                        preg_match_all("/isblank\((.[^\)]*)\)/i",$evalString,$isBlankcalls);
695                        preg_match_all("/isLiteral\((.[^\)]*)\)/i",$evalString,$isLiteralcalls);
696                        preg_match_all("/lang\((.[^\)]*)\)/i",$evalString,$langcalls);
697                        preg_match_all("/datatype\((.[^\)]*)\)/i",$evalString,$datatypecalls);
698                        preg_match_all("/str\((.[^\)]*)\)/i",$evalString,$stringcalls);
699
700                        // is Bound
701                        if(count($boundcalls[1])>0)
702                        $function['bound'] = $boundcalls[1];
703                        else
704                        $function['bound'] = false;
705
706                        // is URI
707                        if(count($isUricalls[1])>0)
708                        $function['isUri'] = $isUricalls[1];
709                        else
710                        $function['isUri'] = false;
711
712                        // is Blank
713                        if(count($isBlankcalls[1])>0)
714                        $function['isBlank'] = $isBlankcalls[1];
715                        else
716                        $function['isBlank'] = false;
717
718                        // is Literal
719                        if(count($isLiteralcalls[1])>0)
720                        $function['isLiteral'] = $isLiteralcalls[1];
721                        else
722                        $function['isLiteral'] = false;
723
724                        // lang
725                        if(count($langcalls[1])>0)
726                        $function['lang'] = $langcalls[1];
727                        else
728                        $function['lang'] = false;
729
730                        // datatype
731                        if(count($datatypecalls[1])>0)
732                        $function['datatype'] = $datatypecalls[1];
733                        else
734                        $function['datatype'] = false;
735
736                        // string
737                        if(count($stringcalls[1])>0)
738                        $function['string'] = $stringcalls[1];
739                        else
740                        $function['string'] = false;
741
742
743                        foreach($pattern['patternResult'] as $key => $res){
744                            $result = false;
745                            $evalString = $this->fillConstraintString($vars,$res,$constr,$function);
746                            $evalString = '$result =('.$evalString.');';
747                            // evaluate Constraint
748                            @eval($evalString);
749
750                            if(!$result)
751                            unset($patternlist[$patkey]['patternResult'][$key]);
752
753                        }
754                    }
755                }
756            }
757        }
758        return $patternlist;
759    }
760
761    /**
762    * Builds an evaluation string to determine wether the result passes
763    * the filter or not. This string is evaluatet by the php buildin eval() function
764    *
765    * @param  Array      $vars a list which contains the used variables
766    * @param  Array      $res  the result part which have to be evaluated
767    * @param  Constraint $constraint the Constrain object
768    * @param  Array      $function an Array which contains the used functions
769    * @return String
770    */
771
772    protected function fillConstraintString($vars,$res,$constraint,$function){
773
774        $boundExpr = false;
775        $evalString = $constraint->getExpression();
776
777        // extract Literals
778        $pattern1 = "/\".[^\"]*\"[^\^\@]/";
779        $pattern2 = "/\'.[^\']*\'[^\^\@]/";
780        preg_match_all($pattern1,$evalString,$hits1);
781        preg_match_all($pattern2,$evalString,$hits2);
782
783        foreach($hits1[0] as $k => $val){
784            $evalString = preg_replace('/\".[^\"]*\"[^\^]/','_REPLACED1_'.$k++,$evalString,1);
785        }
786        foreach($hits2[0] as $k => $val){
787            $evalString = preg_replace('/\".[^\"]*\"[^\^]/','_REPLACED2_'.$k++,$evalString,1);
788        }
789
790        // replace namespaces
791        $prefs = $this->query->getPrefixes();
792        foreach($prefs as $key => $val){
793            if($key == '')
794            $key = ' ';
795            $evalString = preg_replace("/^(".$key."\:)(.[^\s]*)|([\s\(]?[^\^])(".$key."\:)(.[^\s\)]*)([\s\)]?)/","$3'<".$val."$2$5>'$6",$evalString);
796
797            $evalString = preg_replace("/(\^)(".$key."\:)(.[^\s]*)/","$1<".$val."$3>",$evalString);
798        }
799
800        $xsd = "http\:\/\/www.w3.org\/2001\/XMLSchema\#";
801
802        // evaluate bound calls
803        if($function['bound']){
804            $boundExpr = true;
805            foreach($function['bound'] as $var){
806                if(isset($res[$var]) && $res[$var]!=="")
807                $replacement = 'true';
808                else
809                $replacement = 'false';
810                $evalString = preg_replace("/bound\(\\".$var."\)/i",$replacement,$evalString);
811            }
812
813        }
814        // evaluate isBlank calls
815        if($function['isBlank']){
816            foreach($function['isBlank'] as $var){
817                if(isset($res[$var]) && $res[$var]!=="" && $res[$var] instanceof BlankNode )
818                $replacement = 'true';
819                else
820                $replacement = 'false';
821                $evalString = preg_replace("/isBlank\(\\".$var."\)/i",$replacement,$evalString);
822            }
823
824        }
825        // evaluate isLiteral calls
826        if($function['isLiteral']){
827            foreach($function['isLiteral'] as $var){
828                if(isset($res[$var]) && $res[$var]!=="" && $res[$var] instanceof Literal  )
829                $replacement = 'true';
830                else
831                $replacement = 'false';
832                $evalString = preg_replace("/isLiteral\(\\".$var."\)/i",$replacement,$evalString);
833            }
834
835        }
836        // evaluate isUri calls
837        if($function['isUri']){
838            foreach($function['isUri'] as $var){
839                if(isset($res[$var]) && $res[$var]!=="" && $res[$var] instanceof Resource && $res[$var]->getUri() && !$res[$var] instanceof BlankNode )
840                $replacement = 'true';
841                else
842                $replacement = 'false';
843                $evalString = preg_replace("/isUri\(\\".$var."\)/i",$replacement,$evalString);
844            }
845        }
846        // evaluate lang calls
847        if($function['lang']){
848            foreach($function['lang'] as $var){
849                if(isset($res[$var]) && $res[$var]!=="" && $res[$var] instanceof Literal && $res[$var]->getLanguage() )
850                $replacement = '"'.$res[$var]->getLanguage().'"';
851                else
852                $replacement = 'null';
853                $evalString = preg_replace("/lang\(\\".$var."\)/i",$replacement,$evalString);
854            }
855        }
856        // evaluate datatype calls
857        if($function['datatype']){
858            foreach($function['datatype'] as $var){
859                if(isset($res[$var]) && $res[$var]!=="" && $res[$var] instanceof Literal && $res[$var]->getDatatype() )
860                $replacement = '\'<'.$res[$var]->getDatatype().'>\'';
861                else
862                $replacement = 'false';
863                $evalString = preg_replace("/datatype\(\\".$var."\)/i",$replacement,$evalString);
864            }
865        }
866        // evaluate string calls
867        if($function['string']){
868            foreach($function['string'] as $var){
869                if($var{0}=='?' || $var{0}=='$'){
870                    if(isset($res[$var]) && $res[$var]!==""){
871                        $replacement = "'str_".$res[$var]->getLabel()."'";
872                        if($res[$var] instanceof BlankNode)
873                        $replacement = "''";
874                    }else{
875                        $replacement = 'false';
876                    }
877                    $evalString = preg_replace("/str\(\\".$var."\)/i",$replacement,$evalString);
878                }else{
879                    if($var{0}=='<'){
880                        $evalString = preg_replace("/str\(\s*\<(.[^\>]*)\>\s*\)/i","'str_$1'",$evalString);
881                    }
882                    if($var{0}=='"'){
883                        $evalString = preg_replace("/str\(\s*\"(.[^\>]*)\"\@[a-z]*\s*\)/i","'str_$1'",$evalString);
884                    }
885                }
886
887            }
888        }
889        // evaluate VARS
890        foreach($vars[0] as $var){
891            if(isset($res[$var])&&$res[$var]!== ""){
892                //$replacement = "'".$res[$var]->getLabel()."'";
893                $replacement = '" "';
894                if($res[$var] instanceof Literal){
895                    if($res[$var]->getDatatype()!= null){
896                        if($res[$var]->getDatatype() == XML_SCHEMA.'boolean')
897                        $replacement = $res[$var]->getLabel();
898                        if($res[$var]->getDatatype() == XML_SCHEMA.'double')
899                        $replacement = $res[$var]->getLabel();
900                        if($res[$var]->getDatatype() == XML_SCHEMA.'integer')
901                        $replacement = $res[$var]->getLabel();
902                        if($res[$var]->getDatatype() == XML_SCHEMA.'dateTime')
903                        $replacement = strtotime($res[$var]->getLabel());
904                    }else{
905                        if($res[$var]->getLabel()=="")
906                        $replacement = 'false';
907                        else
908                        $replacement = "'str_".$res[$var]->getLabel()."'";
909                    }
910                }else{
911                    if($res[$var] instanceof Resource){
912                        $replacement = "'<".$res[$var]->getLabel().">'";
913                    }
914                }
915                $evalString = preg_replace("/\\".$var."/",$replacement,$evalString);
916            }
917
918            // problem with PHP: false < 13 is true
919            if(isset($res[$var])){
920                if($res[$var] === ""){
921                    if($boundExpr)
922                    $evalString = preg_replace("/\\".$var."/","false",$evalString);
923                    else
924                    $evalString = 'false';
925                }
926            }else{
927                $evalString = preg_replace("/\\".$var."/","false",$evalString);
928            }
929
930        }
931
932        // replace '=' with '=='
933        $evalString = preg_replace("/(.[^\=])(\=)(.[^\=])/","$1==$3",$evalString);
934
935
936        // rewrite Literals
937        foreach($hits1[0] as $k => $val){
938            $pattern = '/_REPLACED1_'.$k.'/';
939            $evalString = preg_replace($pattern,$hits1[0][$k],$evalString,1);
940        }
941
942        foreach($hits2[0] as $k => $val){
943            $pattern = '/_REPLACED2_'.$k.'/';
944            $evalString = preg_replace($pattern,$hits2[0][$k],$evalString,1);
945        }
946
947        // replace xsd:boolean expressions
948        $pattern = $pattern = '/\"\s?true\s?\"\^\^\<'.$xsd.'boolean\>|\'\s?true\s?\'\^\^xsd:boolean/';
949        $evalString = preg_replace($pattern,"true",$evalString);
950
951        $pattern = $pattern = '/\"\s?false\s?\"\^\^\<'.$xsd.'boolean\>|\'\s?false\s?\'\^\^xsd:boolean/';
952        $evalString = preg_replace($pattern,"false",$evalString);
953
954        // replace xsd:date expressions
955        $pattern = "/\"(.[^\"]*)\"\^\^".$xsd."dateTime/";
956        preg_match_all($pattern,$evalString,$hits);
957
958        foreach($hits[1] as $dummy)
959        $evalString = preg_replace("/\".[^\"]*\"\^\^".$xsd."dateTime/",strtotime($dummy),$evalString,1);
960
961
962        $evalString = preg_replace("/(\'\<".$xsd."dateTime\()(.[^\)]*\))\>\'/","dateTime($2",$evalString);
963
964        $evalString = preg_replace("/(\'\<".$xsd."integer\()(.[^\)]*\))\>\'/","integer($2",$evalString);
965
966        // tag plain literals
967        $evalString = preg_replace("/\"(.[^\"]*)\"([^\^])|\"(.[^\"]*)\"$/","'str_$1$3'$2",$evalString);
968
969        return $evalString;
970    }
971
972    /**
973    * Sorts the results.
974    *
975    * @param  Array  $vartable List containing the unsorted result vars
976    * @return Array  List containing the sorted result vars
977    */
978    protected function sortVars($vartable)
979    {
980        $newTable = array();
981        $mod = $this->query->getSolutionModifier();
982        // if no ORDER BY solution modifier return vartable
983        if($mod['order by']!= null){
984            $order = $mod['order by'];
985            $map = $this->buildVarmap($order,$vartable);
986            foreach($map as $val){
987                $newTable[] = $vartable[$val];
988            }
989        }else{
990            $newTable = $vartable;
991        }
992
993        if($mod['offset'] != null){
994            $newTable = array_slice ($newTable, $mod['offset']);
995        }
996        if($mod['limit'] != null){
997            $newTable = array_slice($newTable,0,$mod['limit']);
998        }
999
1000        return $newTable;
1001    }
1002
1003    /**
1004    * Sorts the result table.
1005    *
1006    * @param  String $order (ASC/DESC)
1007    * @param  Array  $vartable the vartable
1008    * @return Array  A map that contains the new order of the result vars
1009    */
1010    protected function buildVarmap($order, $vartable)
1011    {
1012        $n= 0;
1013        $result = array();
1014        $num_var = array();
1015        foreach($order as $variable)
1016        $num_var[$variable['val']] = 0;
1017
1018        foreach($vartable as $k => $x){
1019            foreach($order as $value){
1020                // if the value is a typed Literal try to determine if it
1021                // a numeric datatype
1022                if($x[$value['val']] instanceof Literal){
1023                    $dtype = $x[$value['val']]->getDatatype();
1024                    if($dtype){
1025                        switch($dtype){
1026                            case XML_SCHEMA."integer":
1027                            $num_var[$value['val']]++;
1028                            break;
1029                            case XML_SCHEMA."double":
1030                            $num_var[$value['val']]++;
1031                            break;
1032
1033                        }
1034                    }
1035                }
1036                if($x[$value['val']]){
1037                    if($x[$value['val']]instanceof Literal){
1038                        $pref = "2";
1039                    }
1040                    if($x[$value['val']]instanceof Resource){
1041                        $pref = "1";
1042                    }
1043                    if($x[$value['val']]instanceof BlankNode){
1044                        $pref = "0";
1045                    }
1046                    $result[$value['val']][$n] = $pref.$x[$value['val']]->getLabel();
1047                }else{
1048                    $result[$value['val']][$n] = "";
1049                }
1050            }
1051            $result['oldKey'][$n] = $k;
1052            $n++;
1053        }
1054        $sortString = "";
1055        foreach($order as $value){
1056            if($num_var[$value['val']] == $n)
1057            $sort = SORT_NUMERIC;
1058            else
1059            $sort = SORT_STRING;
1060
1061            if($value['type'] == 'asc')
1062            $type = SORT_ASC;
1063            else
1064            $type = SORT_DESC;
1065
1066            $sortString = $sortString.'$result["'.$value['val'].'"],'.$type.','.$sort.',';
1067        }
1068        $sortString = "array_multisort(".$sortString.'$result["oldKey"]);';
1069
1070        @eval($sortString);
1071        return $result['oldKey'];
1072    }
1073
1074
1075
1076    /**
1077    * Eliminates duplicate results.
1078    *
1079    * @param  Array  $vartable a table that contains the result vars and their bindings
1080    * @return Array the result table without duplicate results
1081    */
1082    protected function distinct($vartable)
1083    {
1084        $index = array();
1085        foreach($vartable as $key => $value){
1086            $key_index="";
1087            foreach($value as $k => $v)
1088            if($v instanceof Object)
1089                $key_index = $key_index.$k.$v->toString();
1090            if(isset($index[$key_index]))
1091            unset($vartable[$key]);
1092            else
1093            $index[$key_index]= 1;
1094        }
1095        return $vartable;
1096    }
1097
1098
1099    /**
1100    * Prints a query result as HTML table.
1101    * You can change the colors in the configuration file.
1102    *
1103    * @param array $queryResult [][?VARNAME] = object Node
1104    * @return void
1105    */
1106    public function writeQueryResultAsHtmlTable($queryResult) {
1107        // Import Package Utility
1108        include_once(RDFAPI_INCLUDE_DIR.PACKAGE_UTILITY);
1109
1110        if ( $queryResult[0] == null) {
1111            echo 'no match<br>';
1112            return;
1113        }
1114        if ( $queryResult == 'false') {
1115            echo 'boolean: false<br>';
1116            return;
1117        }
1118        if ( $queryResult == 'true') {
1119            echo 'boolean: true<br>';
1120            return;
1121        }
1122
1123
1124        echo '<table border="1" cellpadding="3" cellspacing="0"><tr><td><b>No.</b></td>';
1125        foreach ($queryResult[0] as $varName => $value)
1126        echo "<td align='center'><b>$varName</b></td>";
1127        echo '</tr>';
1128
1129        foreach ($queryResult as $n => $var) {
1130
1131
1132            echo '<tr><td width="20" align="right">' .($n + 1) .'.</td>';
1133            foreach ($var as $varName => $value) {
1134                if($value !=''){
1135                    echo INDENTATION . INDENTATION . '<td bgcolor="';
1136                    echo RDFUtil::chooseColor($value);
1137                    echo '">';
1138                    echo '<p>';
1139
1140                    $lang  = NULL;
1141                    $dtype = NULL;
1142                    if (is_a($value, 'Literal')) {
1143                        if ($value->getLanguage() != NULL)
1144                        $lang = ' <b>(xml:lang="' . $value->getLanguage() . '") </b> ';
1145                        if ($value->getDatatype() != NULL)
1146                        $dtype = ' <b>(rdf:datatype="' . $value->getDatatype() . '") </b> ';
1147                    }
1148                    echo  RDFUtil::getNodeTypeName($value) .$value->getLabel() . $lang . $dtype .'</p>';
1149                }else{
1150                    echo "<td bgcolor='white'>unbound";
1151                }
1152            }
1153            echo '</tr>';
1154        }
1155        echo '</table>';
1156    }
1157
1158
1159
1160    /*
1161    *   Dumb getters
1162    */
1163
1164
1165
1166    public function getQuery()
1167    {
1168        return $this->query;
1169    }//public function getQuery()
1170
1171
1172
1173    public function getDataset()
1174    {
1175        return $this->dataset;
1176    }//public function getDataset()
1177
1178} // end: Class SparqlEngine
1179
1180?>
Note: See TracBrowser for help on using the repository browser.