//
// Copyright (C) 2007 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import gov.nasa.jpf.ListenerAdapter;
import gov.nasa.jpf.search.Search;
/**
* a generic, listener- created trace over property specific operations
*
* we could register this as a listener itself, but since it usually is used from
* a listener, we might as well just delegate from there
*/
public class Trace<T> extends ListenerAdapter implements Iterable<T> {
TraceElement<T> lastElement;
TraceElement<T> lastTransition;
// for HeuristicSearches. Ok, that's braindead but at least no need for cloning
HashMap<Integer,TraceElement<T>> storedTransition;
// iterator that traverses the trace LIFO, i.e. starting from the last T
class TraceIterator implements Iterator<T> {
TraceElement<T> cur;
TraceIterator () {
cur = lastElement;
}
public boolean hasNext () {
return (cur != null);
}
public T next () {
if (cur != null){
T op = cur.op;
cur = cur.prevElement;
return op;
} else {
return null;
}
}
public void remove () {
throw new UnsupportedOperationException("TraceElement removal not supported");
}
}
public Iterator<T> iterator() {
return new TraceIterator();
}
public void addOp (T o){
TraceElement<T> op = new TraceElement<T>(o);
if (lastElement == null){
lastElement = op;
} else {
assert lastElement.stateId == 0;
op.prevElement = lastElement;
lastElement = op;
}
}
public void removeLastOp() {
if (lastElement != null){
lastElement = lastElement.prevElement;
}
}
public T getLastOp() {
if (lastElement != null) {
return lastElement.getOp();
}
return null;
}
public int size() {
int n=0;
for (TraceElement<T> te = lastElement; te != null; te = te.prevElement) {
n++;
}
return n;
}
public List<T> getOps () {
// this is a rather braindead way around the limitation that we can't explicitly
// create an T[] array object
ArrayList<T> list = new ArrayList<T>();
for (TraceElement<T> te = lastElement; te != null; te = te.prevElement) {
list.add(te.getOp());
}
// reverse
for (int i=0, j=list.size()-1; i<j; i++, j--) {
T tmp = list.get(j);
list.set(j, list.get(i));
list.set(i, tmp);
}
return list;
}
@Override
public void stateAdvanced (Search search) {
if (search.isNewState() && (lastElement != null)) {
int stateId = search.getStateId();
for (TraceElement<T> op=lastElement; op != null; op=op.prevElement) {
assert op.stateId == 0;
op.stateId = stateId;
}
lastElement.prevTransition = lastTransition;
lastTransition = lastElement;
}
lastElement = null;
}
@Override
public void stateBacktracked (Search search){
int stateId = search.getStateId();
while ((lastTransition != null) && (lastTransition.stateId > stateId)){
lastTransition = lastTransition.prevTransition;
}
lastElement = null;
}
@Override
public void stateStored (Search search) {
if (storedTransition == null){
storedTransition = new HashMap<Integer,TraceElement<T>>();
}
// always called after stateAdvanced
storedTransition.put(search.getStateId(), lastTransition);
}
@Override
public void stateRestored (Search search) {
int stateId = search.getStateId();
TraceElement<T> op = storedTransition.get(stateId);
if (op != null) {
lastTransition = op;
storedTransition.remove(stateId); // not strictly required, but we don't come back
}
}
public Trace<?> clone() {
TraceElement<T> e0 = null, eLast = null;
for (TraceElement<T> e = lastElement; e != null; e = e.prevElement){
TraceElement<T> ec = e.clone();
if (eLast != null){
eLast.prevElement = ec;
eLast = ec;
} else {
e0 = eLast = ec;
}
}
Trace<T> t = new Trace<T>();
t.lastElement = e0;
return t;
}
}