/******************************************************************************* * 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/validator/WellDesignedOptionalsValidator.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: WellDesignedOptionalsValidator.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.validator; import java.util.HashSet; import java.util.Set; import org.openanzo.glitter.query.QueryInformation; import org.openanzo.glitter.query.QueryValidator; import org.openanzo.glitter.syntax.abstrakt.BGP; import org.openanzo.glitter.syntax.abstrakt.GraphPattern; import org.openanzo.glitter.syntax.abstrakt.Optional; import org.openanzo.glitter.syntax.abstrakt.TreeNode; import org.openanzo.rdf.Variable; /** * Rejects queries that contain an OPTIONAL that contains a variable that is not in the required part of the OPTIONAL but does appear some other place in the * query. * * @author lee <lee@cambridgesemantics.com> * */ public class WellDesignedOptionalsValidator implements QueryValidator { private String error = null; public String getValidationError() { return this.error; } public String getValidatorDescription() { return "Validates that the query does not contain an OPTIONAL clause with an optional part containing a variable not appearing in the required part yet appearing in the rest of the query."; } public boolean validateQuery(QueryInformation query) { return validateQueryNode(query.getQueryPattern(), null, query.getQueryPattern()); } protected boolean validateQueryNode(TreeNode n, TreeNode parent, GraphPattern query) { boolean valid = true; if (n == null) return true; if (n instanceof Optional) { Optional opt = (Optional) n; Set<Variable> mustVars = opt.getMustMatchPattern() != null ? opt.getMustMatchPattern().getReferencedVariables() : new HashSet<Variable>(), mayVars = opt.getMayMatchPattern().getReferencedVariables(); mayVars.removeAll(mustVars); if (mayVars.size() > 0) { // we need to determine if any of these variables occurs outside of this // node of the AST. in lieu (TODO) of any better way to do this, we // substitute out this optional node for a dummy node and then check // if the overall tree contains any of these variables // // TODO -- I think that this coarse test is not strictly true; instead, // I think that this is malformed only if the variable occurs elsewhere // within the same context that this optional appears (that is, not within // a disjunctive alternative to the one that this optional appears in). As // currently written, then, this test is too restrictive and will restrict // constructs such as: // { ?s <b> <c> OPTONAL {?s <e> ?x}] // UNION // { ?s <h> <i> OPTONAL {?s <k> ?x}] // which should not be restricted. This is captured in the (currently // failing) test QueryValidatorsTest.testWellDesignedOptionalsValidator5 // It also rejects repeated OPTIONALs such as: // { ?s ?p ?o OPTIONAL {?o dc:title ?lbl } OPTIONAL {?o rdfs:label ?lbl } // TODO Should it? // Opt(Opt(n, y), y) TreeNode dummy = new BGP(); parent.replaceChild(n, dummy); for (Variable v : mayVars) { if (query.mightBindVariable(v)) { this.error = "Variable " + v + " appears in optional pattern but not in required part."; valid = false; break; } } parent.replaceChild(dummy, n); } } for (TreeNode tn : n.getChildren()) { valid = validateQueryNode(tn, n, query); if (!valid) break; } return valid; } }