package org.aksw.sparqlify.type_system;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* Return the set of SQL function declarations for a given sparql function id
*
* @author raven
*
* @param <T>
* @param <I>
*/
//interface SparqlSqlFunctionMap<T> {
// Collection<MethodEntry<T>> getSqlImpls(String id);
//}
/**
* T is the type
* I some implementation of a function
*
* @author raven
*
* @param <T>
* @param <I>
*/
public class FunctionModelImpl<T>
implements FunctionModel<T>
{
//private static final Logger logger = LoggerFactory.getLogger(FunctionModel.class);
//private Multimap<String, MethodEntry<T>> nameToMethodEntry = ArrayListMultimap.create();
private Map<String, MethodEntry<T>> idToMethodEntry = new HashMap<String, MethodEntry<T>>();
private Multimap<String, String> nameToIds = HashMultimap.create();
private DirectSuperTypeProvider<T> typeHierarchyProvider; // = new TypeHierarchyProviderImpl(typeHierarchy);
// This is the map for coercion functions
private Multimap<T, MethodEntry<T>> sourceToTargets = ArrayListMultimap.create();
// This maps symbols to inverse functions
private Map<String, String> inverses = new HashMap<String, String>();
public Map<String, String> getInverses() {
return inverses;
}
public static <K, V> K getFirstKey(Multimap<K, V> mmap, Object key) {
K result = null;
for(Entry<K, V> entry : mmap.entries()) {
V k = entry.getValue();
K v = entry.getKey();
if(key.equals(k)) {
result = v;
break;
}
}
return result;
}
@Override
public String getNameById(String id) {
// TODO Optimize this lookup!
String result = getFirstKey(nameToIds, id);
return result;
}
public Collection<MethodEntry<T>> getMethodEntries() {
return idToMethodEntry.values();
}
// Map<String, Map<String, String>> tags = new HashMap<String, Map<String, String>>();
//
// public Map<String, Map<String, String>> getTags() {
// return tags;
// }
public FunctionModelImpl(DirectSuperTypeProvider<T> typeHierarchyProvider) {
this.typeHierarchyProvider = typeHierarchyProvider;
}
@Override
public Collection<String> getIdsByName(String name)
{
Collection<String> result = nameToIds.get(name);
return result;
}
// public static void main(String[] args) throws IOException {
//
// Map<String, String> typeHierarchy = MapReader
// .readFromResource("/type-hierarchy.default.tsv");
//
// Map<String, String> typeMap = MapReader
// .readFromResource("/type-map.h2.tsv");
//
// // TODO HACK Do not add types programmatically
// typeMap.put("INTEGER", "int");
//
// typeHierarchy.putAll(typeMap);
//
//
//
// IBiSetMultimap<TypeToken, TypeToken> h = TypeSystemImpl.createHierarchyMap(typeHierarchy);
// TypeHierarchyProviderImpl thp = new TypeHierarchyProviderImpl(h);
//
// FunctionModel<TypeToken> model = new FunctionModelImpl<TypeToken>(thp);
//
// model.registerFunction("plus_float", "+", MethodSignature.create(false, TypeToken.Float, TypeToken.Float, TypeToken.Float));
// //model.registerCoercion("to_int", "to_int", MethodSignature.create(false, TypeToken.Double, TypeToken.Int));
// model.registerCoercion("to_float", "to_float", MethodSignature.create(false, TypeToken.Float, TypeToken.Int));
//
//
// TypeToken Geometry = TypeToken.alloc("geometry");
// TypeToken Geography = TypeToken.alloc("geography");
//
// model.registerFunction("st_intersects_geometry", "st_intersects", MethodSignature.create(false, TypeToken.Boolean, Geometry, Geometry));
// model.registerFunction("st_intersects_geography", "st_intersects", MethodSignature.create(false, TypeToken.Boolean, Geography, Geography));
//
// {
// Collection<CandidateMethod<TypeToken>> cands = model.lookupByName("st_intersects", Arrays.asList(Geometry, Geometry));
// System.out.println("Number of candidates: " + cands.size());
// System.out.println(cands);
// }
//
// {
// Collection<CandidateMethod<TypeToken>> cands = model.lookupByName("+", Arrays.asList(TypeToken.Int, TypeToken.Int));
// System.out.println("Number of candidates: " + cands.size());
// System.out.println(cands);
// }
//
//
//
// }
public void lookupCoercionRec(T argType, T targetType, int depth, Set<CandidateMethod<T>> result) {
Collection<MethodEntry<T>> targets = sourceToTargets.get(argType);
if(targets != null) {
boolean found = false;
for(MethodEntry<T> target : targets) {
T tt = target.getSignature().getReturnType();
Integer distance = TypeHierarchyUtils.getDistance(targetType, tt, typeHierarchyProvider);
if(distance != null) {
MethodDistance md = new MethodDistance(distance, depth);
CandidateMethod<T> candidate = new CandidateMethod<T>(target, null, md);
result.add(candidate);
found = true;
}
}
if(found) {
return;
}
}
Collection<T> superTypes = typeHierarchyProvider.getDirectSuperTypes(argType);
for(T superType : superTypes) {
lookupCoercionRec(superType, targetType, depth + 1, result);
}
}
public Set<CandidateMethod<T>> lookupCoercions(T argType, T targetType) {
Set<CandidateMethod<T>> result = new HashSet<CandidateMethod<T>>();
lookupCoercionRec(argType, targetType, 0, result);
return result;
}
public CandidateMethod<T> lookupCoercion(T argType, T targetType) {
Set<CandidateMethod<T>> tmp = lookupCoercions(argType, targetType);
CandidateMethod<T> result;
if(tmp.size() != 1) {
result = null;
} else {
result = tmp.iterator().next();
}
return result;
}
@Deprecated
public MethodEntry<T> registerFunction(String id, String name, MethodSignature<T> signature) {
//Collection<MethodEntry<T>> signatures = nameToMethodEntry.get(name);
// TODO More thorough checks on the type hierarchy...
// if(signatures.contains(name)) {
// throw new RuntimeException("Function " + name + " with signature " + signature + " already registered");
// }
MethodEntry<T> entry = new MethodEntry<T>(id, MethodDeclaration.create(name, signature));
idToMethodEntry.put(id, entry);
//signatures.add(entry);
nameToIds.put(name, id);
return entry;
}
@Deprecated
public void registerCoercion(String id, String name, MethodSignature<T> signature) {
List<T> paramTypes = signature.getParameterTypes();
if(paramTypes.size() != 1) {
throw new RuntimeException("Coercions must only have 1 paramater");
}
T sourceType = paramTypes.get(0);
Collection<MethodEntry<T>> targets = sourceToTargets.get(sourceType);
MethodEntry<T> entry = new MethodEntry<T>(id, MethodDeclaration.create(name, signature));
targets.add(entry);
idToMethodEntry.put(id, entry);
}
public MethodEntry<T> lookupById(String id) {
MethodEntry<T> result = idToMethodEntry.get(id);
return result;
}
//public Collection<CandidateMethod<T>> lookupByName(String functionName, T ... argType) {
public Collection<MethodEntry<T>> lookupByName(String name) {
List<MethodEntry<T>> result = new ArrayList<MethodEntry<T>>();
Collection<String> ids = nameToIds.get(name);
for(String id : ids) {
MethodEntry<T> method = idToMethodEntry.get(id);
if(method != null) {
result.add(method);
}
}
return result;
}
public Collection<CandidateMethod<T>> lookupByName(String functionName, List<T> argTypes) {
Collection<MethodEntry<T>> signatures = lookupByName(functionName);
Collection<CandidateMethod<T>> result = lookup(signatures, argTypes);
return result;
}
public Collection<CandidateMethod<T>> lookup(Collection<MethodEntry<T>> candidates, List<T> argTypes) {
// Check if there is an appropriate signature registered
List<CandidateMethod<T>> result = new ArrayList<CandidateMethod<T>>();
for(MethodEntry<T> candidate : candidates) {
MethodSignature<T> signature = candidate.getSignature();
List<T> paramTypes = signature.getParameterTypes();
if(paramTypes.size() > argTypes.size()) {
continue; // Not enough arguments provided
}
if(!signature.isVararg() && paramTypes.size() < argTypes.size()) {
continue; // Too many arguments provided
}
int n = argTypes.size(); //Math.min(argTypes.size(), signature.getParameterTypes().size());
int m = paramTypes.size();
boolean isCandidate = true;
List<ParamDistance> distances = new ArrayList<ParamDistance>(argTypes.size());
List<CandidateMethod<T>> coercions = new ArrayList<CandidateMethod<T>>();
for(int i = 0; i < n ; ++i) {
T paramType;
if(i < m) {
paramType = paramTypes.get(i);
} else {
paramType = signature.getVarArgType();
}
T argType = argTypes.get(i);
boolean usesCoercion = false;
Integer distance = TypeHierarchyUtils.getDistance(argType, paramType, typeHierarchyProvider);
// Try with coercion instead
CandidateMethod<T> coercion = null;
if(distance == null) {
coercion = lookupCoercion(argType, paramType);
if(coercion == null) {
isCandidate = false;
} else {
distance = coercion.getDistance().getArgTypeDistances().get(0).getDistance();
usesCoercion = true;
}
}
coercions.add(coercion);
ParamDistance dist = new ParamDistance(distance, usesCoercion);
distances.add(dist);
if(!isCandidate) {
break;
}
}
if(isCandidate) {
MethodDistance distance = new MethodDistance(new ParamDistance(0, false), distances);
CandidateMethod<T> tmp = new CandidateMethod<T>(candidate, coercions, distance);
tryInsertCandidate(result, tmp);
//result.add(tmp);
}
}
return result;
}
/**
* Inserts a method candidate if it is not subsumed by another one (based on the distance)
*
* @param result
* @param candidate
*/
public static <T> void tryInsertCandidate(List<CandidateMethod<T>> result, CandidateMethod<T> candidate) {
MethodDistance a = candidate.getDistance();
Iterator<CandidateMethod<T>> it = result.iterator();
boolean isSubsumed = false;
while(it.hasNext()) {
CandidateMethod<T> item = it.next();
MethodDistance b = item.getDistance();
Integer d = a.compare(b);
if(d != null) {
if(d < 0) {
it.remove();
} else {
isSubsumed = true;
}
}
}
if(!isSubsumed) {
result.add(candidate);
}
}
@Override
public MethodEntry<T> registerFunction(MethodDeclaration<T> declaration) {
MethodEntry<T> result = this.registerFunction(declaration.toString(), declaration.getName(), declaration.getSignature());
return result;
}
@Override
public void registerCoercion(MethodDeclaration<T> declaration) {
this.registerCoercion(declaration.toString(), declaration.getName(), declaration.getSignature());
}
/*
public ExprFunction pickCandidate(Collection<RegisteredFunction> candidates, List<Expr> args) {
switch(candidates.size()) {
case 0: {
logger.warn("Returning false; although it should be type-error");
//return new SqlExprValue(false);
return null;
}
case 1: {
RegisteredFunction regFn = candidates.iterator().next();
ExprFunction result = new E_SqlFunctionRegistered(regFn, args);
return result;
//return new SqlStringTransformerRegisteredFunction(resultFn);
//return new S_Function(this.sqlFunctionName, args, pair.getKey().getReturnType(), pair.getValue());
}
default: {
logger.warn("Multiple overloads matched: " + candidates);
logger.warn("Returning false; although it should be type-error");
//return new SqlExprValue(false);
return null;
}
}
}
*/
}