/*******************************************************************************
* Copyright (c) 2007 Cambridge Semantics Incorporated.
* 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:
* Cambridge Semantics Incorporated
*******************************************************************************/
package org.openanzo.glitter.query;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.openanzo.glitter.expression.builtin.PolymorphicEq;
import org.openanzo.glitter.expression.builtin.PolymorphicNe;
import org.openanzo.glitter.query.rewriter.VariableRewriter;
import org.openanzo.glitter.syntax.abstrakt.Expression;
import org.openanzo.glitter.syntax.abstrakt.FunctionCall;
import org.openanzo.glitter.syntax.abstrakt.SimpleExpression;
import org.openanzo.glitter.syntax.abstrakt.TreeNode;
import org.openanzo.glitter.util.Glitter;
import org.openanzo.rdf.Bindable;
import org.openanzo.rdf.Resource;
import org.openanzo.rdf.TriplePatternComponent;
import org.openanzo.rdf.Value;
import org.openanzo.rdf.utils.Pair;
/**
* Set of utilities for solution generation
*
*/
public class SolutionUtils {
protected static SolutionSet singletonSolution(Bindable k, Value v) {
SolutionSet ss = new SolutionList();
PatternSolution ps = new PatternSolutionImpl(k, v);
ss.add(ps);
return ss;
}
private static final SolutionSet unconstrainedSolutions;
static {
unconstrainedSolutions = new SolutionList();
unconstrainedSolutions.add(new PatternSolutionImpl());
}
/**
* Get the unconstrained solution set. This is different from no bindings.
*
* @return the unconstrained solution set. This is different from no bindings.
*/
public static SolutionList unconstrainedSolutions() {
return new SolutionList(unconstrainedSolutions);
}
/**
* Get no bindings. This is different from the unconstrained solution set.
*
* @return a static solution list with no bindings
*/
public static SolutionSet noSolutions() {
return new SolutionList();
}
private static Map<Bindable, Value> extractFixedBindings(SolutionSet answers) {
HashMap<Bindable, Pair<HashSet<Value>, Integer>> answersByVariable = new HashMap<Bindable, Pair<HashSet<Value>, Integer>>();
for (PatternSolution solution : answers) {
for (Bindable b : solution.getBoundDomain(false)) {
Pair<HashSet<Value>, Integer> p = answersByVariable.get(b);
if (p == null) {
p = new Pair<HashSet<Value>, Integer>(new HashSet<Value>(), 0);
answersByVariable.put(b, p);
}
p.first.add(solution.getBinding(b));
p.second = Integer.valueOf(p.second.intValue() + 1);
}
}
int goal = answers.size();
HashMap<Bindable, Value> values = new HashMap<Bindable, Value>();
for (Entry<Bindable, Pair<HashSet<Value>, Integer>> entry : answersByVariable.entrySet()) {
Pair<HashSet<Value>, Integer> p = entry.getValue();
if (p.first.size() == 1 && p.second == goal) {
Value term = p.first.iterator().next();
// We don't extract bindings for bindable objects, since that would
// confuse further calls to the backend which would rebind this term
if (!(term instanceof Bindable))
values.put(entry.getKey(), p.first.iterator().next());
}
}
return values;
}
/**
* Substitute the fixed bindings within the tree node
*
* @param n
* node to augment
* @param answerConstraints
* fixed bindings used for augmenting the node
* @return the augmented node
*/
public static TreeNode substituteFixedBindings(TreeNode n, SolutionSet answerConstraints) {
Map<Bindable, Value> fixedBindings = extractFixedBindings(answerConstraints);
if (fixedBindings.size() > 0) {
TreeNode node = Glitter.rewriteTree(n, new VariableRewriter(fixedBindings));
if (node != n) {
n.getParent().replaceChild(n, node);
n = node; // for the rest of this function call
}
}
return n;
}
/**
* For any PolymorphicEq filter that replaces a bindable, then place it in the constraints
*
* @param n
* node to augment
* @param answerConstraints
* constraints
*/
public static void substituteFilterBindings(TreeNode n, SolutionSet answerConstraints) {
Set<Expression> filters = n.getInScopeFilterSet();
Set<Expression> removed = new HashSet<Expression>();
for (Expression filter : filters) {
if (filter instanceof FunctionCall) {
FunctionCall fc = ((FunctionCall) filter);
if (fc.getFunction() instanceof PolymorphicEq) {
if (fc.getArguments().size() == 2) {
Expression e1 = fc.getArguments().get(0);
Expression e2 = fc.getArguments().get(1);
if (e1 instanceof SimpleExpression && e2 instanceof SimpleExpression) {
TriplePatternComponent v1 = ((SimpleExpression) e1).getTerm();
TriplePatternComponent v2 = ((SimpleExpression) e2).getTerm();
if (v1 instanceof Bindable && v2 instanceof Resource) {
if (singleUseVariable((Bindable) v1, filters)) {
answerConstraints.add(new PatternSolutionImpl((Bindable) v1, (Resource) v2));
removed.add(filter);
}
} else if (v2 instanceof Bindable && v1 instanceof Resource) {
if (singleUseVariable((Bindable) v2, filters)) {
answerConstraints.add(new PatternSolutionImpl((Bindable) v2, (Resource) v1));
removed.add(filter);
}
}
}
}
}
}
}
for (Expression filter : removed) {
n.getFilters().remove(filter);
}
if (removed.size() > 0) {
n.invalidateCache();
}
}
/**
* Return true if given bindable is only referenced in one filter
*
* @param b
* bindable to check
* @param filters
* filters in which to check bindable
* @return true if given bindable is only referenced in one filter
*/
public static boolean singleUseVariable(Bindable b, Set<Expression> filters) {
int seen = 0;
for (Expression filter : filters) {
if (filter instanceof FunctionCall) {
if (filter.getReferencedVariables().contains(b)) {
seen++;
if (seen > 1)
return false;
}
}
}
return true;
}
/**
* Return true if given bindable is only referenced in equality filters
*
* @param b
* bindable to check
* @param filters
* filters in which to check bindable
* @return true if given bindable is only referenced in equality filters
*/
public static boolean onlyEqualityBindings(Bindable b, Set<Expression> filters) {
for (Expression filter : filters) {
if (filter instanceof FunctionCall) {
FunctionCall fc = ((FunctionCall) filter);
for (Expression e : fc.getArguments()) {
if (e instanceof SimpleExpression) {
TriplePatternComponent v1 = ((SimpleExpression) e).getTerm();
if (v1.equals(b) && !((fc.getFunction() instanceof PolymorphicEq) || (fc.getFunction() instanceof PolymorphicNe))) {
return false;
}
}
}
} else if (filter.getReferencedVariables().contains(b)) {
return false;
}
}
return true;
}
}