package org.smoothbuild.lang.function.def;
import static com.google.common.base.Preconditions.checkArgument;
import static org.smoothbuild.lang.function.base.Parameters.filterOptionalParameters;
import static org.smoothbuild.lang.function.base.Parameters.filterRequiredParameters;
import static org.smoothbuild.lang.function.base.Parameters.parametersToMap;
import static org.smoothbuild.lang.type.Conversions.canConvert;
import static org.smoothbuild.lang.type.Types.allTypes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.smoothbuild.lang.function.base.Parameter;
import org.smoothbuild.lang.type.Type;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
public class ParametersPool {
private final ImmutableMap<String, Parameter> parameters;
private final ImmutableMap<Type, TypedParametersPool> typePools;
private final Map<Type, Set<Parameter>> optionalParametersMap;
private final Map<Type, Set<Parameter>> requiredParametersMap;
public ParametersPool(List<Parameter> parameters) {
this.parameters = parametersToMap(parameters);
this.optionalParametersMap = createParametersMap(filterOptionalParameters(parameters));
this.requiredParametersMap = createParametersMap(filterRequiredParameters(parameters));
this.typePools = createTypePools(optionalParametersMap, requiredParametersMap);
}
public Parameter take(String name) {
Parameter parameter = parameters.get(name);
checkArgument(parameter != null);
return take(parameter);
}
public Parameter take(Parameter parameter) {
boolean hasBeenRemoved = remove(parameter);
checkArgument(hasBeenRemoved);
return parameter;
}
private boolean remove(Parameter parameter) {
if (parameter.isRequired()) {
return requiredParametersMap.get(parameter.type()).remove(parameter);
} else {
return optionalParametersMap.get(parameter.type()).remove(parameter);
}
}
public TypedParametersPool assignableFrom(Type type) {
return typePools.get(type);
}
public Set<Parameter> allRequired() {
Set<Parameter> result = new HashSet<>();
for (TypedParametersPool typedParamPool : typePools.values()) {
Iterables.addAll(result, typedParamPool.requiredParameters());
}
return result;
}
public Set<Parameter> allOptional() {
Set<Parameter> result = new HashSet<>();
for (TypedParametersPool typedParamPool : typePools.values()) {
Iterables.addAll(result, typedParamPool.optionalParameters());
}
return result;
}
private static ImmutableMap<Type, TypedParametersPool> createTypePools(
Map<Type, Set<Parameter>> optionalParametersMap,
Map<Type, Set<Parameter>> requiredParametersMap) {
Builder<Type, TypedParametersPool> builder = ImmutableMap.builder();
for (Type type : allTypes()) {
Set<Parameter> optional = parametersAssignableFromType(type, optionalParametersMap);
Set<Parameter> required = parametersAssignableFromType(type, requiredParametersMap);
builder.put(type, new TypedParametersPool(optional, required));
}
return builder.build();
}
private static Set<Parameter> parametersAssignableFromType(Type type,
Map<Type, Set<Parameter>> paramsMap) {
Set<Parameter> parameters = paramsMap.get(type);
for (Type currentType : allTypes()) {
if (canConvert(type, currentType)) {
parameters = Sets.union(parameters, paramsMap.get(currentType));
}
}
return parameters;
}
private static Map<Type, Set<Parameter>> createParametersMap(
ImmutableList<Parameter> parameters) {
Map<Type, Set<Parameter>> map = new HashMap<>();
for (Type type : allTypes()) {
HashSet<Parameter> set = new HashSet<>();
for (Parameter parameter : parameters) {
if (parameter.type() == type) {
set.add(parameter);
}
}
map.put(type, set);
}
return map;
}
}