org.apache.jackrabbit.util.WeakIdentityCollection Maven / Gradle / Ivy
Show all versions of jackrabbit-jcr-commons Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.jackrabbit.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.BitSet;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
/**
* WeakIdentityCollection
implements a Collection with weak values.
* Equality of elements is tested using the == operator.
*
* This collection does not hide the fact that the garbage collector will remove
* a mapping at some point in time. Thus, the {@link java.util.Iterator} returned
* by this collection might return null
values. The same applies
* to the method {@link #toArray()} in both its variants.
*/
public class WeakIdentityCollection implements Collection {
/**
* The weak references.
*/
private transient WeakRef[] elementData;
/**
* The current number of elements in {@link #elementData}.
*/
private int size;
/**
* The reference queue to poll for references that point to unreachable
* objects.
*/
private final ReferenceQueue refQueue = new ReferenceQueue();
/**
* BitSet where a set bit indicates that the slot at this position in
* {@link #elementData} is empty and can be reused.
*/
private final BitSet emptySlots = new BitSet();
/**
* Creates a new WeakIdentityCollection.
*
* @param initialCapacity the initial capacity.
*/
public WeakIdentityCollection(int initialCapacity) {
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
this.elementData = new WeakRef[initialCapacity];
}
/**
* Returns the current size of this collection.
*
* @return the current size of this collection.
*/
public int size() {
return size;
}
/**
* Returns true
if this collection is empty.
*
* @return true
if this collection is empty.
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Releases all references held by this collection.
*/
public void clear() {
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
emptySlots.clear();
}
/**
* Adds object o
to this collection.
*
* @param o the object to add.
* @return always true
as this collection allows duplicates.
* @throws NullPointerException if o
is null
.
*/
public boolean add(Object o) {
if (o == null) {
throw new NullPointerException("Object must not be null");
}
// poll refQueue for a slot we can reuse
WeakRef ref = (WeakRef) refQueue.poll();
if (ref != null) {
elementData[ref.index] = new WeakRef(o, ref.index);
cleanQueue();
} else if (!emptySlots.isEmpty()) {
int idx = emptySlots.nextSetBit(0);
elementData[idx] = new WeakRef(o, idx);
emptySlots.clear(idx);
} else {
ensureCapacity(size + 1);
elementData[size++] = new WeakRef(o, size - 1);
}
return true;
}
/**
* Returns true
if this collection contains o
.
*
* @param o element whose presence in this collection is to be tested.
* @return true
if this collection contains the specified
* element
*/
public boolean contains(Object o) {
for (int i = 0; i < size; i++) {
if (elementData[i].get() == o) {
return true;
}
}
return false;
}
/**
* Removes the object o
from this collection if it is present.
*
* @param o the object to remove.
* @return true
if this collection changed as a result of the
* call.
*/
public boolean remove(Object o) {
for (int i = 0; i < size; i++) {
if (elementData[i].get() == o) {
emptySlots.set(i);
// overwrite entry with dummy ref
elementData[i] = new WeakRef(null, i);
return true;
}
}
return false;
}
/**
* @throws UnsupportedOperationException always.
*/
public boolean addAll(Collection c) {
throw new UnsupportedOperationException("addAll");
}
/**
* @throws UnsupportedOperationException always.
*/
public boolean containsAll(Collection c) {
throw new UnsupportedOperationException("containsAll");
}
/**
* @throws UnsupportedOperationException always.
*/
public boolean removeAll(Collection c) {
throw new UnsupportedOperationException("removeAll");
}
/**
* @throws UnsupportedOperationException always.
*/
public boolean retainAll(Collection c) {
throw new UnsupportedOperationException("retainAll");
}
/**
* Returns an {@link java.util.Iterator} over the elements of this
* collection. The returned iterator is not fail-fast. That is, it does
* not throw a {@link java.util.ConcurrentModificationException} if this
* collection is modified while iterating over the collection.
*
* @return an {@link java.util.Iterator} over the elements of this
* collection.
*/
public Iterator iterator() {
return new Iter();
}
/**
* Returns an array containing all of the elements in this collection. The
* returned array may contain null
elements!
*
* @return an array containing all of the elements in this collection.
*/
public Object[] toArray() {
Object[] result = new Object[size];
for (int i = 0; i < result.length; i++) {
result[i] = elementData[i].get();
}
return result;
}
/**
* The returned array may contain null
elements!
* {@inheritDoc}
*/
public Object[] toArray(Object a[]) {
if (a.length < size) {
a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
}
for (int i = 0; i < size; i++) {
a[i] = elementData[i].get();
}
if (a.length > size) {
a[size] = null;
}
return a;
}
/**
* Ensures that the internal {@link #elementData} has
* minCapacity
.
*
* @param minCapacity the minimal capacity to ensure.
*/
private void ensureCapacity(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = new WeakRef[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, size);
}
}
/**
* Polls the reference queue until no reference is available anymore.
*/
private void cleanQueue() {
WeakRef ref;
while ((ref = (WeakRef) refQueue.poll()) != null) {
emptySlots.set(ref.index);
}
}
/**
* Iterator implementation for this collecation.
*/
private final class Iter implements Iterator {
/**
* current index.
*/
private int index;
/**
* The current element data.
*/
private Reference[] elements = elementData;
/**
* The current size of this collection.
*/
private int size = WeakIdentityCollection.this.size;
/**
* @throws UnsupportedOperationException always.
*/
public void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* @inheritDoc
*/
public boolean hasNext() {
return index < size;
}
/**
* @inheritDoc
*/
public Object next() {
if (index >= size) {
throw new NoSuchElementException();
}
return elements[index++].get();
}
}
/**
* Weak reference with index value that points to the slot in {@link
* WeakIdentityCollection#elementData}.
*/
private final class WeakRef extends WeakReference {
/**
* The index of this weak reference.
*/
private final int index;
/**
* Creates a new WeakRef.
*
* @param referent object the new weak reference will refer to.
* @param index the index of this weak reference.
*/
public WeakRef(Object referent, int index) {
super(referent, refQueue);
this.index = index;
}
}
}