Source code: org/progeeks/meta/AbstractListMutator.java
1 /*
2 * $Id: AbstractListMutator.java,v 1.1 2003/08/27 21:39:23 pspeed Exp $
3 *
4 * Copyright (c) 2001-2002, Paul Speed
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1) Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 * 2) Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3) Neither the names "Progeeks", "Meta-JB", nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 package org.progeeks.meta;
34
35 import java.beans.*;
36 import java.util.*;
37
38 import org.progeeks.meta.*;
39
40 /**
41 * <p>Abstract implementation of ListMutator providing basic
42 * functionality for dealing with List values.</p>
43 *
44 * <p>Subclasses are required to implement at least the
45 * get(int) and size() methods for read-only access.</p>
46 *
47 * <p>For a modifiable list, set(int, object) should be overriden
48 * and for a resizble list, add(object) and remove(int) should be
49 * overridden. Otherwise, these methods throw UnsupportedOperation
50 * exception.</p>
51 *
52 * <p>To support change notification, subclasses must provide
53 * an implementation of the firePropertyChangeEvent() method.</p>
54 *
55 * Note: this may eventually move to the meta package
56 * and be re-extended here just to provide the ability
57 * to do external change notifications.
58 *
59 * @version $Revision: 1.1 $
60 * @author Paul Speed
61 */
62 public abstract class AbstractListMutator implements ListMutator
63 {
64 /**
65 * Returns the number of elements in the list value.
66 */
67 public abstract int size();
68
69 /**
70 * Returns the object at the specified location.
71 */
72 public abstract Object get( int index );
73
74 /**
75 * Adds the object to the list value at the specified index.
76 *
77 * @throws UnsupportedOperationException if the list value does
78 * not support insertions.
79 */
80 public void add( int index, Object object )
81 {
82 throw new UnsupportedOperationException();
83 }
84
85 /**
86 * Replaces the value at the specified location.
87 *
88 * @throws UnsupportedOperationException if the list value does
89 * not support replacing elements.
90 */
91 public Object set( int index, Object object )
92 {
93 throw new UnsupportedOperationException();
94 }
95
96 /**
97 * Removes the object at the specified location and returns
98 * the locations value prior to removal.
99 *
100 * @throws UnsupportedOperationException if the list value does
101 * not support removes.
102 */
103 public Object remove( int index )
104 {
105 throw new UnsupportedOperationException();
106 }
107
108 /**
109 * Returns an iterator over the elements in this list value.
110 */
111 public Iterator iterator()
112 {
113 return( new IndexIterator() );
114 }
115
116 /**
117 * Adds the object to the list value, returning true
118 * if the contents of the list actually changed as a result
119 * of the operation.
120 *
121 * @throws UnsupportedOperationException if the list value does
122 * not support adds.
123 */
124 public boolean add( Object object )
125 {
126 add( size(), object );
127
128 return( true );
129 }
130
131 /**
132 * Removes the specified object from the list value, returning true
133 * if the list contents were modified as a result of the action.
134 *
135 * @throws UnsupportedOperationException if the list value does
136 * not support removes.
137 */
138 public boolean remove( Object object )
139 {
140 int index = 0;
141 for( Iterator i = iterator(); i.hasNext(); index++ )
142 {
143 Object element = i.next();
144 if( object == element || (object != null && object.equals(element)) )
145 {
146 remove( index );
147 return( true );
148 }
149 }
150 return( false );
151 }
152
153 /**
154 * Clears the contents of the list value.
155 *
156 * @throws UnsupportedOperationException if the list value does
157 * not support being cleared.
158 */
159 public void clear()
160 {
161 while( size() > 0 )
162 remove( 0 );
163 }
164
165 /**
166 * Implemented by subclasses to deliver a change event to
167 * the appropriately registered listeners.
168 */
169 protected abstract void firePropertyChangeEvent( PropertyChangeEvent event );
170
171 /**
172 * Sends an event to interested listeners that this property
173 * has had elements inserted. These methods are public so that
174 * changes made external to the internal data structures can still
175 * cause notifications.
176 */
177 public void fireElementsInserted( int first, int last )
178 {
179 PropertyChangeEvent event = new ListPropertyChangeEvent( getParentObject(),
180 getPropertyName(),
181 getValue(), first, last,
182 ListPropertyChangeEvent.INSERT );
183 firePropertyChangeEvent( event );
184 }
185
186 /**
187 * Sends an event to interested listeners that this property
188 * has had elements inserted. These methods are public so that
189 * changes made external to the internal data structures can still
190 * cause notifications.
191 */
192 public void fireElementsModified( int first, int last )
193 {
194 PropertyChangeEvent event = new ListPropertyChangeEvent( getParentObject(),
195 getPropertyName(),
196 getValue(), first, last,
197 ListPropertyChangeEvent.UPDATE );
198 firePropertyChangeEvent( event );
199 }
200
201 /**
202 * Sends an event to interested listeners that this property
203 * has had elements inserted. These methods are public so that
204 * changes made external to the internal data structures can still
205 * cause notifications.
206 */
207 public void fireElementsRemoved( int first, int last )
208 {
209 PropertyChangeEvent event = new ListPropertyChangeEvent( getParentObject(),
210 getPropertyName(),
211 getValue(), first, last,
212 ListPropertyChangeEvent.DELETE );
213 firePropertyChangeEvent( event );
214 }
215
216 private class IndexIterator implements Iterator
217 {
218 /*
219 * Note: no concurrent modification checks are done.
220 * This needs to be added.
221 */
222
223 /**
224 * Next element that iwll be returned by next().
225 */
226 int nextIndex = 0;
227
228 /**
229 * Last index returned by next().
230 */
231 int lastIndex = -1;
232
233 public boolean hasNext()
234 {
235 return( nextIndex != size() );
236 }
237
238 public Object next()
239 {
240 try
241 {
242 Object ret = get( nextIndex );
243 lastIndex = nextIndex;
244 nextIndex++;
245
246 return( ret );
247 }
248 catch( IndexOutOfBoundsException e )
249 {
250 throw new NoSuchElementException( String.valueOf(nextIndex) );
251 }
252 }
253
254 public void remove()
255 {
256 if( lastIndex == -1 )
257 throw new IllegalStateException( "remove() called before next()." );
258
259 try
260 {
261 AbstractListMutator.this.remove( lastIndex );
262 if( lastIndex < nextIndex )
263 nextIndex--;
264 lastIndex = -1;
265 }
266 catch( IndexOutOfBoundsException e )
267 {
268 // Can't really be anything else at this point.
269 throw new ConcurrentModificationException();
270 }
271 }
272 }
273 }
274