/*******************************************************************************
* 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
*******************************************************************************/
package org.rascalmpl.value.impl.fast;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.impl.AbstractValue;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.visitors.IValueVisitor;
/**
* Implementation of ITuple.
*
* @author Arnold Lankamp
*/
/*package*/ class Tuple extends AbstractValue implements ITuple{
protected final static TypeFactory typeFactory = TypeFactory.getInstance();
private Type cachedTupleType;
protected final IValue[] elements;
public static ITuple newTuple(Type tupleType, IValue[] elements) {
return new Tuple(tupleType, elements);
}
private Tuple(Type tupleType, IValue[] elements) {
super();
this.cachedTupleType = tupleType;
this.elements = elements;
}
/*package*/ static ITuple newTuple(IValue... elements) {
return new Tuple(elements);
}
private Tuple(IValue... elements) {
super();
this.elements = elements;
}
public Type getType() {
if (cachedTupleType == null) {
cachedTupleType = TypeFactory.getInstance().tupleType(elements);
}
return cachedTupleType;
}
public int arity() {
return elements.length;
}
public IValue get(int i) {
return elements[i];
}
public IValue get(String label) {
return elements[getType().getFieldIndex(label)];
}
public Iterator<IValue> iterator() {
return new TupleIterator(this);
}
public <T, E extends Throwable> T accept(IValueVisitor<T, E> v) throws E {
return v.visitTuple(this);
}
public ITuple set(int index, IValue arg) {
int nrOfElements = elements.length;
IValue[] newElements = new IValue[nrOfElements];
Type[] elementTypes = new Type[nrOfElements];
for (int i = nrOfElements - 1; i >= 0; i--) {
IValue element = elements[i];
newElements[i] = element;
elementTypes[i] = element.getType();
}
newElements[index] = arg;
elementTypes[index] = arg.getType();
return new Tuple(typeFactory.tupleType(elementTypes), newElements);
}
public ITuple set(String label, IValue arg) {
int nrOfElements = elements.length;
IValue[] newElements = new IValue[nrOfElements];
Type[] elementTypes = new Type[nrOfElements];
for (int i = nrOfElements - 1; i >= 0; i--) {
IValue element = elements[i];
newElements[i] = element;
elementTypes[i] = element.getType();
}
newElements[getType().getFieldIndex(label)] = arg;
elementTypes[getType().getFieldIndex(label)] = arg.getType();
return new Tuple(typeFactory.tupleType(elementTypes), newElements);
}
public IValue select(int... indexes) {
if (indexes.length == 1)
return get(indexes[0]);
int nrOfElements = indexes.length;
IValue[] elements = new IValue[nrOfElements];
Type[] elementTypes = new Type[nrOfElements];
for (int i = nrOfElements - 1; i >= 0; i--) {
IValue element = get(indexes[i]);
elements[i] = element;
elementTypes[i] = element.getType();
}
return new Tuple(typeFactory.tupleType(elementTypes), elements);
}
public IValue selectByFieldNames(String... fields) {
if (fields.length == 1)
return get(fields[0]);
int nrOfElements = fields.length;
IValue[] elements = new IValue[nrOfElements];
Type[] elementTypes = new Type[nrOfElements];
for (int i = nrOfElements - 1; i >= 0; i--) {
IValue element = get(fields[i]);
elements[i] = element;
elementTypes[i] = element.getType();
}
return new Tuple(typeFactory.tupleType(elementTypes), elements);
}
public int hashCode() {
int hash = 1331;
for (int i = elements.length - 1; i >= 0; i--) {
hash -= (hash << 19) + (hash >>> 8);
hash ^= elements[i].hashCode();
}
return hash - (hash << 7);
}
public boolean equals(Object o) {
if (o == this)
return true;
if (o == null)
return false;
if (o.getClass() == getClass()) {
Tuple otherTuple = (Tuple) o;
IValue[] otherElements = otherTuple.elements;
int nrOfElements = elements.length;
if (otherElements.length == nrOfElements) {
for (int i = nrOfElements - 1; i >= 0; i--) {
if (!otherElements[i].equals(elements[i]))
return false;
}
return true;
}
}
return false;
}
public boolean isEqual(IValue value) {
if (value == this)
return true;
if (value == null)
return false;
if (value instanceof Tuple) {
Tuple otherTuple = (Tuple) value;
if (!getType().comparable(otherTuple.getType()))
return false;
IValue[] otherElements = otherTuple.elements;
int nrOfElements = elements.length;
if (otherElements.length == nrOfElements) {
for (int i = nrOfElements - 1; i >= 0; i--) {
if (!otherElements[i].isEqual(elements[i]))
return false;
}
return true;
}
}
return false;
}
private static class TupleIterator implements Iterator<IValue> {
private final IValue[] elements;
private int index = 0;
public TupleIterator(Tuple tuple) {
super();
elements = tuple.elements;
}
public boolean hasNext() {
return index < elements.length;
}
public IValue next() {
if (!hasNext())
throw new NoSuchElementException("No more elements in this iteration.");
return elements[index++];
}
public void remove() {
throw new UnsupportedOperationException("This iterator doesn't support removal.");
}
}
}