/*******************************************************************************
* Copyright (c) 2009 Centrum Wiskunde en Informatica (CWI)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Arnold Lankamp - interfaces and implementation
* Paul Klint - added new methods and SubList
*******************************************************************************/
package org.rascalmpl.value.impl.fast;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListRelation;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.exceptions.IllegalOperationException;
import org.rascalmpl.value.impl.AbstractValue;
import org.rascalmpl.value.impl.func.ListFunctions;
import org.rascalmpl.value.impl.util.collections.ShareableValuesList;
import org.rascalmpl.value.io.StandardTextWriter;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.visitors.IValueVisitor;
/**
* Implementation of IList.
*
* @author Arnold Lankamp
*/
/*package*/ class List extends AbstractValue implements IList{
protected final static TypeFactory typeFactory = TypeFactory.getInstance();
protected final static Type voidType = typeFactory.voidType();
protected final Type listType;
protected final Type elementType;
protected final ShareableValuesList data;
protected int hashCode = 0;
/*package*/ static IList newList(Type elementType, ShareableValuesList data) {
return new List(elementType, data);
}
private List(Type elementType, ShareableValuesList data){
super();
if (data.isEmpty())
this.elementType = voidType;
else
this.elementType = elementType;
this.listType = typeFactory.listType(this.elementType);
this.data = data;
}
/*package*/ static ListWriter createListWriter(Type eltType){
return new ListWriter(eltType);
}
/*package*/ static ListWriter createListWriter(){
return new ListWriter();
}
public Type getType(){
return listType;
}
public Type getElementType(){
return elementType;
}
public int length(){
return data.size();
}
public boolean isEmpty(){
return length() == 0;
}
public IValue get(int index){
return data.get(index);
}
public boolean contains(IValue element){
return data.contains(element);
}
public Iterator<IValue> iterator(){
return data.iterator();
}
public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E{
return v.visitList(this);
}
public IList append(IValue element){
ShareableValuesList newData = new ShareableValuesList(data);
newData.append(element);
Type newElementType = elementType.lub(element.getType());
return new ListWriter(newElementType, newData).done();
}
public IList concat(IList other){
ShareableValuesList newData = new ShareableValuesList(data);
Iterator<IValue> otherIterator = other.iterator();
while(otherIterator.hasNext()){
newData.append(otherIterator.next());
}
Type newElementType = elementType.lub(other.getElementType());
return new ListWriter(newElementType, newData).done();
}
public IList insert(IValue element){
ShareableValuesList newData = new ShareableValuesList(data);
newData.insert(element);
Type newElementType = elementType.lub(element.getType());
return new ListWriter(newElementType, newData).done();
}
public IList put(int index, IValue element) throws IndexOutOfBoundsException{
ShareableValuesList newData = new ShareableValuesList(data);
newData.set(index, element);
Type newElementType = elementType.lub(element.getType());
return new ListWriter(newElementType, newData).done();
}
public IList replace(int first, int second, int end, IList repl)
throws FactTypeUseException, IndexOutOfBoundsException {
ShareableValuesList newData = new ShareableValuesList();
int rlen = repl.length();
int increment = Math.abs(second - first);
if(first < end){
int dataIndex = 0;
// Before begin
while(dataIndex < first){
newData.append(data.get(dataIndex++));
}
int replIndex = 0;
boolean wrapped = false;
// Between begin and end
while(dataIndex < end){
newData.append(repl.get(replIndex++));
if(replIndex == rlen){
replIndex = 0;
wrapped = true;
}
dataIndex++; //skip the replaced element
for(int j = 1; j < increment && dataIndex < end; j++){
newData.append(data.get(dataIndex++));
}
}
if(!wrapped){
while(replIndex < rlen){
newData.append(repl.get(replIndex++));
}
}
// After end
int dlen = data.size();
while( dataIndex < dlen){
newData.append(data.get(dataIndex++));
}
} else {
// Before begin (from right to left)
int dataIndex = data.size() - 1;
while(dataIndex > first){
newData.insert(data.get(dataIndex--));
}
// Between begin (right) and end (left)
int replIndex = 0;
boolean wrapped = false;
while(dataIndex > end){
newData.insert(repl.get(replIndex++));
if(replIndex == repl.length()){
replIndex = 0;
wrapped = true;
}
dataIndex--; //skip the replaced element
for(int j = 1; j < increment && dataIndex > end; j++){
newData.insert(data.get(dataIndex--));
}
}
if(!wrapped){
while(replIndex < rlen){
newData.insert(repl.get(replIndex++));
}
}
// Left of end
while(dataIndex >= 0){
newData.insert(data.get(dataIndex--));
}
}
Type newElementType = elementType.lub(repl.getElementType());
return new ListWriter(newElementType, newData).done();
}
public IList delete(int index){
ShareableValuesList newData = new ShareableValuesList(data);
newData.remove(index);
Type newElementType = TypeFactory.getInstance().voidType();
for(IValue el : newData)
newElementType = newElementType.lub(el.getType());
return new ListWriter(newElementType, newData).done();
}
public IList delete(IValue element){
ShareableValuesList newData = new ShareableValuesList(data);
if (newData.remove(element)) {
Type newElementType = TypeFactory.getInstance().voidType();
for (IValue el : newData) {
newElementType = newElementType.lub(el.getType());
}
return new ListWriter(newElementType, newData).done();
}
return this;
}
public IList reverse(){
ShareableValuesList newData = new ShareableValuesList(data);
newData.reverse();
return new ListWriter(elementType, newData).done();
}
@Override
public IList shuffle(Random rand) {
ShareableValuesList newData = new ShareableValuesList(data);
// we use Fisher–Yates shuffle (or Knuth shuffle)
// unbiased and linear time, since set and get are O(1)
for (int i= newData.size() - 1; i >= 1; i--) {
// we use the stack as tmp variable :)
newData.set(i, newData.set(rand.nextInt(i + 1), newData.get(i)));
}
return new ListWriter(elementType, newData).done();
}
public IList sublist(int offset, int length){
if(length < 4){
return materializedSublist(offset, length);
}
return new SubList(this, offset, length);
}
IList materializedSublist(int offset, int length){
ShareableValuesList newData = data.subList(offset, length);
Type newElementType = TypeFactory.getInstance().voidType();
for(IValue el : newData) {
if (newElementType.equals(this.elementType)) {
// the type can only get more specific
// once we've reached the type of the whole list, we can stop lubbing.
break;
}
newElementType = newElementType.lub(el.getType());
}
return new ListWriter(newElementType, newData).done();
}
public int hashCode(){
if (hashCode == 0) {
hashCode = data.hashCode();
}
return hashCode;
}
public boolean equals(Object o){
if(o == this) return true;
if(o == null) return false;
if(o instanceof List) {
List otherList = (List) o;
if (getType() != otherList.getType()) return false;
if (hashCode() != otherList.hashCode()) return false;
if (listType != otherList.listType) return false;
return data.equals(otherList.data);
}
if(o instanceof SubList){
IList otherList = (SubList) o;
if (getType() != otherList.getType()) return false;
if (hashCode() != otherList.hashCode()) return false;
if (listType != otherList.getType()) return false;
return ListFunctions.equals(ValueFactory.getInstance(), this, otherList);
}
return false;
}
public boolean isEqual(IValue value){
if(value == this) return true;
if(value == null) return false;
if(value instanceof List){
List otherList = (List) value;
return data.isEqual(otherList.data);
}
else if (value instanceof IList) {
return ListFunctions.isEqual(ValueFactory.getInstance(), this, value);
}
return false;
}
public IList product(IList lst){
Type resultType = TypeFactory.getInstance().tupleType(getElementType(),lst.getElementType());
ListWriter w = new ListWriter(resultType);
for(IValue t1 : this){
for(IValue t2 : lst){
IValue vals[] = {t1, t2};
ITuple t3 = Tuple.newTuple(resultType, vals);
w.insert(t3);
}
}
return (IList) w.done();
}
public IList intersect(IList other) {
IListWriter w = ValueFactory.getInstance().listWriter();
if(other instanceof List){
List o = (List) other;
for(IValue v : data){
if(o.data.contains(v)){
w.append(v);
}
}
return w.done();
}
IList o = (IList) other;
for(IValue v : data){
if(o.contains(v)){
w.append(v);
}
}
return w.done();
}
public IList subtract(IList lst) {
IListWriter w = ValueFactory.getInstance().listWriter();
for (IValue v: this.data) {
if (lst.contains(v)) {
lst = lst.delete(v);
} else
w.append(v);
}
return w.done();
}
public boolean isSubListOf(IList lst) {
int j = 0;
nextchar:
for(IValue elm : this.data){
while(j < lst.length()){
if(elm.isEqual(lst.get(j))){
j++;
continue nextchar;
} else
j++;
}
return false;
}
return true;
}
@Override
public boolean isRelation() {
return getType().isListRelation();
}
@Override
public IListRelation<IList> asRelation() {
if (!isRelation())
throw new IllegalOperationException(
"Cannot be viewed as a relation.", getType());
return new RelationViewOnList(this);
}
}
class SubList extends AbstractValue implements IList {
private final IList base;
private final int offset;
private final int length;
private int hashCode;
private final Type elementType;
SubList(IList base, int offset, int length){
this.base = base;
this.offset = offset;
this.length = length;
int end = offset + length;
if(offset < 0) throw new IndexOutOfBoundsException("Offset may not be smaller than 0.");
if(length < 0) throw new IndexOutOfBoundsException("Length may not be smaller than 0.");
if(end > base.length()) throw new IndexOutOfBoundsException("'offset + length' may not be larger than 'list.size()'");
Type newElementType = TypeFactory.getInstance().voidType();
Type baseElementType = base.getElementType();
for(int i = offset; i < end; i++){
IValue el = base.get(i);
if (newElementType.equals(baseElementType)) {
// the type can only get more specific
// once we've reached the type of the whole list, we can stop lubbing.
break;
}
newElementType = newElementType.lub(el.getType());
}
elementType = newElementType;
}
IList materialize(){
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
w.append(base.get(i));
}
return w.done();
}
IList maybeMaterialize(IList lst){
if(lst instanceof SubList){
return ((SubList) lst).materialize();
}
return lst;
}
@Override
public Type getType() {
return TypeFactory.getInstance().listType(elementType);
}
@Override
public Type getElementType(){
return elementType;
}
public int hashCode(){
if (hashCode == 0) {
hashCode = ListFunctions.hashCode( ValueFactory.getInstance(), this);
}
return hashCode;
}
public boolean equals(Object o){
if(o == this) return true;
if(o == null) return false;
if(o instanceof List) {
return ((List) o).equals(this);
}
if(o instanceof SubList){
SubList otherList = (SubList) o;
return base.equals(otherList.base) && offset == otherList.offset && length == otherList.length;
}
return false;
}
@Override
public boolean isEqual(IValue value){
if(value == this) return true;
if(value == null) return false;
if(value instanceof List){
List otherList = (List) value;
return otherList.isEqual(this);
}
else if (value instanceof IList) {
return ListFunctions.isEqual(ValueFactory.getInstance(), this, value);
}
return false;
}
@Override
public <T, E extends Throwable> T accept(IValueVisitor<T, E> v) throws E {
return v.visitList(this);
}
public String toString() {
return StandardTextWriter.valueToString(this);
}
@Override
public Iterator<IValue> iterator() {
return new SubListIterator(base, offset, length);
}
@Override
public IList append(IValue val) {
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
w.append(base.get(i));
}
w.append(val);
return w.done();
}
@Override
public IListRelation<IList> asRelation() {
return materialize().asRelation();
}
@Override
public IList concat(IList lst) {
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
w.append(base.get(i));
}
for(IValue v : lst){
w.append(v);
}
return w.done();
}
@Override
public boolean contains(IValue val) {
int end = offset + length;
for(int i = offset; i < end; i++){
if(base.get(i).equals(val)){
return true;
}
}
return false;
}
@Override
public IList delete(IValue val) {
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
IValue elm = base.get(i);
if(!elm.equals(val)){
w.append(elm);
}
}
return w.done();
}
@Override
public IList delete(int n) {
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
if(i != n){
w.append(base.get(i));
}
}
return w.done();
}
@Override
public IValue get(int n) throws IndexOutOfBoundsException {
return base.get(offset + n);
}
@Override
public IList insert(IValue val) {
ListWriter w = new ListWriter();
int end = offset + length;
w.insert(val);;
for(int i = offset; i < end; i++){
w.append(base.get(i));
}
return w.done();
}
@Override
public IList intersect(IList arg0) {
return materialize().intersect(maybeMaterialize(arg0));
}
@Override
public boolean isEmpty() {
return length == 0;
}
@Override
public boolean isRelation() {
return getType().isListRelation();
}
@Override
public boolean isSubListOf(IList lst) {
int end = offset + length;
int j = 0;
nextchar:
for(int i = offset; i < end; i++){
IValue elm = base.get(i);
while(j < lst.length()){
if(elm.isEqual(lst.get(j))){
j++;
continue nextchar;
} else
j++;
}
return false;
}
return true;
}
@Override
public int length() {
return length;
}
@Override
public IList product(IList arg0) {
return materialize().product(maybeMaterialize(arg0));
}
@Override
public IList put(int n, IValue val) throws FactTypeUseException, IndexOutOfBoundsException {
ListWriter w = new ListWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
if(i == n){
w.append(val);
} else {
w.append(base.get(i));
}
}
return w.done();
}
@Override
public IList replace(int arg0, int arg1, int arg2, IList arg3)
throws FactTypeUseException, IndexOutOfBoundsException {
return materialize().replace(arg0, arg1, arg2, arg3);
}
@Override
public IList reverse() {
ListWriter w = new ListWriter();
for(int i = offset + length - 1; i >= offset; i--){
w.append(base.get(i));
}
return w.done();
}
@Override
public IList shuffle(Random arg0) {
return materialize().shuffle(arg0);
}
@Override
public IList sublist(int offset, int length) {
return new SubList(base, this.offset + offset, length);
}
@Override
public IList subtract(IList lst) {
IListWriter w = ValueFactory.getInstance().listWriter();
int end = offset + length;
for(int i = offset; i < end; i++){
IValue v = base.get(i);
if (lst.contains(v)) {
lst = lst.delete(v);
} else
w.append(v);
}
return w.done();
}
}
final class SubListIterator implements Iterator<IValue> {
private int cursor;
private final int end;
private final IList base;
public SubListIterator(IList base, int offset, int length) {
this.base = base;
this.cursor = offset;
this.end = offset + length;
}
public boolean hasNext() {
return this.cursor < end;
}
public IValue next() {
if(this.hasNext()) {
return base.get(cursor++);
}
throw new NoSuchElementException();
}
public void remove() {
throw new UnsupportedOperationException();
}
}