2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package gov.nasa.jpf.util;
20 import gov.nasa.jpf.JPFException;
21 import gov.nasa.jpf.SystemAttribute;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.util.NoSuchElementException;
28 * a minimal container API that transparently handles Object lists which can
29 * degenerate to single values that are stored directly. Value lists are
30 * implemented by means of a private Node type, which is transparently handled
31 * through the (static) ObjectList API
33 * No null objects can be stored in the list. No list can only contain a single
36 * Conversion between single objects and lists is done transparently if you
37 * follow a pattern like:
39 * Object attr; // either a single value or a list
41 * attr = ObjectList.remove(attr, v);
43 * If there is only one remaining value in a list, the corresponding Node will
44 * be replaced with this value.
48 * We assume that attribute collections are small, otherwise retrieval and
49 * deletion with this API becomes rather inefficient
51 * NOTE: while ObjectList heads are stored in simple Object fields within the
52 * user (and therefore could be just overwritten by simple assignments)
53 * YOU SHOULD NOT DO THIS! Other extensions or JPF itself could rely on
54 * current attributes. In case you have to change the whole list, use
55 * set(oldAttrs,newAttr), which checks if there currently is a SystemAttribute
56 * instance in the list, in which case it throws a JPFException unless the
57 * new attibute value is also a gov.nasa.jpf.SystemAttribute instance. Use
58 * forceSet(null) if you really have to remove lists with SystemAttributes
64 * attr = AttrContainer.add( newAttr, attr);
66 * MyAttr a = AttrContainer.getNext( MyAttr.class, attr);
68 * attr = AttrContainer.remove( a, attr);
70 * for (MyAttr a = ObjectList.getFirst(MyAttr.class, attr); a != null;
71 * a = ObjectList.getNext(MyAttr.class, attr, a)) {..}
74 public abstract class ObjectList {
76 // there are no instances, this class is only a static API
77 private ObjectList(){}
79 private static class Node implements Cloneable {
83 Node(Object data, Node next) {
89 public boolean equals(Object o){
90 if (o instanceof Node){
93 for (; n != null && no != null; n=n.next, no=no.next){
94 if (!n.data.equals(no.data)){
98 return (n == null) && (no == null);
105 protected Node clone(){
107 return (Node)super.clone();
108 } catch (CloneNotSupportedException cnsx){
109 throw new RuntimeException("Node clone failed");
113 // recursively clone up to the node with the specified data
114 public Node cloneWithReplacedData (Object oldData, Object newData){
115 Node newThis = clone();
117 if (data.equals(oldData)){
118 newThis.data = newData;
120 } else if (next != null) {
121 newThis.next = next.cloneWithReplacedData(oldData, newData);
127 public Node cloneWithRemovedData (Object oldData){
128 Node newThis = clone();
131 if (next.data.equals(oldData)){
132 newThis.next = next.next;
134 newThis.next = next.cloneWithRemovedData( oldData);
142 public static class Iterator implements java.util.Iterator<Object>, Iterable<Object> {
145 Iterator (Object head){
150 public boolean hasNext() {
155 public Object next() {
157 if (cur instanceof Node){
162 } else { // single attr value
168 throw new NoSuchElementException();
173 public void remove() {
174 // we can't remove since that would have to change the head field in the client
175 throw new UnsupportedOperationException();
179 public java.util.Iterator<Object> iterator(){
184 static final Iterator emptyIterator = new Iterator(null);
186 public static Iterator iterator (Object head){
188 return emptyIterator;
190 return new Iterator(head);
194 public static class TypedIterator<A> implements java.util.Iterator<A>, Iterable<A> {
198 TypedIterator (Object head, Class<A> type){
202 if (head instanceof Node){
203 for (Node n = (Node)head; n != null; n = n.next){
204 if (type.isAssignableFrom(n.data.getClass())) {
209 } else if (head != null) {
210 if (type.isAssignableFrom(head.getClass())) {
217 public boolean hasNext() {
225 if (cur instanceof Node){
226 Node nCur = (Node)cur;
230 for (Node n=nCur.next; n != null; n=n.next){
231 if (type.isAssignableFrom(n.data.getClass())){
239 } else { // single attr value
246 throw new NoSuchElementException();
251 public void remove() {
252 // we can't remove since that would have to change the head field in the client
253 throw new UnsupportedOperationException();
257 public java.util.Iterator<A> iterator(){
262 static final TypedIterator<Object> emptyTypedIterator = new TypedIterator<Object>(null,Object.class);
264 public static <A> TypedIterator<A> typedIterator (Object head, Class<A> type){
266 return (TypedIterator<A>) emptyTypedIterator;
268 return new TypedIterator<A>(head, type);
273 * this returns either the first value if there is only one element, or
274 * a Node list containing all the values in the order they are provided
276 * note - elements in the list occur in order of arguments, whereas normal
277 * add() always adds at the head of the list
279 public static Object createList(Object... values){
280 if (values.length == 0){
283 } else if (values.length == 1){
287 Node node = null, next = null;
289 for (int i=values.length-1; i>=0; i--){
290 node = new Node(values[i], next);
297 public static Object valueOf (Object o){
298 return (o instanceof Node) ? ((Node)o).data : o;
301 public static Object set (Object head, Object newHead){
302 if (head == null || newHead instanceof SystemAttribute){
303 return newHead; // Ok to overwrite
306 if (head instanceof Node){
307 // check if there is any SystemAttribute in the list
308 for (Node n = (Node)head; n != null; n = n.next){
309 if (n.data instanceof SystemAttribute){
310 throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
314 return newHead; // Ok to overwrite
316 } else { // single data attribute
317 if (head instanceof SystemAttribute){
318 throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
320 return newHead; // Ok to overwrite
327 * just to provide a way to overwrite SystemAttributes (e.g. with null)
329 public static Object forceSet (Object head, Object newHead){
334 public static Object add (Object head, Object data){
338 } else if (data == null){
342 if (head instanceof Node){
343 return new Node(data, (Node)head);
345 } else { // was single value -> turn into list
346 Node p = new Node(head,null);
347 return new Node(data, p);
352 public static Object replace (Object head, Object oldData, Object newData){
353 if (oldData == null){
356 if (newData == null){
357 return remove(head, oldData); // no null data, remove oldData
360 if (head instanceof Node){
361 // <2do> perhaps we should first check if it is there
362 return ((Node)head).cloneWithReplacedData(oldData, newData);
364 } else { // single object
365 if (oldData.equals(head)){
373 public static Object remove (Object head, Object data){
374 if (head == null || data == null){
378 if (head instanceof Node) {
379 Node nh = (Node) head;
382 if (nhn != null && nhn.next == null) { // 2 node case - reduce if found
383 if (nh.data.equals(data)) {
385 } else if (nhn.data.equals(data)) {
387 } else { // not there
392 return nh.cloneWithRemovedData(data);
394 } else { // single object
395 if (head.equals(data)){
403 public static boolean contains (Object head, Object o){
404 if (head == null || o == null){
407 } else if (head instanceof Node){
408 for (Node n = (Node)head; n != null; n = n.next){
409 if (o.equals(n.data)){
416 return o.equals(head);
420 public static boolean containsType (Object head, Class<?> type){
421 if (head == null || type == null){
424 } else if (head instanceof Node){
425 for (Node n = (Node)head; n != null; n = n.next){
426 if (type.isAssignableFrom(n.data.getClass())){
433 return type.isAssignableFrom(head.getClass());
437 //--- various qualifiers
439 public static boolean isList (Object head){
440 return (head instanceof Node);
443 public static boolean isEmpty(Object head){
447 public static int size(Object head){
450 if (head instanceof Node){
451 for (Node n = (Node) head; n != null; n = n.next) {
463 public static int numberOfInstances (Object head, Class<?> type){
466 if (head instanceof Node){
467 for (Node n = (Node) head; n != null; n = n.next) {
468 if (type.isInstance(n.data)){
474 if (type.isInstance(head)){
484 public static Object get (Object head, int idx){
485 if (head instanceof Node){
487 for (Node n = (Node) head; n != null; n = n.next) {
501 public static Object getFirst(Object head){
502 if (head instanceof Node){
503 return ((Node)head).data;
509 public static Object getNext(Object head, Object prevData){
510 if (head instanceof Node){
512 if (prevData != null){
513 for (; n != null && n.data != prevData; n=n.next);
524 if (prevData == null){
532 public static <A> A getFirst (Object head, Class<A> type){
534 if (type.isAssignableFrom(head.getClass())) {
538 if (head instanceof Node) {
539 for (Node n = (Node) head; n != null; n = n.next) {
540 if (type.isAssignableFrom(n.data.getClass())) {
550 public static <A> A getNext (Object head, Class<A> type, Object prevData){
551 if (head instanceof Node){
553 if (prevData != null){
554 for (; n != null && n.data != prevData; n=n.next);
562 for (; n != null; n = n.next) {
563 if (type.isAssignableFrom(n.data.getClass())) {
568 } else if (head != null) {
569 if (prevData == null){
570 if (type.isAssignableFrom(head.getClass())){
579 public static void hash (Object head, HashData hd){
580 if (head instanceof Node){
581 for (Node n = (Node) head; n != null; n = n.next) {
585 } else if (head != null){
590 public static boolean equals( Object head1, Object head2){
592 return head1.equals(head2);
594 return head2 == null; // both null is treated as equal
598 static Object cloneData (Object o) throws CloneNotSupportedException {
599 if (o instanceof CloneableObject) {
600 CloneableObject co = (CloneableObject) o;
603 } else if (o != null) {
604 Class<?> cls = o.getClass();
606 Method m = cls.getMethod("clone");
607 // it can't be static because this would mask Object.clone()
608 // since Class.getMethod() only returns publics, we don't have to set accessible
611 } catch (NoSuchMethodException nsmx){
612 // since Object.clone() would throw it (unless this is a Cloneable, in which
613 // case there most probably is a public clone() and we would not have
614 // gotten here), there is no use trying to call it
615 throw new CloneNotSupportedException("no public clone(): " + o);
616 } catch (InvocationTargetException ix){
617 throw new RuntimeException( "generic clone failed: " + o, ix.getCause());
618 } catch (IllegalAccessException iax){
619 throw new RuntimeException("clone() not accessible: " + o);
627 static Node cloneNode (Node n) throws CloneNotSupportedException {
631 return new Node( cloneData(n.data), cloneNode(n.next));
635 public static Object clone (Object head) throws CloneNotSupportedException {
636 if (head instanceof Node){
637 return cloneNode( (Node)head);
639 } else if (head != null){
640 return cloneData( head);