/*******************************************************************************
* Copyright 2012 Analog Devices, Inc.
*
* 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.analog.lyric.collect;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.common.collect.Ordering;
@NonNullByDefault(false) // to avoid clashes with methods overridden from standard library
public class SkipSet<E> extends AbstractSkipList<E>
implements Set<E>, ReleasableIterableCollection<E>, Cloneable
{
/*
* Construction
*/
public SkipSet(@NonNull Comparator<? super E> comparator)
{
super(comparator, (short)1);
}
/**
* @since 0.05
*/
public static @NonNull <T extends Comparable<T>> SkipSet<T> create()
{
return create(Ordering.natural());
}
/**
* @since 0.05
*/
public static @NonNull <T> SkipSet<T> create(@NonNull Comparator<? super T> comparator)
{
return new SkipSet<T>(comparator);
}
/**
* @since 0.05
*/
public static @NonNull <T extends Comparable<T>> SkipSet<T> create(@NonNull Collection<? extends T> collection)
{
final SkipSet<T> set = create();
set.addAll(collection);
return set;
}
/**
* @since 0.05
*/
public static @NonNull <T> SkipSet<T> create(@NonNull SortedSet<T> set)
{
final SkipSet<T> result = create(set.comparator());
result.addAll(set);
return result;
}
/**
* @since 0.05
*/
public static @NonNull <T> SkipSet<T> create(@NonNull SkipSet<T> set)
{
final SkipSet<T> result = create(set.comparator());
result.addAll(set);
return result;
}
/*----------------
* Object methods
*/
/**
* Returns a copy of the set.
* <p>
* SUBCLASS NOTE: this does not use the default {@link Object#clone} implementation. Therefore subclasses
* should not assume that fields introduced by the subclass will be cloned automatically.
* @since 0.05
*/
@Override
public SkipSet<E> clone()
{
return create(this);
}
@Override
public boolean equals(Object other)
{
if (this == other)
{
return true;
}
if (other instanceof Set)
{
Set<?> otherSet = (Set<?>)other;
if (otherSet.size() == size())
{
// TODO: special case if other is a SkipSet or a SortedSet with the same comparator
for (Object[] node = this.getNextNode(this.head); node != null; node = this.getNextNode(node))
{
if (!otherSet.contains(node[0]))
{
return false;
}
}
return true;
}
}
return false;
}
@Override
public int hashCode()
{
// Implements computation described in Set.hashCode()
int hash = 0;
for (Object[] node = this.getNextNode(this.head); node != null; node = this.getNextNode(node))
{
hash += node[0].hashCode();
}
return hash;
}
/*
* Set methods
*/
@Override
public boolean add(E value)
{
final int prevSize = this.size();
this.addNode(value);
return this.size() != prevSize;
}
@Override
public boolean addAll(Collection<? extends E> c)
{
boolean changed = false;
java.util.Iterator<? extends E> iter = c.iterator();
// TODO: add special case for when c is a SkipSet or SortedSet with same comparator.
while (iter.hasNext())
{
E val = iter.next();
changed |= this.add(val);
}
if (iter instanceof ReleasableIterator)
{
((ReleasableIterator<? extends E>)iter).release();
}
return changed;
}
@Override
public boolean contains(Object searchValue)
{
try
{
@SuppressWarnings("unchecked")
E val = (E)searchValue;
return this.contains2(val);
}
catch (ClassCastException ex)
{
return false;
}
}
@Override
public boolean containsAll(Collection<?> collection)
{
try
{
@SuppressWarnings("unchecked")
Collection<? extends E> c = (Collection<? extends E>) collection;
return this.containsAll2(c);
}
catch (ClassCastException ex)
{
return false;
}
}
public static class Iterator<E> extends KeyIterator<E> implements ReleasableIterator<E>
{
private static final ThreadLocal<KeyIterator<?>> reusableInstance = new ThreadLocal<KeyIterator<?>>();
public Iterator(@Nullable SkipSet<E> set)
{
super(set);
}
protected static <T> Iterator<T> make(@Nullable SkipSet<T> set)
{
@SuppressWarnings("unchecked")
@Nullable Iterator<T> iter = (Iterator<T>)Iterator.reusableInstance.get();
if (iter != null)
{
Iterator.reusableInstance.set(null);
iter.reset(set);
}
else
{
iter = new Iterator<T>(set);
}
return iter;
}
@Override
public void release()
{
if (Iterator.reusableInstance.get() == null)
{
this.reset(null);
Iterator.reusableInstance.set(this);
}
}
public void reset(@Nullable SkipSet<E> set)
{
super.reset(set);
}
}
@Override
public @NonNull Iterator<E> iterator()
{
return Iterator.make(this);
}
@Override
public boolean remove(Object value)
{
try
{
@SuppressWarnings("unchecked")
E val = (E)value;
return this.remove2(val);
}
catch (ClassCastException ex)
{
return false;
}
}
/** Like {@link #remove} but argument must be of type {@code E}. */
public boolean remove2(E value)
{
return super.removeNode(value) != null;
}
@Override
public boolean removeAll(Collection<?> c)
{
boolean changed = false;
java.util.Iterator<?> iter = c.iterator();
while (iter.hasNext())
{
Object val = iter.next();
changed |= this.remove(val);
}
if (iter instanceof ReleasableIterator)
{
((ReleasableIterator<?>)iter).release();
}
return changed;
}
@Override
public boolean retainAll(Collection<?> c)
{
boolean changed = false;
Iterator<E> iter = this.iterator();
for (E elt = iter.next(); elt != null; elt = iter.next())
{
if (!c.contains(elt))
{
iter.remove();
changed = true;
}
}
iter.release();
return changed;
}
@Override
public Object[] toArray()
{
return this.toArray(new Object[this.size()]);
}
@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a)
{
T[] array = a;
if (array.length < this.size())
{
array = (T[]) Array.newInstance(a.getClass().getComponentType(), this.size());
}
Object[] node = this.head;
int i = 0;
while (true)
{
Object[] next = this.getNextNode(node);
if (next == null)
{
break;
}
node = next;
array[i++] = (T)this.getNodeKey(node);
}
return array;
}
/*
* SortedSet methods - this class does not explicitly implement this interface since the subset methods are missing.
*/
/**
* Returns first element of set.
* @throws NoSuchElementException if set is empty.
*/
public E first()
{
final Object[] node = this.firstNode();
if (node == null)
{
throw new NoSuchElementException();
}
return this.getNodeKey(node);
}
/**
* Returns last element of set.
* @throws NoSuchElementException if set is empty.
*/
public E last()
{
final Object[] node = this.lastNode();
if (node == null)
{
throw new NoSuchElementException();
}
return this.getNodeKey(node);
}
/*
* NavigableSet methods - this class does not explicitly implement this interface since methods are missing.
*/
/**
* Returns lowest value in set that is greater than or equal to {@code value} or null.
* This is faster than {@link #floor} method.
*/
public @Nullable E ceiling(E value)
{
Object[] node = this.findCeilingNode(value);
return node == null ? null : this.getNodeKey(node);
}
/**
* Returns greatest value in set that is less than or equal to {@code value} or null.
*/
public @Nullable E floor(E value)
{
Object[] node = this.findFloorNode(value);
return node == null ? null : this.getNodeKey(node);
}
public @Nullable E higher(E key)
{
Object[] node = this.findHigherNode(key);
return node == null ? null : this.getNodeKey(node);
}
public @Nullable E lower(E value)
{
return this.getNodeKey(this.findLowerNode(value));
}
public @Nullable E pollFirst()
{
Object[] node = this.pollFirstNode();
return node == null ? null : this.getNodeKey(node);
}
public @Nullable E pollLast()
{
Object[] node = this.pollLastNode();
return node == null ? null : this.getNodeKey(node);
}
/*
* SkipSet methods
*/
/** Like {@link #contains} but argument must be of type {@code E}. */
public boolean contains2(E searchValue)
{
return this.containsNode(searchValue);
}
/** Like {@link #remove} but argument must be of type {@link Collection}{@code <? extends E>}. */
public boolean containsAll2(Collection<? extends E> c)
{
boolean containsAll = true;
java.util.Iterator<? extends E> iter = c.iterator();
while (iter.hasNext())
{
E val = iter.next();
if (!this.contains2(val))
{
containsAll = false;
break;
}
}
if (iter instanceof ReleasableIterator)
{
((ReleasableIterator<? extends E>)iter).release();
}
return containsAll;
}
}