blob: 4d0435ebb43c71b5dc6f9ecc6af0e7df9b8531ea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-present Sonatype, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.inject;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* NON-thread-safe {@link Collection} of elements kept alive by soft/weak {@link Reference}s.
*/
final class MildElements<T>
extends AbstractCollection<T>
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private final ReferenceQueue<T> queue = new ReferenceQueue<T>();
final List<Reference<T>> list;
private final boolean soft;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
MildElements( final List<Reference<T>> list, final boolean soft )
{
this.list = list;
this.soft = soft;
}
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
@Override
public boolean add( final T element )
{
compact();
return list.add( soft ? new Soft<T>( element, queue, list.size() )
: new Weak<T>( element, queue, list.size() ) );
}
@Override
public int size()
{
compact();
return list.size();
}
@Override
public Iterator<T> iterator()
{
compact();
return new Itr();
}
// ----------------------------------------------------------------------
// Implementation methods
// ----------------------------------------------------------------------
/**
* Compacts the list by replacing unreachable {@link Reference}s with ones from the end.
*/
private void compact()
{
for ( Reference<? extends T> ref; ( ref = queue.poll() ) != null; )
{
evict( ref );
}
}
/**
* Evicts a single {@link Reference} from the list; replacing it with one from the end.
*
* @param ref The reference to evict
*/
void evict( final Reference<? extends T> ref )
{
final int index = ( (Indexable) ref ).index( -1 );
if ( index >= 0 )
{
final Reference<T> last = list.remove( list.size() - 1 );
if ( ref != last )
{
( (Indexable) last ).index( index );
list.set( index, last );
}
}
}
// ----------------------------------------------------------------------
// Implementation types
// ----------------------------------------------------------------------
/**
* Represents an element that can be indexed.
*/
private interface Indexable
{
int index( int index );
}
/**
* {@link Iterator} that iterates over reachable {@link Reference}s in the list.
*/
final class Itr
implements Iterator<T>
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private int index;
private T nextElement;
private boolean haveElement;
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public boolean hasNext()
{
// find next element that is still reachable
while ( null == nextElement && index < list.size() )
{
nextElement = list.get( index++ ).get();
}
return null != nextElement;
}
public T next()
{
haveElement = hasNext();
if ( haveElement )
{
// populated by hasNext()
final T element = nextElement;
nextElement = null;
return element;
}
throw new NoSuchElementException();
}
public void remove()
{
if ( haveElement )
{
evict( list.get( --index ) );
haveElement = false;
}
else
{
throw new IllegalStateException();
}
}
}
/**
* Soft {@link Indexable} element.
*/
private static final class Soft<T>
extends SoftReference<T>
implements Indexable
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private int index;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
Soft( final T value, final ReferenceQueue<T> queue, final int index )
{
super( value, queue );
this.index = index;
}
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public int index( final int newIndex )
{
final int oldIndex = index;
index = newIndex;
return oldIndex;
}
}
/**
* Weak {@link Indexable} element.
*/
private static final class Weak<T>
extends WeakReference<T>
implements Indexable
{
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private int index;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
Weak( final T value, final ReferenceQueue<T> queue, final int index )
{
super( value, queue );
this.index = index;
}
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
public int index( final int newIndex )
{
final int oldIndex = index;
index = newIndex;
return oldIndex;
}
}
}