source: Dev/branches/rest-dojo-ui/server/classes/Model.php @ 311

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

[Client] Use toaster to show errors from server
[Client] Create widget for mainmenu, cleaner loading of index.html now.
[Server] Renamed answerset datetime to more meaningful creationdate

File size: 16.2 KB
RevLine 
[303]1<?php
2
3require_once RDFAPI_INCLUDE_DIR . 'RDFAPI.php';
4
5interface RdfResource {
6
7    function getUri();
8
9    function getUid();
10
11    function getType();
12}
13
14abstract class RdfObject implements RdfResource, IteratorAggregate {
15    // namespace and uri functions
16
17    const NS = 'http://tbm.tudelft.nl/researchtool/';
18    const RESOURCES_NS = 'http://tbm.tudelft.nl/researchtool/resources/';
19    const PREDICATES_NS = 'http://tbm.tudelft.nl/researchtool/predicates/';
20
21    public static function isResourceUri($str) {
22        $ptrn = '/^' . preg_quote(self::RESOURCES_NS, '/') . '\w+\/\w+$/';
23        return (bool) preg_match($ptrn, $str);
24    }
25
26    public static function resourceUriToTypeAndUid($uri) {
27        if (!self::isResourceUri($uri)) {
28            throw new Exception("Given URI $uri is not a valid resource.");
29        }
30        $ptrn = '/^' . preg_quote(self::RESOURCES_NS, '/') . '(\w+)\/(\w+)$/';
31        $matches = array();
32        preg_match_all($ptrn, $uri, $matches);
33        return array('type' => $matches[1][0], 'uid' => $matches[2][0]);
34    }
35
36    public static function typeAndUidToResourceUri($type, $uid) {
37        return self::RESOURCES_NS . $type . '/' . $uid;
38    }
39
40    // type functions
41
42    private static $fieldsCache = array();
43
44    private static function TYPE() {
45        return get_called_class();
46    }
47
48    private static function FIELDS() {
49        $type = static::TYPE();
50        if (!isset(self::$fieldsCache[$type])) {
51            self::$fieldsCache[$type] = static::createFIELDS();
52        }
53        return self::$fieldsCache[$type];
54    }
55
56    /** Provides the field of this object - implemented by specific type classes
57     * Retrieves an array with the specification of the fields of this object.
58     * The array contains RdfField objects. This function is called only once
59     * per type, because the result is cached.
60     */
61    protected abstract static function createFIELDS();
62
63    // instance fields
64
65    private $uid = null;
66    private $uri = null;
67    private $values = array();
68
69    public function getUri() {
70        return $this->uri;
71    }
72
73    public function getUid() {
74        return $this->uid;
75    }
76
77    public function getType() {
78        return static::TYPE();
79    }
80
81    // PHP magic functions to expose values
82
83    public function __construct($uid = null, $values = array()) {
84        if (empty($uid)) {
85            $this->uid = md5(uniqid(rand(), true));
86        } else {
87            $this->uid = $uid;
88        }
89        $this->uri = self::typeAndUidToResourceUri(static::TYPE(), $this->uid);
90        foreach ($values as $name => $value) {
91            $this->$name = $value;
92        }
93    }
94
95    private function ensure_field($name) {
96        if (!array_key_exists($name, static::FIELDS())) {
97            throw new Exception("Field " . $name . " not defined on " . self::TYPE());
98        }
99    }
100
101    private function get_field($name) {
102        self::ensure_field($name);
103        $fields = static::FIELDS();
104        return $fields[$name];
105    }
106
107    public function __isset($name) {
108        $this->ensure_field($name);
109        return isset($this->values[$name]);
110    }
111
112    public function __get($name) {
113        $field = self::get_field($name);
114        if (isset($this->values[$name])) {
115            return $this->values[$name];
116        }
117        return $field->get_null();
118    }
119
120    public function __set($name, $value) {
121        self::ensure_field($name);
122        $this->values[$name] = $value;
123    }
124
125    public function __unset($name) {
126        unset($this->values[$name]);
127    }
128
129    public function getIterator() {
130        return new ArrayIterator($this->values);
131    }
132
133    public function __toString() {
134        return self::TYPE() . '(uid=' . $this->uid . ')';
135    }
136
137    // RDF database functions
138
139    private static function get_db_filename() {
140        $fileName = 'data/' . static::TYPE() . 's/' . static::TYPE() . 's.rdf';
141        $dirName = dirname($fileName);
142        if (!is_dir($dirName)) {
143            mkdir($dirName, 0777 /* PHP's default value */, $recursive = TRUE);
144        }
145        return $fileName;
146    }
147
148    private static function get_model() {
149        $fileName = self::get_db_filename();
150        $model = ModelFactory::getDefaultModel();
151        if (file_exists($fileName)) {
152            $model->load($fileName);
153        }
[305]154        $model->addNamespace('rtr', self::RESOURCES_NS);
155        $model->addNamespace('rtp', self::PREDICATES_NS);
[303]156        return $model;
157    }
158
159    private static function save_model($model) {
160        $fileName = self::get_db_filename();
161        return $model->saveAs($fileName, 'rdf');
162    }
163
164    protected function evaluate() {
165        foreach (static::FIELDS() as $name => $field) {
166            if ($field->required()
167                    && (!is_set($this->values[$name])
168                    || ( $field->multiple() && empty($this->values[$name]) ) )) {
169                throw new Exception("Required field $name not found.");
170            }
171        }
172    }
173
174    public function save() {
175        $this->evaluate();
176
177        $model = self::get_model();
178
179        // create new resource and remove old saved resource
180        $resource = new Resource($this->uri);
181        $model->subtract($model->find($resource, null, null));
182
183        // set type
184        $model->add(new Statement($resource,
185                        new Resource(RDF_NAMESPACE_URI . 'type'),
186                        new Resource(self::RESOURCES_NS . static::TYPE())));
187
188        // add fields
189        foreach (static::FIELDS() as $name => $field) {
190            if (isset($this->values[$name])) {
191                $values = $this->values[$name];
192                if (!$field->multiple()) {
193                    $values = array($values);
194                }
195                foreach ($values as $value) {
196                    $type = $field->type();
197                    $type::save($model, $resource, $name, $value);
198                }
199            }
200        }
201
202        if (!self::save_model($model)) {
203            throw new Exception('Save failed: internal RDFAPI error.');
204        }
205    }
206
207    public static function get($predicates = array()) {
208        $model = self::get_model();
209
210        $query = '';
211        $query .= "PREFIX predicates: <" . self::PREDICATES_NS . ">\n";
212        $query .= "PREFIX resources: <" . self::RESOURCES_NS . ">\n";
213        $query .= "SELECT *\n";
214        $query .= "WHERE {\n";
215        $query .= "\t?object a resources:" . static::TYPE() . " .\n";
216        foreach (static::FIELDS() as $name => $field) {
217            if (!$field->multiple()) {
218                $cond = "?object predicates:" . $name . " ?" . $name;
219                if ($field->required() || array_key_exists($name, $predicates)) {
220                    $query .= "\t$cond .\n";
221                } else {
222                    $query .= "\tOPTIONAL { $cond . }\n";
223                }
224            }
225        }
226        if (!empty($predicates)) {
227            $preds = array();
228            foreach ($predicates as $name => $value) {
229                if ($name == 'uid') {
230                    if (!is_string($value)) {
231                        $value = $value->getUid();
232                    }
233                    $preds[] = 'str(?object) = "' . self::RESOURCES_NS . self::TYPE() . '/' . $value . '"';
234                } else {
235                    $type = self::get_field($name)->type();
236                    $preds[] = $type::predicate($name, $value);
237                }
238            }
239            $query .= "\tFILTER ( " . join(" && ", $preds) . " ) .\n";
240        }
241        $query .= "}\n";
242
243        $results = $model->sparqlQuery($query);
244        $objects = array();
245        if (!empty($results)) {
246            $realClass = self::TYPE();
247            foreach ($results as $result) {
248                $uri = $result['?object']->uri;
249                $values = array();
250                foreach (static::FIELDS() as $name => $field) {
251                    if (!$field->multiple()) {
252                        $queryName = "?$name";
253                        if (isset($result[$queryName]) && !empty($result[$queryName])) {
254                            $type = $field->type();
255                            $values[$name] = $type::load($result[$queryName]);
256                        }
257                    } else {
258                        $values[$name] = self::getMultiple($model, $uri, $name, $field);
259                    }
260                }
261                $info = self::resourceUriToTypeAndUid($uri);
262                $objects[] = new $realClass($info['uid'], $values);
263            }
264        }
265        return $objects;
266    }
267
268    private static function getMultiple($model, $uri, $name, $field) {
269        $query = '';
270        $query .= "PREFIX predicates: <" . self::PREDICATES_NS . ">\n";
271        $query .= "SELECT DISTINCT ?item\n";
272        $query .= "WHERE { <$uri> predicates:$name ?item . }\n";
273
274        $results = $model->sparqlQuery($query);
275        $values = array();
276        if (!empty($results)) {
277            foreach ($results as $result) {
278                $type = $field->type();
279                $values[] = $type::load($result['?item']);
280            }
281        }
282        return $values;
283    }
284
285}
286
287class RdfPointer implements RdfResource {
288
289    private $uri = null;
290    private $uid = null;
291    private $type = null;
292
293    public function __construct($uri) {
294        $this->uri = $uri;
295        $info = RdfObject::resourceUriToTypeAndUid($uri);
296        $this->uid = $info['uid'];
297        $this->type = $info['type'];
298    }
299
300    public function getUri() {
301        return $this->uri;
302    }
303
304    public function getUid() {
305        return $this->uid;
306    }
307
308    public function getType() {
309        return $this->type;
310    }
311
312    public function get() {
313        $type = $this->type;
314        return $type::get(array('uid' => $this->uid));
315    }
316
317}
318
319class RdfField {
320
321    const REQUIRED = 1;
322    const MULTIPLE = 2;
323
324    private $type;
325    private $conditions;
326
327    public function __construct($type, $conditions = array()) {
328        $this->type = $type;
329        $this->conditions = $conditions;
330    }
331
332    public function type() {
333        return $this->type;
334    }
335
336    public function required() {
337        return in_array(self::REQUIRED, $this->conditions);
338    }
339
340    public function multiple() {
341        return in_array(self::MULTIPLE, $this->conditions);
342    }
343
344}
345
346function RdfField($type, $conditions = array()) {
347    return new RdfField($type, $conditions);
348}
349
350interface RdfType {
351
352    static function save($model, $resource, $predicateUri, $value);
353
354    static function load($value);
355
356    static function predicate($name, $value);
357}
358
359class RdfString implements RdfType {
360
361    static function save($model, $resource, $name, $value) {
362        $obj = new Literal($value);
363        $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj));
364    }
365
366    static function load($value) {
367        return $value->label;
368    }
369
370    static function predicate($name, $value) {
371        return 'str(?' . $name . ') = "' . $value . '"';
372    }
373
374}
375
376function RdfString() {
377    return new RdfString();
378}
379
380class RdfDatetime implements RdfType {
381
382    static function save($model, $resource, $name, $value) {
383        $obj = new Literal($value->getTimestamp());
384        $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj));
385    }
386
387    static function load($value) {
388        $obj = new DateTime();
389        $obj->setTimestamp(intval($value->label));
390        return $obj;
391    }
392
393    static function predicate($name, $value) {
394        return 'str(?' . $name . ') = "' . $value . '"';
395    }
396
397}
398
399function RdfDatetime() {
400    return new RdfDatetime();
401}
402
403class RdfBoolean implements RdfType {
404
405    static function save($model, $resource, $name, $value) {
406        $model->add(new Statement($resource,
407                        new Resource(RdfObject::PREDICATES_NS . $name),
408                        new Literal((bool) $value)));
409    }
410
411    static function load($value) {
412        return (bool) $value->label;
413    }
414
415    static function predicate($name, $value) {
416        return 'str(?' . $name . ') = "' . $value . '"';
417    }
418
419}
420
421function RdfBoolean() {
422    return new RdfBoolean();
423}
424
425class RdfReference implements RdfType {
426
427    static function save($model, $resource, $name, $value) {
428        if (!is_string($value)) {
429            $value = $value->getUri();
430        }
431        $obj = new Resource($value);
432        $model->add(new Statement($resource, new Resource(RdfObject::PREDICATES_NS . $name), $obj));
433    }
434
435    static function load($value) {
436        return new RdfPointer($value->uri);
437    }
438
439    static function predicate($name, $value) {
440        return '?' . $name . ' = <' . $value . '>';
441    }
442
443}
444
445function RdfReference() {
446    return new RdfReference();
447}
448
449class Answer extends RdfObject {
450
451    protected static function createFIELDS() {
452        return array(
453            'question' => RdfField(RdfReference()),
454            'values' => RdfField(RdfString(), array(RdfField::MULTIPLE))
455        );
456    }
457
458}
459
460class AnswerSet extends RdfObject {
461
462    protected static function createFIELDS() {
463        return array(
464            'survey' => RdfField(RdfReference()),
465            'respondent' => RdfField(RdfReference()),
[311]466            'creationdate' => RdfField(RdfDatetime()),
[303]467            'answers' => RdfField(RdfReference(), array(RdfField::MULTIPLE))
468        );
469    }
470
471}
472
473class Application extends RdfObject {
474
475    protected static function createFIELDS() {
476        return array(
477            'title' => RdfField(RdfString()),
478            'description' => RdfField(RdfString()),
479            'style' => RdfField(RdfString())
480        );
481    }
482
483}
484
485class ApplicationInstance extends RdfObject {
486
487    protected static function createFIELDS() {
488        return array(
489            'application' => RdfField(RdfReference()),
490            'starttime' => RdfField(RdfDatetime()),
491            'endtime' => RdfField(RdfDatetime()),
492            'open' => RdfField(RdfBoolean())
493        );
494    }
495
496}
497
498class Question extends RdfObject {
499
500    protected static function createFIELDS() {
501        return array(
502            'code' => RdfField(RdfString()),
503            'title' => RdfField(RdfString()),
504            'type' => RdfField(RdfString()),
505            'description' => RdfField(RdfString()),
506            'category' => RdfField(RdfString()),
507            'answers' => RdfField(RdfString(), array(RdfField::MULTIPLE))
508        );
509    }
510
511}
512
513class Session extends RdfObject {
514
515    protected static function createFIELDS() {
516        return array(
517            'title' => RdfField(RdfString()),
518            'creator' => RdfField(RdfReference()),
519            'creationdate' => RdfField(RdfDatetime()),
520            'pipeline' => RdfField(RdfReference(), array(RdfField::MULTIPLE))
521        );
522    }
523
524}
525
526class SessionInstance extends RdfObject {
527
528    protected static function createFIELDS() {
529        return array(
530            'title' => RdfField(RdfString()),
531            'location' => RdfField(RdfString()),
532            'facilitator' => RdfField(RdfReference()),
533            'starttime' => RdfField(RdfDatetime()),
534            'endtime' => RdfField(RdfDatetime()),
535            'notes' => RdfField(RdfString(), array(RdfField::MULTIPLE)),
536            'session' => RdfField(RdfReference()),
537            'surveyinstances' => RdfField(RdfReference(), array(RdfField::MULTIPLE)),
538            'applicationinstances' => RdfField(RdfReference(), array(RdfField::MULTIPLE))
539        );
540    }
541
542}
543
544class Survey extends RdfObject {
545
546    protected static function createFIELDS() {
547        return array(
548            'title' => RdfField(RdfString()),
549            'description' => RdfField(RdfString()),
550            'creator' => RdfField(RdfReference()),
551            'questions' => RdfField(RdfString(), array(RdfField::MULTIPLE))
552        );
553    }
554
555}
556
557class SurveyInstance extends RdfObject {
558
559    protected static function createFIELDS() {
560        return array(
561            'survey' => RdfField(RdfReference()),
562            'starttime' => RdfField(RdfDatetime()),
563            'endtime' => RdfField(RdfDatetime()),
564            'open' => RdfField(RdfBoolean()),
565            'presetanswers' => RdfField(RdfReference(), array(RdfField::MULTIPLE)),
566            'answersets' => RdfField(RdfReference(), array(RdfField::MULTIPLE))
567        );
568    }
569
570}
571
572class User extends RdfObject {
573
574    protected static function createFIELDS() {
575        return array(
576            'email' => RdfField(RdfString()),
577            'passwordHash' => RdfField(RdfString()),
578            'passwordSalt' => RdfField(RdfString())
579        );
580    }
581
582}
583
584?>
Note: See TracBrowser for help on using the repository browser.