1 | /* |
---|
2 | * This program is free software; you can redistribute it and/or modify |
---|
3 | * it under the terms of the GNU General Public License as published by |
---|
4 | * the Free Software Foundation; either version 2 of the License, or |
---|
5 | * (at your option) any later version. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * GNU General Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the GNU General Public License |
---|
13 | * along with this program; if not, write to the Free Software |
---|
14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
15 | */ |
---|
16 | |
---|
17 | /* |
---|
18 | * Queue.java |
---|
19 | * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand |
---|
20 | * |
---|
21 | * Modified March-May 2004 by Mark Utting to add JML specs |
---|
22 | * (this was done as the example solution of an assignment for a |
---|
23 | * software engineering course, so the specifications are more precise |
---|
24 | * and complete than one would normally do). |
---|
25 | * Passed a static analysis using ESC/Java-2.0a6 with no warnings. |
---|
26 | */ |
---|
27 | |
---|
28 | package weka.core; |
---|
29 | |
---|
30 | import java.io.Serializable; |
---|
31 | |
---|
32 | /** |
---|
33 | * Class representing a FIFO queue. |
---|
34 | * |
---|
35 | * @author Len Trigg (trigg@cs.waikato.ac.nz) |
---|
36 | * @version $Revision: 5953 $ |
---|
37 | */ |
---|
38 | public class Queue |
---|
39 | extends Object |
---|
40 | implements Serializable, RevisionHandler { |
---|
41 | |
---|
42 | /** for serialization */ |
---|
43 | private static final long serialVersionUID = -1141282001146389780L; |
---|
44 | |
---|
45 | /** |
---|
46 | * Represents one node in the queue. |
---|
47 | */ |
---|
48 | protected class QueueNode |
---|
49 | implements Serializable, RevisionHandler { |
---|
50 | |
---|
51 | /** for serialization */ |
---|
52 | private static final long serialVersionUID = -5119358279412097455L; |
---|
53 | |
---|
54 | /** The next node in the queue */ |
---|
55 | protected /*@ spec_public @*/ QueueNode m_Next; |
---|
56 | |
---|
57 | /** The nodes contents |
---|
58 | */ |
---|
59 | protected /*@ non_null spec_public @*/ Object m_Contents; |
---|
60 | |
---|
61 | /** |
---|
62 | * Creates a queue node with the given contents |
---|
63 | */ |
---|
64 | //@ requires contents != null; |
---|
65 | //@ assignable m_Contents, m_Next; |
---|
66 | //@ ensures m_Contents == contents; |
---|
67 | //@ ensures m_Next == null; |
---|
68 | public QueueNode(Object contents) { |
---|
69 | m_Contents = contents; |
---|
70 | next(null); |
---|
71 | } |
---|
72 | |
---|
73 | /** |
---|
74 | * Sets the next node in the queue, and returns it. |
---|
75 | */ |
---|
76 | //@ requires next != this ; |
---|
77 | //@ assignable m_Next; |
---|
78 | //@ ensures m_Next==next && \result==next; |
---|
79 | public QueueNode next(QueueNode next) { |
---|
80 | return m_Next = next; |
---|
81 | } //@ nowarn Invariant; // Because it stupidly checks the Queue invariant! |
---|
82 | |
---|
83 | /** |
---|
84 | * Gets the next node in the queue. |
---|
85 | */ |
---|
86 | //@ ensures \result == m_Next; |
---|
87 | public /*@ pure @*/ QueueNode next() { |
---|
88 | return m_Next; |
---|
89 | } |
---|
90 | |
---|
91 | /** |
---|
92 | * Sets the contents of the node. |
---|
93 | */ |
---|
94 | //@ requires contents != null; |
---|
95 | //@ assignable m_Contents; |
---|
96 | //@ ensures m_Contents == contents && \result == contents; |
---|
97 | public Object contents(Object contents) { |
---|
98 | return m_Contents = contents; |
---|
99 | } |
---|
100 | |
---|
101 | /** |
---|
102 | * Returns the contents in the node. |
---|
103 | */ |
---|
104 | //@ ensures \result == m_Contents; |
---|
105 | public /*@ pure @*/ Object contents() { |
---|
106 | return m_Contents; |
---|
107 | } |
---|
108 | |
---|
109 | /** |
---|
110 | * Returns the revision string. |
---|
111 | * |
---|
112 | * @return the revision |
---|
113 | */ |
---|
114 | public String getRevision() { |
---|
115 | return RevisionUtils.extract("$Revision: 5953 $"); |
---|
116 | } |
---|
117 | } |
---|
118 | |
---|
119 | /** Store a reference to the head of the queue */ |
---|
120 | protected /*@ spec_public @*/ QueueNode m_Head = null; |
---|
121 | |
---|
122 | /** Store a reference to the tail of the queue */ |
---|
123 | protected /*@ spec_public @*/ QueueNode m_Tail = null; |
---|
124 | |
---|
125 | /** Store the c m_Tail.m_Nexturrent number of elements in the queue */ |
---|
126 | protected /*@ spec_public @*/ int m_Size = 0; |
---|
127 | |
---|
128 | //@ public invariant m_Head == null <==> m_Tail == null; |
---|
129 | //@public invariant m_Tail != null ==> m_Tail.m_Next == null; |
---|
130 | //@ public invariant m_Size >= 0; |
---|
131 | //@ public invariant m_Size == 0 <==> m_Head == null; |
---|
132 | //@ public invariant m_Size == 1 <==> m_Head != null && m_Head == m_Tail; |
---|
133 | //@ public invariant m_Size > 1 ==> m_Head != m_Tail; |
---|
134 | //@ public invariant m_Size > 1 <== m_Head != m_Tail; |
---|
135 | |
---|
136 | |
---|
137 | |
---|
138 | /** |
---|
139 | * Removes all objects from the queue m_Tail.m_Next. |
---|
140 | */ |
---|
141 | //@ assignable m_Size, m_Head, m_Tail; |
---|
142 | //@ ensures m_Size == 0; |
---|
143 | //@ ensures m_Head == null; |
---|
144 | //@ ensures m_Tail == null; |
---|
145 | public final synchronized void removeAllElements() { |
---|
146 | m_Size = 0; |
---|
147 | m_Head = null; |
---|
148 | m_Tail = null; |
---|
149 | } |
---|
150 | |
---|
151 | /** |
---|
152 | * Appends an object to the back of the queue. |
---|
153 | * |
---|
154 | * @param item the object to be appended |
---|
155 | * @return the object appended |
---|
156 | */ |
---|
157 | //@ requires item != null; |
---|
158 | //@ assignable m_Head, m_Tail, m_Tail.m_Next, m_Head.m_Next, m_Size; |
---|
159 | //@ ensures m_Head != null; |
---|
160 | //@ ensures m_Tail != \old(m_Tail); |
---|
161 | //@ ensures m_Size == \old(m_Size) + 1; |
---|
162 | //@ ensures \old(m_Size) == 0 ==> m_Head == m_Tail; |
---|
163 | //@ ensures \old(m_Size) != 0 ==> m_Head == \old(m_Head); |
---|
164 | //@ ensures m_Tail.contents() == \old(item); |
---|
165 | //@ ensures \result == item; |
---|
166 | public synchronized Object push(Object item) { |
---|
167 | QueueNode newNode = new QueueNode(item); |
---|
168 | |
---|
169 | if (m_Head == null) { |
---|
170 | m_Head = m_Tail = newNode; |
---|
171 | } else { |
---|
172 | m_Tail = m_Tail.next(newNode); |
---|
173 | } |
---|
174 | m_Size++; |
---|
175 | return item; |
---|
176 | } |
---|
177 | |
---|
178 | /** |
---|
179 | * Pops an object from the front of the queue. |
---|
180 | * |
---|
181 | * @return the object at the front of the queue |
---|
182 | * @exception RuntimeException if the queue is empty |
---|
183 | */ |
---|
184 | //@ assignable m_Head, m_Tail, m_Size; |
---|
185 | //@ ensures m_Size == \old(m_Size) - 1; |
---|
186 | //@ ensures m_Head == \old(m_Head.m_Next); |
---|
187 | //@ ensures m_Head != null ==> m_Tail == \old(m_Tail); |
---|
188 | //@ ensures \result == \old(m_Head.m_Contents); |
---|
189 | //@ signals (RuntimeException) \old(m_Head) == null; |
---|
190 | public synchronized Object pop() |
---|
191 | throws RuntimeException // REDUNDANT, BUT ESCJAVA REQUIRES THIS |
---|
192 | { |
---|
193 | if (m_Head == null) { |
---|
194 | throw new RuntimeException("Queue is empty"); |
---|
195 | } |
---|
196 | Object retval = m_Head.contents(); |
---|
197 | m_Size--; |
---|
198 | m_Head = m_Head.next(); |
---|
199 | // Here we need to either tell ESC/Java some facts about |
---|
200 | // the contents of the list after popping off the head, |
---|
201 | // or turn off the 'invariant' warnings. |
---|
202 | // |
---|
203 | //@ assume m_Size == 0 <==> m_Head == null; |
---|
204 | //@ assume m_Size == 1 <==> m_Head == m_Tail; |
---|
205 | if (m_Head == null) { |
---|
206 | m_Tail = null; |
---|
207 | } |
---|
208 | return retval; |
---|
209 | } |
---|
210 | |
---|
211 | /** |
---|
212 | * Gets object from the front of the queue. |
---|
213 | * |
---|
214 | * @return the object at the front of the queue |
---|
215 | * @exception RuntimeException if the queue is empty |
---|
216 | */ |
---|
217 | //@ ensures \result == \old(m_Head.m_Contents); |
---|
218 | //@ signals (RuntimeException) \old(m_Head) == null; |
---|
219 | public /*@ pure @*/ synchronized Object peek() |
---|
220 | throws RuntimeException |
---|
221 | { |
---|
222 | if (m_Head == null) { |
---|
223 | throw new RuntimeException("Queue is empty"); |
---|
224 | } |
---|
225 | return m_Head.contents(); |
---|
226 | } |
---|
227 | |
---|
228 | /** |
---|
229 | * Checks if queue is empty. |
---|
230 | * |
---|
231 | * @return true if queue is empty |
---|
232 | */ |
---|
233 | //@ ensures \result <==> m_Head == null; |
---|
234 | public /*@ pure @*/ boolean empty() { |
---|
235 | return m_Head == null; |
---|
236 | } |
---|
237 | |
---|
238 | /** |
---|
239 | * Gets queue's size. |
---|
240 | * |
---|
241 | * @return size of queue |
---|
242 | */ |
---|
243 | //@ ensures \result == m_Size; |
---|
244 | public /*@ pure @*/ int size() { |
---|
245 | return m_Size; |
---|
246 | } |
---|
247 | |
---|
248 | /** |
---|
249 | * Produces textual description of queue. |
---|
250 | * |
---|
251 | * @return textual description of queue |
---|
252 | */ |
---|
253 | //@ also |
---|
254 | //@ ensures \result != null; |
---|
255 | //@ ensures (* \result == textual description of the queue *); |
---|
256 | public /*@ pure @*/ String toString() { |
---|
257 | |
---|
258 | String retval = "Queue Contents "+m_Size+" elements\n"; |
---|
259 | QueueNode current = m_Head; |
---|
260 | if (current == null) { |
---|
261 | return retval + "Empty\n"; |
---|
262 | } else { |
---|
263 | while (current != null) { |
---|
264 | retval += current.contents().toString()+"\n"; //@nowarn Modifies; |
---|
265 | current = current.next(); |
---|
266 | } |
---|
267 | } |
---|
268 | return retval; |
---|
269 | } //@ nowarn Post; |
---|
270 | |
---|
271 | /** |
---|
272 | * Returns the revision string. |
---|
273 | * |
---|
274 | * @return the revision |
---|
275 | */ |
---|
276 | public String getRevision() { |
---|
277 | return RevisionUtils.extract("$Revision: 5953 $"); |
---|
278 | } |
---|
279 | |
---|
280 | /** |
---|
281 | * Main method for testing this class. |
---|
282 | * |
---|
283 | * @param argv a set of strings that are pushed on a test queue |
---|
284 | */ |
---|
285 | //@ requires argv.length >= 0; |
---|
286 | //@ requires argv != null; |
---|
287 | //@ requires (\forall int i; 0 <= i && i < argv.length; argv[i] != null); |
---|
288 | public static void main(String [] argv) { |
---|
289 | |
---|
290 | try { |
---|
291 | Queue queue = new Queue(); |
---|
292 | for(int i = 0; i < argv.length; i++) { |
---|
293 | queue.push(argv[i]); |
---|
294 | } |
---|
295 | System.out.println("After pushing command line arguments"); |
---|
296 | System.out.println(queue.toString()); |
---|
297 | while (!queue.empty()) { |
---|
298 | System.out.println("Pop: " + queue.pop().toString()); |
---|
299 | } |
---|
300 | // try one more pop, to make sure we get an exception |
---|
301 | try |
---|
302 | { |
---|
303 | queue.pop(); |
---|
304 | System.out.println("ERROR: pop did not throw exception!"); |
---|
305 | } |
---|
306 | catch (RuntimeException ex) |
---|
307 | { |
---|
308 | System.out.println("Pop on empty queue correctly gave exception."); |
---|
309 | } |
---|
310 | } catch (Exception ex) { |
---|
311 | System.out.println(ex.getMessage()); |
---|
312 | } |
---|
313 | } |
---|
314 | } |
---|