source: Dev/branches/rest-dojo-ui/jos-branch/server/rdfapi/sparql/SparqlEngine.php @ 312

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