// @(#)$Id: JMLListNode.java-generic,v 1.56 2006/12/02 23:38:15 leavens Exp $ // Copyright (C) 2005 Iowa State University // // This file is part of the runtime library of the Java Modeling Language. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation; either version 2.1, // of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with JML; see the file LesserGPL.txt. If not, write to the Free // Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA // 02110-1301 USA. package org.jmlspecs.unfinished; /** An implementation class used in various models. These are * singly-linked list nodes containing values. The empty * list is represented by null, which makes dealing with these lists * tricky. Normal users should use {@link JMLValueSequence} instead of this * type to avoid these difficulties. * * <p> * This type uses ".equals" to compare elements. The cons method * clones elements that are passed into the list. * * @version $Revision: 1.56 $ * @author Gary T. Leavens * @author Albert L. Baker * @see JMLValueSequence * @see JMLValueBag * @see JMLValueSet */ //-@ immutable // FIXME: adapt this file to non-null-by-default and remove the following modifier. /*@ nullable_by_default @*/ /*@ pure spec_public @*/ class JMLListValueNode implements JMLValueType { //*********************** equational theory *********************** /*@ public invariant (\forall JMLListValueNode l2; @ (\forall JMLType e1, e2; @ (\forall \bigint n; @ equational_theory(this, l2, e1, e2, n) ))); @*/ /** An `equational' specification of lists, for use in the invariant. */ /*@ public normal_behavior @ {| @ // The following define itemAt and size. The behavior @ // of the other methods is defined based by these two @ // methods. @ @ ensures \result <==> @ (new JMLListValueNode(e1, null)).size() == 1; @ also @ ensures \result <==> @ (ls != null) @ ==> (new JMLListValueNode(e1, ls)).size() == 1 + ls.size(); @ also @ ensures \result <==> @ (ls != null) @ ==> (ls.next == null) == (ls.size() == 1); @ also @ ensures \result <==> @ ls != null && ls.next != null @ ==> ls.size() == 1 + ls.next.size(); @ also @ ensures \result <==> @ (ls != null && ls.val != null) @ ==> ls.val.equals(ls.head()); @ also @ ensures \result <==> @ (e1 != null) @ ==> cons(e1, ls).head().equals(e1); @ also @ ensures \result <==> @ (ls != null && ls.val != null) @ ==> ls.itemAt(0).equals(ls.head()); @ also @ ensures \result <==> @ ls != null && 0 < n && n < ls.size() @ ==> ls.itemAt(n).equals(ls.next.itemAt(n - 1)); @ |} public pure model boolean equational_theory( JMLListValueNode ls, JMLListValueNode ls2, JMLType e1, JMLType e2, \bigint n); @*/ //@ public model JMLDataGroup elementState; /** The data contained in this list node. */ public final JMLType val; //@ in objectState; maps val.objectState \into elementState; /** The next node in this list. */ public final JMLListValueNode next; //@ in objectState; maps next.elementState \into elementState; /** The type of the elements in this list. This type is an upper * bound on the element's types. The type is computed * pessimistically, so that the order of adding elements does not * matter; that is, if any element in the list is null, then we * use JMLType as the type of the list's elements. */ //@ ghost public \TYPE elementType; //@ public constraint elementType == \old(elementType); //@ public invariant elementType <: \type(JMLType); //@ public invariant val != null ==> \typeof(val) <: elementType; //@ public invariant val == null ==> \type(JMLType) == elementType; /*@ public invariant_redundantly @ containsNull ==> \type(JMLType) == elementType; @*/ //@ public invariant next != null ==> next.elementType <: elementType; /** Whether this list can contain null elements; */ //@ ghost public boolean containsNull; //@ public constraint containsNull == \old(containsNull); //@ public invariant containsNull <==> has(null); /*@ protected @ invariant containsNull <==> @ val == null || (next != null && next.containsNull); @*/ //@ public invariant owner == null; //************************* Constructors ******************************** /** Initialize this list to have the given item as its first * element followed by the given list. * This does not do any cloning. * * @param item the value to place at the head of this list. * @param nxt the _JMLListValueNode to make the tail of this list. */ /*@ public normal_behavior @ requires item != null; @ assignable val, next, elementType, containsNull, owner; @ ensures val.equals(item) && next == nxt @ && \typeof(item) <: elementType @ && (nxt != null ==> nxt.elementType <: elementType) @ && (nxt == null ==> elementType == \typeof(item)) @ && containsNull == (nxt == null ? false : nxt.containsNull); @ also @ public normal_behavior @ requires item == null; @ assignable val, next, elementType, containsNull, owner; @ ensures val == null && next == nxt @ && elementType == \type(JMLType) @ && containsNull; @ @ implies_that @ ensures val == item && next == nxt; @ ensures item == null ==> containsNull; @ ensures item != null && nxt != null @ ==> containsNull == nxt.containsNull; @*/ public JMLListValueNode(JMLType item, JMLListValueNode nxt) { //@ set owner = null; val = item; next = nxt; /*@ set elementType @ = (item == null @ ? \type(JMLType) @ : (nxt == null ? \typeof(item) @ : ((\typeof(item) <: nxt.elementType) @ ? nxt.elementType @ // types aren't totally ordered! @ : ((nxt.elementType <: \typeof(item)) @ ? \typeof(item) @ : \type(JMLType))))); @*/ /*@ set containsNull @ = (val == null || (next != null && next.containsNull)); @*/ } //@ nowarn Invariant; /** Return a JMLListValueNode containing the given element * followed by the given list. * * Note that cons() adds elements to the front of a list. * It handles any necessary cloning for value lists and it handles * inserting null elements. * * @param hd the value to place at the head of the result. * @param tl the JMLListValueNode to make the tail of the result. */ /*@ public normal_behavior @ ensures \result.headEquals(hd) && \result.next == tl; @ ensures \result.equals(new JMLListValueNode(hd, tl)); @ @ implies_that public normal_behavior @ ensures \result != null @ && \result.containsNull <==> @ hd == null || (tl != null && tl.containsNull); @*/ public static /*@ pure @*/ JMLListValueNode cons(/*@ nullable @*/ JMLType hd, /*@ nullable @*/ JMLListValueNode tl) { if (hd == null) { JMLListValueNode ret = new JMLListValueNode(null, tl); //@ assume ret.containsNull; return ret; } else { Object h = hd.clone(); //@ assume h != null && h instanceof JMLType; JMLListValueNode ret = new JMLListValueNode((JMLType)h, tl); //@ assume ret.containsNull <==> tl != null && tl.containsNull; return ret; } } //**************************** Observers ********************************** /** Return the first element in this list. * Note that head() handles any cloning necessary for value lists. */ /*@ public normal_behavior @ requires val != null; @ ensures \result != null && \result.equals(val); @ also @ public normal_behavior @ requires val == null; @ ensures \result == null; @ @ implies_that @ ensures \result != null ==> \typeof(\result) <: elementType; @ ensures !containsNull ==> \result != null; @*/ public JMLType head() { JMLType ret = (val == null ? null : (JMLType)val.clone()); //@ nowarn Cast; //@ assume ret != null ==> \typeof(ret) <: elementType; //@ assume !containsNull ==> ret != null; return ret; } /** Tell if the head of the list is ".equals" to the given * item. * @see #has(JMLType) */ /*@ public normal_behavior @ requires val != null; @ ensures \result == (val.equals(item)); @ also @ public normal_behavior @ requires val == null; @ ensures \result == (item == null); @*/ public boolean headEquals(JMLType item) { return elem_equals(val, item); } /** Tell if the given elements are equal, taking null into account. */ private static /*@ pure @*/ boolean elem_equals(JMLType item1, JMLType item2) { return (item1 != null && item1.equals(item2)) || (item1 == null && item2 == null); } /** Return the ith element of a list. * @see #getItem(\bigint) */ /*@ public normal_behavior @ requires 0 <= i && i < length(); @ ensures \result != null ==> @ (* \result ".equals" the ith element of this *); @ also @ public exceptional_behavior @ requires !(0 <= i && i < length()); @ signals_only JMLListException; @ @ implies_that @ ensures \result != null ==> \typeof(\result) <: elementType; @ ensures !containsNull ==> \result != null; @*/ /*@ model public JMLType itemAt(\bigint i) throws JMLListException { if (i < 0) { throw new JMLListException("Index to itemAt(int) is negative " + i); } else { \bigint k = i; assert k >= 0; JMLListValueNode ptr = this; maintaining k >= 0; decreasing k; while (ptr != null && k > 0) { k = k - 1; ptr = ptr.next; } if (ptr == null) { throw new JMLListException("Index to itemAt(\bigint) out of range."); } else { assert ptr != null && k == 0; assume ptr.val != null ==> \typeof(ptr.val) <: \type(JMLType); JMLType ret = (ptr.val == null ? null : (JMLType)(ptr.val).clone()); assume ret != null ==> \typeof(ret) <: elementType; assume !containsNull ==> ret != null; return ret; } } } @*/ /** Return the ith element of a list. * @see #getItem(int) */ /*@ public normal_behavior @ requires 0 <= i && i < int_length(); @ ensures \result != null ==> @ (* \result ".equals" the ith element of this *); @ also @ public exceptional_behavior @ requires !(0 <= i && i < int_length()); @ signals_only JMLListException; @ @ implies_that @ ensures \result != null ==> \typeof(\result) <: elementType; @ ensures !containsNull ==> \result != null; @*/ public JMLType itemAt(int i) throws JMLListException { if (i < 0) { throw new JMLListException("Index to itemAt(int) is negative " + i); } else { int k = i; //@ assert k >= 0; JMLListValueNode ptr = this; //@ maintaining k >= 0; //@ decreasing k; while (ptr != null && k > 0) { k--; ptr = ptr.next; } if (ptr == null) { throw new JMLListException("Index to itemAt(int) out of range."); } else { //@ assert ptr != null && k == 0; /*@ assume ptr.val != null @ ==> \typeof(ptr.val) <: \type(JMLType); @*/ JMLType ret = (ptr.val == null ? null : (JMLType)(ptr.val).clone()); //@ assume ret != null ==> \typeof(ret) <: elementType; //@ assume !containsNull ==> ret != null; return ret; } } } /** Tells the number of elements in the sequence; a synonym for length */ /*@ public normal_behavior @ ensures (* \result is the number of JMLListValueNode(s) in this *); @ @ implies_that @ ensures \result > 0; @*/ /*@ @ public model pure \bigint size() { \bigint count = 0; JMLListValueNode ptr = this; maintaining (* ptr is count next's from this *); while (ptr != null) { ptr = ptr.next; count = count + 1; } return count; @ } @*/ /** Tells the number of elements in the sequence; a synonym for length */ /*@ public normal_behavior @ ensures \result == size(); @ @ implies_that @ ensures \result > 0; @*/ /*@ @ public model pure \bigint length() { @ return size(); @ } @*/ /** Tells the number of elements in the list; a synonym for length. */ /*@ public normal_behavior @ ensures \result == size(); @ @ implies_that @ ensures \result > 0; @*/ public int int_size() { int count = 0; JMLListValueNode ptr = this; //@ maintaining (* ptr is count next's from this *); while (ptr != null) { ptr = ptr.next; count++; } return count; } /** Tells the number of elements in the list; a synonym for size. */ /*@ public normal_behavior @ ensures \result == length(); @ @ implies_that @ ensures \result > 0; @*/ public int int_length() { return int_size(); } /** Tells whether the given element is ".equals" to an * element in the list. */ /*@ public normal_behavior @ requires item != null; @ ensures \result <==> @ (\exists int i; 0 <= i && i < int_length(); @ (itemAt(i) == null @ ? item == null @ : itemAt(i).equals(item))); @ also @ public normal_behavior @ requires item == null; @ ensures \result <==> @ (\exists int i; 0 <= i && i < int_length(); @ itemAt(i) == null); @*/ public boolean has(JMLType item) { JMLListValueNode ptr = this; //@ maintaining (* no earlier element is elem_equals to item *); while (ptr != null) { if (elem_equals(ptr.val, item)) { return true; } ptr = ptr.next; } return false; } /** Tells whether the elements of this list occur, in * order, at the beginning of the given list, using ".equals" for * comparisons. */ /*@ public normal_behavior @ requires ls2 != null; @ ensures \result <==> @ int_length() <= ls2.int_length() @ && (\forall int i; 0 <= i && i < int_length(); @ (ls2.itemAt(i) == null && itemAt(i) == null) @ || (ls2.itemAt(i) != null && @ ls2.itemAt(i).equals(itemAt(i)))); @ also @ public normal_behavior @ requires ls2 == null; @ ensures !\result; @*/ public boolean isPrefixOf(JMLListValueNode ls2) { if (ls2 == null) { return false; } JMLListValueNode othLst = ls2; JMLListValueNode thisLst = this; /*@ maintaining @ (* all earlier elements of both lists are elem_equals *); @*/ while (thisLst != null && othLst != null) { if (!elem_equals(thisLst.val, othLst.val)) { return false; } thisLst = thisLst.next; othLst = othLst.next; } return thisLst == null; } /** Test whether this object's value is equal to the given argument. */ /*@ also @ public normal_behavior @ requires ls2 != null && ls2 instanceof JMLListValueNode; @ ensures \result <==> isPrefixOf((JMLListValueNode)ls2) @ && ((JMLListValueNode)ls2).isPrefixOf(this); @ also @ public normal_behavior @ requires ls2 == null || !(ls2 instanceof JMLListValueNode); @ ensures \result <==> false; @*/ public boolean equals(/*@ nullable @*/ Object ls2) { if (ls2 != null && ls2 instanceof JMLListValueNode) { JMLListValueNode othLst = (JMLListValueNode)ls2; JMLListValueNode thisLst = this; //@ maintaining (* all earlier elements of both lists are elem_equals *); while (thisLst != null && othLst != null) { if (!elem_equals(thisLst.val, othLst.val)) { return false; } thisLst = thisLst.next; othLst = othLst.next; } return (othLst == thisLst); // both must be null. } else { return false; } } /** Return a hash code for this object. */ public int hashCode() { int hash = 0; JMLListValueNode ptr = this; while (ptr != null) { Object v = ptr.val; if (v != null) { hash += v.hashCode(); } ptr = ptr.next; } return hash; } /** Return the zero-based index of the first occurrence of the given * element in the list, if there is one * @param item the JMLType sought in this. * @return the first index at which item occurs or -1 if it does not. */ /*@ public normal_behavior @ requires has(item) && item != null; @ ensures itemAt(\result).equals(item) @ && (\forall \bigint i; 0 <= i && i < \result; @ !(itemAt(i) != null @ && itemAt(i).equals(item))); @ ensures_redundantly @ (* \result is the first index at which item occurs in this *); @ also @ public normal_behavior @ requires has(item) && item == null; @ ensures itemAt(\result) == null @ && (\forall \bigint i; 0 <= i && i < \result; @ itemAt(i) != null); @ ensures_redundantly @ (* \result is the first index at which null occurs in this *); @ also @ public normal_behavior @ requires !has(item); @ ensures \result == -1; @ @ implies_that @ ensures \result >= -1; @*/ /*@ model public \bigint bi_indexOf(JMLType item) { \bigint idx = 0; JMLListValueNode ptr = this; maintaining (* ptr is idx next's from this *); while (ptr != null) { if (elem_equals(ptr.val, item)) { return idx; } ptr = ptr.next; idx = idx + 1; } return -1; } @*/ /** Return the zero-based index of the first occurrence of the given * element in the list, if there is one * @param item the JMLType sought in this. * @return the first index at which item occurs or -1 if it does not. */ /*@ public normal_behavior @ requires has(item) && item != null; @ ensures itemAt(\result).equals(item) @ && (\forall int i; 0 <= i && i < \result; @ !(itemAt(i) != null @ && itemAt(i).equals(item))); @ ensures_redundantly @ (* \result is the first index at which item occurs in this *); @ also @ public normal_behavior @ requires has(item) && item == null; @ ensures itemAt(\result) == null @ && (\forall int i; 0 <= i && i < \result; @ itemAt(i) != null); @ ensures_redundantly @ (* \result is the first index at which null occurs in this *); @ also @ public normal_behavior @ requires !has(item); @ ensures \result == -1; @ @ implies_that @ ensures \result >= -1; @*/ public int indexOf(JMLType item) { int idx = 0; JMLListValueNode ptr = this; //@ maintaining (* ptr is idx next's from this *); while (ptr != null) { if (elem_equals(ptr.val, item)) { return idx; } ptr = ptr.next; idx++; } return -1; } /** Return the last element in this list. */ /*@ public normal_behavior @ ensures (\result == null && itemAt((int)(int_length()-1)) == null) @ || \result.equals(itemAt((int)(int_length()-1))); @ @ implies_that @ ensures \result != null ==> \typeof(\result) <: elementType; @ ensures !containsNull ==> \result != null; @*/ public JMLType last() { if (next == null) { return head(); } else { JMLListValueNode ptr = this; //@ maintaining ptr != null; while (ptr.next != null) { ptr = ptr.next; } //@ assert ptr.next == null; //@ assume ptr.val != null ==> \typeof(ptr.val) <: \type(JMLType); JMLType ret = (ptr.val == null ? null : (JMLType)(ptr.val).clone()); //@ assume ret != null ==> \typeof(ret) <: elementType; //@ assume !containsNull ==> ret != null; return ret; } } /** Return the zero-based index of the first occurrence of the given * element in the list, if there is one * @param item the JMLType sought in this list. * @return the first zero-based index at which item occurs. * @exception JMLListException if the item does not occur in this list. * @see #itemAt(int) */ /*@ public normal_behavior @ requires has(item); @ {| @ requires item != null; @ ensures \result.equals(itemAt(indexOf(item))); @ also @ requires item == null; @ ensures \result == null; @ |} @ also @ public exceptional_behavior @ requires !has(item); @ signals_only JMLListException; @ implies_that @ ensures \result != null ==> \typeof(\result) <: elementType; @ ensures !containsNull ==> \result != null; @*/ public JMLType getItem(JMLType item) throws JMLListException { JMLListValueNode ptr = this; //@ maintaining (* no earlier element is elem_equals to item *); while (ptr != null) { if (elem_equals(ptr.val, item)) { //@ assume ptr.val != null ==> \typeof(ptr.val) <: elementType; return ptr.val; } ptr = ptr.next; } throw new JMLListException("No matching item in list."); } // ******************** building new JMLValueLists *********************** /** Return a clone of this object. */ /*@ also @ public normal_behavior @ ensures \result != null && \result instanceof JMLListValueNode @ && ((JMLListValueNode)\result).equals(this); @*/ public /*@ non_null @*/ Object clone() { // Recall that cons() handles cloning. JMLListValueNode ret = cons(val, (next == null ? null : (JMLListValueNode) next.clone())); return ret; } //@ nowarn Post; /** Return a list containing the first n elements in this list. * @param n the number of elements to leave in the result. * @return null if n is not positive or greater than the length of this list. */ /*@ public normal_behavior @ {| @ requires 0 < n && n <= length(); @ ensures \result != null @ && \result.length() == n @ && (\forall \bigint i; 0 <= i && i < n; @ \result.itemAt(i) == itemAt(i)); @ also @ requires n <= 0; @ ensures \result == null; @ also @ requires length() < n; @ ensures this.equals(\result); @ |} @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ /*@ model public JMLListValueNode prefix(\bigint n) { return (n <= 0 ? null : new JMLListValueNode(val, (next == null ? null : next.prefix(n-1))) ); } @*/ /** Return a list containing the first n elements in this list. * @param n the number of elements to leave in the result. * @return null if n is not positive or greater than the length of this list. */ /*@ public normal_behavior @ {| @ requires 0 < n && n <= int_length(); @ ensures \result != null @ && \result.int_length() == n @ && (\forall int i; 0 <= i && i < n; @ \result.itemAt(i) == itemAt(i)); @ also @ requires n <= 0; @ ensures \result == null; @ also @ requires int_length() < n; @ ensures this.equals(\result); @ |} @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ public JMLListValueNode prefix(int n) { return (n <= 0 ? null : new JMLListValueNode(val, (next == null ? null : next.prefix(n-1))) ); } /** Return a list containing all but the first n elements in this list. * @param n the number of elements to remove * @return null if n is negative or greater than the length of this list. */ /*@ public normal_behavior @ {| @ requires 0 < n && n < length(); @ ensures \result != null @ && \result.length() == length() - n @ && (\forall \bigint i; n <= i && i < length(); @ \result.itemAt(i-n) == itemAt(i)); @ also @ requires n <= 0; @ ensures this.equals(\result); @ also @ requires length() <= n; @ ensures \result == null; @ |} @ @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ /*@ model public JMLListValueNode removePrefix(\bigint n) { return (n <= 0 ? this : (next == null ? null : next.removePrefix(n-1)) ); } @*/ /** Return a list containing all but the first n elements in this list. * @param n the number of elements to remove * @return null if n is negative or greater than the length of this list. */ /*@ public normal_behavior @ {| @ requires 0 < n && n < int_length(); @ ensures \result != null @ && \result.int_length() == int_length() - n @ && (\forall int i; n <= i && i < int_length(); @ \result.itemAt((int)(i-n)) == itemAt(i)); @ also @ requires n <= 0; @ ensures this.equals(\result); @ also @ requires int_length() <= n; @ ensures \result == null; @ |} @ @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ public JMLListValueNode removePrefix(int n) { return (n <= 0 ? this : (next == null ? null : next.removePrefix(n-1)) ); } /** Return a list like this list, but without the element at the * given zero-based index. * @param n the zero-based index into the list. * @return null if the index is out of range or if the list was of size 1. */ /*@ public normal_behavior @ requires n == 0 && length() == 1; @ ensures \result == null; @ also @ public normal_behavior @ requires n == 0 && length() > 1; @ ensures \result.equals(removePrefix(1)); @ also @ public normal_behavior @ requires 0 < n && n < length(); @ ensures \result != null @ && \result.length() == length() - 1 @ && \result.equals(prefix(n).concat(removePrefix(n+1))); @ // !FIXME! This spec is inconsistent. Take n == length() - 1. @ // then removePrefix(n+1) --> removePrefix(length()) --> null. @ // But concat requires non_null. Maybe spec of concat should be relaxed? @ @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ /*@ model public JMLListValueNode removeItemAt(\bigint n) { return (n <= 0 ? next : (next == null ? null : new JMLListValueNode(val, next.removeItemAt(n-1))) ); } @*/ /** Return a list like this list, but without the element at the * given zero-based index. * @param n the zero-based index into the list. * @return null if the index is out of range or if the list was of size 1. */ /*@ public normal_behavior @ requires n == 0 && int_length() == 1; @ ensures \result == null; @ also @ public normal_behavior @ requires n == 0 && int_length() > 1; @ ensures \result.equals(removePrefix(1)); @ also @ public normal_behavior @ requires 0 < n && n < int_length(); @ ensures \result != null @ && \result.int_length() == int_length() - 1 @ && \result.equals(prefix(n).concat(removePrefix((int)(n+1)))); @ // !FIXME! This spec is inconsistent. Take n == int_length() - 1. @ // then removePrefix((int)(n+1)) --> removePrefix(int_length()) --> null. @ // But concat requires non_null. Maybe spec of concat should be relaxed? @ @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ public JMLListValueNode removeItemAt(int n) { return (n <= 0 ? next : (next == null ? null : new JMLListValueNode(val, next.removeItemAt(n-1))) ); } /** Return a list like this, but with item replacing the element at the * given zero-based index. * @param n the zero-based index into this list. * @param item the item to put at index index */ /*@ public normal_behavior @ requires 0 <= n && n < length(); @ ensures \result != null && \result.length() == length(); @ also @ public normal_behavior @ requires n == 0 && length() == 1; @ ensures \result != null @ && \result.equals(cons(item, next)); @ also @ public normal_behavior @ requires n == 0 && length() > 1; @ ensures \result != null @ && \result.equals(removePrefix(1).prepend(item)); @ also @ public normal_behavior @ requires 0 < n && n == length()-1; @ ensures \result != null @ && \result.equals(prefix(n).append(item)); @ also @ public normal_behavior @ requires 0 < n && n < length()-1; @ ensures \result != null && \result.length() == length() @ && \result.equals(prefix(n) @ .concat(removePrefix(n+1).prepend(item))); @ implies_that @ requires 0 <= n; @ ensures n == 0 ==> \result != null; @ ensures item == null && \result != null ==> \result.containsNull; @ ensures item != null && !containsNull && \result != null @ ==> !\result.containsNull; @*/ /*@ model public JMLListValueNode replaceItemAt(\bigint n, JMLType item) { return (n <= 0 ? cons(item, next) // cons() handles any necessary cloning : (next == null ? null : new JMLListValueNode(val, next.replaceItemAt(n-1, item))) ); } nowarn Post; @*/ /** Return a list like this, but with item replacing the element at the * given zero-based index. * @param n the zero-based index into this list. * @param item the item to put at index index */ /*@ public normal_behavior @ requires 0 <= n && n < int_length(); @ ensures \result != null && \result.int_length() == int_length(); @ also @ public normal_behavior @ requires n == 0 && int_length() == 1; @ ensures \result != null @ && \result.equals(cons(item, next)); @ also @ public normal_behavior @ requires n == 0 && int_length() > 1; @ ensures \result != null @ && \result.equals(removePrefix(1).prepend(item)); @ also @ public normal_behavior @ requires 0 < n && n == int_length()-1; @ ensures \result != null @ && \result.equals(prefix(n).append(item)); @ also @ public normal_behavior @ requires 0 < n && n < int_length()-1; @ ensures \result != null && \result.int_length() == int_length() @ && \result.equals(prefix(n) @ .concat(removePrefix(n+1).prepend(item))); @ implies_that @ requires 0 <= n; @ ensures n == 0 ==> \result != null; @ ensures item == null && \result != null ==> \result.containsNull; @ ensures item != null && !containsNull && \result != null @ ==> !\result.containsNull; @*/ public JMLListValueNode replaceItemAt(int n, JMLType item) { return (n <= 0 ? cons(item, next) // cons() handles any necessary cloning : (next == null ? null : new JMLListValueNode(val, next.replaceItemAt(n-1, item))) ); } //@ nowarn Post; /** Return a list containing all but the last element in this. */ /*@ public normal_behavior // !FIXME! inconsistent spec [when int_length() == 1] @ ensures \result == null ==> int_length() == 1; @ ensures \result != null ==> \result.equals(prefix((int)(int_length() - 1))); @ implies_that @ ensures !containsNull && \result != null ==> !\result.containsNull; @*/ public JMLListValueNode removeLast() { return (next == null ? null : new JMLListValueNode(val, next.removeLast())); } /** Return a list that is the concatenation of this with the given * lists. * @param ls2 the list to place at the end of this list in the * result. * @return the concatenation of this list and ls2. */ /*@ public normal_behavior @ requires ls2 != null; @ ensures \result != null @ && \result.int_length() == int_length() + ls2.int_length() @ && (\forall int i; 0 <= i && i < int_length(); @ \result.itemAt(i) == itemAt(i)) @ && (\forall int i; 0 <= i && i < ls2.int_length(); @ \result.itemAt((int)(int_length()+i)) == ls2.itemAt(i)); @ ensures (* \result is the concatenation of this followed by ls2 *); @*/ public /*@ non_null @*/ JMLListValueNode concat(/*@ non_null @*/ JMLListValueNode ls2) { return (next == null ? new JMLListValueNode(val, ls2) : new JMLListValueNode(val, next.concat(ls2)) ); } /** Return a list that is like this, but with the given item at * the front. Note that this clones the item if necessary. * @param item the element to place at the front of the result. */ /*@ public normal_behavior @ ensures \result != null && \result.equals(cons(item, this)); @ ensures_redundantly \result != null @ && \result.int_length() == int_length() + 1; @*/ public /*@ non_null @*/ JMLListValueNode prepend(JMLType item) { // cons() handles any necessary cloning return cons(item, this); } /** Return a list that consists of this list and the given element. */ /*@ public normal_behavior @ ensures \result != null @ && \result.equals(cons(item, this.reverse()).reverse()); @ ensures_redundantly \result != null @ && \result.int_length() == int_length() + 1; @*/ public /*@ non_null @*/ JMLListValueNode append(JMLType item) { // To avoid full recursion, we build the reverse of what we want in // revret, then reverse it. To make sure we only clone once, // we let reverse do the cloning JMLListValueNode ptr = this; JMLListValueNode revret = null; //@ maintaining (* reverse(revret) concatenated with ptr equals this *); while (ptr != null) { revret = new JMLListValueNode(ptr.val, revret); // don't clone yet ptr = ptr.next; } return (new JMLListValueNode(item, revret)).reverse(); } /** Return a list that is the reverse of this list. */ /*@ public normal_behavior @ ensures \result.int_length() == int_length() @ && (\forall int i; 0 <= i && i < int_length(); @ (\result.itemAt((int)(int_length()-i-1)) != null @ && \result.itemAt((int)(int_length()-i-1)).equals(itemAt(i))) @ || (\result.itemAt((int)(int_length()-i-1)) == null @ && itemAt(i) == null) ); @ ensures_redundantly @ (* \result has the same elements but with the items @ arranged in the reverse order *); @ @ implies_that @ ensures elementType == \result.elementType; @ ensures containsNull <==> \result.containsNull; @*/ public /*@ non_null @*/ JMLListValueNode reverse() { JMLListValueNode ptr = this; JMLListValueNode ret = null; //@ loop_invariant ptr != this ==> ret != null; //@ maintaining (* ret is the reverse of items in this up to ptr *); while (ptr != null) { //@ assume ptr.val != null ==> \typeof(ptr.val) <: elementType; ret = new JMLListValueNode(ptr.val == null ? null : (JMLType)(ptr.val).clone(), ret); ptr = ptr.next; } //@ assert ptr == null && ret != null; //@ assume elementType == ret.elementType; //@ assume containsNull <==> ret.containsNull; return ret; } /** Return a list that is like this list but with the given item * inserted before the given index. */ /*@ public normal_behavior @ requires 0 < n && n <= length(); @ ensures \result != null @ && \result.equals(prefix(n).concat(cons(item, removePrefix(n)))); @ also @ public normal_behavior @ requires n == 0; @ ensures \result != null && \result.equals(cons(item, this)); @ also @ public exceptional_behavior @ requires !(0 <= n && n <= length()); @ signals_only JMLListException; @*/ /*@ model public non_null JMLListValueNode insertBefore(\bigint n, JMLType item) throws JMLListException { if ( n < 0 || (n > 1 && next == null) ) { throw new JMLListException("Index to insertBefore out of range."); } else if (n == 0) { return cons(item, this); // cons() handles any necessary cloning } else { assert n > 0; return new JMLListValueNode(val, (next == null ? cons(item, null) : next.insertBefore(n-1, item))); } } @*/ /** Return a list that is like this list but with the given item * inserted before the given index. */ /*@ public normal_behavior @ requires 0 < n && n <= int_length(); @ ensures \result != null @ && \result.equals(prefix(n).concat(cons(item, removePrefix(n)))); @ also @ public normal_behavior @ requires n == 0; @ ensures \result != null && \result.equals(cons(item, this)); @ also @ public exceptional_behavior @ requires !(0 <= n && n <= int_length()); @ signals_only JMLListException; @*/ public /*@ non_null @*/ JMLListValueNode insertBefore(int n, JMLType item) throws JMLListException { if ( n < 0 || (n > 1 && next == null) ) { throw new JMLListException("Index to insertBefore out of range."); } else if (n == 0) { return cons(item, this); // cons() handles any necessary cloning } else { //@ assert n > 0; return new JMLListValueNode(val, (next == null ? cons(item, null) : next.insertBefore(n-1, item))); } } /** Return a list that is like this list but without the first * occurrence of the given item. */ /*@ public normal_behavior @ requires !has(item); @ ensures this.equals(\result); @ also @ public normal_behavior @ old int index = indexOf(item); @ requires has(item); @ ensures \result == null <==> \old(int_size() == 1); @ ensures \result != null && index == 0 @ ==> \result.equals(removePrefix(1)); @ ensures \result != null && index > 0 // !FIXME! [? index == int_length() - 1] @ ==> \result.equals(prefix(index).concat(removePrefix((int)(index+1)))); @*/ public JMLListValueNode remove(JMLType item) { if (item == null) { if (val == null) { return next; } else { return new JMLListValueNode(val, (next == null ? null : next.remove(item))); } } else if (item.equals(val)) { return next; } else { return new JMLListValueNode(val, (next == null ? null : next.remove(item))); } } /** Return a string representation for this list. The output is ML style. */ public /*@ non_null @*/ String toString() { String ret = "["; JMLListValueNode thisLst = this; boolean firstTime = true; while (thisLst != null) { if (!firstTime) { ret += ", "; } else { firstTime = false; } if (thisLst.val == null) { ret += "null"; } else { ret += thisLst.val.toString(); } thisLst = thisLst.next; } ret += "]"; return ret; } }