1 /*
2  * Created on 15-Mar-2006
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package com.aelitis.azureus.core.util;
21 
22 import java.lang.ref.WeakReference;
23 import java.util.*;
24 
25 import org.gudy.azureus2.core3.util.AEDiagnostics;
26 import org.gudy.azureus2.core3.util.AEDiagnosticsEvidenceGenerator;
27 import org.gudy.azureus2.core3.util.Constants;
28 import org.gudy.azureus2.core3.util.Debug;
29 import org.gudy.azureus2.core3.util.IndentWriter;
30 
31 public class
32 CopyOnWriteList<T>
33 implements Iterable<T>
34 {
35 	private static final boolean LOG_STATS = false;
36 
37 	//private int mutation_count = 0;
38 
39 	private List<T>	list = Collections.EMPTY_LIST;
40 
41 	private final boolean	use_linked_list;
42 
43 	private boolean	visible = false;
44 
45 	private int initialCapacity;
46 
47 	private static CopyOnWriteList stats;
48 
49 	private int	mutation_count;
50 
51 	static {
52 		if (LOG_STATS) {
53 			stats = new CopyOnWriteList(10);
AEDiagnostics.addEvidenceGenerator(new AEDiagnosticsEvidenceGenerator() { public void generate(IndentWriter writer) { writer.println(R); writer.indent(); try { long count = 0; long size = 0; for (Iterator iter = stats.iterator(); iter.hasNext();) { WeakReference wf = (WeakReference) iter.next(); CopyOnWriteList cowList = (CopyOnWriteList) wf.get(); if (cowList != null) { count++; size += cowList.size(); } } writer.println(count + R + size + R); if ( count > 0 ){ writer.println((size/count) + R); } } catch (Throwable t) { } finally { writer.exdent(); } } })54 			AEDiagnostics.addEvidenceGenerator(new AEDiagnosticsEvidenceGenerator() {
55 				public void generate(IndentWriter writer) {
56 					writer.println("COWList Info");
57 					writer.indent();
58 					try {
59 						long count = 0;
60 						long size = 0;
61 						for (Iterator iter = stats.iterator(); iter.hasNext();) {
62 							WeakReference wf = (WeakReference) iter.next();
63 							CopyOnWriteList cowList = (CopyOnWriteList) wf.get();
64 							if (cowList != null) {
65 								count++;
66 								size += cowList.size();
67 							}
68 						}
69 						writer.println(count + " lists with " + size + " total entries");
70 						if ( count > 0 ){
71 							writer.println((size/count) + " avg size");
72 						}
73 					} catch (Throwable t) {
74 					} finally {
75 						writer.exdent();
76 					}
77 				}
78 			});
79 		}
80 	}
81 
82 	/**
83 	 * @param i
84 	 */
CopyOnWriteList(int initialCapacity)85 	public CopyOnWriteList(int initialCapacity) {
86 		this.initialCapacity = initialCapacity;
87 		use_linked_list = false;
88 		if (LOG_STATS) {
89 			stats.add(new WeakReference(this));
90 		}
91 	}
92 
93 	/**
94 	 *
95 	 */
CopyOnWriteList()96 	public CopyOnWriteList() {
97 		// Smaller default initial capacity as most of our lists are small
98 		// Last check on 7/24/2008: 444 lists with 456 total entries
99 		this.initialCapacity = 1;
100 		use_linked_list = false;
101 		if (LOG_STATS) {
102 			stats.add(new WeakReference(this));
103 		}
104 	}
105 
106 	public
CopyOnWriteList(boolean _use_linked_list )107 	CopyOnWriteList(boolean _use_linked_list ) {
108 		this.initialCapacity = 1;
109 		use_linked_list = _use_linked_list;
110 		if (LOG_STATS) {
111 			stats.add(new WeakReference(this));
112 		}
113 	}
114 
115 	public int
getMutationCount()116 	getMutationCount()
117 	{
118 		synchronized( this ){
119 
120 			return( mutation_count );
121 		}
122 	}
123 
124 	public void
add( T obj )125 	add(
126 		T	obj )
127 	{
128 		synchronized( this ){
129 
130 			mutation_count++;
131 
132 			if ( visible ){
133 
134 				List<T>	new_list = use_linked_list?new LinkedList<T>( list ):new ArrayList<T>( list );
135 
136 				//mutated();
137 
138 				new_list.add( obj );
139 
140 				list	= new_list;
141 
142 				visible = false;
143 
144 			}else{
145 				if (list == Collections.EMPTY_LIST) {
146 					list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity);
147 				}
148 
149 				list.add( obj );
150 			}
151 		}
152 	}
153 
154 		/**
155 		 *
156 		 * @param obj
157 		 * @return true if added, false if not
158 		 */
159 
160 	public boolean
addIfNotPresent( T obj )161 	addIfNotPresent(
162 		T	obj )
163 	{
164 		synchronized( this ){
165 
166 			if ( list.contains( obj )){
167 
168 				return(false );
169 			}else{
170 
171 				add( obj );
172 
173 				return( true );
174 			}
175 		}
176 	}
177 
178 	public void
add( int index, T obj )179 	add(
180 		int	index,
181 		T	obj )
182 	{
183 		if ( Constants.IS_CVS_VERSION && use_linked_list ){
184 			Debug.out( "hmm" );
185 		}
186 		synchronized( this ){
187 
188 			mutation_count++;
189 
190 			if ( visible ){
191 
192 				List<T>	new_list = use_linked_list?new LinkedList<T>(list):new ArrayList<T>( list );
193 
194 				//mutated();
195 
196 				new_list.add( index, obj );
197 
198 				list	= new_list;
199 
200 				visible = false;
201 
202 			}else{
203 				if (list == Collections.EMPTY_LIST) {
204 					list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity);
205 				}
206 
207 				list.add( index, obj );
208 			}
209 		}
210 	}
211 
212 	public void
addAll( Collection<T> c )213 	addAll(
214 		Collection<T>	c )
215 	{
216 		synchronized( this ){
217 
218 			mutation_count++;
219 
220 			if ( visible ){
221 
222 				List<T>	new_list = use_linked_list?new LinkedList<T>( list ):new ArrayList<T>( list );
223 
224 				//mutated();
225 
226 				new_list.addAll( c );
227 
228 				list	= new_list;
229 
230 				visible = false;
231 
232 			}else{
233 				if (list == Collections.EMPTY_LIST) {
234 					list = use_linked_list?new LinkedList<T>():new ArrayList<T>(initialCapacity);
235 				}
236 
237 				list.addAll( c );
238 			}
239 		}
240 	}
241 
242 	public T
get( int index )243 	get(
244 		int		index )
245 	{
246 		if ( Constants.IS_CVS_VERSION && use_linked_list ){
247 			Debug.out( "hmm" );
248 		}
249 
250 		synchronized( this ){
251 
252 			return( list.get(index));
253 		}
254 	}
255 
256 	public boolean
remove( T obj )257 	remove(
258 		T	obj )
259 	{
260 		synchronized( this ){
261 
262 			mutation_count++;
263 
264 			if ( visible ){
265 
266 				List<T>	new_list = use_linked_list?new LinkedList<T>(list):new ArrayList<T>( list );
267 
268 				//mutated();
269 
270 				boolean result = new_list.remove( obj );
271 
272 				list	= new_list;
273 
274 				visible = false;
275 
276 				return( result );
277 
278 			}else{
279 
280 				return( list.remove( obj ));
281 			}
282 		}
283 	}
284 
285 	public void
clear()286 	clear()
287 	{
288 		synchronized( this ){
289 
290 			mutation_count++;
291 
292 			list	= Collections.EMPTY_LIST;
293 
294 			visible = false;
295 		}
296 	}
297 
298 	public boolean
contains( T obj )299 	contains(
300 		T	obj )
301 	{
302 		synchronized( this ){
303 
304 			return( list.contains( obj ));
305 		}
306 	}
307 
308 	public Iterator<T>
iterator()309 	iterator()
310 	{
311 		synchronized( this ){
312 
313 			visible = true;
314 
315 			return( new CopyOnWriteListIterator( list.iterator()));
316 		}
317 	}
318 
319 	public List<T>
getList()320 	getList()
321 	{
322 			// TODO: we need to either make this a read-only-list or obey the copy-on-write semantics correctly...
323 
324 		synchronized( this ){
325 
326 			visible = true;
327 
328 			if ( Constants.IS_CVS_VERSION ){
329 
330 				return( Collections.unmodifiableList( list ));
331 
332 			}else{
333 
334 				return( list );
335 			}
336 		}
337 	}
338 
339 	public int
size()340 	size()
341 	{
342 		synchronized( this ){
343 
344 			return( list.size());
345 		}
346 	}
347 
348 	public boolean
isEmpty()349 	isEmpty()
350 	{
351 		synchronized( this ){
352 
353 			return list.isEmpty();
354 		}
355 	}
356 
357 	public Object[]
toArray()358 	toArray()
359 	{
360 		synchronized( this ){
361 
362 			return( list.toArray());
363 		}
364 	}
365 
366 	public T[]
toArray( T[] x )367   	toArray(
368   		T[]	 x )
369   	{
370 		synchronized( this ){
371 
372 			return( list.toArray(x));
373 		}
374   	}
375 
376 	/*
377 	private void
378 	mutated()
379 	{
380 		mutation_count++;
381 
382 		if ( mutation_count%10 == 0 ){
383 
384 			System.out.println( this + ": mut=" + mutation_count );
385 		}
386 	}
387 	*/
388 
389 	private class
390 	CopyOnWriteListIterator
391 		implements Iterator<T>
392 	{
393 		private Iterator<T>	it;
394 		private T			last;
395 
396 		protected
CopyOnWriteListIterator( Iterator<T> _it )397 		CopyOnWriteListIterator(
398 			Iterator<T>		_it )
399 		{
400 			it		= _it;
401 		}
402 
403 		public boolean
hasNext()404 		hasNext()
405 		{
406 			return( it.hasNext());
407 		}
408 
409 		public T
next()410 		next()
411 		{
412 			last	= it.next();
413 
414 			return( last );
415 		}
416 
417 		public void
remove()418 		remove()
419 		{
420 				// don't actually remove it from the iterator. can't go backwards with this iterator so this is
421 				// not a problem
422 
423 			if ( last == null ){
424 
425 				throw( new IllegalStateException( "next has not been called!" ));
426 			}
427 
428 			CopyOnWriteList.this.remove( last );
429 		}
430 	}
431 
getInitialCapacity()432 	public int getInitialCapacity() {
433 		return initialCapacity;
434 	}
435 
setInitialCapacity(int initialCapacity)436 	public void setInitialCapacity(int initialCapacity) {
437 		this.initialCapacity = initialCapacity;
438 	}
439 }
440