//
// Copyright (C) 2010 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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.NoSuchElementException;
import gov.nasa.jpf.JPFException;
import gov.nasa.jpf.SystemAttribute;
/**
* a minimal container API that transparently handles Object lists which can
* degenerate to single values that are stored directly. Value lists are
* implemented by means of a private Node type, which is transparently handled
* through the (static) ObjectList API
*
* No null objects can be stored in the list. No list can only contain a single
* Node object
*
* Conversion between single objects and lists is done transparently if you
* follow a pattern like:
*
* Object attr; // either a single value or a list
* ..
* attr = ObjectList.remove(attr, v);
*
* If there is only one remaining value in a list, the corresponding Node will
* be replaced with this value.
*
* iterators are LIFO.
*
* We assume that attribute collections are small, otherwise retrieval and
* deletion with this API becomes rather inefficient
*
* NOTE: while ObjectList heads are stored in simple Object fields within the
* user (and therefore could be just overwritten by simple assignments)
* YOU SHOULD NOT DO THIS! Other extensions or JPF itself could rely on
* current attributes. In case you have to change the whole list, use
* set(oldAttrs,newAttr), which checks if there currently is a SystemAttribute
* instance in the list, in which case it throws a JPFException unless the
* new attibute value is also a gov.nasa.jpf.SystemAttribute instance. Use
* forceSet(null) if you really have to remove lists with SystemAttributes
*
*
* usage:
* Object attr;
* ...
* attr = AttrContainer.add( newAttr, attr);
*
* MyAttr a = AttrContainer.getNext( MyAttr.class, attr);
*
* attr = AttrContainer.remove( a, attr);
*
* for (MyAttr a = ObjectList.getFirst(MyAttr.class, attr); a != null;
* a = ObjectList.getNext(MyAttr.class, attr, a)) {..}
*
*/
@SuppressWarnings("unchecked")
public abstract class ObjectList {
// there are no instances, this class is only a static API
private ObjectList(){}
private static class Node {
Object data;
Node next;
Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public boolean equals(Object o){
if (o instanceof Node){
Node n = this;
Node no = (Node)o;
for (; n != null && no != null; n=n.next, no=no.next){
if (!n.data.equals(no.data)){
return false;
}
}
return (n == null) && (no == null);
} else {
return false;
}
}
}
public static class Iterator implements java.util.Iterator<Object>, Iterable<Object> {
Object cur;
Iterator (Object head){
cur = head;
}
public boolean hasNext() {
return cur != null;
}
public Object next() {
if (cur != null){
if (cur instanceof Node){
Node n = (Node)cur;
cur = n.next;
return n.data;
} else { // single attr value
Object n = cur;
cur = null;
return n;
}
} else {
throw new NoSuchElementException();
}
}
public void remove() {
// we can't remove since that would have to change the head field in the client
throw new UnsupportedOperationException();
}
public java.util.Iterator<Object> iterator(){
return this;
}
}
static final Iterator emptyIterator = new Iterator(null);
public static Iterator iterator (Object head){
if (head == null){
return emptyIterator;
} else {
return new Iterator(head);
}
}
public static class TypedIterator<A> implements java.util.Iterator<A>, Iterable<A> {
Object cur;
Class<A> type;
TypedIterator (Object head, Class<A> type){
this.type = type;
this.cur = null;
if (head instanceof Node){
for (Node n = (Node)head; n != null; n = n.next){
if (type.isAssignableFrom(n.data.getClass())) {
cur = n;
break;
}
}
} else if (head != null) {
if (type.isAssignableFrom(head.getClass())) {
cur = head;
}
}
}
public boolean hasNext() {
return cur != null;
}
public A next() {
if (cur != null){
if (cur instanceof Node){
Node nCur = (Node)cur;
cur = null;
A d = (A)nCur.data;
for (Node n=nCur.next; n != null; n=n.next){
if (type.isAssignableFrom(n.data.getClass())){
cur = n;
break;
}
}
return d;
} else { // single attr value
A d = (A)cur;
cur = null;
return d;
}
} else {
throw new NoSuchElementException();
}
}
public void remove() {
// we can't remove since that would have to change the head field in the client
throw new UnsupportedOperationException();
}
public java.util.Iterator<A> iterator(){
return this;
}
}
static final TypedIterator<Object> emptyTypedIterator = new TypedIterator<Object>(null,Object.class);
public static <A> TypedIterator<A> typedIterator (Object head, Class<A> type){
if (head == null){
return (TypedIterator<A>) emptyTypedIterator;
} else {
return new TypedIterator<>(head, type);
}
}
/**
* this returns either the first value if there is only one element, or
* a Node list containing all the values in the order they are provided
*
* note - elements in the list occur in order of arguments, whereas normal
* add() always adds at the head of the list
*/
public static Object createList(Object... values){
if (values.length == 0){
return null;
} else if (values.length == 1){
return values[0];
} else {
Node node = null, next = null;
for (int i=values.length-1; i>=0; i--){
node = new Node(values[i], next);
next = node;
}
return node;
}
}
public static Object valueOf (Object o){
return (o instanceof Node) ? ((Node)o).data : o;
}
public static Object set (Object head, Object newHead){
if (head == null || newHead instanceof SystemAttribute){
return newHead; // Ok to overwrite
} else {
if (head instanceof Node){
// check if there is any SystemAttribute in the list
for (Node n = (Node)head; n != null; n = n.next){
if (n.data instanceof SystemAttribute){
throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
}
}
return newHead; // Ok to overwrite
} else { // single data attribute
if (head instanceof SystemAttribute){
throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
} else {
return newHead; // Ok to overwrite
}
}
}
}
/**
* just to provide a way to overwrite SystemAttributes (e.g. with null)
*/
public static Object forceSet (Object head, Object newHead){
return newHead;
}
public static Object add (Object head, Object data){
if (head == null){
return data;
} else if (data == null){
return head;
} else {
if (head instanceof Node){
return new Node(data, (Node)head);
} else { // was single value -> turn into list
Node p = new Node(head,null);
return new Node(data, p);
}
}
}
public static Object replace (Object head, Object oldData, Object newData){
if (oldData == null){
return head;
}
if (newData == null){
return remove(head, oldData);
}
if (head instanceof Node){
for (Node n = (Node)head; n != null; n = n.next){
if (oldData.equals(n.data)){
n.data = newData;
}
}
return head;
} else {
if (oldData.equals(head)){
return newData;
} else {
return head;
}
}
}
public static Object remove(Object head, Object data){
if (head == null){
return null;
} else if (data == null){
return head;
} else {
if (head instanceof Node){
Node nh = (Node)head;
if (data.equals(nh.data)){
nh = nh.next;
} else {
Node n = nh;
while (true) {
Node nn = n.next;
if (nn != null){
if (nn.data == data){
n.next = nn.next;
break;
} else {
n = nn;
}
} else { // end reached
break;
}
}
}
if (nh.next == null){ // reduce list
return nh.data;
} else {
return nh;
}
} else { // head not a node -> single value
if (data.equals(head)){
return null;
} else {
return head;
}
}
}
}
public static boolean contains (Object head, Object o){
if (head == null || o == null){
return false;
} else if (head instanceof Node){
for (Node n = (Node)head; n != null; n = n.next){
if (o.equals(n.data)){
return true;
}
}
return false;
} else {
return o.equals(head);
}
}
public static boolean containsType (Object head, Class<?> type){
if (head == null || type == null){
return false;
} else if (head instanceof Node){
for (Node n = (Node)head; n != null; n = n.next){
if (type.isAssignableFrom(n.data.getClass())){
return true;
}
}
return false;
} else {
return type.isAssignableFrom(head.getClass());
}
}
//--- various qualifiers
public static boolean isList (Object head){
return (head instanceof Node);
}
public static boolean isEmpty(Object head){
return head == null;
}
public static int size(Object head){
int len = 0;
if (head instanceof Node){
for (Node n = (Node) head; n != null; n = n.next) {
len++;
}
} else {
if (head != null){
len = 1;
}
}
return len;
}
public static int numberOfInstances (Object head, Class<?> type){
int len = 0;
if (head instanceof Node){
for (Node n = (Node) head; n != null; n = n.next) {
if (type.isInstance(n.data)){
len++;
}
}
} else {
if (head != null){
if (type.isInstance(head)){
len = 1;
}
}
}
return len;
}
public static Object get (Object head, int idx){
if (head instanceof Node){
int i=0;
for (Node n = (Node) head; n != null; n = n.next) {
if (i++ == idx){
return n.data;
}
}
} else {
if (idx == 0){
return head;
}
}
return null;
}
public static Object getFirst(Object head){
if (head instanceof Node){
return ((Node)head).data;
} else {
return head;
}
}
public static Object getNext(Object head, Object prevData){
if (head instanceof Node){
Node n = (Node)head;
if (prevData != null){
for (; n != null && n.data != prevData; n=n.next);
if (n == null){
return null;
} else {
n = n.next;
}
}
return n.data;
} else {
if (prevData == null){
return head;
}
}
return null;
}
public static <A> A getFirst (Object head, Class<A> type){
if (head != null){
if (type.isAssignableFrom(head.getClass())) {
return (A) head;
}
if (head instanceof Node) {
for (Node n = (Node) head; n != null; n = n.next) {
if (type.isAssignableFrom(n.data.getClass())) {
return (A) n.data;
}
}
}
}
return null;
}
public static <A> A getNext (Object head, Class<A> type, Object prevData){
if (head instanceof Node){
Node n = (Node)head;
if (prevData != null){
for (; n != null && n.data != prevData; n=n.next);
if (n == null){
return null;
} else {
n = n.next;
}
}
for (; n != null; n = n.next) {
if (type.isAssignableFrom(n.data.getClass())) {
return (A) n.data;
}
}
} else if (head != null) {
if (prevData == null){
if (type.isAssignableFrom(head.getClass())){
return (A)head;
}
}
}
return null;
}
public static void hash (Object head, HashData hd){
if (head instanceof Node){
for (Node n = (Node) head; n != null; n = n.next) {
hd.add(n.data);
}
} else if (head != null){
hd.add(head);
}
}
public static boolean equals( Object head1, Object head2){
if (head1 != null){
return head1.equals(head2);
} else {
return head2 == null; // both null is treated as equal
}
}
static Object cloneData (Object o) throws CloneNotSupportedException {
if (o instanceof CloneableObject) {
CloneableObject co = (CloneableObject) o;
return co.clone();
} else if (o != null) {
Class<?> cls = o.getClass();
try {
Method m = cls.getMethod("clone");
// it can't be static because this would mask Object.clone()
// since Class.getMethod() only returns publics, we don't have to set accessible
return m.invoke(o);
} catch (NoSuchMethodException nsmx){
// since Object.clone() would throw it (unless this is a Cloneable, in which
// case there most probably is a public clone() and we would not have
// gotten here), there is no use trying to call it
throw new CloneNotSupportedException("no public clone(): " + o);
} catch (InvocationTargetException ix){
throw new RuntimeException( "generic clone failed: " + o, ix.getCause());
} catch (IllegalAccessException iax){
throw new RuntimeException("clone() not accessible: " + o);
}
} else {
return null;
}
}
static Node cloneNode (Node n) throws CloneNotSupportedException {
if (n == null){
return null;
} else {
return new Node( cloneData(n.data), cloneNode(n.next));
}
}
public static Object clone (Object head) throws CloneNotSupportedException {
if (head instanceof Node){
return cloneNode( (Node)head);
} else if (head != null){
return cloneData( head);
} else {
return null;
}
}
}