/* * Copyright 2000-2011 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.diagnostic.Logger; import org.jetbrains.annotations.NotNull; 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> * </ul> */ public class UnsafeWeakList<T> extends AbstractList<T> { private static final Logger LOG = Logger.getInstance("#com.intellij.util.containers.UnsafeWeakList"); protected final WeakReferenceArray<T> myArray; public UnsafeWeakList() { this(new WeakReferenceArray<T>()); } // For testing only UnsafeWeakList(@NotNull WeakReferenceArray<T> array) { myArray = array; } @Override public T get(int index) { return myArray.get(index); } @Override public boolean add(T element) { tryReduceCapacity(-1); myArray.add(element); return true; } @Override public boolean contains(Object o) { return super.contains(o); } public boolean addIfAbsent(T element) { tryReduceCapacity(-1); if (contains(element)) return false; myArray.add(element); return true; } @Override public void add(int index, T element) { tryReduceCapacity(-1); myArray.add(index, element); } @Override public T remove(int index) { tryReduceCapacity(-1); return myArray.remove(index); } @Override protected void removeRange(int fromIndex, int toIndex) { for (int i = fromIndex; i < toIndex; i++) { myArray.remove(i); } tryReduceCapacity(-1); } @NotNull @Override public Iterator<T> iterator() { return new MyIterator(); } @Override public int size() { return myArray.size(); } public void clear(int index) { myArray.removeReference(index); } public List<T> toStrongList() { List<T> result = new ArrayList<T>(myArray.size()); myArray.toStrongCollection(result); return result; } private int tryReduceCapacity(int trackIndex) { modCount++; if (canReduceCapacity()) { return myArray.reduceCapacity(trackIndex); } else { return probablyCompress(trackIndex); } } private int myCompressCountdown = 10; private int probablyCompress(int trackIndex) { myCompressCountdown--; if (myCompressCountdown > 0) return trackIndex; int newIndex = myArray.compress(trackIndex); myCompressCountdown = myArray.size() + 10; return newIndex; } private boolean canReduceCapacity() { return WeakReferenceArray.MINIMUM_CAPACITY * 2 < myArray.getCapacity() && myArray.getCapacity() > myArray.getAliveCount() * 3; } protected class MyIterator implements Iterator<T> { private int myNextIndex = -1; private int myCurrentIndex = -1; private T myNextElement = null; private int myModCount = modCount; public MyIterator() { findNext(); } protected void findNext() { myNextElement = null; while (myNextElement == null) { myNextIndex = myArray.nextValid(myNextIndex); if (myNextIndex >= myArray.size()) { myNextIndex = -1; myNextElement = null; return; } myNextElement = myArray.get(myNextIndex); } } @Override public boolean hasNext() { return myNextElement != null; } @Override public T next() { if (modCount != myModCount) throw new ConcurrentModificationException(); if (myNextElement == null) throw new NoSuchElementException(); T element = myNextElement; myCurrentIndex = myNextIndex; findNext(); return element; } @Override public void remove() { if (myCurrentIndex == -1) throw new IllegalStateException(); myArray.remove(myCurrentIndex); final int removedIndex = myCurrentIndex; int newIndex = tryReduceCapacity(myNextIndex); myCurrentIndex = -1; myModCount = modCount; if (!hasNext()) return; if (newIndex < 0) { LOG.error(" was: " + myNextIndex + " got: " + newIndex + " size: " + myArray.size() + " current: " + removedIndex); } myNextIndex = newIndex; LOG.assertTrue(myArray.get(myNextIndex) == myNextElement); } } }