1   //========================================================================
2   //Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.util;
16  
17  import java.util.AbstractList;
18  import java.util.Collection;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.NoSuchElementException;
22  import java.util.Queue;
23  
24  /* ------------------------------------------------------------ */
25  /** Queue backed by circular array.
26   * 
27   * This partial Queue implementation (also with {@link #pop()} for stack operation)
28   * is backed by a growable circular array.
29   * 
30   * @author gregw
31   *
32   * @param <E>
33   */
34  public class ArrayQueue<E> extends AbstractList<E> implements Queue<E>
35  {
36      public final int DEFAULT_CAPACITY=64;
37      public final int DEFAULT_GROWTH=32;
38      private Object _lock=this;
39      private Object[] _elements;
40      private int _nextE;
41      private int _nextSlot;
42      private int _size;
43      private int _growCapacity;
44  
45      /* ------------------------------------------------------------ */
46      public ArrayQueue()
47      {
48          _elements=new Object[64];
49          _growCapacity=32;
50      }
51  
52      /* ------------------------------------------------------------ */
53      public ArrayQueue(int capacity)
54      {
55          _elements=new Object[capacity];
56          _growCapacity=-1;
57      }
58  
59      /* ------------------------------------------------------------ */
60      public ArrayQueue(int initCapacity,int growBy)
61      {
62          _elements=new Object[initCapacity];
63          _growCapacity=growBy;
64      }
65  
66      /* ------------------------------------------------------------ */
67      public ArrayQueue(int initCapacity,int growBy,Object lock)
68      {
69          _elements=new Object[initCapacity];
70          _growCapacity=growBy;
71          _lock=lock;
72      }
73  
74      /* ------------------------------------------------------------ */
75      public int getCapacity()
76      {
77          return _elements.length;
78      }
79      
80      /* ------------------------------------------------------------ */
81      public boolean add(E e)
82      {
83          synchronized(_lock)
84          {
85              _size++;
86              _elements[_nextSlot++]=e;
87              if (_nextSlot==_elements.length)
88                  _nextSlot=0;
89              if (_nextSlot==_nextE)
90                  grow();
91          }
92          return true;
93      }
94      
95      /* ------------------------------------------------------------ */
96      /**
97       * add without synchronization or bounds checking
98       * @see #add(Object)
99       */
100     public void addUnsafe(E e)
101     {
102         _size++;
103         _elements[_nextSlot++]=e;
104         if (_nextSlot==_elements.length)
105             _nextSlot=0;
106         if (_nextSlot==_nextE)
107             grow();
108     }
109 
110     /* ------------------------------------------------------------ */
111     public E element()
112     {
113         synchronized(_lock)
114         {
115             if (_nextSlot==_nextE)
116                 throw new NoSuchElementException();
117             return (E)_elements[_nextE];
118         }
119     }
120 
121     /* ------------------------------------------------------------ */
122     public boolean offer(E e)
123     {
124         synchronized(_lock)
125         {
126             _size++;
127             _elements[_nextSlot++]=e;
128             if (_nextSlot==_elements.length)
129                 _nextSlot=0;
130             if (_nextSlot==_nextE)
131             {
132                 if (_growCapacity<=0)
133                     return false;
134 
135                 Object[] elements=new Object[_elements.length+_growCapacity];
136 
137                 int split=_elements.length-_nextE;
138                 if (split>0)
139                     System.arraycopy(_elements,_nextE,elements,0,split);
140                 if (_nextE!=0)
141                     System.arraycopy(_elements,0,elements,split,_nextSlot);
142 
143                 _elements=elements;
144                 _nextE=0;
145                 _nextSlot=_size;
146             }
147 
148             return true;
149         }
150     }
151 
152     /* ------------------------------------------------------------ */
153     public E peek()
154     {
155         synchronized(_lock)
156         {
157             if (_nextSlot==_nextE)
158                 return null;
159             return (E)_elements[_nextE];
160         }
161     }
162 
163     /* ------------------------------------------------------------ */
164     public E poll()
165     {
166         synchronized(_lock)
167         {
168             if (_size==0)
169                 return null;
170             E e = (E)_elements[_nextE];
171             _elements[_nextE]=null;
172             _size--;
173             if (++_nextE==_elements.length)
174                 _nextE=0;
175             return e;
176         }
177     }
178 
179     /* ------------------------------------------------------------ */
180     public E remove()
181     {
182         synchronized(_lock)
183         {
184             if (_nextSlot==_nextE)
185                 throw new NoSuchElementException();
186             E e = (E)_elements[_nextE++];
187             if (_nextE==_elements.length)
188                 _nextE=0;
189             return e;
190         }
191     }
192 
193     /* ------------------------------------------------------------ */
194     public void clear()
195     {
196         synchronized(_lock)
197         {
198             _size=0;
199             _nextE=0;
200             _nextSlot=0;
201         }
202     }
203 
204     /* ------------------------------------------------------------ */
205     public boolean isEmpty()
206     {
207         synchronized(_lock)
208         {
209             return _size==0;
210         }
211     }
212 
213     /* ------------------------------------------------------------ */
214     public int size()
215     {
216         return _size;
217     }
218 
219     /* ------------------------------------------------------------ */
220     public E get(int index)
221     {
222         synchronized(_lock)
223         {
224             if (index<0 || index>=_size)
225                 throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
226             int i = _nextE+index;
227             if (i>=_elements.length)
228                 i-=_elements.length;
229             return (E)_elements[i];
230         }
231     }
232 
233     /* ------------------------------------------------------------ */
234     /**
235      * Get without synchronization or bounds checking.
236      * @see get(int)
237      */
238     public E getUnsafe(int index)
239     {
240         int i = _nextE+index;
241         if (i>=_elements.length)
242             i-=_elements.length;
243         return (E)_elements[i];
244     }
245 
246     /* ------------------------------------------------------------ */
247     public E remove(int index)
248     {
249         synchronized(_lock)
250         {
251             if (index<0 || index>=_size)
252                 throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
253 
254             int i = _nextE+index;
255             if (i>=_elements.length)
256                 i-=_elements.length;
257             E old=(E)_elements[i];
258             
259             if (i<_nextSlot)
260             {
261                 // 0                         _elements.length
262                 //       _nextE........._nextSlot
263                 System.arraycopy(_elements,i+1,_elements,i,_nextSlot-i);
264                 _nextSlot--;
265                 _size--;
266             }
267             else
268             {
269                 // 0                         _elements.length
270                 // ......_nextSlot   _nextE..........
271                 System.arraycopy(_elements,i+1,_elements,i,_elements.length-1);
272                 if (_nextSlot>0)
273                 {
274                     _elements[_elements.length]=_elements[0];
275                     System.arraycopy(_elements,1,_elements,0,_nextSlot-1);
276                     _nextSlot--;
277                 }
278                 else
279                     _nextSlot=_elements.length-1;
280 
281                 _size--;
282             }
283             
284             return old;
285         }
286     }
287 
288     /* ------------------------------------------------------------ */
289     public E set(int index, E element)
290     {
291         synchronized(_lock)
292         {
293             if (index<0 || index>=_size)
294                 throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
295 
296             int i = _nextE+index;
297             if (i>=_elements.length)
298                 i-=_elements.length;
299             E old=(E)_elements[i];
300             _elements[i]=element;
301             return old;
302         }
303     }
304     
305     /* ------------------------------------------------------------ */
306     public void add(int index, E element)
307     {
308         synchronized(_lock)
309         {
310             if (index<0 || index>_size)
311                 throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
312 
313             if (index==_size)
314             {
315                 add(element);
316             }
317             else
318             {
319                 int i = _nextE+index;
320                 if (i>=_elements.length)
321                     i-=_elements.length;
322                 
323                 _size++;
324                 _nextSlot++;
325                 if (_nextSlot==_elements.length)
326                     _nextSlot=0;
327                 
328                 if (_nextSlot==_nextE)
329                     grow();
330 
331                 if (i<_nextSlot)
332                 {
333                     // 0                         _elements.length
334                     //       _nextE.....i..._nextSlot
335                     // 0                         _elements.length
336                     // ..i..._nextSlot   _nextE..........
337                     System.arraycopy(_elements,i,_elements,i+1,_nextSlot-i);
338                     _elements[i]=element;
339                 }
340                 else
341                 {
342                     // 0                         _elements.length
343                     // ......_nextSlot   _nextE.....i....
344                     if (_nextSlot>0)
345                     {
346                         System.arraycopy(_elements,0,_elements,1,_nextSlot);
347                         _elements[0]=_elements[_elements.length-1];
348                     }
349 
350                     System.arraycopy(_elements,i,_elements,i+1,_elements.length-i-1);
351                     _elements[i]=element;
352                 }
353             }
354         }
355     }
356 
357     protected void grow()
358     {
359         if (_growCapacity<=0)
360             throw new IllegalStateException("Full");
361 
362         Object[] elements=new Object[_elements.length+_growCapacity];
363 
364         int split=_elements.length-_nextE;
365         if (split>0)
366             System.arraycopy(_elements,_nextE,elements,0,split);
367         if (_nextE!=0)
368             System.arraycopy(_elements,0,elements,split,_nextSlot);
369 
370         _elements=elements;
371         _nextE=0;
372         _nextSlot=_size;
373     }
374 }