View Javadoc

1   /*   Copyright 2004 BEA Systems, Inc.
2    *
3    *   Licensed under the Apache License, Version 2.0 (the "License");
4    *   you may not use this file except in compliance with the License.
5    *   You may obtain a copy of the License at
6    *
7    *       http://www.apache.org/licenses/LICENSE-2.0
8    *
9    *   Unless required by applicable law or agreed to in writing, software
10   *   distributed under the License is distributed on an "AS IS" BASIS,
11   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   *   See the License for the specific language governing permissions and
13   *   limitations under the License.
14   */
15  
16  package com.bea.xml.stream.util;
17  
18  import java.util.AbstractCollection;
19  import java.util.Arrays;
20  import java.util.ConcurrentModificationException;
21  import java.util.Iterator;
22  import java.util.NoSuchElementException;
23  
24  public final class CircularQueue extends AbstractCollection {
25  
26    // This is the largest capacity allowed by this implementation
27    private static final int MAX_CAPACITY = 1 << 30;
28    private static final int DEFAULT_CAPACITY = 1 << 8;
29  
30    private int size          = 0;
31    private int producerIndex = 0;
32    private int consumerIndex = 0;
33  
34    // capacity must be a power of 2 at all times
35    private int capacity;
36    private int maxCapacity;
37  
38    // we mask with capacity -1.  This variable caches that values
39    private int bitmask; 
40  
41    private Object[] q;
42  
43    public CircularQueue() {
44      this(DEFAULT_CAPACITY);
45    }
46  
47    // Construct a queue which has at least the specified capacity.  If
48    // the value specified is a power of two then the queue will be
49    // exactly the specified size.  Otherwise the queue will be the
50    // first power of two which is greater than the specified value.
51    public CircularQueue(int c) {
52      this(c, MAX_CAPACITY);
53    }
54  
55    public CircularQueue(int c, int mc) {
56      if (c > mc) {
57        throw new IllegalArgumentException("Capacity greater than maximum");
58      }
59  
60      if (mc > MAX_CAPACITY) {
61        throw new IllegalArgumentException("Maximum capacity greater than " +
62          "allowed");
63      }
64  
65      for (capacity = 1; capacity < c; capacity <<= 1) ;
66      for (maxCapacity = 1; maxCapacity < mc; maxCapacity <<= 1) ;
67  
68      bitmask = capacity - 1;
69      q = new Object[capacity];
70    }
71  
72    // Constructor used by clone()
73    private CircularQueue(CircularQueue oldQueue) {
74      size = oldQueue.size;
75      producerIndex = oldQueue.producerIndex;
76      consumerIndex = oldQueue.consumerIndex;
77      capacity = oldQueue.capacity;
78      maxCapacity = oldQueue.maxCapacity;
79      bitmask = oldQueue.bitmask;
80      q = new Object[oldQueue.q.length];
81      System.arraycopy(oldQueue.q, 0, q, 0, q.length);
82    }
83  
84    private boolean expandQueue() {
85      // double the size of the queue
86      // This design assumes that this is a rare case
87  
88      if (capacity == maxCapacity) {
89        return false;
90      }
91  
92      int old_capacity = capacity;
93      Object[] old_q    = q;
94  
95      capacity += capacity;
96      bitmask   = capacity - 1;
97      q         = new Object[capacity];
98  
99      System.arraycopy(old_q, consumerIndex, q, 0, old_capacity - consumerIndex);
100 
101     if (consumerIndex != 0) {
102       System.arraycopy(old_q, 0, q, old_capacity - consumerIndex, 
103         consumerIndex);
104     }
105 
106     consumerIndex = 0;
107     producerIndex = size;
108 
109     return true;
110   }
111 
112   public boolean add(Object obj) {
113     if (size == capacity) {
114       // no room
115       if (!expandQueue()) return false;
116     }
117 
118     size++;
119     q[producerIndex] = obj;
120 
121     producerIndex = (producerIndex + 1) & bitmask;
122 
123     return true;
124   }
125 
126   public Object remove() {
127     Object obj;
128     
129     if (size == 0) return null;
130     
131     size--;
132     obj = q[consumerIndex];
133     q[consumerIndex] = null; // allow gc to collect
134     
135     consumerIndex = (consumerIndex + 1) & bitmask;
136 
137     return obj;
138   }
139 
140   public boolean isEmpty() { return size == 0; }
141 
142   public int size() { return size; }
143 
144   public int capacity() { return capacity; }
145 
146   public Object peek() {
147     if (size == 0) return null;
148     return q[consumerIndex];
149   }
150 
151   public void clear() {
152     Arrays.fill(q, null);
153     size = 0;
154     producerIndex = 0;
155     consumerIndex = 0;
156   }
157 
158   public Object clone() {
159     return new CircularQueue(this);
160   }
161 
162   public String toString() {
163     StringBuffer s = new StringBuffer(super.toString() + " - capacity: '" +
164       capacity() + "' size: '" + size() + "'");
165 
166     if (size > 0) {
167       s.append(" elements:");
168       for (int i = 0; i < size; ++i) {
169         s.append('\n');
170         s.append('\t');
171         s.append(q[consumerIndex + i & bitmask].toString());
172       }      
173     }
174 
175     return s.toString();
176   }
177 
178   public Iterator iterator() {
179     return new Iterator() {
180       private final int ci = consumerIndex;
181       private final int pi = producerIndex;
182       private int s = size;
183       private int i = ci;
184 
185       public boolean hasNext() {
186         checkForModification();
187         return s > 0;
188       }
189 
190       public Object next() {
191         checkForModification();
192         if (s == 0) throw new NoSuchElementException();
193     
194         s--;
195         Object r = q[i];
196         i = (i + 1) & bitmask;
197 
198         return r;
199       }
200 
201       public void remove() {
202         throw new UnsupportedOperationException();
203       }
204 
205       private void checkForModification() {
206         if (ci != consumerIndex) throw new ConcurrentModificationException();
207         if (pi != producerIndex) throw new ConcurrentModificationException();
208       }
209     };
210   }
211 }