/*
* Copyright 2008 Fedora Commons, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mulgara.sparql;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mulgara.parser.MulgaraParserException;
import org.mulgara.query.ConstraintConjunction;
import org.mulgara.query.ConstraintDifference;
import org.mulgara.query.ConstraintDisjunction;
import org.mulgara.query.ConstraintExpression;
import org.mulgara.query.ConstraintFilter;
import org.mulgara.query.ConstraintOptionalJoin;
import org.mulgara.query.filter.And;
import org.mulgara.query.filter.Filter;
import org.mulgara.query.filter.value.Bool;
/**
* This object transforms a {@link ConstraintExpression} into a minimized {@link ConstraintExpression}.
*
* @created May 06, 2008
* @author Paula Gearon
* @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a>
*/
public class PatternTransformer {
/**
* Perform the mapping of the graph pattern and return the results as a {@link ConstraintExpression}.
* @return The mapped constraint expression.
*/
static public ConstraintExpression transform(ConstraintExpression constraints) throws MulgaraParserException {
Transformer<? extends ConstraintExpression> tx = txMap.get(constraints);
if (tx == null) return constraints;
return tx.internalTx(constraints);
}
/** A mapping of constraint expressions to Transformers. */
private static Map<Class<? extends ConstraintExpression>,Transformer<? extends ConstraintExpression>> txMap =
new HashMap<Class<? extends ConstraintExpression>,Transformer<? extends ConstraintExpression>>();
/**
* The class for the mapping of {@link ConstraintExpression}s to {@link ConstraintExpression}s.
*/
private static abstract class Transformer<T extends ConstraintExpression> {
/** An entry point for the tx operation. This method handles casting to be compatible with the generic template. */
@SuppressWarnings("unchecked")
public ConstraintExpression internalTx(ConstraintExpression constraints) throws MulgaraParserException {
return tx((T)constraints);
}
public abstract ConstraintExpression tx(T constraints) throws MulgaraParserException;
/** Identify the class to be mapped by the extension. */
public abstract Class<T> getTxType();
}
/**
* Utility method to add a transformer to the map, keyed on the class it transforms.
* @param tx The transformer to add to the map.
*/
static void addToMap(Transformer<?> tx) {
txMap.put(tx.getTxType(), tx);
}
/**
* Initialize the mapping of patterns to the constraint builders.
*/
static {
addToMap(new FilterTx());
addToMap(new LeftJoinTx());
addToMap(new ConjunctionTx());
addToMap(new DisjunctionTx());
addToMap(new DifferenceTx());
}
/**
* Creates a conjunction of filters, skipping any TRUE values on the way.
* @param lhs The first filter to join
* @param rhs The second filter to join
* @return A new filter that represents the conjunction of the lhs and the rhs
*/
private static Filter and(Filter lhs, Filter rhs) {
if (lhs == Bool.TRUE) return rhs;
if (rhs == Bool.TRUE) return lhs;
return new And(lhs, rhs);
}
/**
* Maps a list of constraint expressions to a list of transformed constraint expressions.
* This would be better done as a closure, but we can't, especially with the "changed" flag.
* @param elements The list to be transformed
* @return A new list full of transformed items, or else the original if nothing was changed.
* @throws MulgaraParserException Due to a bad transformation.
*/
private static List<ConstraintExpression> txList(List<ConstraintExpression> elements) throws MulgaraParserException {
boolean changed = false;
List<ConstraintExpression> newList = new ArrayList<ConstraintExpression>();
for (ConstraintExpression c: elements) {
ConstraintExpression tx = transform(c);
if (tx != c) changed = true;
newList.add(tx);
}
return changed ? newList : elements;
}
/**
* Map filtered constraints to the flattening operation.
* Filter(X1,Filter(X2,A)) => Filter(X2 && X1, A)
*/
private static class FilterTx extends Transformer<ConstraintFilter> {
public Class<ConstraintFilter> getTxType() { return ConstraintFilter.class; }
public ConstraintExpression tx(ConstraintFilter constraint) throws MulgaraParserException {
ConstraintExpression innerConstraint = constraint.getUnfilteredConstraint();
if (innerConstraint instanceof ConstraintFilter) {
// found Filter(X1,Filter(X2,A))
ConstraintFilter innerFiltered = (ConstraintFilter)transform(innerConstraint); // Filter(X2,A)
return new ConstraintFilter(innerFiltered.getUnfilteredConstraint(), and(innerFiltered.getFilter(), constraint.getFilter()));
}
return constraint;
}
}
/**
* Based on the syntactic (not algebraic) transformations:
* LeftJoin(A, Filter(X1, B), X2) => LeftJoin(A, B, X1 && X2)
* LeftJoin(A, LeftJoin(B, C, X1), X2) => LeftJoin(A, LeftJoin(B, C, true), X1 && X2)
*/
private static class LeftJoinTx extends Transformer<ConstraintOptionalJoin> {
public Class<ConstraintOptionalJoin> getTxType() { return ConstraintOptionalJoin.class; }
public ConstraintExpression tx(ConstraintOptionalJoin leftJoin) throws MulgaraParserException {
ConstraintExpression op = leftJoin.getOptional();
if (op instanceof ConstraintFilter) {
// found LeftJoin(A, Filter(X1, B), X2)
ConstraintFilter filter = (ConstraintFilter)transform(op); // Filter(X1, B)
Filter f = and(filter.getFilter(), leftJoin.getFilter()); // X1 && X2
return new ConstraintOptionalJoin(transform(leftJoin.getMain()), filter.getUnfilteredConstraint(), f);
}
if (op instanceof ConstraintOptionalJoin) {
// found LeftJoin(A, LeftJoin(B, C, X1), X2)
ConstraintOptionalJoin subJoin = (ConstraintOptionalJoin)transform(op); // LeftJoin(B, C, X1)
ConstraintOptionalJoin newSubJoin = new ConstraintOptionalJoin(subJoin.getMain(), subJoin.getOptional(), Bool.TRUE);
Filter newFilter = and(subJoin.getFilter(), leftJoin.getFilter()); // X1 && X2
return new ConstraintOptionalJoin(transform(leftJoin.getMain()), newSubJoin, newFilter);
}
return leftJoin;
}
}
/**
* Recurse the transformations down.
* Normalization to sum of products can be done here (but isn't).
*/
private static class DifferenceTx extends Transformer<ConstraintDifference> {
public Class<ConstraintDifference> getTxType() { return ConstraintDifference.class; }
public ConstraintExpression tx(ConstraintDifference constraint) throws MulgaraParserException {
ConstraintExpression minuend = transform(constraint.getLhs());
ConstraintExpression subtrahend = transform(constraint.getRhs());
return minuend == constraint.getLhs() && subtrahend == constraint.getRhs() ?
new ConstraintDifference(minuend, subtrahend) : constraint;
}
}
/**
* Recurse the transformations down.
* Normalization to sum of products can be done here (but isn't).
*/
private static class ConjunctionTx extends Transformer<ConstraintConjunction> {
public Class<ConstraintConjunction> getTxType() { return ConstraintConjunction.class; }
public ConstraintExpression tx(ConstraintConjunction constraint) throws MulgaraParserException {
List<ConstraintExpression> elements = constraint.getElements();
List<ConstraintExpression> newElements = txList(elements);
return elements != newElements ? new ConstraintConjunction(newElements) : constraint;
}
}
/**
* Recurse the transformations down.
* Normalization to sum of products can be done here (but isn't).
*/
private static class DisjunctionTx extends Transformer<ConstraintDisjunction> {
public Class<ConstraintDisjunction> getTxType() { return ConstraintDisjunction.class; }
public ConstraintExpression tx(ConstraintDisjunction constraint) throws MulgaraParserException {
List<ConstraintExpression> elements = constraint.getElements();
List<ConstraintExpression> newElements = txList(elements);
return elements != newElements ? new ConstraintDisjunction(newElements) : constraint;
}
}
}