/* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Queue.java * Copyright (C) 1999-2012 University of Waikato, Hamilton, New Zealand * * Modified March-May 2004 by Mark Utting to add JML specs * (this was done as the example solution of an assignment for a * software engineering course, so the specifications are more precise * and complete than one would normally do). * Passed a static analysis using ESC/Java-2.0a6 with no warnings. */ package weka.core; import java.io.Serializable; /** * Class representing a FIFO queue. * * @author Len Trigg (trigg@cs.waikato.ac.nz) * @version $Revision: 8034 $ */ public class Queue extends Object implements Serializable, RevisionHandler { /** for serialization */ private static final long serialVersionUID = -1141282001146389780L; /** * Represents one node in the queue. */ protected class QueueNode implements Serializable, RevisionHandler { /** for serialization */ private static final long serialVersionUID = -5119358279412097455L; /** The next node in the queue */ protected /*@ spec_public @*/ QueueNode m_Next; /** The nodes contents */ protected /*@ non_null spec_public @*/ Object m_Contents; /** * Creates a queue node with the given contents */ //@ requires contents != null; //@ assignable m_Contents, m_Next; //@ ensures m_Contents == contents; //@ ensures m_Next == null; public QueueNode(Object contents) { m_Contents = contents; next(null); } /** * Sets the next node in the queue, and returns it. */ //@ requires next != this ; //@ assignable m_Next; //@ ensures m_Next==next && \result==next; public QueueNode next(QueueNode next) { return m_Next = next; } //@ nowarn Invariant; // Because it stupidly checks the Queue invariant! /** * Gets the next node in the queue. */ //@ ensures \result == m_Next; public /*@ pure @*/ QueueNode next() { return m_Next; } /** * Sets the contents of the node. */ //@ requires contents != null; //@ assignable m_Contents; //@ ensures m_Contents == contents && \result == contents; public Object contents(Object contents) { return m_Contents = contents; } /** * Returns the contents in the node. */ //@ ensures \result == m_Contents; public /*@ pure @*/ Object contents() { return m_Contents; } /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } } /** Store a reference to the head of the queue */ protected /*@ spec_public @*/ QueueNode m_Head = null; /** Store a reference to the tail of the queue */ protected /*@ spec_public @*/ QueueNode m_Tail = null; /** Store the c m_Tail.m_Nexturrent number of elements in the queue */ protected /*@ spec_public @*/ int m_Size = 0; //@ public invariant m_Head == null <==> m_Tail == null; //@public invariant m_Tail != null ==> m_Tail.m_Next == null; //@ public invariant m_Size >= 0; //@ public invariant m_Size == 0 <==> m_Head == null; //@ public invariant m_Size == 1 <==> m_Head != null && m_Head == m_Tail; //@ public invariant m_Size > 1 ==> m_Head != m_Tail; //@ public invariant m_Size > 1 <== m_Head != m_Tail; /** * Removes all objects from the queue m_Tail.m_Next. */ //@ assignable m_Size, m_Head, m_Tail; //@ ensures m_Size == 0; //@ ensures m_Head == null; //@ ensures m_Tail == null; public final synchronized void removeAllElements() { m_Size = 0; m_Head = null; m_Tail = null; } /** * Appends an object to the back of the queue. * * @param item the object to be appended * @return the object appended */ //@ requires item != null; //@ assignable m_Head, m_Tail, m_Tail.m_Next, m_Head.m_Next, m_Size; //@ ensures m_Head != null; //@ ensures m_Tail != \old(m_Tail); //@ ensures m_Size == \old(m_Size) + 1; //@ ensures \old(m_Size) == 0 ==> m_Head == m_Tail; //@ ensures \old(m_Size) != 0 ==> m_Head == \old(m_Head); //@ ensures m_Tail.contents() == \old(item); //@ ensures \result == item; public synchronized Object push(Object item) { QueueNode newNode = new QueueNode(item); if (m_Head == null) { m_Head = m_Tail = newNode; } else { m_Tail = m_Tail.next(newNode); } m_Size++; return item; } /** * Pops an object from the front of the queue. * * @return the object at the front of the queue * @exception RuntimeException if the queue is empty */ //@ assignable m_Head, m_Tail, m_Size; //@ ensures m_Size == \old(m_Size) - 1; //@ ensures m_Head == \old(m_Head.m_Next); //@ ensures m_Head != null ==> m_Tail == \old(m_Tail); //@ ensures \result == \old(m_Head.m_Contents); //@ signals (RuntimeException) \old(m_Head) == null; public synchronized Object pop() throws RuntimeException // REDUNDANT, BUT ESCJAVA REQUIRES THIS { if (m_Head == null) { throw new RuntimeException("Queue is empty"); } Object retval = m_Head.contents(); m_Size--; m_Head = m_Head.next(); // Here we need to either tell ESC/Java some facts about // the contents of the list after popping off the head, // or turn off the 'invariant' warnings. // //@ assume m_Size == 0 <==> m_Head == null; //@ assume m_Size == 1 <==> m_Head == m_Tail; if (m_Head == null) { m_Tail = null; } return retval; } /** * Gets object from the front of the queue. * * @return the object at the front of the queue * @exception RuntimeException if the queue is empty */ //@ ensures \result == \old(m_Head.m_Contents); //@ signals (RuntimeException) \old(m_Head) == null; public /*@ pure @*/ synchronized Object peek() throws RuntimeException { if (m_Head == null) { throw new RuntimeException("Queue is empty"); } return m_Head.contents(); } /** * Checks if queue is empty. * * @return true if queue is empty */ //@ ensures \result <==> m_Head == null; public /*@ pure @*/ boolean empty() { return m_Head == null; } /** * Gets queue's size. * * @return size of queue */ //@ ensures \result == m_Size; public /*@ pure @*/ int size() { return m_Size; } /** * Produces textual description of queue. * * @return textual description of queue */ //@ also //@ ensures \result != null; //@ ensures (* \result == textual description of the queue *); public /*@ pure @*/ String toString() { String retval = "Queue Contents "+m_Size+" elements\n"; QueueNode current = m_Head; if (current == null) { return retval + "Empty\n"; } else { while (current != null) { retval += current.contents().toString()+"\n"; //@nowarn Modifies; current = current.next(); } } return retval; } //@ nowarn Post; /** * Returns the revision string. * * @return the revision */ public String getRevision() { return RevisionUtils.extract("$Revision: 8034 $"); } /** * Main method for testing this class. * * @param argv a set of strings that are pushed on a test queue */ //@ requires argv.length >= 0; //@ requires argv != null; //@ requires (\forall int i; 0 <= i && i < argv.length; argv[i] != null); public static void main(String [] argv) { try { Queue queue = new Queue(); for(int i = 0; i < argv.length; i++) { queue.push(argv[i]); } System.out.println("After pushing command line arguments"); System.out.println(queue.toString()); while (!queue.empty()) { System.out.println("Pop: " + queue.pop().toString()); } // try one more pop, to make sure we get an exception try { queue.pop(); System.out.println("ERROR: pop did not throw exception!"); } catch (RuntimeException ex) { System.out.println("Pop on empty queue correctly gave exception."); } } catch (Exception ex) { System.out.println(ex.getMessage()); } } }