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}