/*******************************************************************************
* Copyright (c) 2013-2014 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
*******************************************************************************/
package org.rascalmpl.value.impl.persistent;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map.Entry;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.IMapWriter;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.exceptions.UnexpectedElementTypeException;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.util.AbstractTypeBag;
import org.rascalmpl.value.util.EqualityUtils;
import io.usethesource.capsule.DefaultTrieMap;
import io.usethesource.capsule.TransientMap;
final class MapWriter implements IMapWriter {
@SuppressWarnings("unchecked")
private static final Comparator<Object> equivalenceComparator = EqualityUtils
.getEquivalenceComparator();
protected AbstractTypeBag keyTypeBag;
protected AbstractTypeBag valTypeBag;
protected final TransientMap<IValue, IValue> mapContent;
protected final boolean checkUpperBound;
protected final Type upperBoundKeyType;
protected final Type upperBoundValType;
protected IMap constructedMap;
MapWriter() {
super();
this.checkUpperBound = false;
this.upperBoundKeyType = null;
this.upperBoundValType = null;
keyTypeBag = AbstractTypeBag.of();
valTypeBag = AbstractTypeBag.of();
mapContent = DefaultTrieMap.transientOf();
constructedMap = null;
}
MapWriter(Type prototypeType) {
super();
this.checkUpperBound = false;
this.upperBoundKeyType = prototypeType.getKeyType();
this.upperBoundValType = prototypeType.getValueType();
if (prototypeType.hasFieldNames()) {
keyTypeBag = AbstractTypeBag.of(prototypeType.getKeyLabel());
valTypeBag = AbstractTypeBag.of(prototypeType.getValueLabel());
} else {
keyTypeBag = AbstractTypeBag.of();
valTypeBag = AbstractTypeBag.of();
}
mapContent = DefaultTrieMap.transientOf();
constructedMap = null;
}
@Override
public void put(IValue key, IValue value) {
checkMutation();
final Type keyType = key.getType();
final Type valType = value.getType();
if (checkUpperBound) {
if (!keyType.isSubtypeOf(upperBoundKeyType)) {
throw new UnexpectedElementTypeException(upperBoundKeyType, keyType);
}
if (!valType.isSubtypeOf(upperBoundValType)) {
throw new UnexpectedElementTypeException(upperBoundValType, valType);
}
}
final IValue replaced = mapContent.__putEquivalent(key, value, equivalenceComparator);
keyTypeBag = keyTypeBag.increase(keyType);
valTypeBag = valTypeBag.increase(valType);
if (replaced != null) {
final Type replacedType = replaced.getType();
valTypeBag = valTypeBag.decrease(replacedType);
}
}
@Override
public void putAll(IMap map) {
putAll(map.entryIterator());
}
@Override
public void putAll(java.util.Map<IValue, IValue> map) {
putAll(map.entrySet().iterator());
}
private void putAll(Iterator<Entry<IValue, IValue>> entryIterator) {
checkMutation();
while (entryIterator.hasNext()) {
Entry<IValue, IValue> entry = entryIterator.next();
IValue key = entry.getKey();
IValue value = entry.getValue();
this.put(key, value);
}
}
@Override
public void insert(IValue... values) {
insertAll(Arrays.asList(values));
}
@Override
public void insertAll(Iterable<? extends IValue> collection) {
checkMutation();
Iterator<? extends IValue> collectionIterator = collection.iterator();
while (collectionIterator.hasNext()) {
final Object item = collectionIterator.next();
if (!(item instanceof ITuple)) {
throw new IllegalArgumentException("Argument must be of ITuple type.");
}
final ITuple tuple = (ITuple) item;
if (tuple.arity() != 2) {
throw new IllegalArgumentException("Tuple must have an arity of 2.");
}
put(tuple.get(0), tuple.get(1));
}
}
protected void checkMutation() {
if (constructedMap != null) {
throw new UnsupportedOperationException("Mutation of a finalized map is not supported.");
}
}
@Override
public IMap done() {
if (constructedMap == null) {
constructedMap = new PDBPersistentHashMap(keyTypeBag, valTypeBag, mapContent.freeze());
}
return constructedMap;
}
}