/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.collections; import com.jcwhatever.nucleus.utils.PreCon; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; import javax.annotation.Nullable; /** * A {@link Deque} implementation whose head and tail are connected. The head and tail * can be moved by invoking the {@link #next} or {@link #prev} methods. */ public class CircularQueue<E> implements Deque<E> { private Entry _current; // current is head, left is tail private volatile int _size; /** * Moves the head and tail of the deque to the right (direction towards tail). * * @return The new head element. * * @throws java.util.NoSuchElementException if the collection is empty. */ @Nullable public E next() { if (_size == 0) throw new NoSuchElementException(); _current = _current.right; return _current.value; } /** * Moves the head and tail of the deque to the left. (direction towards head) * * @return The new head element. * * @throws java.util.NoSuchElementException if the collection is empty. */ @Nullable public E prev() { if (_size == 0) throw new NoSuchElementException(); _current = _current.left; return _current.value; } @Override public void addFirst(@Nullable E value) { Entry entry = new Entry(value); addEntry(entry); _current = entry; } @Override public void addLast(@Nullable E value) { Entry entry = new Entry(value); addEntry(entry); } @Override public boolean offerFirst(@Nullable E value) { addFirst(value); return true; } @Override public boolean offerLast(@Nullable E value) { addLast(value); return true; } @Override @Nullable public E removeFirst() { if (_size == 0) throw new NoSuchElementException("Deque is empty."); return pollFirst(); } @Override public E removeLast() { if (_size == 0) throw new NoSuchElementException("Deque is empty."); return pollLast(); } @Override @Nullable public E pollFirst() { if (_size == 0) return null; Entry removed = _current; if (_size == 1) { _current = null; _size = 0; return removed.value; } _current.left.right = _current.right; _current.right.left = _current.left; _current = _current.right; _size--; return removed.value; } @Override @Nullable public E pollLast() { if (_size == 0) return null; Entry removed; if (_size == 1) { removed = _current; _current = null; _size = 0; return removed.value; } removed = _current.left; removed.left.right = _current; _current.left = removed.left; _size--; return removed.value; } @Override public E getFirst() { if (_size == 0) throw new NoSuchElementException("Deque is empty."); return peekFirst(); } @Override public E getLast() { if (_size == 0) throw new NoSuchElementException("Deque is empty."); return peekLast(); } @Override @Nullable public E peekFirst() { if (_size == 0) return null; return _current.value; } @Override public E peekLast() { if (_size == 0) return null; return _current.left.value; } @Override public boolean removeFirstOccurrence(@Nullable Object o) { Iterator<E> iterator = new ItrRight(); return removeOccurrence(o, iterator); } @Override public boolean removeLastOccurrence(@Nullable Object o) { Iterator<E> iterator = new ItrLeft(); return removeOccurrence(o, iterator); } @Override public boolean add(@Nullable E value) { addLast(value); return true; } @Override public boolean offer(@Nullable E value) { return offerLast(value); } @Override @Nullable public E remove() { return removeFirst(); } @Override @Nullable public E poll() { return pollFirst(); } @Override @Nullable public E element() { if (_size == 0) throw new NoSuchElementException("Deque is empty."); return peek(); } @Override @Nullable public E peek() { return peekFirst(); } @Override public void push(@Nullable E value) { addFirst(value); } @Override @Nullable public E pop() { return removeFirst(); } @Override public boolean remove(@Nullable Object o) { return removeFirstOccurrence(o); } @Override public boolean containsAll(Collection<?> c) { PreCon.notNull(c); for (Object o : c) { if (!contains(o)) return false; } return true; } @Override public boolean addAll(Collection<? extends E> c) { PreCon.notNull(c); boolean isChanged = false; for (E value : c) { isChanged = add(value) || isChanged; } return isChanged; } @Override public boolean removeAll(Collection<?> c) { PreCon.notNull(c); boolean isChanged = false; for (Object o : c) { isChanged = remove(o) || isChanged; } return isChanged; } @Override public boolean retainAll(Collection<?> c) { PreCon.notNull(c); boolean isChanged = false; Iterator<E> iterator = new ItrRight(); while (iterator.hasNext()) { E element = iterator.next(); if (!c.contains(element)) { iterator.remove(); isChanged = true; } } return isChanged; } @Override public void clear() { _size = 0; _current = null; } @Override public boolean contains(@Nullable Object o) { Iterator<E> iterator = new ItrRight(); while (iterator.hasNext()) { E element = iterator.next(); if (o == null && element == null) return true; if (o != null && o.equals(element)) return true; } return false; } @Override public int size() { return _size; } @Override public boolean isEmpty() { return _size == 0; } @Override public Iterator<E> iterator() { return new ItrRight(); } @Override public Object[] toArray() { Object[] array = new Object[_size]; Iterator<E> iterator = new ItrRight(); int index = 0; while (iterator.hasNext()) { array[index] = iterator.next(); index++; } return array; } @Override public <T1> T1[] toArray(T1[] array) { Iterator<E> iterator = new ItrRight(); int index = 0; while (iterator.hasNext()) { @SuppressWarnings("unchecked") T1 value = (T1)iterator.next(); array[index] = value; index++; } return array; } @Override public Iterator<E> descendingIterator() { return new ItrLeft(); } private void addEntry (Entry entry) { if (_size == 0) { _current = entry; entry.left = entry; entry.right = entry; } else { entry.left = _current.left; entry.right = _current; _current.left.right = entry; _current.left = entry; } _size++; } private boolean removeOccurrence(Object o, Iterator<E> iterator) { while (iterator.hasNext()) { E value = iterator.next(); if (o == null && value == null) { iterator.remove(); return true; } else if (o != null && o.equals(value)) { iterator.remove(); return true; } } return false; } private class ItrRight implements Iterator<E> { Entry next = null; int steps = 0; boolean invokedHasNext; boolean invokedNext; @Override public boolean hasNext() { invokedHasNext = true; return _size > 0 && steps < _size; } @Override public E next() { if (!invokedHasNext) throw new IllegalStateException("Cannot invoke 'next' until after invoking 'hasNext'."); if (!hasNext()) throw new NoSuchElementException(); invokedHasNext = false; invokedNext = true; steps++; if (next == null) { next = _current.right; return _current.value; } next = next.right; return next.left.value; } @Override public void remove() { if (!invokedNext) throw new IllegalStateException("Cannot invoke 'remove' until after invoking 'next'."); invokedNext = false; steps--; if (_size == 1) { _current = null; _size = 0; next = null; } else { if (next.left == _current) { _current = next; } next.left.left.right = next; next.left = next.left.left; _size--; } } } private class ItrLeft implements Iterator<E> { Entry next; int steps = 0; boolean invokedHasNext; boolean invokedNext; @Override public boolean hasNext() { invokedHasNext = true; return _size > 0 && steps < _size; } @Override public E next() { if (!invokedHasNext) throw new IllegalStateException("Cannot invoke 'next' until after invoking 'hasNext'."); if (!hasNext()) throw new NoSuchElementException(); invokedHasNext = false; invokedNext = true; steps++; if (next == null) { next = _current.left.left; return _current.left.value; } next = next.left; return next.right.value; } @Override public void remove() { if (!invokedNext) throw new IllegalStateException("Cannot invoke 'remove' until after invoking 'next'."); invokedNext = false; steps--; if (_size == 1) { _current = null; _size = 0; next = null; } else { if (next.right == _current) { _current = next; } next.right.right.left = next; next.right = next.right.right; _size--; } } } private class Entry { Entry left; E value; Entry right; Entry(E value) { this.value = value; } } }