source: Dev/branches/rest-dojo-ui/jos-branch/server/classes/Model.php @ 312

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