KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > rdf > model > impl > RDFListImpl


1 /*****************************************************************************
2  * Source code information
3  * -----------------------
4  * Original author Ian Dickinson, HP Labs Bristol
5  * Author email Ian.Dickinson@hp.com
6  * Package Jena 2
7  * Web http://sourceforge.net/projects/jena/
8  * Created 24 Jan 2003
9  * Filename $RCSfile: RDFListImpl.java,v $
10  * Revision $Revision: 1.13 $
11  * Release status $State: Exp $
12  *
13  * Last modified on $Date: 2005/02/21 12:14:49 $
14  * by $Author: andy_seaborne $
15  *
16  * (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
17  * (see footer for full conditions)
18  *****************************************************************************/

19
20 // Package
21
///////////////
22
package com.hp.hpl.jena.rdf.model.impl;
23
24
25 // Imports
26
///////////////
27
import com.hp.hpl.jena.ontology.*;
28 import com.hp.hpl.jena.rdf.model.*;
29 import com.hp.hpl.jena.shared.*;
30 import com.hp.hpl.jena.util.iterator.*;
31 import com.hp.hpl.jena.enhanced.*;
32 import com.hp.hpl.jena.graph.*;
33 import com.hp.hpl.jena.vocabulary.*;
34
35 import java.util.*;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40
41 /**
42  * <p>
43  * Standard implementation the list abstraction from rdf.model.
44  * </p>
45  *
46  * @author Ian Dickinson, HP Labs
47  * (<a HREF="mailto:Ian.Dickinson@hp.com" >email</a>)
48  * @version CVS $Id: RDFListImpl.java,v 1.13 2005/02/21 12:14:49 andy_seaborne Exp $
49  */

50 public class RDFListImpl
51     extends ResourceImpl
52     implements RDFList
53 {
54     // Constants
55
//////////////////////////////////
56

57
58     // Static variables
59
//////////////////////////////////
60

61     /**
62      * A factory for generating RDFList facets from nodes in enhanced graphs.
63      */

64     public static Implementation factory = new Implementation() {
65         public EnhNode wrap( Node n, EnhGraph eg ) {
66             if (canWrap( n, eg )) {
67                 RDFListImpl impl = new RDFListImpl( n, eg );
68                 
69                 // pass on the vocabulary terms, if available
70
if (eg instanceof OntModel) {
71                     Profile prof = ((OntModel) eg).getProfile();
72                     impl.m_listFirst = prof.FIRST();
73                     impl.m_listRest = prof.REST();
74                     impl.m_listNil = prof.NIL();
75                     impl.m_listType = prof.LIST();
76                 }
77                 
78                 return impl;
79             }
80             else {
81                 throw new JenaException( "Cannot convert node " + n + " to RDFList");
82             }
83         }
84             
85         public boolean canWrap( Node node, EnhGraph eg ) {
86             Graph g = eg.asGraph();
87             
88             // if we are using a language profile, get the first, rest and next resources from there
89
Resource first = RDF.first;
90             Resource rest = RDF.rest;
91             Resource nil = RDF.nil;
92             
93             if (eg instanceof OntModel) {
94                 Profile prof = ((OntModel) eg).getProfile();
95                 first = prof.FIRST();
96                 rest = prof.REST();
97                 nil = prof.NIL();
98             }
99             
100             // node will support being an RDFList facet if it has rdf:type rdf:List, is nil, or is in the domain of a list property
101
return node.equals( nil.asNode() ) ||
102                     g.find( node, first.asNode(), Node.ANY ).hasNext() ||
103                     g.find( node, rest.asNode(), Node.ANY ).hasNext() ||
104                     g.find( node, RDF.type.asNode(), RDF.List.asNode() ).hasNext();
105         }
106     };
107
108     /** Flag to indicate whether we are checking for valid lists during list operations. Default false. */
109     protected static boolean s_checkValid = false;
110     
111     private static final Log log = LogFactory.getLog( RDFListImpl.class );
112     
113
114     // Instance variables
115
//////////////////////////////////
116

117     /** Error message if validity check fails */
118     protected String JavaDoc m_errorMsg = null;
119     
120     /** Pointer to the node that is the tail of the list */
121     protected RDFList m_tail = null;
122     
123     /** The URI for the 'first' property in this list */
124     protected Property m_listFirst = RDF.first;
125     
126     /** The URI for the 'rest' property in this list */
127     protected Property m_listRest = RDF.rest;
128     
129     /** The URI for the 'nil' Resource in this list */
130     protected Resource m_listNil = RDF.nil;
131     
132     /** The URI for the rdf:type of this list */
133     protected Resource m_listType = RDF.List;
134     
135     
136     
137     // Constructors
138
//////////////////////////////////
139

140     /**
141      * <p>
142      * Construct an implementation of RDFList in the given graph, where the
143      * given node is the head of the list.
144      * </p>
145      *
146      * @param n The node that is the head of the list, currently
147      * @param g The enh graph that contains n
148      */

149     public RDFListImpl( Node n, EnhGraph g ) {
150         super( n, g );
151     }
152     
153     
154     // External signature methods
155
//////////////////////////////////
156

157     // vocabulary terms
158
public Resource listType() { return m_listType; }
159     public Resource listNil() { return m_listNil; }
160     public Property listFirst() { return m_listFirst; }
161     public Property listRest() { return m_listRest; }
162     public Class JavaDoc listAbstractionClass() { return RDFList.class; }
163     
164     
165     /**
166      * <p>
167      * Answer the number of elements in the list.
168      * </p>
169      *
170      * @return The length of the list as an integer
171      */

172     public int size() {
173         if (s_checkValid) {
174             checkValid();
175         }
176         
177         int size = 0;
178         
179         for (Iterator i = iterator(); i.hasNext(); i.next()) {
180             size++;
181         }
182         return size;
183     }
184     
185     
186     /**
187      * <p>
188      * Answer the value that is at the head of the list.
189      * </p>
190      *
191      * @return The value that is associated with the head of the list.
192      * @exception EmptyListException if this list is the empty list
193      */

194     public RDFNode getHead() {
195         if (s_checkValid) {
196             checkValid();
197         }
198         
199         checkNotNil( "Tried to get the head of an empty list" );
200         
201         return getRequiredProperty( listFirst() ).getObject();
202     }
203     
204     
205     /**
206      * <p>
207      * Update the head of the list to have the given value, and return the
208      * previous value.
209      * </p>
210      *
211      * @param value The value that will become the value of the list head
212      * @exception EmptyListException if this list is the empty list
213      */

214     public RDFNode setHead( RDFNode value ) {
215         if (s_checkValid) {
216             checkValid();
217         }
218         
219         checkNotNil( "Tried to set the head of an empty list" );
220         
221         // first remove the existing head
222
Statement current = getRequiredProperty( listFirst() );
223         RDFNode n = current.getObject();
224         current.remove();
225         
226         // now add the new head value to the graph
227
addProperty( listFirst(), value );
228         
229         return n;
230     }
231     
232     
233     /**
234      * <p>
235      * Answer the list that is the tail of this list.
236      * </p>
237      *
238      * @return The tail of the list, as a list
239      * @exception EmptyListException if this list is the empty list
240      */

241     public RDFList getTail() {
242         if (s_checkValid) {
243             checkValid();
244         }
245         
246         checkNotNil( "Tried to get the tail of an empty list" );
247         
248         Resource tail = getRequiredProperty( listRest() ).getResource();
249         return (RDFList) tail.as( listAbstractionClass() );
250     }
251     
252     
253     /**
254      * <p>
255      * Update the list cell at the front of the list to have the given list as
256      * tail. The old tail is returned, and remains in the model.
257      * </p>
258      *
259      * @param tail The new tail for this list.
260      * @return The old tail.
261      */

262     public RDFList setTail( RDFList tail ) {
263         if (s_checkValid) {
264             checkValid();
265         }
266         
267         checkNotNil( "Tried to set the tail of an empty list" );
268
269         return (RDFList) (setTailAux( this, tail, listRest() )).as( listAbstractionClass() );
270     }
271     
272     
273     /**
274      * Answer true if this list is the empty list.
275      *
276      * @return True if this is the empty (nil) list, otherwise false.
277      */

278     public boolean isEmpty() {
279         if (s_checkValid) {
280             checkValid();
281         }
282         
283         return equals( listNil() );
284     }
285     
286     
287     /**
288      * <p>
289      * Return a reference to a new list cell whose head is <code>value</code>
290      * and whose tail is this list.
291      * </p>
292      *
293      * @param value A new value to add to the head of the list
294      * @return The new list, whose head is <code>value</code>
295      */

296     public RDFList cons( RDFNode value ) {
297         if (s_checkValid) {
298             checkValid();
299         }
300         
301         // create a new, anonymous typed resource to be the list cell
302
// map to a list facet
303
return (RDFList) (newListCell( value, this )).as( listAbstractionClass() );
304     }
305     
306     
307     /**
308      * <p>
309      * Add the given value to the end of the list. This is a side-effecting
310      * operation on the underlying model that is only defined if this is not the
311      * empty list. If this list is the empty (nil) list, we cannot perform a
312      * side-effecting update without changing the URI of this node (from <code>rdf:nil</code)
313      * to a blank-node for the new list cell) without violating a Jena invariant.
314      * Therefore, this update operation will throw an exception if an attempt is
315      * made to add to the nil list. Safe ways to add to an empty list include
316      * {@link #with} and {@link #cons}.
317      * </p>
318      *
319      * @param value A value to add to the end of the list
320      * @exception EmptyListUpdateException if an attempt is made to
321      * <code>add</code> to the empty list.
322      */

323     public void add( RDFNode value ) {
324         if (s_checkValid) {
325             checkValid();
326         }
327         
328         // if this is the empty list, we have to barf
329
if (isEmpty()) {
330             throw new EmptyListUpdateException( "Attempt to add() to the empty list (rdf:nil)" );
331         }
332         
333         // get the tail of the list (which may be cached)
334
RDFList tail = findElement( true, 0 );
335         
336         // now do the concatenate
337
setTailAux( tail, newListCell( value, listNil() ), listRest() );
338     }
339     
340     
341     /**
342      * <p>
343      * Answer the list that is this list with the given value added to the end
344      * of the list. This operation differs from {@link #add} in that it will
345      * always work, even on an empty list, but the return value is the updated
346      * list. Specifically, in the case of adding a value to the empty list, the
347      * returned list will not be the same as this list. <strong>Client code should
348      * not assume that this is an in-place update, but should ensure that the resulting
349      * list is asserted back into the graph into the appropriate relationships.</strong>
350      * </p>
351      *
352      * @param value A value to add to the end of the list
353      * @return The list that results from adding a value to the end of this list
354      */

355     public RDFList with( RDFNode value ) {
356         if (s_checkValid) {
357             checkValid();
358         }
359         
360         // if this is the empty list, we create a new node containing value - i.e. cons
361
if (isEmpty()) {
362             return cons( value );
363         }
364         
365         // get the tail of the list (which may be cached)
366
RDFList tail = findElement( true, 0 );
367         
368         // now do the concatenate
369
setTailAux( tail, newListCell( value, listNil() ), listRest() );
370         return this;
371     }
372     
373     
374     /**
375      * <p>
376      * Answer the node that is the i'th element of the list, assuming that the
377      * head is item zero. If the list is too short to have an i'th element,
378      * throws a {@link ListIndexException}.
379      * </p>
380      *
381      * @param i The index into the list, from 0
382      * @return The list value at index i, or null
383      * @exception ListIndexException if the list has fewer than (i + 1)
384      * elements.
385      */

386     public RDFNode get( int i ) {
387         if (s_checkValid) {
388             checkValid();
389         }
390         
391         checkNotNil( "Tried to get an element from the empty list" );
392         return findElement( false, i ).getHead();
393     }
394     
395     
396     /**
397      * <p>
398      * Replace the value at the i'th position in the list with the given value.
399      * If the list is too short to have an i'th element, throws a {@link
400      * ListIndexException}.
401      * </p>
402      *
403      * @param i The index into the list, from 0
404      * @param value The new value to associate with the i'th list element
405      * @return The value that was previously at position i in the list
406      * @exception ListIndexException if the list has fewer than (i + 1)
407      * elements.
408      */

409     public RDFNode replace( int i, RDFNode value ) {
410         if (s_checkValid) {
411             checkValid();
412         }
413         
414         checkNotNil( "Tried to replace a value in the empty list" );
415         return findElement( false, i ).setHead( value );
416     }
417     
418     
419     /**
420      * <p>
421      * Answer true if the given node appears as the value of a value of any
422      * of the cells of this list.
423      * </p>
424      *
425      * @param value A value to test for
426      * @return True if the list contains value.
427      */

428     public boolean contains( RDFNode value ) {
429         return indexOf( value, 0 ) >= 0;
430     }
431     
432     
433     /**
434      * <p>
435      * Answer the index of the first occurrence of the given value in the list,
436      * or -1 if the value is not in the list.
437      * </p>
438      *
439      * @param value The value to search for
440      * @return The index of the first occurrence of value in the list, or
441      * <code>-1</code> if not found.
442      */

443     public int indexOf( RDFNode value ) {
444         return indexOf( value, 0 );
445     }
446     
447     
448     /**
449      * <p>
450      * Answer the index of the first occurrence of the given value in the list
451      * after index <code>start</code>, or -1 if the value is not in the list
452      * after the given start point.
453      * </p>
454      *
455      * @param value The value to search for
456      * @param start The index into the list to start searching from
457      * @return The index (from zero, the front of the list) of the first
458      * occurrence of <code>value</code> in the list not less than
459      * <code>start</code>, or <code>-1</code> if not found.
460      * @exception ListIndexException if <code>start</code> is greater than the
461      * length of the list.
462      */

463     public int indexOf( RDFNode value, int start ) {
464         if (s_checkValid) {
465             checkValid();
466         }
467         
468         // first get to where we start
469
Resource l = findElement( false, start );
470         int index = start;
471         
472         Property head = listFirst();
473         Property tail = listRest();
474         Resource nil = listNil();
475         
476         boolean found = l.hasProperty( head, value );
477         
478         // search for the element whose value is, er, value
479
while (!found && !l.equals( nil )) {
480             l = l.getRequiredProperty( tail ).getResource();
481             index++;
482             found = l.hasProperty( head, value );
483         }
484
485         return found ? index : -1;
486     }
487     
488     
489     /**
490      * <p>
491      * Answer a new list that is formed by adding each element of this list to
492      * the head of the the list formed from the
493      * given <code>nodes</code>. This is a non side-effecting
494      * operation on either this list or the given list, but generates a copy
495      * of this list. For a more storage efficient alternative, see {@link
496      * #concatenate concatenate}.
497      * </p>
498      *
499      * @param nodes An iterator whose range is RDFNode
500      * @return A new RDFList that contains all of this elements of this list,
501      * followed by all of the elements of the given iterator.
502      */

503     public RDFList append( Iterator nodes ) {
504         return append( copy( nodes) );
505     }
506             
507             
508     /**
509      * <p>
510      * Answer a new list that is formed by adding each element of this list to
511      * the head of the given <code>list</code>. This is a non side-effecting
512      * operation on either this list or the given list, but generates a copy
513      * of this list. For a more storage efficient alternative, see {@link
514      * #concatenate concatenate}.
515      * </p>
516      *
517      * @param list The argument list
518      * @return A new RDFList that contains all of this elements of this list,
519      * followed by all of the elements of the given list.
520      */

521     public RDFList append( RDFList list ) {
522         if (s_checkValid) {
523             checkValid();
524         }
525         
526         if (isEmpty()) {
527             // special case
528
return list;
529         }
530         else {
531             // could do this recursively, but for long lists it's better to iterate
532
// do the copy, then change the last tail pointer to point to the arg
533
RDFList copy = copy( iterator() );
534             copy.concatenate( list );
535             return copy;
536         }
537     }
538     
539     
540     /**
541      * <p>
542      * Change the tail of this list to point to the given list, so that this
543      * list becomes the list of the concatenation of the elements of both lists.
544      * This is a side-effecting operation on this list; for a non side-effecting
545      * alternative, see {@link #append}. Due to the problem of maintaining
546      * the URI invariant on a node, this operation will throw an exception if an
547      * attempt is made to concatenate onto an empty list. To avoid this, test for
548      * an empty list: if true replace the empty list with the argument list, otherwise
549      * proceed with the concatenate as usual. An alternative solution is to use
550      * {@link #append} and replace the original list with the return value.
551      * </p>
552      *
553      * @param list The argument list to concatenate to this list
554      * @exception EmptyListUpdateException if this list is the nil list
555      */

556     public void concatenate( RDFList list ) {
557         if (s_checkValid) {
558             checkValid();
559         }
560         
561         if (isEmpty()) {
562             // concatenating list onto the empty list is an error
563
throw new EmptyListUpdateException( "Tried to concatenate onto the empty list" );
564         }
565         else {
566             // find the end of this list and link it to the argument list
567
findElement( true, 0 ).setTail( list );
568         }
569     }
570     
571     
572     /**
573      * <p>
574      * Add the nodes returned by the given iterator to the end of this list.
575      * </p>
576      *
577      * @param nodes An iterator whose range is RDFNode
578      * @exception EmptyListUpdateException if this list is the nil list
579      * @see #concatenate(RDFList) for details on avoiding the empty list update exception.
580      */

581     public void concatenate( Iterator nodes ) {
582         // make a list of the nodes and add to the end of this
583
concatenate( copy( nodes ) );
584     }
585             
586         
587     /**
588      * <p>
589      * Answer a list that contains all of the elements of this list in the same
590      * order, but is a duplicate copy in the underlying model.
591      * </p>
592      *
593      * @return A copy of the current list
594      */

595     public RDFList copy() {
596         if (s_checkValid) {
597             checkValid();
598         }
599         
600         return copy( iterator() );
601     }
602     
603     
604     /**
605      * <p>
606      * Apply a function to each value in the list in turn.
607      * </p>
608      *
609      * @param fn The function to apply to each list node.
610      */

611     public void apply( ApplyFn fn ) {
612         if (s_checkValid) {
613             checkValid();
614         }
615         
616         for (Iterator i = iterator(); i.hasNext(); ) {
617             fn.apply( (RDFNode) i.next() );
618         }
619     }
620     
621     
622     /**
623      * <p>
624      * Apply a function to each value in the list in turn, accumulating the
625      * results in an accumulator. The final value of the accumulator is returned
626      * as the value of <code>reduce()</code>.
627      * </p>
628      *
629      * @param fn The reduction function to apply
630      * @param initial The initial value for the accumulator
631      * @return The final value of the accumulator.
632      */

633     public Object JavaDoc reduce( ReduceFn fn, Object JavaDoc initial ) {
634         if (s_checkValid) {
635             checkValid();
636         }
637         
638         Object JavaDoc acc = initial;
639         
640         for (Iterator i = iterator(); i.hasNext(); ) {
641             acc = fn.reduce( (RDFNode) i.next(), acc );
642         }
643         
644         return acc;
645     }
646     
647     
648     /**
649      * <p>Answer an iterator of the elements of this list, to each of which
650      * the given map function has been applied.</p>
651      * @param fn A Map function
652      * @return The iterator of the elements of this list mapped with the given map function.
653      */

654     public ExtendedIterator mapWith( Map1 fn ) {
655         return iterator().mapWith( fn );
656     }
657         
658         
659     /**
660      * <p>
661      * Remove the value from the head of the list. The tail of the list remains
662      * in the model. Note that no changes are made to list cells that point to
663      * this list cell as their tail. Immediately following a
664      * <code>removeHead</code> operation, such lists will be in a non-valid
665      * state.
666      * </p>
667      *
668      * @return The remainder of the list after the head is removed (i&#046;e&#046; the
669      * pre-removal list tail)
670      */

671     public RDFList removeHead() {
672         if (s_checkValid) {
673             checkValid();
674         }
675         
676         checkNotNil( "Attempted to delete the head of a nil list" );
677         
678         RDFList tail = getTail();
679         removeProperties();
680         
681         return tail;
682     }
683     
684     
685     /**
686      * <p>Remove the given value from this list. If <code>val</code> does not occur in
687      * the list, no action is taken. Since removing the head of the list will invalidate
688      * the list head cell, in general the list must return the list that results from this
689      * operation. However, in many cases the return value will be the same as the object
690      * that this method is invoked on</p>
691      *
692      * @param val The value to be removed from the list
693      * @return The resulting list, which will be the same as the current list in most
694      * cases, except when <code>val</code> occurs at the head of the list.
695      */

696     public RDFList remove( RDFNode val ) {
697         if (s_checkValid) {
698             checkValid();
699         }
700         
701         RDFList prev = null;
702         RDFList cell = this;
703         boolean searching = true;
704         
705         while (searching && !cell.isEmpty()) {
706             if (cell.getHead().equals( val )) {
707                 // found the value to be removed
708
RDFList tail = cell.getTail();
709                 if (prev != null) {
710                     prev.setTail( tail );
711                 }
712                 
713                 cell.removeProperties();
714                 
715                 // return this unless we have removed the head element
716
return (prev == null) ? tail : this;
717             }
718             else {
719                 // not found yet
720
prev = cell;
721                 cell = cell.getTail();
722             }
723         }
724         
725         // not found
726
return this;
727     }
728         
729     
730     /**
731      * <p>Deprecated. Since an <code>RDFList</code> does not behave like a Java container, it is not
732      * the case that the contents of the list can be removed and the container filled with values
733      * again. Therefore, this method name has been deprecated in favour of {@link #removeList}</p>
734      * @deprecated Replaced by {@link #removeList}
735      */

736     public void removeAll() {
737         removeList();
738     }
739     
740     
741     /**
742      * <p>Remove all of the components of this list from the model. Once this operation
743      * has completed, the {@link RDFList} resource on which it was called will no
744      * longer be a resource in the model, so further methods calls on the list object
745      * (for example, {@link #size} will fail. Due to restrictions on the encoding
746      * of lists in RDF, it is not possible to perform an operation which empties a list
747      * and then adds further values to that list. Client code wishing to perform
748      * such an operation should do so in two steps: first remove the old list, then
749      * create a new list with the new contents. It is important that RDF statements
750      * that reference the old list (in the object position) be updated to point
751      * to the newly created list.
752      * Note that this
753      * is operation is only removing the list cells themselves, not the resources
754      * referenced by the list - unless being the object of an <code>rdf:first</code>
755      * statement is the only mention of that resource in the model.</p>
756      */

757     public void removeList() {
758         for (Iterator i = collectStatements().iterator(); i.hasNext(); ) {
759             ((Statement) i.next()).remove();
760         }
761     }
762     
763     
764     /**
765      * <p>Answer a set of all of the RDF statements whose subject is one of the cells
766      * of this list.</p>
767      * @return A list of the statements that form the encoding of this list.
768      */

769     public Set collectStatements() {
770         Set stmts = new HashSet();
771         RDFList l = this;
772         
773         do {
774             // collect all statements of this list cell
775
for (Iterator i = l.listProperties(); i.hasNext(); ) {
776                 stmts.add( i.next() );
777             }
778             
779             // move on to next cell
780
l = l.getTail();
781         } while (!l.isEmpty());
782         
783         return stmts;
784     }
785     
786     
787     /**
788      * <p>
789      * Answer an iterator over the elements of the list. Note that this iterator
790      * does not take a snapshot of the list, so changes to the list statements
791      * in the model while iterating will affect the behaviour of the iterator.
792      * To get an iterator that is not affected by model changes, use {@link
793      * #asJavaList}.
794      * </p>
795      *
796      * @return A closable iterator over the elements of the list.
797      */

798     public ExtendedIterator iterator() {
799         return new RDFListIterator( this );
800     }
801     
802     
803     /**
804      * <p>
805      * Answer the contents of this RDF list as a Java list of RDFNode values.
806      * </p>
807      *
808      * @return The contents of this list as a Java List.
809      */

810     public List asJavaList() {
811         List l = new ArrayList();
812         
813         for (Iterator i = iterator(); i.hasNext(); ) {
814             l.add( i.next() );
815         }
816         
817         return l;
818     }
819     
820     
821     /**
822      * <p>
823      * Answer true if this list has the same elements in the same order as the
824      * given list. Note that the standard <code>equals</code> test just tests
825      * for equality of two given list cells. While such a test is sufficient
826      * for many purposes, this test provides a broader equality definition, but
827      * is correspondingly more expensive to test.
828      * </p>
829      *
830      * @param list The list to test against
831      * @return True if the given list and this list are the same length, and
832      * contain equal elements in the same order.
833      */

834     public boolean sameListAs( RDFList list ) {
835         if (s_checkValid) {
836             checkValid();
837         }
838         
839         Resource r0 = this;
840         Resource r1 = list;
841         
842         Property head = listFirst();
843         Property tail = listRest();
844         Resource nil = listNil();
845         
846         // iterate through to the end of the list
847
while (!(r0.equals( nil ) || r1.equals( nil ))) {
848             RDFNode n0 = r0.getRequiredProperty( head ).getObject();
849             RDFNode n1 = r1.getRequiredProperty( head ).getObject();
850             
851             if (n0 == null || !n0.equals( n1 )) {
852                 // not equal at this position
853
return false;
854             }
855             else {
856                 // advance along the lists
857
r0 = r0.getRequiredProperty( tail ).getResource();
858                 r1 = r1.getRequiredProperty( tail ).getResource();
859             }
860         }
861         
862         // lists are equal if they terminate together
863
return r0.equals( nil ) && r1.equals( nil );
864     }
865     
866     
867     /**
868      * <p>
869      * Answer true lists are operating in strict mode, in which the
870      * well- formedness of the list is checked at every operation.
871      * </p>
872      *
873      * @return True lists are being strictly checked.
874      */

875     public boolean getStrict() {
876         return s_checkValid;
877     }
878     
879     
880     /**
881      * <p>
882      * Set a flag to indicate whether to strictly check the well-formedness of
883      * lists at each operation. Default false. Note that the flag that is
884      * manipulated is actually a static: it applies to all lists. However, RDFList
885      * is a Java interface, and Java does not permit static methods in interfaces.
886      * </p>
887      *
888      * @param strict The <b>static</b> flag for whether lists will be checked strictly.
889      */

890     public void setStrict( boolean strict ) {
891         s_checkValid = strict;
892     }
893     
894     
895     /**
896      * <p>
897      * Answer true if the list is well-formed, by checking that each node is
898      * correctly typed, and has a head and tail pointer from the correct
899      * vocabulary.
900      * </p>
901      *
902      * @return True if the list is well-formed.
903      */

904     public boolean isValid() {
905         m_errorMsg = null;
906         
907         try {
908             checkValid();
909         }
910         catch (InvalidListException e) {
911             m_errorMsg = e.getMessage();
912         }
913         
914         return (m_errorMsg == null);
915     }
916
917
918     /**
919      * <p>
920      * Answer the error message returned by the last failed validity check,
921      * if any.
922      * </p>
923      *
924      * @return The most recent error message, or null.
925      */

926     public String JavaDoc getValidityErrorMessage() {
927         return m_errorMsg;
928     }
929     
930     
931     /**
932      * <p>
933      * Construct a new list cell with the given value and tail.
934      * </p>
935      *
936      * @param value The value at the head of the new list cell
937      * @param tail The tail of the list cell
938      * @return A new list cell as a resource
939      */

940     public Resource newListCell( RDFNode value, Resource tail ) {
941         // Note: following the RDF WG decision, we no longer assert rdf:type rdf:List for list cells
942
Resource cell = getModel().createResource();
943         
944         // set the head and tail
945
cell.addProperty( listFirst(), value );
946         cell.addProperty( listRest(), tail );
947         
948         return cell;
949     }
950     
951     
952     // Internal implementation methods
953
//////////////////////////////////
954

955     /**
956      * <p>
957      * Answer true if this is a valid list cell, which means either that it
958      * is nil, or it has the appropriate type and a first and next relation.
959      * Updated 17-06-2003: RDFCore last comments process has decided that the
960      * rdf:type of a list is implied by the domain constraints on rdf:first
961      * and rdf:rest, so no longer needs to be asserted directly. The test
962      * for rdf:type has therefore been removed.
963      * </p>
964      *
965      * @return True if this list cell passes basic validity checks
966      */

967     protected void checkValid() {
968         if (!equals( listNil() )) {
969             // note that the rdf:type of list cells is now implied by the RDF M&S
970
// so we don't check explicitly
971
// checkValidProperty( RDF.type, listType() );
972

973             checkValidProperty( listFirst(), null );
974             checkValidProperty( listRest(), null );
975         }
976     }
977     
978     private void checkValidProperty( Property p, RDFNode expected ) {
979         int count = 0;
980         
981         for (StmtIterator j = getModel().listStatements( this, p, expected ); j.hasNext(); j.next()) {
982             count++;
983         }
984         
985         // exactly one value is expected
986
if (count == 0) {
987             if (log.isDebugEnabled()) {
988                 log.debug( "Failed validity check on " + toString() );
989                 for (StmtIterator i = listProperties(); i.hasNext(); ) {
990                     log.debug( " this => " + i.next() );
991                 }
992                 for (StmtIterator i = getModel().listStatements( null, null, this ); i.hasNext(); ) {
993                     log.debug( " => this " + i.next() );
994                 }
995             }
996             throw new InvalidListException( "List node " + toString() + " is not valid: it should have property " +
997                                             p.toString() +
998                                             (expected == null ? "" : ( " with value " + expected )) );
999         }
1000        else if (count > 1) {
1001            throw new InvalidListException( "List node " + toString() + " is not valid: it has more than one value for " +
1002                                            p.toString() );
1003        }
1004    }
1005    
1006    
1007    
1008    /**
1009     * <p>
1010     * Check that the current list cell is not the nil list, and throw an empty
1011     * list exception if it is.
1012     * </p>
1013     *
1014     * @param msg The context message for the empty list exception
1015     * @exception EmptyListException if the list is the nil list
1016     */

1017    protected void checkNotNil( String JavaDoc msg ) {
1018        if (isEmpty()) {
1019            throw new EmptyListException( msg );
1020        }
1021    }
1022    
1023    
1024    /**
1025     * <p>
1026     * Find and return an element of this list - either the last element before
1027     * the end of the list, or the i'th element from the front (starting from
1028     * zero). Note that this method assumes the pre-condition that
1029     * <code>this</code> is not the empty list.
1030     * </p>
1031     *
1032     * @param last If true, find the element whose tail is nil
1033     * @param index If <code>last</code> is false, find the index'th element
1034     * from the head of the list
1035     * @return The list cell
1036     * @exception ListIndexException if try to access an element beyond the end
1037     * of the list
1038     * @exception InvalidListException if try to find the end of a badly formed
1039     * list
1040     */

1041    protected RDFList findElement( boolean last, int index ) {
1042        Property tail = listRest();
1043        Resource nil = listNil();
1044        
1045        Resource l = this;
1046        int i = index;
1047        boolean found = (last && l.hasProperty( tail, nil )) || (!last && (i == 0));
1048        
1049        // search for the element whose tail is nil, or whose index is now zero
1050
while (!found && !l.equals( nil )) {
1051            l = l.getRequiredProperty( tail ).getResource();
1052            found = (last && l.hasProperty( tail, nil )) || (!last && (--i == 0));
1053        }
1054        
1055        if (!found) {
1056            // premature end of list
1057
if (!last) {
1058                throw new ListIndexException( "Tried to access element " + index + " that is beyond the length of the list" );
1059            }
1060            else {
1061                throw new InvalidListException( "Could not find last element of list (suggests list is not valid)" );
1062            }
1063        }
1064        else {
1065            return (RDFList) l.as( listAbstractionClass() );
1066        }
1067    }
1068    
1069
1070    /**
1071     * <p>
1072     * Create a copy of the list of nodes returned by an iterator.
1073     * </p>
1074     *
1075     * @param i An iterator of RDFNodes
1076     * @return A list formed from all of the nodes of i, in sequence
1077     */

1078    protected RDFList copy( Iterator i ) {
1079        Resource list = null;
1080        Resource start = null;
1081        
1082        Property head = listFirst();
1083        Property tail = listRest();
1084        Resource cellType = listType();
1085        
1086        while (i.hasNext()){
1087            // create a list cell to hold the next value from the existing list
1088
Resource cell = getModel().createResource( cellType );
1089            cell.addProperty( head, (RDFNode) i.next() );
1090                
1091            // point the previous list cell to this one
1092
if (list != null) {
1093                list.addProperty( tail, cell );
1094            }
1095            else {
1096                // must be the first cell we're adding
1097
start = cell;
1098            }
1099                
1100            list = cell;
1101        }
1102            
1103        // finally close the list
1104
list.addProperty( tail, listNil() );
1105            
1106        return (RDFList) start.as( listAbstractionClass() );
1107    }
1108    
1109    
1110    /**
1111     * <p>
1112     * Helper method for setting the list tail, that assumes we have
1113     * a resource that is a list.
1114     * </p>
1115     *
1116     * @param root The resource representing the list cell we're setting the
1117     * tail of
1118     * @param tail The new tail for this list, as a resource.
1119     * @return The old tail, as a resource.
1120     */

1121    protected static Resource setTailAux( Resource root, Resource tail, Property pTail ) {
1122        Statement current = root.getRequiredProperty( pTail );
1123        Resource oldTail = current.getResource();
1124            
1125        // out with the old, in with the new
1126
current.remove();
1127        root.addProperty( pTail, tail );
1128            
1129        return oldTail;
1130    }
1131        
1132        
1133    
1134    //==============================================================================
1135
// Inner class definitions
1136
//==============================================================================
1137

1138    /**
1139     * <p>
1140     * Iterator that can step along chains of list pointers to the end of the
1141     * list.
1142     * </p>
1143     */

1144    protected class RDFListIterator
1145        extends NiceIterator
1146    {
1147        // Instance variables
1148

1149        /** The current list node */
1150        protected RDFList m_head;
1151        
1152        /** The most recently seen node */
1153        protected RDFList m_seen = null;
1154        
1155        
1156        // Constructor
1157
//////////////
1158

1159        /**
1160         * Construct an iterator for walking the list starting at head
1161         */

1162        protected RDFListIterator( RDFList head ) {
1163            m_head = head;
1164        }
1165        
1166        
1167        // External contract methods
1168
////////////////////////////
1169

1170        /**
1171         * @see Iterator#hasNext
1172         */

1173        public boolean hasNext() {
1174            return !m_head.isEmpty();
1175        }
1176
1177        /**
1178         * @see Iterator#next
1179         */

1180        public Object JavaDoc next() {
1181            m_seen = m_head;
1182            m_head = m_head.getTail();
1183            
1184            return m_seen.getHead();
1185        }
1186
1187        /**
1188         * @see Iterator#remove
1189         */

1190        public void remove() {
1191            if (m_seen == null) {
1192                throw new IllegalStateException JavaDoc( "Illegal remove from list operator" );
1193            }
1194            
1195            // will remove three statements in a well-formed list
1196
((Resource) m_seen).removeProperties();
1197            m_seen = null;
1198        }
1199    }
1200}
1201
1202
1203/*
1204    (c) Copyright 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
1205    All rights reserved.
1206
1207    Redistribution and use in source and binary forms, with or without
1208    modification, are permitted provided that the following conditions
1209    are met:
1210
1211    1. Redistributions of source code must retain the above copyright
1212       notice, this list of conditions and the following disclaimer.
1213
1214    2. Redistributions in binary form must reproduce the above copyright
1215       notice, this list of conditions and the following disclaimer in the
1216       documentation and/or other materials provided with the distribution.
1217
1218    3. The name of the author may not be used to endorse or promote products
1219       derived from this software without specific prior written permission.
1220
1221    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1222    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1223    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1224    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1225    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1226    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1227    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1228    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1229    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1230    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1231*/

1232
Popular Tags