source: Dev/branches/jos-branch/server/classes/Model.php @ 315

Last change on this file since 315 was 315, checked in by jkraaijeveld, 13 years ago

[Server] Started work on validating if a change is valid. RdfObjects? now need a getModifiablefields method which returns the fields which can be modified for an object in its particular state. The objects which have different modifiable fields depending on the situation (see state diagram) need a 'state' value, which is an integer for now. Evaluate now checks whether a modified field is valid and throws an exception if its not.

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