| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.rya.forwardchain.rule; |
| |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.openrdf.model.Value; |
| import org.openrdf.query.algebra.BNodeGenerator; |
| import org.openrdf.query.algebra.Extension; |
| import org.openrdf.query.algebra.ExtensionElem; |
| import org.openrdf.query.algebra.MultiProjection; |
| import org.openrdf.query.algebra.Projection; |
| import org.openrdf.query.algebra.ProjectionElem; |
| import org.openrdf.query.algebra.ProjectionElemList; |
| import org.openrdf.query.algebra.StatementPattern; |
| import org.openrdf.query.algebra.ValueConstant; |
| import org.openrdf.query.algebra.Var; |
| import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; |
| |
| /** |
| * Query visitor that identifies all triple patterns produced by a "CONSTRUCT" |
| * query. Finds the topmost instance of a {@link Projection} or |
| * {@link MultiProjection}, and expects the variables projected to include |
| * "subject", "predicate", and "object". Each projection is converted to a |
| * {@link StatementPattern}, where any constant values are expected to be |
| * provided by an Extension directly underneath the projection, if applicable. |
| * <p> |
| * Undefined behavior if applied to a query other than a CONSTRUCT query. |
| * <p> |
| * Does not report any constraints on possible consequent triples beyond the |
| * constant values, where appropriate, of each part of the triple. Therefore, |
| * this analysis may produce an overly broad set of possible consequents |
| * compared to some more sophisticated method. |
| */ |
| public class ConstructConsequentVisitor extends QueryModelVisitorBase<RuntimeException> { |
| private Set<StatementPattern> consequentStatementPatterns = new HashSet<>(); |
| |
| private static final String SUBJECT_VAR_NAME = "subject"; |
| private static final String PREDICATE_VAR_NAME = "predicate"; |
| private static final String OBJECT_VAR_NAME = "object"; |
| |
| /** |
| * Get the possible conclusions of this construct rule. |
| * @return StatementPatterns representing the possible triple patterns that |
| * can be inferred. |
| */ |
| public Set<StatementPattern> getConsequents() { |
| return consequentStatementPatterns; |
| } |
| |
| /** |
| * Get the names of any bnodes generated by this construct rule. |
| * @return Variable names corresponding to new entities |
| */ |
| public Set<StatementPattern> getBnodes() { |
| return consequentStatementPatterns; |
| } |
| |
| @Override |
| public void meet(Projection projection) { |
| if (projection.getArg() instanceof Extension) { |
| recordConsequent(projection.getProjectionElemList(), |
| ((Extension) projection.getArg()).getElements()); |
| } |
| else { |
| recordConsequent(projection.getProjectionElemList(), Arrays.asList()); |
| } |
| } |
| |
| @Override |
| public void meet(MultiProjection projection) { |
| List<ExtensionElem> bindings; |
| if (projection.getArg() instanceof Extension) { |
| bindings = ((Extension) projection.getArg()).getElements(); |
| } |
| else { |
| bindings = Arrays.asList(); |
| } |
| for (ProjectionElemList template : projection.getProjections()) { |
| recordConsequent(template, bindings); |
| } |
| } |
| |
| private void recordConsequent(ProjectionElemList variables, List<ExtensionElem> extensionElements) { |
| Map<String, Value> bindings = new ConcurrentHashMap<>(); |
| Map<String, Value> values = new ConcurrentHashMap<>(); |
| Set<String> queryBnodes = new HashSet<>(); |
| Set<String> projectedBnodes = new HashSet<>(); |
| for (ExtensionElem ee : extensionElements) { |
| if (ee.getExpr() instanceof ValueConstant) { |
| bindings.put(ee.getName(), ((ValueConstant) ee.getExpr()).getValue()); |
| } |
| else if (ee.getExpr() instanceof BNodeGenerator) { |
| queryBnodes.add(ee.getName()); |
| } |
| } |
| for (ProjectionElem var : variables.getElements()) { |
| String sourceName = var.getSourceName(); |
| String targetName = var.getTargetName(); |
| Value constValue = bindings.get(sourceName); |
| if (constValue != null) { |
| values.put(targetName, constValue); |
| } |
| else if (queryBnodes.contains(sourceName)) { |
| projectedBnodes.add(targetName); |
| } |
| } |
| Var subjVar = new Var(SUBJECT_VAR_NAME, values.get(SUBJECT_VAR_NAME)); |
| Var predVar = new Var(PREDICATE_VAR_NAME, values.get(PREDICATE_VAR_NAME)); |
| Var objVar = new Var(OBJECT_VAR_NAME, values.get(OBJECT_VAR_NAME)); |
| subjVar.setAnonymous(projectedBnodes.contains(SUBJECT_VAR_NAME)); |
| predVar.setAnonymous(projectedBnodes.contains(PREDICATE_VAR_NAME)); |
| objVar.setAnonymous(projectedBnodes.contains(OBJECT_VAR_NAME)); |
| StatementPattern sp = new StatementPattern(subjVar, predVar, objVar); |
| consequentStatementPatterns.add(sp); |
| } |
| } |