package xapi.collect.impl;
import xapi.annotation.inject.InstanceDefault;
import xapi.collect.api.Fifo;
import xapi.util.api.ConvertsValue;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A simple, fast, threadsafe, one-way, single-linked list.
*
* This primitive collection eats nulls (ignores them on add),
* it will only return null from take() when it is empty,
* and it handles concurrency by synchronizing on a single object for the
* whole collection. A read in the middle of a write might get missed if
* the timing is very close, but no reads will be missed it you call take()
* until the fifo is drained.
*
* @author "James X. Nelson (james@wetheinter.net)"
*
* @param <E> - The type of item stores in the fifo
*/
@InstanceDefault(implFor=Fifo.class)
public class SimpleFifo <E> implements Fifo<E>, Iterable<E>, Serializable{
private static final long serialVersionUID = 2525421548842680240L;
private class Itr implements Iterator<E> {
Node<E> last = head, node = last;
@Override
public boolean hasNext() {
return node.next != null;
}
@Override
public E next() {
last = node;
node = node.next;
if (node == null)
throw new NoSuchElementException();
return node.item;
}
@Override
public void remove() {
Node<E> expected = node;
synchronized(head) {
if (last.next != expected)
return;
if (node == tail)
tail = last;
if (last == node) {
if (last == head)
throw new IllegalStateException();
return;
}
last.next = node.next;
node.next = null;//help gc
node = last;
size--;
}
}
}
@Override
public boolean contains(E item) {
for (E e : forEach())
if (equals(item,e))
return true;
return false;
}
protected final static class Node <E> implements Serializable {
public Node() {
}
private static final long serialVersionUID = -4821223918445216546L;
public E item;
public Node<E> next;
@Override
public String toString() {
return String.valueOf(item);
}
}
protected Node<E> head = new Node<>();
private transient Node<E> tail;
private int size;
public SimpleFifo() {
tail = head;
}
public SimpleFifo(E[] els) {
this();
for (E el : els) {
if (el == null) {
return;
}
give(el);
}
}
@Override
public Fifo<E> give(E item) {
if (item==null)return this;
Node<E> add = new Node<>();
add.item = item;
synchronized (head) {
size++;
tail.next = add;
tail = add;
}
return this;
}
@Override
public void clear() {
head.next = null;
tail = head;
size = 0;
}
@Override
public boolean remove(E item) {
Node<E> start = head, next = head.next;
int was;
synchronized(head) {
was = size;
while (next != null) {
if (equals(next.item, item)) {
size--;
if (next == tail) {
tail = start;
tail.next = null;
} else {
start.next = next.next;
}
next.next = null;//help gc
next = start.next;
}else {
start = next;
next = next.next;
}
}
}
return was>size;
}
protected boolean equals(E one, E two) {
return one.equals(two);
}
@Override
public E take() {
synchronized (head) {
Node<E> next = head.next;
if (next == null) {
tail = head;
return null;
}
size--;
head.next = next.next;
if (next == tail)
tail = head;
try {
return next.item;
}finally {
//drop all unneeded references!
next.item = null;
}
}
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return head == tail;
}
@Override
protected void finalize() throws Throwable {
synchronized (head) {
head.next = null;
tail = null;
}
}
@Override
public String toString() {
return " ["+head.next +" -> "+tail+"]";
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
@Override
public Iterable<E> forEach() {
return this;
}
@Override
public String join(String delim) {
Node<E> n = head.next;
if (n == null)return "";
StringBuilder b = new StringBuilder();
b.append(n.item);
while ((n = n.next)!=null) {
b.append(delim);
b.append(n.item);
}
return b.toString();
}
public String join(String delim, ConvertsValue<E, String> serializer) {
Node<E> n = head.next;
if (n == null)return "";
StringBuilder b = new StringBuilder();
b.append(serialize(serializer, n.item));
while ((n = n.next)!=null) {
b.append(delim);
b.append(serializer.convert(n.item));
}
return b.toString();
}
protected String serialize(ConvertsValue<E, String> serializer, E item) {
return serializer == null ? String.valueOf(item) : serializer.convert(item);
}
@Override
@SuppressWarnings("unchecked")
public Fifo<E> giveAll(E ... elements) {
for (E element : elements)
give(element);
return this;
}
@Override
public Fifo<E> giveAll(Iterable<E> elements) {
for (E element : elements)
give(element);
return this;
}
}