blob: 7be7ad63ff4fc187e8f49c40bb7bc2e580dbf98d [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.containers;
import com.intellij.openapi.util.Condition;
import com.intellij.reference.SoftReference;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
/**
* Implementation of the {@link java.util.List} interface which:
* <ul>
* <li>Stores elements using weak semantics (see {@link java.lang.ref.WeakReference})</li>
* <li>Automatically reclaims storage for garbage collected elements</li>
* <li>Is NOT thread safe</li>
* <li>Is NOT RandomAccess, because garbage collector can remove element at any time</li>
* <li>Does NOT support null elements</li>
* </ul>
*/
public class UnsafeWeakList<T> extends AbstractList<T> {
protected final List<MyReference<T>> myList;
private final ReferenceQueue<T> myQueue = new ReferenceQueue<T>();
private int myAlive;
private static class MyReference<T> extends WeakReference<T> {
private final int index;
MyReference(int index, T referent, ReferenceQueue<? super T> queue) {
super(referent, queue);
this.index = index;
}
}
public UnsafeWeakList() {
myList = new ArrayList<MyReference<T>>();
}
public UnsafeWeakList(int capacity) {
myList = new ArrayList<MyReference<T>>(capacity);
}
boolean processQueue() {
boolean processed = false;
MyReference<T> reference;
while ((reference = (MyReference<T>)myQueue.poll()) != null) {
int index = reference.index;
if (index < myList.size() && reference == myList.get(index)) { // list may have changed while the reference was dangling in queue
nullizeAt(index);
}
processed = true;
}
if (myAlive < myList.size() / 2) {
reduceCapacity();
}
return processed;
}
private void nullizeAt(int index) {
myList.set(index, null);
myAlive--;
}
private void reduceCapacity() {
int toSaveAlive = 0;
for (int i=0; i<myList.size();i++) {
MyReference<T> reference = myList.get(i);
if (reference == null) continue;
T t = reference.get();
if (t == null) {
myAlive--;
continue;
}
if (toSaveAlive != i) {
myList.set(toSaveAlive, new MyReference<T>(toSaveAlive, t, myQueue));
}
toSaveAlive++;
}
if (toSaveAlive != myList.size()) {
myList.subList(toSaveAlive, myList.size()).clear();
modCount++;
}
myAlive = toSaveAlive;
}
private void append(@NotNull T element) {
myList.add(new MyReference<T>(myList.size(), element, myQueue));
myAlive++;
modCount++;
}
@Override
public boolean add(@NotNull T element) {
processQueue();
append(element);
return true;
}
public boolean addIfAbsent(@NotNull T element) {
processQueue();
if (contains(element)) return false;
append(element);
return true;
}
@Override
public void clear() {
processQueue();
myList.clear();
myAlive = 0;
modCount++;
}
@TestOnly
int listSize() {
return myList.size();
}
@NotNull
@Override
public Iterator<T> iterator() {
return new MyIterator();
}
private class MyIterator implements Iterator<T> {
private final int startModCount;
int curIndex;
T curElement;
int nextIndex = -1;
T nextElement;
private MyIterator() {
startModCount = modCount;
findNext();
}
private boolean findNext() {
if (modCount != startModCount) throw new ConcurrentModificationException();
curIndex = nextIndex;
curElement = nextElement;
nextElement = null;
nextIndex = -1;
for (int i= curIndex +1; i<myList.size();i++) {
T t = SoftReference.dereference(myList.get(i));
if (t != null) {
nextElement = t;
nextIndex = i;
return true;
}
}
return false;
}
@Override
public boolean hasNext() {
return nextElement != null;
}
@Override
public T next() {
if (!hasNext()) throw new NoSuchElementException();
findNext();
return curElement;
}
@Override
public void remove() {
if (curElement == null) throw new NoSuchElementException();
int index = curIndex;
nullizeAt(index);
}
}
@Override
public boolean remove(@NotNull Object o) {
processQueue();
for (int i = 0; i < myList.size(); i++) {
T t = SoftReference.dereference(myList.get(i));
if (t != null && t.equals(o)) {
nullizeAt(i);
return true;
}
}
return false;
}
@Override
public boolean addAll(@NotNull Collection<? extends T> c) {
processQueue();
return super.addAll(c);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
processQueue();
return super.removeAll(c);
}
private static final Function<MyReference<Object>, Object> DEREF = new Function<MyReference<Object>, Object>() {
@Override
public Object fun(MyReference<Object> reference) {
return SoftReference.dereference(reference);
}
};
private static <X> Function<MyReference<X>, X> deref() {
return (Function)DEREF;
}
@NotNull
public List<T> toStrongList() {
Function<MyReference<T>, T> deref = deref();
return ContainerUtil.mapNotNull(myList, deref);
}
@Override
public int size() {
throwNotRandomAccess();
return -1;
}
@Override
public boolean isEmpty() {
if (myList.isEmpty()) return true;
Condition<MyReference<T>> notNull = notNull();
return ContainerUtil.find(myList, notNull) == null;
}
private static <T> Condition<MyReference<T>> notNull() {
return (Condition)NOT_NULL;
}
private static final Condition<MyReference<Object>> NOT_NULL = new Condition<MyReference<Object>>() {
@Override
public boolean value(MyReference<Object> reference) {
return SoftReference.dereference(reference) != null;
}
};
@Override
public T set(int index, T element) {
return throwNotRandomAccess();
}
@Override
public int indexOf(Object o) {
throwNotRandomAccess();
return -1;
}
@Override
public int lastIndexOf(Object o) {
throwNotRandomAccess();
return -1;
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
throwNotRandomAccess();
return false;
}
@NotNull
@Override
public List<T> subList(int fromIndex, int toIndex) {
throwNotRandomAccess();
return this;
}
@Override
public void add(int index, T element) {
throwNotRandomAccess();
}
@Override
public T remove(int index) {
return throwNotRandomAccess();
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
throwNotRandomAccess();
}
@Override
public T get(int index) {
return throwNotRandomAccess();
}
private T throwNotRandomAccess() {
throw new IncorrectOperationException("UnsafeWeakList is not RandomAccess, use list.iterator()");
}
}