source: Dev/trunk/rdfapi/resModel/ResList.php @ 12

Last change on this file since 12 was 12, checked in by basvannuland, 14 years ago

Added RAP RDF API
Added RDF reader writer for save and load survey

File size: 16.8 KB
Line 
1<?php
2// ----------------------------------------------------------------------------------
3// Class: ResList
4// ----------------------------------------------------------------------------------
5
6/**
7* Implementation of an rdf:Collection (rdf:List)
8* Provides a convenience encapsulation for lists formed from
9* chains of RDF statements arranged to form a head/tail cons-cell
10* structure.
11*
12* A well-formed list has cells that are made up of three statements:
13* one denoting the rdf:type of the list cell, one denoting the link
14* to the value of the list at that point, and one pointing to the
15* list tail. If a list cell is not well-formed, list operations may
16* fail in unpredictable ways. However, to explicitly check that the
17* list is well-formed at all times is expensive, but you can call
18* the isValid() method to manually check, if the list is well formed.
19*
20*
21* @version  $Id: ResList.php 320 2006-11-21 09:38:51Z tgauss $
22* @author Daniel Westphal <mail at d-westphal dot de>
23
24*
25* @package resModel
26* @access       public
27*/
28class ResList extends ResResource
29{
30        /**
31        * Holds a ResResource with the uri rdf:rest
32        * @var          ResResource
33        * @access       private
34        */
35        var $rdfRestResource;
36       
37        /**
38        * Holds a ResResource with the uri rdf:first
39        * @var          ResResource
40        * @access       private
41        */
42        var $rdfFirstResource;
43               
44        /**
45        * Holds a ResResource with the uri rdf:nil
46        * @var          ResResource
47        * @access       private
48        */
49        var $rdfNilResource;
50       
51       
52        /**
53    * Constructor
54        * You can supply a URI
55    *
56    * @param string $uri
57        * @access       public
58    */         
59        function ResList($uri = null)
60        {
61                //call the parent's constructor
62                parent::ResResource($uri);
63                //initialize vars
64                $this->rdfRestResource = new ResResource(RDF_NAMESPACE_URI.RDF_REST);
65                $this->rdfFirstResource = new ResResource(RDF_NAMESPACE_URI.RDF_FIRST);
66                $this->rdfNilResource = new ResResource(RDF_NAMESPACE_URI.RDF_NIL);
67        }
68
69        /**
70        * Returns the value of the list element at the specified position or null.
71        *
72        * @param        integer $position
73        * @return       ResResource
74        * @access       public
75        */
76        function get($position)
77        {
78                //init
79                $listElement=$this;
80                //walk through the list until the position in the list is reached
81                for ($i=0;$i<$position;$i++)
82                {
83                        $listElement=$this->_getRestElement($listElement);
84                        if($listElement===null)
85                                return null;   
86                }
87                //return the associated value
88                return $this->_getValue($listElement); 
89        }       
90
91        /**
92        *  Add the given value to the end of the list.
93        *  it is only defined if this is not the empty list.
94        *
95        * @param        object ResResource      $resource
96        * @return       boolean
97        * @access       public
98        */
99        function add($resource)
100        {
101                //return false if this list is the empty list
102                if($this->uri==RDF_NAMESPACE_URI.RDF_NIL)
103                        return false;
104               
105                //if this is the first value   
106                if ($this->isEmpty())
107                {
108                        $newLastElement =& $this;
109                } else
110                //if there are other values in the list
111                {
112                        //get the last list element
113                        $lastElement=$this->_getListElement();
114                        //remove the rdf:rest property
115                        $lastElement->removeAll($this->rdfRestResource);
116                        //create a new list element
117                        $newLastElement=$this->model->createResource();
118                        //concatenate the new list element with the list
119                        $lastElement->addProperty($this->rdfRestResource,$newLastElement);
120                }
121                //add the value
122                $newLastElement->addProperty($this->rdfFirstResource,$resource);
123                //ad the rdf:nil property to the last list element
124                $newLastElement->addProperty($this->rdfRestResource,$this->rdfNilResource);
125               
126                return true;
127        }
128       
129        /**
130        *  Update the head of the list to have the given value,
131        *  and return the previous value.
132        *
133        * @param        object ResResource      $value
134        * @return       ResResource
135        * @access       public
136        */
137        //todo: error handling, when empty list
138        function setHead($value)
139        {       
140                //save the old value   
141                $oldValue=$this->getHead();
142                //remove the old value
143                $this->removeAll($this->rdfFirstResource);
144                //add the new value
145                $this->addProperty($this->rdfFirstResource,$value);
146               
147                //return the old value
148                return $oldValue;
149        }
150       
151        /**
152        * Get the value that is associated with the head of the list.
153        *
154        * @return       ResResource
155        * @access       public
156        */
157        //todo: error handling, falls empty list
158        function getHead()
159        {
160                return $this->_getValue($this);
161        }
162       
163        /**
164        *  Remove the head of the list. The tail of the list
165        *  remains in the model. Note that no changes are made to
166        *  list cells that point to this list cell as their tail.       
167        *
168        * @return       ResList
169        * @access       public
170        */
171        function removeHead()
172        {
173                //get the second list element
174                $rest=$this->_getRestElement($this);
175                //remove the first element
176                $this->removeAll($this->rdfFirstResource);
177                $this->removeAll($this->rdfRestResource);
178                //change this Resource URI to that of the second list element
179                //thus makin it the fist element
180                $this->uri=$rest->getURI();
181               
182                //return the new list
183                return $this;
184        }       
185
186        /**
187        * Get the Position of the first occurrence of the given value in the list,
188        * or -1 if the value is not in the list.
189        * You can supply an offset to search for values. (First element has offset 0)
190        * Default is 0
191        *
192        * @param        object ResResource      $resource
193    * @param    integer $offset
194        * @return       integer
195        * @access       public
196        */
197        function indexOf($resource, $offset = 0)
198        {
199                //init
200                $element=$this;
201                $actualIndex=0;
202
203                //walk through the list until the value is found and the position is higher than
204                //the offset
205                while ($actualIndex < $offset || !$resource->equals($this->_getValue($element)))
206                {
207                        //get next list element
208                        $element=$this->_getRestElement($element);
209                        $actualIndex++;
210                       
211                        //if the end of the list is reached and the value isn't found
212                        if ($element===null)
213                                return null;
214                }
215                //return the index value
216                return $actualIndex;   
217        }
218       
219        /**
220        * Replace the value at the i'th position in the list with the given value
221        *
222        * @param        integer                         $index
223        * @param        object ResResource      $resource
224        * @return       object ResResource
225        * @access       public
226        */
227        function replace($index, $resource)
228        {
229                //get the list element at the $index position
230                $listElement=$this->_getListElement($index);
231                //get the old value
232                $oldValue=$this->_getValue($listElement);
233                //remove the old value
234                $listElement->removeAll($this->rdfFirstResource);
235                //add the new value
236                $listElement->addProperty($this->rdfFirstResource,$resource);
237                //return the old value
238                return $oldValue;
239        }
240       
241        /**
242        * Answer true if the given node appears as the value of a value
243        * of any of the cells of this list.
244        *
245        * @param        object ResResource      $value
246        * @return       boolean
247        * @access       public
248        */
249        function contains($value)
250        {
251                //return true, if a position was found.
252                $result=$this->indexOf($value);
253                return ($result!==null);
254        }
255       
256        /**
257        * Get the list that is the tail of this list.
258        *
259        * @return       object ResList
260        * @access       public
261        */
262        function getTail()
263        {
264                //get the second list element
265                $nextListElement= $this->_getRestElement($this);
266                //return the second element as new list
267                return $this->model->createList($nextListElement->getURI());   
268        }
269       
270        /**
271        * Remove all of the components of this list from the model.
272        * Note that this is operation is only removing the list cells
273        * themselves, not the resources referenced by the list -
274        * unless being the object of an rdf:first  statement is the
275        * only mention of that resource in the model.
276        *
277        * @return       boolean
278        * @access       public
279        */
280        function removeList()
281        {
282                $element=$this;
283
284                while ($element!==null)
285                {
286                        $nextElement=$this->_getRestElement($element);
287                       
288                        $element->removeAll($this->rdfFirstResource);
289                        $element->removeAll($this->rdfRestResource);
290                                       
291                        if (($nextElement !== null) && ($nextElement->getURI()!==RDF_NAMESPACE_URI.RDF_NIL))
292                        {
293                                $element=$nextElement;
294                        } else
295                        {
296                                return true;
297                        }
298                }
299                return false;
300        }
301       
302        /**
303        * Returns true, if this list is empty
304        *
305        * @param        object Statement        $statement
306        * @return       integer
307        * @access       public
308        */
309        function isEmpty()
310        {
311                return !$this->hasProperty($this->rdfFirstResource);
312        }
313       
314        /**
315        * Get all values in the list as an array of ResResources
316        *
317        * @return       array
318        * @access       public
319        */
320        function getContentInArray()
321        {
322                $result=array();
323                $element=$this;
324
325                while ($element!==null)
326                {
327                        //add the value of the current element to the result if is set.
328                        $value=$this->_getValue($element);
329                        if ($value!==null)
330                                $result[]=$value;
331                       
332                        //walk through the list until it's end         
333                        $nextElement=$this->_getRestElement($element);
334                        if (($nextElement !== null) && ($nextElement->getURI()!==RDF_NAMESPACE_URI.RDF_NIL))
335                        {
336                                $element=$nextElement;
337                        } else
338                        {
339                                break;
340                        }
341                }
342                //return the result
343                return $result;
344        }
345       
346        /**
347        * Change the tail of this list to point to the given list, so that this list
348        * becomes the list of the concatenation of the elements of
349        * both lists. This is a side-effecting operation on this list;
350        * for a non side-effecting alternative, see append.
351        *
352        *
353        * @param        object ResList  $ResList
354        * @access       public
355        */
356        function concatenate($ResList)
357        {
358                //get the last list element
359                $lastElement=$this->_getListElement();
360                //remove the old tail (rdf:nil)
361                $lastElement->removeAll($this->rdfRestResource);
362                //add the $ResList as new tail
363                $lastElement->addProperty($this->rdfRestResource,$ResList);
364        }
365       
366        /**
367        * Answer a new list that is formed by adding each element of
368        * this list to the head of the given list. This is a non
369        * side-effecting operation on either this list or the given
370        * list, but generates a copy of this list. For a more storage
371        * efficient alternative, see concatenate
372        *
373        * @param        object ResList  $ResList
374        * @return       object ResList
375        * @access       public
376        */
377        function append($resList)
378        {
379                //get a copy of this list
380                $newList=$this->copy();
381               
382                //add all values from the $resList to the new list
383                foreach ($resList->getContentInArray() as $value)
384                {
385                        $newList->add($value); 
386                }
387                //return the new list
388                return $newList;
389        }
390       
391        /**
392        * Answer a list that contains all of the elements of this
393        * list in the same order, but is a duplicate copy in the
394        * underlying model.
395        *
396        * @return       object ResList
397        * @access       public
398        */
399        function copy()
400        {
401                //create a new list in the model
402                $newList=$this->model->createList();
403                //add all values from this list to the new list
404                foreach ($this->getContentInArray() as $value)
405                {
406                        $newList->add($value); 
407                }
408                //return the new list
409                return $newList;
410        }
411       
412        /**
413        * Return a reference to a new list cell whose head is value 
414        * and whose tail is this list.
415        *
416        * @param        object ResResource      $value
417        * @return       object ResList
418        * @access       public
419        */
420        function cons($value)
421        {
422                //create a new list
423                $newList=$this->model->createList();
424                //add the new value
425                $newList->add($value);
426                //set this list as the tail of the new list
427                $newList->setTail($this);
428               
429                //return the new list
430                return $newList;
431        }
432       
433        /**
434        * Update the list cell at the front of the list to have the
435        * given list as tail. The old tail is returned, and remains
436        * in the model.
437        *
438        * @param        object ResList  $resList
439        * @return       object Reslist
440        * @access       public
441        */
442        function setTail($resList)
443        {
444                //save the old tail
445                $oldTail=$this->getTail();
446                //remove the old tail
447                $this->removeAll($this->rdfRestResource);
448                //add the $resList as new Tail
449                $this->addProperty($this->rdfRestResource,$resList);
450               
451                //return the old tail
452                return $oldTail;
453        }
454       
455        /**
456        * Answer true if this list has the same elements in the
457        * same order as the given list. Note that the standard equals
458        * test just tests for equality of two given list cells.
459        * While such a test is sufficient for many purposes, this
460        * test provides a broader equality definition, but is
461        * correspondingly more expensive to test.
462        *
463        * @param        object ResList  $resList
464        * @return       boolean
465        * @access       public
466        */
467        function sameListAs($resList)
468        {
469                //init
470                $indexPos=0;
471                do
472                {
473                        //get the values for both lists at the actual position
474                        $thisValue=$this->get($indexPos);
475                        $thatValue=$resList->get($indexPos);
476                        //if the values aren't equal, return false
477                        if (($thisValue !== null) && !$thisValue->equals($thatValue))
478                                return false;
479                               
480                        $indexPos++;
481                        //walk until this list reaches a null value (end)       
482                } while ($thisValue!==null);
483               
484                //if the other list has a null value at this position too, return true
485                //else return false
486                return ($thatValue===null);
487        }
488       
489        /**
490        * Remove the given value from this list.
491        * If value does not occur in the list, no action is taken. Since removing the
492        * head of the list will invalidate the list head cell, in
493        * general the list must return the list that results from
494        * this operation. However, in many cases the return value
495        * will be the same as the object that this method is invoked
496        * on.
497        *
498        * @param        object ResResource      $value
499        * @return       object ResList
500        * @access       public
501        */
502        function remove($value)
503        {
504                //if the value is the value of the first list element(head)
505                //call the remove head position and return the new head
506                if ($value->equals($this->_getValue($this)))
507                        return $this->removeHead();
508       
509                $element=$this;
510                do
511                {       
512                        $newElement=$this->_getRestElement($element);
513                       
514                        //if the value equals the value of the current list element
515                        if ($newElement !== null && $value->equals($this->_getValue($newElement)))
516                        {
517                                //remove the link to the list element to be removed
518                                $element->removeAll($this->rdfRestResource);
519                                //add a link to the list element AFTER the element to be deleted
520                                $element->addProperty($this->rdfRestResource,$this->_getRestElement($newElement));
521                                //remove the list element with values
522                                $newElement->removeAll($this->rdfFirstResource);
523                                $newElement->removeAll($this->rdfRestResource);
524                                //return this ResList
525                                return $this;   
526                        }
527                        $element=$newElement;
528                } while ($element!==null);
529                //return this list
530                return $this;   
531        }
532       
533        /**
534        * Answer true if the list is well-formed, by checking that each
535        * node is correctly typed, and has a head and tail pointer from
536        * the correct vocabulary
537        *
538        * @return       boolean
539        * @access       public
540        */
541        function isValid()
542        {
543                $element=$this;
544                if ($this->_getValue($this)=== null && $this->_getRestElement($this) === null)
545                        return true;
546                do
547                {
548                        //return true if the last element is a rdf:nil
549                        if ($element->getURI() == RDF_NAMESPACE_URI.RDF_NIL)
550                                return true;
551                        //return false, if the current element has no associated value
552                        if ($this->_getValue($element) === null)
553                                return false;
554                                               
555                        $element=$this->_getRestElement($element);     
556                } while ($element !== null);
557                //return false, if the current element has no rdf:rest property
558                return false;
559        }
560       
561        /**
562        * Get the associated rdf:rest Resource from the suplied ResList element
563        *
564        * @param        object ResList  $listElement
565        * @return       object ResList
566        * @access       private
567        */
568        function _getRestElement($listElement)
569        {
570                //get the rdf:rest property
571                $statement= $this->model->getProperty($listElement,$this->rdfRestResource);
572                //return null, if this property isn't set
573                if ($statement === null)
574                        return null;
575                       
576                //return the value of the rdf:rest property     
577                return $statement->getObject();
578        }
579       
580        /**
581        * Returns the list element at the $index position.
582        *
583        * If to $index is suplied, the last list element will be returned
584       
585        * @param        integer $index
586        * @return       object ResResource
587        * @access       private
588        */
589        function _getListElement($index = null)
590        {
591                $element=$this;
592                $actualIndex=0;
593
594                while ($element!=null)
595                {
596                        //return the current element if index matches the current index
597                        if ($actualIndex === $index)
598                                return $element;
599                       
600                        //return the current element if it the last one         
601                        if ($element->hasProperty($this->rdfRestResource,$this->rdfNilResource))
602                                return $element;
603                       
604                        $nextElement=$this->_getRestElement($element);
605                       
606                        if ($nextElement!==null)
607                        {
608                                $element=$nextElement;
609                                $actualIndex++;
610                        } else
611                        {
612                                break;
613                        }
614                }
615                return $element;
616        }
617       
618        /**
619        * Get the value associated to the $listResource by the rdf:first property
620        *
621        * @param        object ResList  $listResource
622        * @return       object ResResource
623        * @access       private
624        */
625        function _getValue($listResource)
626        {
627                //Return the value of the rdf:first property or null, if it isn't set
628                $statement=$this->model->getProperty($listResource,$this->rdfFirstResource);
629                if ($statement===null)
630                        return null;
631
632                return $statement->getObject();
633        }
634}
635?>
Note: See TracBrowser for help on using the repository browser.