/*******************************************************************************
* Copyright (c) 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.util;
import static io.usethesource.capsule.AbstractSpecialisedImmutableMap.mapOf;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import io.usethesource.capsule.ImmutableMap;
/**
* Stores mapping (Type -> Integer) to keep track of a collection's element
* types. The least upper bound type of is calculated on basis of the map keys.
*/
public abstract class AbstractTypeBag implements Cloneable {
public abstract AbstractTypeBag increase(Type t);
public abstract AbstractTypeBag decrease(Type t);
@Deprecated
public abstract AbstractTypeBag setLabel(String label);
@Deprecated
public abstract String getLabel();
public abstract Type lub();
public abstract AbstractTypeBag clone();
public static AbstractTypeBag of(Type... ts) {
return of(null, ts);
}
public static AbstractTypeBag of(String label, Type... ts) {
return new TypeBag(label, ts);
}
public abstract int size();
/**
* Implementation of <@link AbstractTypeBag/> that cached the current least
* upper bound.
*/
private static class TypeBag extends AbstractTypeBag {
private final String label;
private final ImmutableMap<Type, Integer> countMap;
private Type cachedLub;
private TypeBag(String label, ImmutableMap<Type, Integer> countMap) {
this(label, countMap, null);
}
private TypeBag(String label, ImmutableMap<Type, Integer> countMap, Type cachedLub) {
this.label = label;
this.countMap = countMap;
this.cachedLub = cachedLub;
}
private TypeBag(Type... ts) {
this(null, ts);
}
private TypeBag(String label, Type... ts) {
this.label = label;
this.countMap = mapOf();
for (Type t : ts) {
this.increase(t);
}
}
@Override
public AbstractTypeBag increase(Type t) {
final Integer oldCount = countMap.get(t);
final ImmutableMap<Type, Integer> newCountMap;
if (oldCount == null) {
newCountMap = countMap.__put(t, 1);
if (cachedLub == null) {
return new TypeBag(label, newCountMap);
} else {
// update cached type
final Type newCachedLub = cachedLub.lub(t);
return new TypeBag(label, newCountMap, newCachedLub);
}
} else {
newCountMap = countMap.__put(t, oldCount + 1);
return new TypeBag(label, newCountMap);
}
}
@Override
public AbstractTypeBag decrease(Type t) {
final Integer oldCount = countMap.get(t);
if (oldCount == null) {
throw new IllegalStateException(String.format("Type '%s' was not present.", t));
} else if (oldCount > 1) {
// update and decrease count; lub stays the same
final ImmutableMap<Type, Integer> newCountMap = countMap.__put(t, oldCount - 1);
return new TypeBag(label, newCountMap, cachedLub);
} else {
// count was zero, thus remove entry and invalidate cached type
final ImmutableMap<Type, Integer> newCountMap = countMap.__remove(t);
return new TypeBag(label, newCountMap);
}
}
@Deprecated
@Override
public AbstractTypeBag setLabel(String label) {
return new TypeBag(label, countMap, cachedLub);
}
@Deprecated
@Override
public String getLabel() {
return label;
}
@Override
public Type lub() {
if (cachedLub == null) {
Type inferredLubType = TypeFactory.getInstance().voidType();
for (Type t : countMap.keySet()) {
inferredLubType = inferredLubType.lub(t);
}
cachedLub = inferredLubType;
}
return cachedLub;
}
@Override
public AbstractTypeBag clone() {
return new TypeBag(label, countMap);
}
@Override
public String toString() {
return countMap.toString();
}
@Override
public int size() {
return countMap.size();
}
}
}