/*******************************************************************************
* Copyright (c) 2013 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:
*
* * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Paul Klint - Paul.Klint@cwi.nl - CWI
*
* Based on code by:
*
* * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.rascalmpl.value.impl.reference;
import java.util.LinkedList;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.exceptions.UnexpectedElementTypeException;
import org.rascalmpl.value.impl.AbstractWriter;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
/**
* This class does not guarantee thread-safety. Users must lock the writer object for thread safety.
* It is thread-friendly however.
*/
/*package*/ class ListWriter extends AbstractWriter implements IListWriter {
protected Type eltType;
protected final java.util.List<IValue> listContent;
protected IList constructedList;
private final boolean inferred;
/*package*/ ListWriter(Type eltType){
super();
this.eltType = eltType;
this.inferred = false;
listContent = new LinkedList<>();
constructedList = null;
}
/*package*/ ListWriter(){
super();
this.eltType = TypeFactory.getInstance().voidType();
inferred = true;
listContent = new LinkedList<>();
constructedList = null;
}
private void checkMutation(){
if(constructedList != null) throw new UnsupportedOperationException("Mutation of a finalized list is not supported.");
}
private static void checkInsert(IValue elem, Type eltType) throws FactTypeUseException{
Type type = elem.getType();
if(!type.isSubtypeOf(eltType)){
throw new UnexpectedElementTypeException(eltType, type);
}
}
private void put(int index, IValue elem){
if (inferred) {
eltType = eltType.lub(elem.getType());
}
else {
checkInsert(elem, eltType);
}
listContent.add(index, elem);
}
public void insert(IValue elem) throws FactTypeUseException {
checkMutation();
put(0, elem);
}
@Override
public void insert(IValue[] elems, int start, int length) throws FactTypeUseException{
checkMutation();
checkBounds(elems, start, length);
for(int i = start + length - 1; i >= start; i--){
updateType(elems[i]);
put(0, elems[i]);
}
}
@Override
public IValue replaceAt(int index, IValue elem) throws FactTypeUseException, IndexOutOfBoundsException {
checkMutation();
updateType(elem);
checkInsert(elem, eltType);
return listContent.set(index, elem);
}
@Override
public void insert(IValue... elems) throws FactTypeUseException{
insert(elems, 0, elems.length);
}
@Override
public void insertAt(int index, IValue[] elems, int start, int length) throws FactTypeUseException{
checkMutation();
checkBounds(elems, start, length);
for(int i = start + length - 1; i >= start; i--) {
if (inferred) {
eltType = eltType.lub(elems[i].getType());
}
put(index, elems[i]);
}
}
@Override
public void insertAt(int index, IValue... elems) throws FactTypeUseException{
insertAt(index, elems, 0, 0);
}
public void append(IValue elem) throws FactTypeUseException{
checkMutation();
updateType(elem);
put(listContent.size(), elem);
}
@Override
public void append(IValue... elems) throws FactTypeUseException{
checkMutation();
for(IValue elem : elems){
updateType(elem);
put(listContent.size(), elem);
}
}
@Override
public void appendAll(Iterable<? extends IValue> collection) throws FactTypeUseException{
checkMutation();
for(IValue v : collection){
updateType(v);
put(listContent.size(), v);
}
}
private void updateType(IValue v) {
if (inferred) {
eltType = eltType.lub(v.getType());
}
}
@Override
public IList done() {
// Temporary fix of the static vs dynamic type issue
eltType = TypeFactory.getInstance().voidType();
for(IValue el : listContent)
eltType = eltType.lub(el.getType());
// ---
if (constructedList == null) {
constructedList = new List(eltType, listContent);
}
return constructedList;
}
private void checkBounds(IValue[] elems, int start, int length) {
if(start < 0) throw new ArrayIndexOutOfBoundsException("start < 0");
if((start + length) > elems.length) throw new ArrayIndexOutOfBoundsException("(start + length) > elems.length");
}
@Override
public IValue get(int i) throws IndexOutOfBoundsException {
return listContent.get(i);
}
@Override
public int length() {
return listContent.size();
}
}