// ex: se sts=4 sw=4 expandtab:
/*
* Yeti core library.
*
* Copyright (c) 2007,2008,2009 Madis Janson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package yeti.lang;
import java.util.Arrays;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
abstract class AMList extends AList implements Serializable {
int start;
abstract int _size();
abstract Object[] array();
// used by length
public long length() {
int l = _size() - start;
return l > 0 ? l : 0;
}
// used by copy
public Object copy() {
Object[] a = new Object[_size()];
System.arraycopy(array(), start, a, 0, a.length);
return new MList(a);
}
public int hashCode() {
int hashCode = 1;
Object[] array = array();
for (int cnt = _size(), i = start; i < cnt; ++i) {
Object x = array[i];
hashCode = 31 * hashCode + (x == null ? 0 : x.hashCode());
}
return hashCode;
}
public boolean equals(Object obj) {
if (obj == null)
return _size() <= start;
if (obj instanceof AMList) {
AMList o = (AMList) obj;
int cnt = _size();
if (cnt - start != o._size() - o.start)
return false;
Object[] arr = array(), arr_ = o.array();
for (int i = start, j = o.start; i < cnt; ++i, ++j) {
Object a = arr[i], b = arr_[j];
if (a != b && (a == null || !a.equals(b)))
return false;
}
return true;
}
if (!(obj instanceof AList))
return false;
Object[] array = array();
AIter j = (AList) obj;
Object x, y;
for (int cnt = _size(), i = start; i < cnt; ++i) {
if (j == null ||
(x = array[i]) != (y = j.first()) &&
(x == null || !x.equals(y))) {
return false;
}
j = j.next();
}
return j == null;
}
public String toString() {
Object[] array = array();
StringBuffer buf = new StringBuffer("[");
for (int cnt = _size(), i = start; i < cnt; ++i) {
if (i > start)
buf.append(',');
buf.append(Core.show(array[i]));
}
buf.append(']');
return buf.toString();
}
public void forEach(Object fun) {
Fun f = (Fun) fun;
Object[] array = array();
for (int cnt = _size(), i = start; i < cnt; ++i)
f.apply(array[i]);
}
public Object fold(Fun f, Object v) {
Object[] array = array();
for (int cnt = _size(), i = start; i < cnt; ++i)
v = f.apply(v, array[i]);
return v;
}
public Num index(Object v) {
Object[] array = array();
int cnt = _size();
if (v == null) {
for (int i = start; i < cnt; ++i)
if (array[i] == null)
return new IntNum(i - start);
return null;
}
for (int i = start; i < cnt; ++i)
if (v.equals(array[i]))
return new IntNum(i - start);
return null;
}
public AList map(Fun f) {
int cnt = _size();
if (start >= cnt)
return null;
Object[] array = array();
Object[] result = new Object[cnt - start];
for (int i = start; i < cnt; ++i)
result[i - start] = f.apply(array[i]);
return new MList(result);
}
public AList smap(Fun f) {
return map(f);
}
public int compareTo(Object obj) {
Object[] array = array();
int cnt = _size(), r, i = start;
if (!(obj instanceof AMList)) {
AIter j = (AIter) obj;
for (; i < cnt && j != null; ++i) {
if ((obj = array[i]) != null) {
if ((r = ((Comparable) obj).compareTo(j.first())) != 0)
return r;
} else if (j.first() != null) {
return -1;
}
j = j.next();
}
return j != null ? -1 : i < cnt ? 1 : 0;
}
AMList o = (AMList) obj;
Object[] array_ = o.array();
int cnt_ = o._size();
for (int j = o.start; i < cnt && j < cnt_; ++i, ++j) {
if ((obj = array[i]) != null) {
if ((r = ((Comparable) obj).compareTo(array_[j])) != 0) {
return r;
}
} else if (array_[j] != null) {
return -1;
}
}
return cnt < cnt_ ? -1 : cnt > cnt_ ? 1 : 0;
}
public AList reverse() {
Object[] array = array();
int end = _size();
if (end <= start)
return null;
Object[] r = new Object[end - start];
--end;
for (int i = 0; i < r.length; ++i)
r[i] = array[end - i];
return new MList(r);
}
public AList sort() {
int len;
if ((len = _size() - start) <= 0)
return null;
Object[] a;
System.arraycopy(array(), start, a = new Object[len], 0, len);
Arrays.sort(a);
return new MList(a);
}
public AList sort(Fun isLess) {
int len;
if ((len = _size() - start) <= 0)
return null;
Object[] a;
System.arraycopy(array(), start, a = new Object[len], 0, len);
return new MList(a).asort(isLess);
}
}
/** Yeti core library - List. */
public class MList extends AMList implements ByKey {
private static final Object[] EMPTY = {};
private Object[] array;
private int size;
private class SubList extends AMList {
Object first;
private SubList(int start) {
this.first = array[start];
this.start = start;
}
public Object first() {
return start < size ? array[start] : first;
}
public AList rest() {
int p;
return (p = start + 1) < size ? new SubList(p) : null;
}
public AIter next() {
int p;
return (p = start + 1) < size ? new Iter(p) : null;
}
public boolean isEmpty() {
return start >= size;
}
int _size() {
return size;
}
Object[] array() {
return array;
}
public AList take(int from, int count) {
if (from < 0)
from = 0;
from += start;
if (count < 0)
return from < size ? from == start
? this : new SubList(from) : null;
if ((count += start) > size)
count = size;
MList res = new MList(array);
res.start = from;
res.size = count;
return res;
}
public AList find(Fun pred) {
for (int cnt = size, i = start; i < cnt; ++i) {
if (pred.apply(array[i]) == Boolean.TRUE) {
return new SubList(i);
}
}
return null;
}
}
private class Iter extends AIter implements Serializable {
private int i;
private Iter(int start) {
i = start;
}
public Object first() {
if (i >= size) {
throw new IllegalStateException(
"End of list reached or list has shrunken.");
}
return array[i];
}
public AIter next() {
return ++i < size ? this : null;
}
public boolean isEmpty() {
return i >= size;
}
public AIter dup() {
return new Iter(i);
}
AIter write(OutputStream out) throws IOException {
if (i < size) {
byte[] tmp = new byte[size - i];
for (int off = i, j = 0; j < tmp.length; ++j)
tmp[j] = ((Number) array[j + off]).byteValue();
out.write(tmp);
}
return null;
}
}
public MList() {
array = EMPTY;
}
public MList(Object[] array) {
this.array = array;
size = array.length;
}
public MList(AIter iter) {
if (iter == null || iter.isEmpty()) {
array = EMPTY;
} else {
array = new Object[10];
while (iter != null) {
add(iter.first());
iter = iter.next();
}
}
}
// used by setArrayCapacity
public void reserve(int n) {
if (n > array.length) {
Object[] tmp = new Object[n];
System.arraycopy(array, 0, tmp, 0, size);
array = tmp;
}
}
// used by push
public void add(Object o) {
if (size >= array.length) {
Object[] tmp = new Object[size == 0 ? 10 : size * 3 / 2 + 1];
System.arraycopy(array, 0, tmp, 0, array.length);
array = tmp;
}
array[size++] = o;
}
// used by shift
public Object shift() {
if (start >= size)
throw new EmptyArrayException("No first element in empty array");
return array[start++];
}
// used by pop
public Object pop() {
if (start >= size)
throw new EmptyArrayException("Cannot pop from an empty array");
return array[--size];
}
// used by clear
public void clear() {
start = size = 0;
}
// used by head
public Object first() {
if (start >= size)
throw new EmptyArrayException("No first element in empty array");
return array[start];
}
// used by tail
public AList rest() {
int p;
return (p = start + 1) < size ? new SubList(p) : null;
}
// used for iterating lists
public AIter next() {
int p;
return (p = start + 1) < size ? new Iter(p) : null;
}
// used by compiler for i in a
public boolean containsKey(Object index) {
int i;
return (i = ((Number) index).intValue()) >= 0 && i + start < size;
}
// used by compiler for a[i]
public Object vget(Object index) {
int i;
if ((i = ((Number) index).intValue()) < 0)
throw new NoSuchKeyException(i, size - start);
if ((i += start) >= size)
throw new NoSuchKeyException(i - start, size - start);
return array[i];
}
// used by compiler for a[number]
public Object get(int index) {
int i;
if (index < 0 || (i = index + start) >= size)
throw new NoSuchKeyException(index, size - start);
return array[i];
}
// used by compiler for a[i] := x
public Object put(Object index, Object value) {
int i;
if ((i = ((Number) index).intValue()) < 0)
throw new NoSuchKeyException(i, size - start);
if ((i += start) >= size)
throw new NoSuchKeyException(i - start, size - start);
array[i] = value;
return null;
}
// used delete
public Object remove(Object index) {
int i, n;
if ((i = ((Number) index).intValue()) < 0)
throw new NoSuchKeyException(i, size - start);
if ((i += start) >= size)
throw new NoSuchKeyException(i - start, size - start);
if ((n = --size - i) > 0)
System.arraycopy(array, i + 1, array, i, n);
return null;
}
private void removeRange(ListRange range) {
int from = range.first.intValue(),
to = range.last.intValue();
if (range.inc < 0) {
int tmp = from;
from = to;
to = tmp;
}
int n = size - start;
if (from <= to) {
if (from < 0 || from >= n)
throw new NoSuchKeyException(from, n);
if (to < 0 || to >= n)
throw new NoSuchKeyException(to, n);
if (++to < n)
System.arraycopy(array, to + start,
array, from + start, n - to);
size -= to - from;
}
}
// used by deleteAll
public void removeAll(AList keys) {
// a common use case is removing a single range
if (keys instanceof ListRange) {
ListRange range = (ListRange) keys;
if (range.rest == null) {
removeRange(range);
return;
}
}
if (keys == null || keys.isEmpty())
return;
// latter elements must be removed first, so have to sort it
MList kl = new MList();
while (keys != null) {
kl.add(keys);
if (keys instanceof ListRange)
keys = ((ListRange) keys).rest;
else
keys = keys.rest();
}
Object[] ka = kl.asort().array;
Object last = null;
for (int i = kl.size; --i >= 0;) {
if (ka[i] instanceof ListRange) {
removeRange((ListRange) ka[i]);
} else {
Object index = ((AList) ka[i]).first();
if (!index.equals(last)) {
remove(index);
last = index;
}
}
}
}
// used by slice
public MList copy(int from, int to) {
int n = size - start;
if (from < 0 || from > n)
throw new NoSuchKeyException(from, n);
if (to > n)
throw new NoSuchKeyException("Copy range " + from + " to " + to +
" exceeds array length " + n);
if (from >= to)
return new MList();
Object[] subArray = new Object[to - from];
System.arraycopy(array, start + from, subArray, 0, subArray.length);
return new MList(subArray);
}
// used by take
public AList take(int from, int count) {
if (from < 0)
from = 0;
from += start;
if (count < 0)
return from < size ? from == start
? this : (AList) new SubList(from) : null;
if ((count += start) > size)
count = size;
MList res = new MList(array);
res.start = from;
res.size = count;
return res;
}
// used by find
public AList find(Fun pred) {
for (int cnt = size, i = start; i < cnt; ++i) {
if (pred.apply(array[i]) == Boolean.TRUE) {
return new SubList(i);
}
}
return null;
}
// used by empty?
public boolean isEmpty() {
return start >= size;
}
final int _size() {
return size;
}
final Object[] array() {
return array;
}
final MList asort() {
Arrays.sort(array, start, size);
return this;
}
// java sort don't know wtf the Fun is
private static void sort(Object[] a, Object[] tmp,
int from, int to, Fun isLess) {
int split = (from + to) / 2;
if (split - from > 1)
sort(tmp, a, from, split, isLess);
if (to - split > 1)
sort(tmp, a, split, to, isLess);
int i = from, j = split;
while (i < split && j < to) {
if (isLess.apply(tmp[i], tmp[j]) == Boolean.TRUE)
a[from] = tmp[i++];
else
a[from] = tmp[j++];
++from;
}
if (i < split)
System.arraycopy(tmp, i, a, from, split - i);
else if (j < to)
System.arraycopy(tmp, j, a, from, to - j);
}
MList asort(Fun isLess) {
if (size - start > 1) {
Object[] tmp = new Object[size];
System.arraycopy(array, start, tmp, start, size - start);
sort(array, tmp, start, size, isLess);
}
return this;
}
public void setDefault(Fun fun) {
throw new UnsupportedOperationException();
}
public Object[] toArray(Object[] to) {
System.arraycopy(array, start, to, 0, size - start);
return to;
}
public static MList ofList(AList list) {
if (list instanceof MList)
return (MList) list;
return new MList(list);
}
public static MList ofStrArray(Object[] array) {
Object[] tmp = new Object[array.length];
for (int i = 0; i < tmp.length; ++i)
tmp[i] = array[i] == null ? Core.UNDEF_STR : array[i];
return new MList(tmp);
}
}