001/* [{
002Copyright 2011 Nicolas Carranza <nicarran at gmail.com>
003
004This file is part of jpen.
005
006jpen is free software: you can redistribute it and/or modify
007it under the terms of the GNU Lesser General Public License as published by
008the Free Software Foundation, either version 3 of the License,
009or (at your option) any later version.
010
011jpen is distributed in the hope that it will be useful,
012but WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014GNU Lesser General Public License for more details.
015
016You should have received a copy of the GNU Lesser General Public License
017along with jpen.  If not, see <http://www.gnu.org/licenses/>.
018}] */
019package jpen.internal;
020
021import java.lang.ref.WeakReference;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.LinkedList;
025
026public class WeakChain<E>{
027
028        private static  class Cell<E> extends WeakReference<E>{
029                Cell<E> nextCell;
030                Cell(E element, Cell<E> nextCell){
031                        super(element);
032                        if(element==null)
033                                throw new IllegalArgumentException();
034                        this.nextCell=nextCell;
035                }
036        }
037
038        private Cell<E> headCell;
039        private Cell<E> tailCell;
040
041        public synchronized boolean add(E element){
042                Cell<E> newCell=new Cell<E>(element, null);
043                if(tailCell==null){
044                        if(headCell!=null)
045                                throw new AssertionError();
046                        headCell=tailCell=newCell;
047                }else{
048                        tailCell.nextCell=newCell;
049                        tailCell=newCell;
050                }
051                return true;
052        }
053
054        public synchronized void clear(){
055                headCell=tailCell=null;
056        }
057
058        public synchronized boolean remove(final E element){
059                Cell<E> prevCell=null;
060                Cell<E> cell=headCell;
061                if(cell==null)
062                        return false;
063                do{
064                        E cellElement=cell.get();
065                        if(cellElement==null)
066                                removeCell(cell, prevCell);
067                        else if(cellElement==element){
068                                removeCell(cell, prevCell);
069                                return true;
070                        }else
071                                prevCell=cell;
072                }while((cell=cell.nextCell)!=null);
073                return false;
074        }
075
076        private void removeCell(Cell<E> cell, Cell<E> prevCell){
077                if(prevCell!=null){ // cell is not be the head
078                        prevCell.nextCell=cell.nextCell;
079                }else{ // cell must be the head
080                        if(cell!=headCell)
081                                throw new AssertionError();
082                        headCell=cell.nextCell;
083                }
084                if(cell==tailCell){
085                        tailCell=prevCell;
086                        if(tailCell!=null)
087                                tailCell.nextCell=null;
088                }
089        }
090
091        public Collection<E> snapshot(){
092                return snapshot(null);
093        }
094
095        public synchronized Collection<E> snapshot(Collection<E> elements){
096                Cell<E> cell=headCell;
097                if(cell==null)
098                        return elements==null? Collections.<E>emptyList(): elements;
099                if(elements==null){
100                        if(cell==tailCell){// optimization
101                                E cellElement=cell.get();
102                                if(cellElement!=null)
103                                        return Collections.singletonList(cellElement);
104                                removeCell(cell, null);
105                                return Collections.emptyList();
106                        }
107                        elements=new LinkedList<E>();
108                }
109                Cell<E> prevCell=null;
110                do{
111                        E cellElement=cell.get();
112                        if(cellElement==null){
113                                removeCell(cell, prevCell);
114                        }else{
115                                elements.add(cellElement);
116                                prevCell=cell;
117                        }
118                }while((cell=cell.nextCell)!=null);
119                return elements;
120        }
121
122        public void purge(){
123                remove(null);
124        }
125
126        public synchronized boolean isEmpty(){
127                return headCell==null;
128        }
129
130}