aoqi@0: /* aoqi@0: * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.javac.util; aoqi@0: aoqi@0: import java.lang.reflect.Array; aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.AbstractCollection; aoqi@0: import java.util.ListIterator; aoqi@0: import java.util.NoSuchElementException; aoqi@0: aoqi@0: /** A class for generic linked lists. Links are supposed to be aoqi@0: * immutable, the only exception being the incremental construction of aoqi@0: * lists via ListBuffers. List is the main container class in aoqi@0: * GJC. Most data structures and algorithms in GJC use lists rather aoqi@0: * than arrays. aoqi@0: * aoqi@0: *

Lists are always trailed by a sentinel element, whose head and tail aoqi@0: * are both null. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: */ aoqi@0: public class List extends AbstractCollection implements java.util.List { aoqi@0: aoqi@0: /** The first element of the list, supposed to be immutable. aoqi@0: */ aoqi@0: public A head; aoqi@0: aoqi@0: /** The remainder of the list except for its first element, supposed aoqi@0: * to be immutable. aoqi@0: */ aoqi@0: //@Deprecated aoqi@0: public List tail; aoqi@0: aoqi@0: /** Construct a list given its head and tail. aoqi@0: */ aoqi@0: List(A head, List tail) { aoqi@0: this.tail = tail; aoqi@0: this.head = head; aoqi@0: } aoqi@0: aoqi@0: /** Construct an empty list. aoqi@0: */ aoqi@0: @SuppressWarnings("unchecked") aoqi@0: public static List nil() { aoqi@0: return (List)EMPTY_LIST; aoqi@0: } aoqi@0: aoqi@0: private static final List EMPTY_LIST = new List(null,null) { aoqi@0: public List setTail(List tail) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: public boolean isEmpty() { aoqi@0: return true; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: /** Returns the list obtained from 'l' after removing all elements 'elem' aoqi@0: */ aoqi@0: public static List filter(List l, A elem) { aoqi@0: Assert.checkNonNull(elem); aoqi@0: List res = List.nil(); aoqi@0: for (A a : l) { aoqi@0: if (a != null && !a.equals(elem)) { aoqi@0: res = res.prepend(a); aoqi@0: } aoqi@0: } aoqi@0: return res.reverse(); aoqi@0: } aoqi@0: aoqi@0: public List intersect(List that) { aoqi@0: ListBuffer buf = new ListBuffer<>(); aoqi@0: for (A el : this) { aoqi@0: if (that.contains(el)) { aoqi@0: buf.append(el); aoqi@0: } aoqi@0: } aoqi@0: return buf.toList(); aoqi@0: } aoqi@0: aoqi@0: public List diff(List that) { aoqi@0: ListBuffer buf = new ListBuffer<>(); aoqi@0: for (A el : this) { aoqi@0: if (!that.contains(el)) { aoqi@0: buf.append(el); aoqi@0: } aoqi@0: } aoqi@0: return buf.toList(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Create a new list from the first {@code n} elements of this list aoqi@0: */ aoqi@0: public List take(int n) { aoqi@0: ListBuffer buf = new ListBuffer<>(); aoqi@0: int count = 0; aoqi@0: for (A el : this) { aoqi@0: if (count++ == n) break; aoqi@0: buf.append(el); aoqi@0: } aoqi@0: return buf.toList(); aoqi@0: } aoqi@0: aoqi@0: /** Construct a list consisting of given element. aoqi@0: */ aoqi@0: public static List of(A x1) { aoqi@0: return new List(x1, List.nil()); aoqi@0: } aoqi@0: aoqi@0: /** Construct a list consisting of given elements. aoqi@0: */ aoqi@0: public static List of(A x1, A x2) { aoqi@0: return new List(x1, of(x2)); aoqi@0: } aoqi@0: aoqi@0: /** Construct a list consisting of given elements. aoqi@0: */ aoqi@0: public static List of(A x1, A x2, A x3) { aoqi@0: return new List(x1, of(x2, x3)); aoqi@0: } aoqi@0: aoqi@0: /** Construct a list consisting of given elements. aoqi@0: */ aoqi@0: @SuppressWarnings({"varargs", "unchecked"}) aoqi@0: public static List of(A x1, A x2, A x3, A... rest) { aoqi@0: return new List(x1, new List(x2, new List(x3, from(rest)))); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Construct a list consisting all elements of given array. aoqi@0: * @param array an array; if {@code null} return an empty list aoqi@0: */ aoqi@0: public static List from(A[] array) { aoqi@0: List xs = nil(); aoqi@0: if (array != null) aoqi@0: for (int i = array.length - 1; i >= 0; i--) aoqi@0: xs = new List(array[i], xs); aoqi@0: return xs; aoqi@0: } aoqi@0: aoqi@0: public static List from(Iterable coll) { aoqi@0: ListBuffer xs = new ListBuffer<>(); aoqi@0: for (A a : coll) { aoqi@0: xs.append(a); aoqi@0: } aoqi@0: return xs.toList(); aoqi@0: } aoqi@0: aoqi@0: /** Construct a list consisting of a given number of identical elements. aoqi@0: * @param len The number of elements in the list. aoqi@0: * @param init The value of each element. aoqi@0: */ aoqi@0: @Deprecated aoqi@0: public static List fill(int len, A init) { aoqi@0: List l = nil(); aoqi@0: for (int i = 0; i < len; i++) l = new List(init, l); aoqi@0: return l; aoqi@0: } aoqi@0: aoqi@0: /** Does list have no elements? aoqi@0: */ aoqi@0: @Override aoqi@0: public boolean isEmpty() { aoqi@0: return tail == null; aoqi@0: } aoqi@0: aoqi@0: /** Does list have elements? aoqi@0: */ aoqi@0: //@Deprecated aoqi@0: public boolean nonEmpty() { aoqi@0: return tail != null; aoqi@0: } aoqi@0: aoqi@0: /** Return the number of elements in this list. aoqi@0: */ aoqi@0: //@Deprecated aoqi@0: public int length() { aoqi@0: List l = this; aoqi@0: int len = 0; aoqi@0: while (l.tail != null) { aoqi@0: l = l.tail; aoqi@0: len++; aoqi@0: } aoqi@0: return len; aoqi@0: } aoqi@0: @Override aoqi@0: public int size() { aoqi@0: return length(); aoqi@0: } aoqi@0: aoqi@0: public List setTail(List tail) { aoqi@0: this.tail = tail; aoqi@0: return tail; aoqi@0: } aoqi@0: aoqi@0: /** Prepend given element to front of list, forming and returning aoqi@0: * a new list. aoqi@0: */ aoqi@0: public List prepend(A x) { aoqi@0: return new List(x, this); aoqi@0: } aoqi@0: aoqi@0: /** Prepend given list of elements to front of list, forming and returning aoqi@0: * a new list. aoqi@0: */ aoqi@0: public List prependList(List xs) { aoqi@0: if (this.isEmpty()) return xs; aoqi@0: if (xs.isEmpty()) return this; aoqi@0: if (xs.tail.isEmpty()) return prepend(xs.head); aoqi@0: // return this.prependList(xs.tail).prepend(xs.head); aoqi@0: List result = this; aoqi@0: List rev = xs.reverse(); aoqi@0: Assert.check(rev != xs); aoqi@0: // since xs.reverse() returned a new list, we can reuse the aoqi@0: // individual List objects, instead of allocating new ones. aoqi@0: while (rev.nonEmpty()) { aoqi@0: List h = rev; aoqi@0: rev = rev.tail; aoqi@0: h.setTail(result); aoqi@0: result = h; aoqi@0: } aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** Reverse list. aoqi@0: * If the list is empty or a singleton, then the same list is returned. aoqi@0: * Otherwise a new list is formed. aoqi@0: */ aoqi@0: public List reverse() { aoqi@0: // if it is empty or a singleton, return itself aoqi@0: if (isEmpty() || tail.isEmpty()) aoqi@0: return this; aoqi@0: aoqi@0: List rev = nil(); aoqi@0: for (List l = this; l.nonEmpty(); l = l.tail) aoqi@0: rev = new List(l.head, rev); aoqi@0: return rev; aoqi@0: } aoqi@0: aoqi@0: /** Append given element at length, forming and returning aoqi@0: * a new list. aoqi@0: */ aoqi@0: public List append(A x) { aoqi@0: return of(x).prependList(this); aoqi@0: } aoqi@0: aoqi@0: /** Append given list at length, forming and returning aoqi@0: * a new list. aoqi@0: */ aoqi@0: public List appendList(List x) { aoqi@0: return x.prependList(this); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Append given list buffer at length, forming and returning a new aoqi@0: * list. aoqi@0: */ aoqi@0: public List appendList(ListBuffer x) { aoqi@0: return appendList(x.toList()); aoqi@0: } aoqi@0: aoqi@0: /** Copy successive elements of this list into given vector until aoqi@0: * list is exhausted or end of vector is reached. aoqi@0: */ aoqi@0: @Override @SuppressWarnings("unchecked") aoqi@0: public T[] toArray(T[] vec) { aoqi@0: int i = 0; aoqi@0: List l = this; aoqi@0: Object[] dest = vec; aoqi@0: while (l.nonEmpty() && i < vec.length) { aoqi@0: dest[i] = l.head; aoqi@0: l = l.tail; aoqi@0: i++; aoqi@0: } aoqi@0: if (l.isEmpty()) { aoqi@0: if (i < vec.length) aoqi@0: vec[i] = null; aoqi@0: return vec; aoqi@0: } aoqi@0: aoqi@0: vec = (T[])Array.newInstance(vec.getClass().getComponentType(), size()); aoqi@0: return toArray(vec); aoqi@0: } aoqi@0: aoqi@0: public Object[] toArray() { aoqi@0: return toArray(new Object[size()]); aoqi@0: } aoqi@0: aoqi@0: /** Form a string listing all elements with given separator character. aoqi@0: */ aoqi@0: public String toString(String sep) { aoqi@0: if (isEmpty()) { aoqi@0: return ""; aoqi@0: } else { aoqi@0: StringBuilder buf = new StringBuilder(); aoqi@0: buf.append(head); aoqi@0: for (List l = tail; l.nonEmpty(); l = l.tail) { aoqi@0: buf.append(sep); aoqi@0: buf.append(l.head); aoqi@0: } aoqi@0: return buf.toString(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** Form a string listing all elements with comma as the separator character. aoqi@0: */ aoqi@0: @Override aoqi@0: public String toString() { aoqi@0: return toString(","); aoqi@0: } aoqi@0: aoqi@0: /** Compute a hash code, overrides Object aoqi@0: * @see java.util.List#hashCode aoqi@0: */ aoqi@0: @Override aoqi@0: public int hashCode() { aoqi@0: List l = this; aoqi@0: int h = 1; aoqi@0: while (l.tail != null) { aoqi@0: h = h * 31 + (l.head == null ? 0 : l.head.hashCode()); aoqi@0: l = l.tail; aoqi@0: } aoqi@0: return h; aoqi@0: } aoqi@0: aoqi@0: /** Is this list the same as other list? aoqi@0: * @see java.util.List#equals aoqi@0: */ aoqi@0: @Override aoqi@0: public boolean equals(Object other) { aoqi@0: if (other instanceof List) aoqi@0: return equals(this, (List)other); aoqi@0: if (other instanceof java.util.List) { aoqi@0: List t = this; aoqi@0: Iterator oIter = ((java.util.List) other).iterator(); aoqi@0: while (t.tail != null && oIter.hasNext()) { aoqi@0: Object o = oIter.next(); aoqi@0: if ( !(t.head == null ? o == null : t.head.equals(o))) aoqi@0: return false; aoqi@0: t = t.tail; aoqi@0: } aoqi@0: return (t.isEmpty() && !oIter.hasNext()); aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** Are the two lists the same? aoqi@0: */ aoqi@0: public static boolean equals(List xs, List ys) { aoqi@0: while (xs.tail != null && ys.tail != null) { aoqi@0: if (xs.head == null) { aoqi@0: if (ys.head != null) return false; aoqi@0: } else { aoqi@0: if (!xs.head.equals(ys.head)) return false; aoqi@0: } aoqi@0: xs = xs.tail; aoqi@0: ys = ys.tail; aoqi@0: } aoqi@0: return xs.tail == null && ys.tail == null; aoqi@0: } aoqi@0: aoqi@0: /** Does the list contain the specified element? aoqi@0: */ aoqi@0: @Override aoqi@0: public boolean contains(Object x) { aoqi@0: List l = this; aoqi@0: while (l.tail != null) { aoqi@0: if (x == null) { aoqi@0: if (l.head == null) return true; aoqi@0: } else { aoqi@0: if (l.head.equals(x)) return true; aoqi@0: } aoqi@0: l = l.tail; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** The last element in the list, if any, or null. aoqi@0: */ aoqi@0: public A last() { aoqi@0: A last = null; aoqi@0: List t = this; aoqi@0: while (t.tail != null) { aoqi@0: last = t.head; aoqi@0: t = t.tail; aoqi@0: } aoqi@0: return last; aoqi@0: } aoqi@0: aoqi@0: @SuppressWarnings("unchecked") aoqi@0: public static List convert(Class klass, List list) { aoqi@0: if (list == null) aoqi@0: return null; aoqi@0: for (Object o : list) aoqi@0: klass.cast(o); aoqi@0: return (List)list; aoqi@0: } aoqi@0: aoqi@0: private static final Iterator EMPTYITERATOR = new Iterator() { aoqi@0: public boolean hasNext() { aoqi@0: return false; aoqi@0: } aoqi@0: public Object next() { aoqi@0: throw new java.util.NoSuchElementException(); aoqi@0: } aoqi@0: public void remove() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: @SuppressWarnings("unchecked") aoqi@0: private static Iterator emptyIterator() { aoqi@0: return (Iterator)EMPTYITERATOR; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Iterator iterator() { aoqi@0: if (tail == null) aoqi@0: return emptyIterator(); aoqi@0: return new Iterator() { aoqi@0: List elems = List.this; aoqi@0: public boolean hasNext() { aoqi@0: return elems.tail != null; aoqi@0: } aoqi@0: public A next() { aoqi@0: if (elems.tail == null) aoqi@0: throw new NoSuchElementException(); aoqi@0: A result = elems.head; aoqi@0: elems = elems.tail; aoqi@0: return result; aoqi@0: } aoqi@0: public void remove() { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: public A get(int index) { aoqi@0: if (index < 0) aoqi@0: throw new IndexOutOfBoundsException(String.valueOf(index)); aoqi@0: aoqi@0: List l = this; aoqi@0: for (int i = index; i-- > 0 && !l.isEmpty(); l = l.tail) aoqi@0: ; aoqi@0: aoqi@0: if (l.isEmpty()) aoqi@0: throw new IndexOutOfBoundsException("Index: " + index + ", " + aoqi@0: "Size: " + size()); aoqi@0: return l.head; aoqi@0: } aoqi@0: aoqi@0: public boolean addAll(int index, Collection c) { aoqi@0: if (c.isEmpty()) aoqi@0: return false; aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public A set(int index, A element) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public void add(int index, A element) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public A remove(int index) { aoqi@0: throw new UnsupportedOperationException(); aoqi@0: } aoqi@0: aoqi@0: public int indexOf(Object o) { aoqi@0: int i = 0; aoqi@0: for (List l = this; l.tail != null; l = l.tail, i++) { aoqi@0: if (l.head == null ? o == null : l.head.equals(o)) aoqi@0: return i; aoqi@0: } aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: public int lastIndexOf(Object o) { aoqi@0: int last = -1; aoqi@0: int i = 0; aoqi@0: for (List l = this; l.tail != null; l = l.tail, i++) { aoqi@0: if (l.head == null ? o == null : l.head.equals(o)) aoqi@0: last = i; aoqi@0: } aoqi@0: return last; aoqi@0: } aoqi@0: aoqi@0: public ListIterator listIterator() { aoqi@0: return Collections.unmodifiableList(new ArrayList(this)).listIterator(); aoqi@0: } aoqi@0: aoqi@0: public ListIterator listIterator(int index) { aoqi@0: return Collections.unmodifiableList(new ArrayList(this)).listIterator(index); aoqi@0: } aoqi@0: aoqi@0: public java.util.List subList(int fromIndex, int toIndex) { aoqi@0: if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex) aoqi@0: throw new IllegalArgumentException(); aoqi@0: aoqi@0: ArrayList a = new ArrayList(toIndex - fromIndex); aoqi@0: int i = 0; aoqi@0: for (List l = this; l.tail != null; l = l.tail, i++) { aoqi@0: if (i == toIndex) aoqi@0: break; aoqi@0: if (i >= fromIndex) aoqi@0: a.add(l.head); aoqi@0: } aoqi@0: aoqi@0: return Collections.unmodifiableList(a); aoqi@0: } aoqi@0: }