/* * 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.query.Constraint; import org.mulgara.query.ConstraintConjunction; import org.mulgara.query.ConstraintDifference; import org.mulgara.query.ConstraintDisjunction; import org.mulgara.query.ConstraintElement; import org.mulgara.query.ConstraintExpression; import org.mulgara.query.ConstraintFilter; import org.mulgara.query.ConstraintHaving; import org.mulgara.query.ConstraintImpl; import org.mulgara.query.ConstraintIn; import org.mulgara.query.ConstraintIs; import org.mulgara.query.ConstraintNotOccurs; import org.mulgara.query.ConstraintOccurs; import org.mulgara.query.ConstraintOccursLessThan; import org.mulgara.query.ConstraintOccursMoreThan; import org.mulgara.query.ConstraintOperation; import org.mulgara.query.ConstraintOptionalJoin; import org.mulgara.query.QueryException; import org.mulgara.query.SingleTransitiveConstraint; import org.mulgara.query.TransitiveConstraint; import org.mulgara.query.WalkConstraint; /** * Identity transformation on constraint expressions. * * @created July 1, 2008 * @author Paula Gearon * @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a> */ public abstract class IdentityTransformer { /** Maps constraint types to constructors */ protected Map<Class<? extends Constraint>, ConstraintTypeCons<? extends Constraint>> consMap = new HashMap<Class<? extends Constraint>, ConstraintTypeCons<? extends Constraint>>(); /** * Builds a transformer, with identity constructors. */ public IdentityTransformer() { initialize(new ConsImpl(), new ConsIs(), new ConsNotOccurs(), new ConsOccurs(), new ConsOccursLessThan(), new ConsOccursMoreThan(), new ConsSingleTransitive(), new ConsTransitive(), new ConsWalk()); } /** * Builds a transformer, with given constructors. * @param constructors The constructors to use. */ public void initialize(ConstraintTypeCons<?>... constructors) { for (ConstraintTypeCons<?> c: constructors) insert(c); } /** * Gets a constructor for a give constraint type. * @param c A constraint of the desired type. * @return A constructor for the given constraint. */ public ConstraintTypeCons<? extends Constraint> getCons(Constraint c) { return consMap.get(c.getClass()); } /** * Maps a constraint type to its constructor. This will override existing mappings. * @param cons The constraint constructor. */ protected void insert(ConstraintTypeCons<? extends Constraint> cons) { consMap.put(cons.getType(), cons); } /** * Transforms a {@link ConstraintExpression}. * @param expr The expression to transform. * @return The transformed expression. * @throws SymbolicTransformationException An error occurred in the transformation. */ public ConstraintExpression transform(ConstraintExpression expr) throws SymbolicTransformationException { // explicitly handle all the recursive types if (expr instanceof ConstraintFilter) return transformFilter(expr); if (expr instanceof ConstraintIn) return transformIn(expr); if (expr instanceof ConstraintOperation) return transformOperation(expr); // do the actual work of this transformer if (expr instanceof Constraint) return transformConstraint(expr); // By default we do not recognise the constraint type, so pass it unchanged. return expr; } /** * Transforms a {@link ConstraintFilter}. * @param expr The ConstraintFilter to transform. * @return The transformed ConstraintFilter. * @throws SymbolicTransformationException An error occurred in the transformation. */ ConstraintFilter transformFilter(ConstraintExpression expr) throws SymbolicTransformationException { ConstraintFilter filter = (ConstraintFilter)expr; ConstraintExpression inner = filter.getUnfilteredConstraint(); ConstraintExpression tx = transform(inner); return (tx == inner) ? filter : new ConstraintFilter(tx, filter.getFilter()); } /** * Transforms a {@link ConstraintIn}. * @param expr The ConstraintIn to transform. * @return The transformed ConstraintIn. * @throws SymbolicTransformationException An error occurred in the transformation. */ ConstraintIn transformIn(ConstraintExpression expr) throws SymbolicTransformationException { ConstraintIn in = (ConstraintIn)expr; ConstraintExpression inner = in.getConstraintParam(); ConstraintExpression tx = transform(inner); return (tx == inner) ? in : new ConstraintIn(tx, in.getGraph()); } /** * Transforms a {@link ConstraintOperation}. * @param expr The ConstraintOperation to transform. * @return The transformed ConstraintOperation. * @throws SymbolicTransformationException An error occurred in the transformation. */ ConstraintOperation transformOperation(ConstraintExpression expr) throws SymbolicTransformationException { ConstraintOperation operation = (ConstraintOperation)expr; List<ConstraintExpression> ops = operation.getElements(); List<ConstraintExpression> newOps = new ArrayList<ConstraintExpression>(ops.size()); boolean changed = false; for (ConstraintExpression op: ops) { ConstraintExpression tx = transform(op); newOps.add(tx); if (tx != op) changed = true; } if (changed) { OpType operationType = OpType.getType(operation); if (operationType == null) throw new SymbolicTransformationException("Encountered an unknown operation type: " + operation.getClass()); return operationType.newOp(newOps); } return operation; } /** * Transforms a {@link Constraint}. This method is usually replaced to modify instances * of {@link ConstraintImpl}. * @param expr The Constraint to transform. * @return The transformed Constraint. * @throws SymbolicTransformationException An error occurred in the transformation. */ Constraint transformConstraint(ConstraintExpression expr) throws SymbolicTransformationException { Constraint cnstr = (Constraint)expr; try { return getCons(cnstr).newConstraint(cnstr); } catch (NullPointerException e) { throw new SymbolicTransformationException("Unable to transform unknown Constraint type: " + cnstr); } } /** * This enum enumerates the ConstraintOperation types. It has been built to deal with * the fact that constructors for the various types cannot be passed as a lambda. * It also provides a map for the enumerated types to their enumerations, making it * easy for an operation to get to an appropriate constructor. */ private static enum OpType { difference { ConstraintOperation newOp(List<ConstraintExpression> ops) { return new ConstraintDifference(ops.get(0), ops.get(1)); } }, optional { ConstraintOperation newOp(List<ConstraintExpression> ops) { return new ConstraintOptionalJoin(ops.get(0), ops.get(1)); } }, conjunction { ConstraintOperation newOp(List<ConstraintExpression> ops) { return new ConstraintConjunction(ops); } }, disjunction { ConstraintOperation newOp(List<ConstraintExpression> ops) { return new ConstraintDisjunction(ops); } }; abstract ConstraintOperation newOp(List<ConstraintExpression> ops); private static Map<Class<? extends ConstraintOperation>, OpType> opMap = new HashMap<Class<? extends ConstraintOperation>, OpType>(); public static OpType getType(ConstraintOperation op) { return opMap.get(op.getClass()); } static { opMap.put(ConstraintDifference.class, difference); opMap.put(ConstraintOptionalJoin.class, optional); opMap.put(ConstraintConjunction.class, conjunction); opMap.put(ConstraintDisjunction.class, disjunction); } } /** * Interface to describe a constructor along with the type the constructor applies to. */ protected interface ConstraintTypeCons<T extends Constraint> { /** * Method to construct a new constraint of the expected type. * @param c The old version of the constraint. * @return A new Constraint with the same type as <var>c</var>. * @throws SymbolicTransformationException There was an error in the data structure. */ abstract T newConstraint(Constraint c) throws SymbolicTransformationException; /** @return The class handled by this type. */ abstract Class<T> getType(); } /////////////////////////////////////////////////////////////////////// // The following are classes for constructing each of the various types /////////////////////////////////////////////////////////////////////// protected abstract class ConsHaving<T extends ConstraintHaving> implements ConstraintTypeCons<T> { public T newConstraint(Constraint c) { ConstraintHaving h = (ConstraintHaving)c; ConstraintElement[] ops = new ConstraintElement[4]; for (int i = 0; i < ops.length; i++) ops[i] = h.getElement(i); return newHaving(ops); } protected abstract T newHaving(ConstraintElement[] ops); } protected class ConsNotOccurs extends ConsHaving<ConstraintNotOccurs> { public ConstraintNotOccurs newHaving(ConstraintElement[] ops) { return new ConstraintNotOccurs(ops[0], ops[2], ops[3]); } public Class<ConstraintNotOccurs> getType() { return ConstraintNotOccurs.class; } } protected class ConsOccurs extends ConsHaving<ConstraintOccurs> { public ConstraintOccurs newHaving(ConstraintElement[] ops) { return new ConstraintOccurs(ops[0], ops[2], ops[3]); } public Class<ConstraintOccurs> getType() { return ConstraintOccurs.class; } } protected class ConsOccursLessThan extends ConsHaving<ConstraintOccursLessThan> { public ConstraintOccursLessThan newHaving(ConstraintElement[] ops) { return new ConstraintOccursLessThan(ops[0], ops[2], ops[3]); } public Class<ConstraintOccursLessThan> getType() { return ConstraintOccursLessThan.class; } } protected class ConsOccursMoreThan extends ConsHaving<ConstraintOccursMoreThan> { public ConstraintOccursMoreThan newHaving(ConstraintElement[] ops) { return new ConstraintOccursMoreThan(ops[0], ops[2], ops[3]); } public Class<ConstraintOccursMoreThan> getType() { return ConstraintOccursMoreThan.class; } } protected class ConsImpl implements ConstraintTypeCons<ConstraintImpl> { public ConstraintImpl newConstraint(Constraint c) { return new ConstraintImpl(c.getElement(0), c.getElement(1), c.getElement(2), c.getElement(3)); } public Class<ConstraintImpl> getType() { return ConstraintImpl.class; } } protected class ConsIs implements ConstraintTypeCons<ConstraintIs> { public ConstraintIs newConstraint(Constraint c) { return new ConstraintIs(c.getElement(0), c.getElement(2), c.getElement(3)); } public Class<ConstraintIs> getType() { return ConstraintIs.class; } } protected class ConsSingleTransitive implements ConstraintTypeCons<SingleTransitiveConstraint> { public SingleTransitiveConstraint newConstraint(Constraint c) throws SymbolicTransformationException { SingleTransitiveConstraint s = (SingleTransitiveConstraint)c; return new SingleTransitiveConstraint(transformConstraint(s.getTransConstraint())); } public Class<SingleTransitiveConstraint> getType() { return SingleTransitiveConstraint.class; } } protected class ConsTransitive implements ConstraintTypeCons<TransitiveConstraint> { public TransitiveConstraint newConstraint(Constraint c) throws SymbolicTransformationException { TransitiveConstraint t = (TransitiveConstraint)c; return new TransitiveConstraint(transformConstraint(t.getAnchoredConstraint()), transformConstraint(t.getUnanchoredConstraint())); } public Class<TransitiveConstraint> getType() { return TransitiveConstraint.class; } } protected class ConsWalk implements ConstraintTypeCons<WalkConstraint> { public WalkConstraint newConstraint(Constraint c) throws SymbolicTransformationException { WalkConstraint t = (WalkConstraint)c; try { return new WalkConstraint(transformConstraint(t.getAnchoredConstraint()), transformConstraint(t.getUnanchoredConstraint())); } catch (QueryException e) { throw new SymbolicTransformationException("Invalid Walk constraints being transformed", e); } } public Class<WalkConstraint> getType() { return WalkConstraint.class; } } }