/******************************************************************************* * Copyright (c) 2004, 2007 IBM Corporation and 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 * * File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/query/rewriter/NormalFormRewriter.java,v $ * Created by: Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>) * Created on: 10/23/06 * Revision: $Id: NormalFormRewriter.java 164 2007-07-31 14:11:09Z mroy $ * * Contributors: IBM Corporation - initial API and implementation * Cambridge Semantics Incorporated - Fork to Anzo *******************************************************************************/ package org.openanzo.glitter.query.rewriter; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Set; import org.openanzo.glitter.exception.FeatureNotImplementedException; import org.openanzo.glitter.query.TreeRewriter; import org.openanzo.glitter.syntax.abstrakt.Expression; import org.openanzo.glitter.syntax.abstrakt.GraphPattern; import org.openanzo.glitter.syntax.abstrakt.Group; import org.openanzo.glitter.syntax.abstrakt.TreeNode; import org.openanzo.glitter.syntax.abstrakt.Union; import org.openanzo.rdf.Variable; /** * * <pre> * From the Semantics and Complexity of SPARQL paper, some equivalences: * * 2.3.1 * (1) P1 AND P2 === P2 AND P1 ; P1 UNION P2 === P2 UNION P1 * (2) P1 AND (P2 UNION P3) === (P1 AND P2) UNION (P1 AND P3) * similarly: AND(P1, P2, UNION(P3, P4)) === UNION(AND(P1, P2, P3), AND(P1, P2, P4)) * similarly: AND(P1, UNION(P2, P3, ..., Pk) === UNION(AND(P1, P2), AND(P1, P3), ..., AND(P1, Pk)) * similarly: AND(P1, UNION(P2, P3), UNION(P4, P5)) === * UNION(AND(P1, P2, P4), AND(P1, P2, P5), AND(P1, P3, P4), AND(P1, P3, P5)) * similarly: AND(P1, P2, ..., Pi, UNION(Q11, Q12, ..., Q1j), UNION(Q21, Q22, ..., Q2k), ..., UNION(Qm1, Qm2, ..., Qmn)) * === UNION(For Each (x1, x2, ..., xm) in CartesianProduct(Q11...Q1j, Q21...Q2k, Qm1...Qmn) AND(P1, P2, ..., Pi, x1, x2, ..., xm)) * (3) P1 OPT (P2 UNION P3) === (P1 OPT P2) UNION (P1 OPT P3) * Similarly: * OPT(P1, UNION(P2, P3, ..., Pk)) === * UNION(OPT(P1, P2), OPT(P1, P3), ..., OPT(P1, Pk)) * NOTE WELL: (3) DOES NOT HOLD. See http://www.w3.org/2001/sw/DataAccess/tests/data-r2/optional/manifest#dawg-optional-complex-2 * * We also take advantage of associativity properties: * P1 UNION (P2 UNION P3) = (P1 UNION P2) UNION P3 = (for that matter) * UNION(P1, P2, P3) if naryUnion is allowed. * * TODO there's a bit more of a normal form in Theorem 6 (labelled (4)) in the * paper; we may want to investigate that. * * TODO @@ Do these identities hold with FILTERs having Group scope? * * </pre> * * @author Lee * */ public class NormalFormRewriter implements TreeRewriter { private final boolean allowNaryUnion; /** * Constructor. * * @param allowNaryUnion */ public NormalFormRewriter(boolean allowNaryUnion) { this.allowNaryUnion = allowNaryUnion; } public TreeNode rewriteTreeNode(TreeNode node) { // TODO -- bug when a Group just has a single union (end up with an extra group) // On second thought I don't think this is a bug -- the ConjunctiveRewriter will // fix these extra groups if (node instanceof Group) { // we need to find out if there's any unions as children of this group Group g = (Group) node; ArrayList<GraphPattern> nonUnionGPs = new ArrayList<GraphPattern>(), unionGPs = new ArrayList<GraphPattern>(); for (GraphPattern gp : g.getPatterns()) { if (gp instanceof Union) unionGPs.add(gp); else nonUnionGPs.add(gp); } if (unionGPs.size() > 0) { // implement rewrite (2) Union u = new Union(); Group initialGroup = new Group(nonUnionGPs); // Preserve filters and assignments from our group for (Expression filter : g.getFilters()) initialGroup.addFilter(filter); for (Variable v : g.getAssignments().keySet()) for (Expression e : g.getAssignments().get(v)) initialGroup.addAssignment(v, e); // I need to iterate through the cross product of the elements of the unions // and then for each of those sets, combine them with the non-union GPs in // a group and add that group to the union // first, turn each union into a collection of its graph patterns ArrayList<Collection<GraphPattern>> unions = new ArrayList<Collection<GraphPattern>>(); for (GraphPattern cur : unionGPs) unions.add(((Union) cur).getGraphPatterns()); Set<Collection<GraphPattern>> unionCombos = org.openanzo.rdf.utils.Collections.cartesianProduct(unions); // TODO -- rewrite this to support nested binary unions if (unionCombos.size() > 2 && !this.allowNaryUnion) throw new FeatureNotImplementedException("Can't raise more than 2 unions to the top without nary union support"); for (Collection<GraphPattern> combo : unionCombos) { // clone the initial group Group current = initialGroup.clone(); // add this combination of required patterns that have been distributed // from the various unions for (Iterator<GraphPattern> it = combo.iterator(); it.hasNext();) { current.addGraphPattern((GraphPattern)it.next().clone()); } // union it with all the others u.addGraphPattern(current); } return u; } } /* identity (3) does not hold, see note above */ /* else if (node instanceof Optional) { // implement rewrite (3) from above Optional opt = (Optional) node; GraphPattern mayMatch = opt.getMayMatchPattern(); if (mayMatch instanceof Union) { GraphPattern p1 = opt.getMustMatchPattern(); Union u = new Union(); Union oldUnion = (Union) mayMatch; for (Iterator<GraphPattern> it = oldUnion.getChildren(); it.hasNext();) { Optional new_opt = new Optional(p1, it.next(), opt.getFilters()); u.addGraphPattern(new_opt); } return u; } }*/else if (this.allowNaryUnion && node instanceof Union) { // UNION is associative (and can be Nary) ArrayList<GraphPattern> toAdd = new ArrayList<GraphPattern>(); for (Iterator<? extends TreeNode> it = node.getChildren().iterator(); it.hasNext();) { GraphPattern child = (GraphPattern) it.next(); if (child instanceof Union) { // Lee: the following code (well, part of it) is courtesy of my // niece, Julia Marshall. for (TreeNode XAZsdgdfhbit2 : child.getChildren()) { toAdd.add((GraphPattern) XAZsdgdfhbit2); } it.remove(); } } for (GraphPattern gp : toAdd) ((Union) node).addGraphPattern(gp); } return node; } }