001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: * Copyright James Leigh (c) 2006.
004: *
005: * Licensed under the Aduna BSD-style license.
006: */
007: package org.openrdf.query.algebra.evaluation.impl;
008:
009: import java.util.Set;
010:
011: import org.openrdf.model.Value;
012: import org.openrdf.query.BindingSet;
013: import org.openrdf.query.Dataset;
014: import org.openrdf.query.QueryEvaluationException;
015: import org.openrdf.query.algebra.Bound;
016: import org.openrdf.query.algebra.EmptySet;
017: import org.openrdf.query.algebra.Extension;
018: import org.openrdf.query.algebra.ExtensionElem;
019: import org.openrdf.query.algebra.Filter;
020: import org.openrdf.query.algebra.SameTerm;
021: import org.openrdf.query.algebra.TupleExpr;
022: import org.openrdf.query.algebra.ValueConstant;
023: import org.openrdf.query.algebra.ValueExpr;
024: import org.openrdf.query.algebra.Var;
025: import org.openrdf.query.algebra.evaluation.QueryOptimizer;
026: import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
027:
028: /**
029: * A query optimizer that embeds {@link Filter}s with {@link SameTerm}
030: * operators in statement patterns as much as possible. Operators like
031: * sameTerm(X, Y) are processed by renaming X to Y (or vice versa). Operators
032: * like sameTerm(X, <someURI>) are processed by assigning the URI to all
033: * occurring variables with name X.
034: *
035: * @author Arjohn Kampman
036: */
037: public class SameTermFilterOptimizer implements QueryOptimizer {
038:
039: /**
040: * Applies generally applicable optimizations to the supplied query: variable
041: * assignments are inlined.
042: *
043: * @param tupleExpr
044: * @return optimized TupleExpr
045: * @throws QueryEvaluationException
046: */
047: public void optimize(TupleExpr tupleExpr, Dataset dataset,
048: BindingSet bindings) {
049: tupleExpr.visit(new SameTermFilterVisitor());
050: }
051:
052: protected class SameTermFilterVisitor extends
053: QueryModelVisitorBase<RuntimeException> {
054:
055: @Override
056: public void meet(SameTerm sameTerm) {
057: super .meet(sameTerm);
058:
059: if (sameTerm.getParentNode() instanceof Filter) {
060: // SameTerm applies to the filter's argument
061: Filter filter = (Filter) sameTerm.getParentNode();
062:
063: ValueExpr leftArg = sameTerm.getLeftArg();
064: ValueExpr rightArg = sameTerm.getRightArg();
065:
066: // Verify that vars are bound by filterArg
067: Set<String> bindingNames = filter.getArg()
068: .getBindingNames();
069:
070: if (leftArg instanceof Var
071: && !bindingNames.contains(((Var) leftArg)
072: .getName())
073: || rightArg instanceof Var
074: && !bindingNames.contains(((Var) rightArg)
075: .getName())) {
076: // One or both var(s) are unbound, this expression will never
077: // return any results
078: filter.replaceWith(new EmptySet());
079: return;
080: }
081:
082: if (leftArg instanceof Var && rightArg instanceof Var) {
083: // Rename rightArg to leftArg
084: renameVar((Var) rightArg, (Var) leftArg, filter);
085: } else if (leftArg instanceof Var
086: && rightArg instanceof ValueConstant) {
087: bindVar((Var) leftArg, (ValueConstant) rightArg,
088: filter);
089: } else if (rightArg instanceof Var
090: && leftArg instanceof ValueConstant) {
091: bindVar((Var) rightArg, (ValueConstant) leftArg,
092: filter);
093: }
094: }
095: }
096:
097: private void renameVar(Var oldVar, Var newVar, Filter filter) {
098: filter.getArg().visit(
099: new VarRenamer(oldVar.getName(), newVar.getName()));
100:
101: // Replace SameTerm-filter with an Extension, the old variable name
102: // might still be relevant to nodes higher in the tree
103: Extension extension = new Extension(filter.getArg());
104: extension.addElement(new ExtensionElem(new Var(newVar
105: .getName()), oldVar.getName()));
106: filter.replaceWith(extension);
107: }
108:
109: private void bindVar(Var var, ValueConstant valueConstant,
110: Filter filter) {
111: filter.getArg().visit(
112: new VarBinder(var.getName(), valueConstant
113: .getValue()));
114:
115: // No need to keep the comparison, but we do need to make sure
116: // that the variable is not null in case it comes from an
117: // optional statement pattern. Replace the SameTerm constraint with a
118: // Bound constraint.
119: filter.setCondition(new Bound(var));
120: }
121: }
122:
123: protected class VarRenamer extends
124: QueryModelVisitorBase<RuntimeException> {
125:
126: private String oldName;
127:
128: private String newName;
129:
130: public VarRenamer(String oldName, String newName) {
131: this .oldName = oldName;
132: this .newName = newName;
133: }
134:
135: @Override
136: public void meet(Var var) {
137: if (var.getName().equals(oldName)) {
138: var.setName(newName);
139: }
140: }
141: }
142:
143: protected class VarBinder extends
144: QueryModelVisitorBase<RuntimeException> {
145:
146: private String varName;
147:
148: private Value value;
149:
150: public VarBinder(String varName, Value value) {
151: this .varName = varName;
152: this .value = value;
153: }
154:
155: @Override
156: public void meet(Var var) {
157: if (var.getName().equals(varName)) {
158: var.setValue(value);
159: }
160: }
161: }
162: }
|