package org.aksw.sparqlify.core.datatypes; import java.util.ArrayList; import java.util.Arrays; 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 java.util.function.UnaryOperator; import org.aksw.commons.collections.MultiMaps; import org.aksw.commons.collections.multimaps.BiHashMultimap; import org.aksw.commons.collections.multimaps.IBiSetMultimap; import org.aksw.commons.util.reflect.Caster; import org.aksw.commons.util.reflect.ClassUtils; import org.aksw.sparqlify.algebra.sql.exprs2.SqlExpr; import org.aksw.sparqlify.core.TypeToken; import org.aksw.sparqlify.core.cast.ExprSubstitutorSql; import org.aksw.sparqlify.core.sql.expr.evaluation.SqlExprEvaluator; import org.aksw.sparqlify.expr.util.NodeValueUtilsSparqlify; import org.apache.jena.datatypes.RDFDatatype; import org.apache.jena.datatypes.TypeMapper; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.sparql.expr.NodeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; /* class SparqlFunctionMap { private FunctionRegistry registry; public SparqlFunctionMap() { registry.get().get("").create("").build(uri, args). } } */ /* Expr eval(ExprList args, Object context); } */ public class DatatypeSystemCustom implements TypeSystem { private static final Logger logger = LoggerFactory.getLogger(DatatypeSystemCustom.class); private Map<TypeToken, XClass> nameToType; private IBiSetMultimap<TypeToken, TypeToken> typeHierarchy; private transient Map<Class<?>, TypeToken> classToToken; private CoercionSystemImpl coercionSystem = new CoercionSystemImpl(); //private List<XMethod> userFunctions = new ArrayList<> /** * Sparql function URI to SQL Method */ private Multimap<String, XMethod> sqlFunctions = HashMultimap.create(); private Map<String, SparqlFunctionImpl> sparqlFunctions = new HashMap<String, SparqlFunctionImpl>(); //public getSqlFunctions public SparqlFunctionImpl createSparqlFunction(String name, SqlExprEvaluator evaluator) { SparqlFunctionImpl result = new SparqlFunctionImpl(name, evaluator); sparqlFunctions.put(name, result); return result; } public SparqlFunctionImpl getOrCreateSparqlFunction(String name) { SparqlFunctionImpl result = sparqlFunctions.get(name); if(result == null) { result = createSparqlFunction(name, null); //sparqlFunctions.put(name, result); } return result; } public SparqlFunction getSparqlFunction(String name) { //FunctionFactory fnFactory = null; //SparqlFunction result = new SparqlFunctionImpl(name, null); SparqlFunction result = sparqlFunctions.get(name); return result; } public void registerSqlFunction(String sparqlFunctionName, XMethod method) { SparqlFunctionImpl fn = getOrCreateSparqlFunction(sparqlFunctionName); //fn.getSqlFunctionMap().put(method.getName(), method); ExprSubstitutorSql substitutor = (ExprSubstitutorSql)fn.getSubstitutor(); //substitutor. //throw new RuntimeException("Not implemented"); sqlFunctions.put(method.getName(), method); } public static Map<XMethod, TypeDistance[]> findMethodCandidates(Collection<XMethod> candidates, CoercionSystemOld coercions, XClass ...typeSignature) { Map<XMethod, TypeDistance[]> bestMatches = new HashMap<XMethod, TypeDistance[]>(); for(XMethod m : candidates) { TypeDistance[] d = XClassUtils.getTypeDistance(typeSignature, m.getSignature().getParameterTypes().toArray(new XClass[0]), coercions); if(d == null || Arrays.asList(d).contains(null)) { continue; } // All matches that are worse than current candidate are removed // The candidate is only added, if it is not worse than any of the // other candidates boolean canBeAdded = true; for(Iterator<Entry<XMethod, TypeDistance[]>> it = bestMatches.entrySet().iterator(); it.hasNext();) { Entry<XMethod, TypeDistance[]> entry = it.next(); int rel = XClassUtils.getRelation(d, entry.getValue()); if(rel == -1) { it.remove(); } else if(rel > 0) { canBeAdded = false; } } if(canBeAdded) { bestMatches.put(m, d); } } return bestMatches; } public static Map<XMethod, Integer[]> findMethodCandidates(Collection<XMethod> candidates, XClass ...typeSignature) { Map<XMethod, Integer[]> bestMatches = new HashMap<XMethod, Integer[]>(); for(XMethod m : candidates) { Integer[] d = XClassUtils.getDistance(typeSignature, m.getSignature().getParameterTypes().toArray(new XClass[0])); if(d == null || Arrays.asList(d).contains(null)) { continue; } // All matches that are worse than current candidate are removed // The candidate is only added, if it is not worse than any of the // other candidates boolean canBeAdded = true; for(Iterator<Entry<XMethod, Integer[]>> it = bestMatches.entrySet().iterator(); it.hasNext();) { Entry<XMethod, Integer[]> entry = it.next(); int rel = ClassUtils.getRelation(d, entry.getValue()); if(rel == -1) { it.remove(); } else if(rel > 0) { canBeAdded = false; } } if(canBeAdded) { bestMatches.put(m, d); } } return bestMatches; } public SqlMethodCandidate lookupMethod(String sparqlFunctionName, List<TypeToken> argTypes) { SparqlFunction fn = sparqlFunctions.get(sparqlFunctionName); if(fn == null) { return null; } Collection<XMethod> candidates = fn.getSqlMethods(); SqlMethodCandidate result = lookupMethod(candidates, argTypes); return result; } public SqlMethodCandidate lookupMethod(Collection<XMethod> candidates, List<TypeToken> argTypes) { //Collection<XMethod> candidates = sqlFunctions.get(name); List<XClass> resolved = XClassUtils.resolve(this, argTypes); XClass[] tmp = resolved.toArray(new XClass[0]); Map<XMethod, TypeDistance[]> bestMatches = findMethodCandidates(candidates, coercionSystem, tmp); if(bestMatches.size() == 0) { //throw new RuntimeException("No method found: " + name + " " + argTypes); //throw new RuntimeException("No method found: " + " " + argTypes); logger.debug("No method found: " + " " + argTypes); return null; } else if(bestMatches.size() > 1) { throw new RuntimeException("Multiple matches: " + bestMatches); } Entry<XMethod, TypeDistance[]> entry = bestMatches.entrySet().iterator().next(); XMethod method = entry.getKey(); TypeDistance[] typeDistances = entry.getValue(); List<XMethod> argCoercions = new ArrayList<XMethod>(typeDistances.length); for(TypeDistance item : typeDistances) { argCoercions.add(item.getCoercion()); } SqlMethodCandidate result = new SqlMethodCandidate(method, argCoercions); return result; } public List<TypeToken> getDirectSuperClasses(TypeToken typeToken) { Collection<TypeToken> superClasses = typeHierarchy.get(typeToken); List<TypeToken> result = new ArrayList<TypeToken>(superClasses); return result; } private void initNameToType(Map<String, String> typeToClass, Map<String, String> typeToUri, Map<String, String> typeHierarchy) //Map<String, String> typeToClass, Map<String, String> typeToUri, Map<String, String> typeHierarchy) { { Set<String> all = new HashSet<String>(); all.addAll(typeToClass.keySet()); all.addAll(typeToUri.keySet()); all.addAll(typeHierarchy.keySet()); all.addAll(typeHierarchy.values()); Map<TypeToken, XClass> nameToClass = new HashMap<TypeToken, XClass>(); for(String typeName : all) { TypeToken typeToken = TypeToken.alloc(typeName); String className = typeToClass.get(typeName); Class<?> clazz = null; if(className != null) { try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { logger.error("Class '" + className + "' not found"); } } String uri = typeToUri.get(typeName); Node node = null; if(uri != null) { node = NodeFactory.createURI(uri); } XClass datatype = new XClassImpl(this, typeToken, node, clazz); TypeToken token = TypeToken.alloc(typeName); nameToClass.put(token, datatype); } IBiSetMultimap<TypeToken, TypeToken> subToSuperType = new BiHashMultimap<TypeToken, TypeToken>(); for(Entry<String, String> entry : typeHierarchy.entrySet()) { TypeToken subType = TypeToken.alloc(entry.getKey()); TypeToken superType = TypeToken.alloc(entry.getValue()); //XClass subType = nameToType.get(entry.getKey()); //XClass superType = nameToType.get(entry.getValue()); subToSuperType.put(subType, superType); } this.nameToType = nameToClass; this.typeHierarchy = subToSuperType; } private void initClassToTypeCache() { // TODO Only use the most generic type: // usigned short -> integer // byte -> integer // --> integer -> unsigned short // this.classToToken = new HashMap<Class<?>, TypeToken>(); for(Entry<TypeToken, XClass> entry : nameToType.entrySet()) { TypeToken typeName = entry.getKey(); XClass datatype = entry.getValue(); Class<?> clazz = datatype.getCorrespondingClass(); if(clazz == null) { continue; } TypeToken oldMapping = classToToken.get(clazz); if(oldMapping == null || isSuperClassOf(typeName, oldMapping)) { classToToken.put(clazz, typeName); } /* if(oldMapping != null) { logger.warn("Remapping " + clazz.getName() + " to " + datatype + " was: " + oldMapping); } */ } for(Entry<Class<?>, TypeToken> entry : classToToken.entrySet()) { logger.debug("[DatatypeSytem] Added type mapping: " + entry); } } /** * * * @param nameToType * @param typeHierarchy A mapping from sub to super type. */ //public DatatypeSystemCustom(Map<TypeToken, XClass> nameToType, IBiSetMultimap<TypeToken, TypeToken> typeHierarchy) { public DatatypeSystemCustom(Map<String, String> typeToClass, Map<String, String> typeToUri, Map<String, String> typeHierarchy) { initNameToType(typeToClass, typeToUri, typeHierarchy); initClassToTypeCache(); this.coercionSystem = new CoercionSystemImpl(); // this.nameToType = nameToType; // this.typeHierarchy = typeHierarchy; } @Override public TypeToken getTokenForClass(Class<?> clazz) { TypeToken result = classToToken.get(clazz); return result; } /* public boolean isMoreSpecific(TypeToken a, TypeToken b) { Set<TypeToken> commonParents = MultiMaps.getCommonParent(typeHierarchy.asMap(), a, b); } */ @Override public XClass getByName(TypeToken name) { return nameToType.get(name); } @Override public XClass getByName(String name) { return nameToType.get(TypeToken.alloc(name)); } public XClass getByClass(Class<?> clazz) { //org.springframework.util.ClassUtils.getAllInterfacesAsSet(instance) TypeToken typeName = classToToken.get(clazz); XClass result = nameToType.get(typeName); return result; } @Override public XClass requireByName(String name) { XClass result = nameToType.get(TypeToken.alloc(name)); if(result == null) { throw new RuntimeException("No registered datatype found with name '" + name + "'"); } return result; } @Override public NodeValue cast(NodeValue value, TypeToken to) { XClass targetType = getByName(to); Class<?> targetClazz = targetType.getCorrespondingClass(); if(targetClazz == null) { //throw new RuntimeException("No class corresponding to '" + to + "' found."); logger.warn("No class corresponding to '" + to + "' found."); return null; } NodeValue result = tryCast(value, targetType); return result; } // This is totally hacky! public NodeValue tryCast(NodeValue nodeValue, XClass targetType) { Class<?> targetClazz = targetType.getClass(); Object value = NodeValueUtilsSparqlify.getValue(nodeValue); Object castedValue = Caster.tryCast(value, targetClazz); TypeMapper typeMapper = TypeMapper.getInstance(); RDFDatatype datatype = typeMapper.getSafeTypeByName(targetType.getName()); Node tmp = NodeFactory.createLiteral("" + castedValue, datatype); NodeValue result = NodeValue.makeNode(tmp); return result; } @Override public UnaryOperator<SqlExpr> cast(TypeToken from, TypeToken to) { // TODO Auto-generated method stub return null; } @Override public TypeToken mostGenericDatatype(TypeToken from, TypeToken to) { throw new RuntimeException("This method does not make sense. We coulde always return object"); } @Override public Set<TypeToken> supremumDatatypes(TypeToken from, TypeToken to) { return MultiMaps.getCommonParent(typeHierarchy.asMap(), from, to); } @Override public Integer compare(TypeToken a, TypeToken b) { // TODO Auto-generated method stub return null; } public static DatatypeSystemCustom create(Map<String, String> typeToClass, Map<String, String> typeToUri, Map<String, String> typeHierarchy, Logger logger) { DatatypeSystemCustom result = new DatatypeSystemCustom(typeToClass, typeToUri, typeHierarchy); //subToSuperType); return result; } @Override public boolean isSuperClassOf(TypeToken a, TypeToken b) { Collection<TypeToken> superClasses = MultiMaps.transitiveGet(this.typeHierarchy.asMap(), a); boolean result = superClasses.contains(b); return result; } @Override public void registerCoercion(XMethod method) { coercionSystem.register(method); } }