/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package edu.mit.csail.sdg.alloy4; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import edu.mit.csail.sdg.alloy4.ConstList.TempList; /** This list allows add() but disallows remove() and set(); null values are allowed. * * <p> * By making this sacrifice, we are able to provide a very cheap "duplicate method" * that simulates making a copy without actually making a copy. * * <p> * Furthermore, this class's iterator allows concurrent insertion and iteration * (that is, we can iterate over the list while adding elements to the list at the same time). * The iterator is guaranteed to iterate over exactly the elements * that existed at the time that the iterator was created. * * <p><b>Thread Safety:</b> Safe. * * @param <T> - the type of element */ public final class SafeList<T> implements Serializable, Iterable<T> { /** This ensures the class can be serialized reliably. */ private static final long serialVersionUID = 0; /** The actual list of elements; it will be shared by an original SafeList and all its unmodifiable copies. */ private final List<T> list; /** If negative, that means this instance is mutable; otherwise, it is the list size at the time of the copy. */ private final int max; /** Constructs a modifiable empty list. */ public SafeList() { list = new ArrayList<T>(); max = (-1); } /** Constructs a modifiable empty list with the initial capacity. */ public SafeList(int initialCapacity) { list = new ArrayList<T>(initialCapacity); max = (-1); } /** Constructs a modifiable list containing the elements from the given collection. */ public SafeList(Collection<? extends T> initialValue) { list = new ArrayList<T>(initialValue); max = (-1); } /** Constructs a modifiable list containing the elements from the given iterable. */ public SafeList(Iterable<? extends T> initialValue) { list = new ArrayList<T>(); max = (-1); for(T obj: initialValue) list.add(obj); } /** Private constructor for assigning exact values to "list" and "max". */ private SafeList(List<T> list, int max) { this.list = list; this.max = max; } /** Constructs an unmodifiable copy of an existing SafeList. */ public SafeList<T> dup() { synchronized(SafeList.class) { return new SafeList<T>(list, size()); } } /** Constructs a modifiable ArrayList containing the same elements as this list. */ public List<T> makeCopy() { synchronized(SafeList.class) { int n = size(); ArrayList<T> ans = new ArrayList<T>(n); for(int i=0; i<n; i++) ans.add(list.get(i)); return ans; } } /** Constructs an unmodifiable ConstList containing the same elements as this list. */ public ConstList<T> makeConstList() { synchronized(SafeList.class) { int n = size(); TempList<T> ans = new TempList<T>(n); for(int i=0; i<n; i++) ans.add(list.get(i)); return ans.makeConst(); } } /** Computes a hash code that is consistent with SafeList's equals() and java.util.List's hashCode() methods. */ @Override public int hashCode() { int answer = 1; for(Object obj: this) answer = 31*answer + (obj!=null ? obj.hashCode() : 0); return answer; } /** Returns true if (that instanceof List or that instanceof SafeList), and that contains the same elements as this list. */ @SuppressWarnings("unchecked") @Override public boolean equals(Object that) { if (this==that) return true; int n; Iterator<?> b; if (that instanceof List) { n=((List)that).size(); if (n!=size()) return false; b=((List)that).iterator(); } else if (that instanceof SafeList) { n=((SafeList)that).size(); if (n!=size()) return false; b=((SafeList)that).iterator(); } else return false; Iterator<?> a=iterator(); for(int i=0; i<n; i++) { // We must read up to n elements only Object aa=a.next(), bb=b.next(); if (aa==null) { if (bb!=null) return false; } else { if (!aa.equals(bb)) return false; } } return true; } /** Returns true if the list contains the given element. */ public boolean contains(Object item) { for(T entry: this) { if (entry==null) { if (item==null) return true; } else { if (entry.equals(item)) return true; } } return false; } /** Add an element into the list. */ public boolean add(T item) { synchronized(SafeList.class) { if (max>=0) throw new UnsupportedOperationException(); else return list.add(item); } } /** Add a collection of elements into the list. */ public void addAll(Collection<? extends T> items) { synchronized(SafeList.class) { if (max>=0) throw new UnsupportedOperationException(); list.addAll(items); } } /** Get an element from the list. */ public T get(int i) { synchronized(SafeList.class) { if (max>=0 && i>=max) throw new IndexOutOfBoundsException(); else return list.get(i); } } /** Returns the size of the list. */ public int size() { synchronized(SafeList.class) { if (max>=0) return max; else return list.size(); } } /** Returns true if the list is empty. */ public boolean isEmpty() { return size()==0; } /** Returns an iterator that iterates over elements in this list * (in the order that they were inserted). * * <p> Note: This iterator's remove() method always throws UnsupportedOperationException. * * <p> Note: This iterator always returns exactly the list of elements that existed * at the time that the iterator was created (even if the list is modified after that point). */ public Iterator<T> iterator() { synchronized(SafeList.class) { return new Iterator<T>() { private final int imax = (max>=0 ? max : list.size()); private int now = 0; public final T next() { if (now >= imax) throw new NoSuchElementException(); synchronized(SafeList.class) { T answer = list.get(now); now++; return answer; } } public final boolean hasNext() { return now < imax; } public final void remove() { throw new UnsupportedOperationException(); } }; } } /** Returns a String representation of this list. */ @Override public String toString() { StringBuilder sb = new StringBuilder("["); boolean first = true; for(Object x: this) { if (first) first=false; else sb.append(", "); if (x==this) sb.append("(this collection)"); else sb.append(x); } return sb.append(']').toString(); } }