/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Plugged In Software Pty Ltd. All Rights Reserved. * * Contributor(s): N/A. * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.resolver; // Java 2 standard packages; import java.util.HashSet; import java.util.Set; // Third party packages import org.apache.log4j.Logger; // Locally written packages import org.mulgara.query.*; import org.mulgara.resolver.spi.QueryEvaluationContext; import org.mulgara.resolver.spi.ResolverSession; import org.mulgara.store.tuples.LiteralTuples; import org.mulgara.store.tuples.Tuples; /** * This is an implementation of the a trans function that simply infers all * statements of a given predicate. * <p> * Example: <p></p> * <pre> * select $xxx <rdfs:subClassOf> $zzz * where trans($xxx <rdfs:subClassOf> $zzz); * * select $xxx <rdfs:subClassOf> $zzz * where trans(<b> <rdfs:subClassOf> $zzz * and $xxx <rdfs:subClassOf> $zzz); * * select $xxx <rdfs:subClassOf> $zzz * where trans($xxx <rdfs:subClassOf> <b> * and $xxx <rdfs:subClassOf> $zzz); * </pre></p> * * @created 2004-06-07 * * @author Andrew Newman * * @version $Revision: 1.8 $ * * @modified $Date: 2005/01/05 04:58:23 $ * * @maintenanceAuthor $Author: newmana $ * * @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A> * * @copyright © 2003 <A href="http://www.PIsoftware.com/">Plugged In * Software Pty Ltd</A> * * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> */ public abstract class ExhaustiveTransitiveFunction extends TransitiveFunction { /** * Logger. */ private static final Logger logger = Logger.getLogger( ExhaustiveTransitiveFunction.class.getName()); /** * Resolves a transitive constraint that is unanchored. This means finding * all statements matching the predicate statement, and infering a new set * of statements based on this. * * @param query the local calling query object. * @param constraint a constraint describing a transitive predicate with a * supported model type fourth element of the constraint is not a Variable * or is a Variable with the name _from. * @return the answer satisfying the <var>constraint</var> * @throws QueryException if the <var>constraint</var> has a model of an * unsupported type, or if the answer otherwise couldn't be determined * @throws TuplesException If there was a problem while moving around in results * from various query resolutions. */ public static Tuples infer(QueryEvaluationContext query, SingleTransitiveConstraint constraint, GraphExpression graphExpression, ResolverSession session) throws QueryException, TuplesException { if (logger.isDebugEnabled()) { logger.debug("Transitive Constraint is unanchored"); } // Transitive constraint must be a fixed predicate if (!(constraint.getElement(1) instanceof Value)) { throw new QueryException("Predicate in transitive function, " + constraint.getElement(1) + ", currently must be bound to a value."); } // The subject and object must be variables. if (!((constraint.getElement(0) instanceof Variable) && (constraint.getElement(2) instanceof Variable))) { throw new QueryException( "The subject: " + constraint.getElement(0) + " and the object: " + constraint.getElement(2) + " are invalid, both must be variables."); } Tuples initialTuples = null; Tuples workingTuples = null; boolean success = false; try { // Get the wrapped constraint Constraint predConstraint = constraint.getTransConstraint(); // ask for all statements for this predicate initialTuples = query.resolve(graphExpression, predConstraint); // prepare to iterate through anchor, or set of all predicate statements initialTuples.beforeFirst(); boolean tuplesAvailable = initialTuples.next(); // if nothing matches, then there is no work to do if (!tuplesAvailable) { return initialTuples; // return nothing but the original set } // Copy initial tuples to be worked on. workingTuples = (Tuples) initialTuples.clone(); workingTuples.beforeFirst(); // the variables for subject and object Variable subjectVariable = (Variable) predConstraint.getElement(0); Variable objectVariable = (Variable) predConstraint.getElement(2); // not anchored, so need indexes in the correct order int subjectIndex = initialTuples.getColumnIndex(subjectVariable); int objectIndex = initialTuples.getColumnIndex(objectVariable); // set up the final result [$x $y] built from [$x predicate $y] // or [$y $x] when using a back anchor LiteralTuples inferredResult = new LiteralTuples(new Variable[] { subjectVariable, objectVariable }); if (logger.isDebugEnabled()) { logger.debug("Iterating through initial tuples"); logger.debug("First variable="+subjectVariable.getName()+"; Second variable="+objectVariable.getName()); } // iterate over all subjects while (tuplesAvailable) { // set up given statement, this will have the same variable object as the predicate constraint // so create [$x] from [$x predicate $y] LiteralTuples given = new LiteralTuples(new Variable[] {subjectVariable}); // iterate over each row of the tuples to set up a given clause for a new query // add the objects, and remember that we've been here // get the subject and object of the statement, these names are inverted for back anchors long subject = initialTuples.getColumnValue(subjectIndex); long object = initialTuples.getColumnValue(objectIndex); Long subjectL = new Long(subject); if (logger.isDebugEnabled()) { logger.debug("Found tuple: subject="+subject+"; object="+object); } // if using zero-step transitivity then add the subject-subject row if (constraint.isZeroStep()) inferredResult.appendTuple(new long[] {subject, subject}); // remember that the subject has been visited Set<Long> visited = new HashSet<Long>(); visited.add(subjectL); // add the object to the "given" tuples long[] tupleRow = new long[] { object }; given.appendTuple(tupleRow); // go forward adding objects until the subject changes // loop while we have data while ((tuplesAvailable = initialTuples.next())) { // check that the subject has not changed, continue with the inferencing if it has if (subject != initialTuples.getColumnValue(subjectIndex)) { break; } // add the current object to the given. Take advantage that initTuplesIndex // is always 0 for anchored constraints object = initialTuples.getColumnValue(objectIndex); tupleRow[0] = object; given.appendTuple(tupleRow); if (logger.isDebugEnabled()) { logger.debug("Found subsequent tuple: *** object="+object); } // if using zero-step transitivity then add the object-object row if (constraint.isZeroStep()) inferredResult.appendTuple(new long[] {object, object}); } // start inferring for the current subject, using this first set of objects // modifies the inferredResult value for us. inferTransitiveStatements(subjectL, subjectL.longValue(), workingTuples, given, visited, inferredResult ); } if (logger.isDebugEnabled()) { logger.debug("Finished all inferencing"); } success = true; return inferredResult; } finally { try { try { if (initialTuples != null) initialTuples.close(); } finally { if (workingTuples != null) workingTuples.close(); } } catch (TuplesException e) { if (success) throw e; // Everything worked up to this point, re-throw this one. else logger.info("Suppressing exception closing failed tuples", e); // Log and ignore redundant exception. } } } /** * Resolves a transitive constraint that is unanchored which has a starting * point. This means starting from a known subject, predicate or predicate, * object and infering a new set of statements based on this. * * @param query the local calling query object. * @param constraint a constraint describing a transitive predicate with a * supported model type fourth element of the constraint is not a Variable * or is a Variable with the name _from. * @return the answer satisfying the <var>constraint</var> * @throws QueryException if the <var>constraint</var> has a model of an * unsupported type, or if the answer otherwise couldn't be determined * @throws TuplesException If there was a problem while moving around in results * from various query resolutions. */ public static Tuples infer(QueryEvaluationContext query, TransitiveConstraint constraint, GraphExpression graphExpression, ResolverSession session) throws QueryException, TuplesException { if (logger.isDebugEnabled()) { logger.debug("Transitive Constraint is unanchored"); } // Transitive constraint must be a fixed predicate if (!(constraint.getAnchoredConstraint().getElement(1) instanceof Value)) { throw new QueryException("Predicate in transitive function, " + constraint.getAnchoredConstraint().getElement(1) + ", currently must be bound to a value."); } // Either the subject is a variable and the object is a constant or // vice-versa in the first constraint if (((constraint.getAnchoredConstraint().getElement(0) instanceof Variable) && !(constraint.getAnchoredConstraint().getElement(2) instanceof Value)) || ((constraint.getAnchoredConstraint().getElement(0) instanceof Value) && !(constraint.getAnchoredConstraint().getElement(2) instanceof Variable))) { throw new QueryException( "The subject: " + constraint.getAnchoredConstraint().getElement(0) + " and the object: " + constraint.getAnchoredConstraint().getElement(2) + " are invalid, one must be a variable and the other a fixed value" + " around a predicate."); } // Transitive constraint must be a fixed predicate if (!(constraint.getUnanchoredConstraint().getElement(1) instanceof Value)) { throw new QueryException("Predicate in transitive function, " + constraint.getElement(1) + ", currently must be bound to a value."); } // The subject and object must be variables. if (!((constraint.getUnanchoredConstraint().getElement(0) instanceof Variable) && (constraint.getUnanchoredConstraint().getElement(2) instanceof Variable))) { throw new QueryException( "The subject: " + constraint.getUnanchoredConstraint().getElement(0) + " and the object: " + constraint.getUnanchoredConstraint().getElement(2) + " are invalid, both must be variables."); } Tuples initialTuples = null; Tuples workingTuples = null; boolean success = false; try { // Get the wrapped constraint Constraint anchoredConstraint = constraint.getAnchoredConstraint(); Constraint unanchoredConstraint = constraint.getUnanchoredConstraint(); // Create a walk constraint WalkConstraint walkConstraint = new WalkConstraint(anchoredConstraint, unanchoredConstraint); // Walk down the graph getting all the statements for the given predicate initialTuples = WalkFunction.walk(query, walkConstraint, graphExpression, session); // prepare to iterate through anchor, or set of all predicate statements initialTuples.beforeFirst(); boolean tuplesAvailable = initialTuples.next(); // if nothing matches, then there is no work to do if (!tuplesAvailable) { return initialTuples; // return nothing but the original set } // Copy initial tuples to be worked on. workingTuples = (Tuples) initialTuples.clone(); workingTuples.beforeFirst(); // the variables for subject and object Variable subjectVariable = (Variable) unanchoredConstraint.getElement(0); Variable objectVariable = (Variable) unanchoredConstraint.getElement(2); // not anchored, so need indexes in the correct order int subjectIndex = initialTuples.getColumnIndex(subjectVariable); int objectIndex = initialTuples.getColumnIndex(objectVariable); // set up the final result [$x $y] built from [$x predicate $y] // or [$y $x] when using a back anchor LiteralTuples inferredResult = new LiteralTuples( new Variable[] { subjectVariable, objectVariable }); if (logger.isDebugEnabled()) { logger.debug("Iterating through initial tuples"); logger.debug( "First variable=" + subjectVariable.getName() + "; Second variable=" + objectVariable.getName()); } // iterate over all subjects while (tuplesAvailable) { // set up given statement, this will have the same variable object as the predicate constraint // so create [$x] from [$x predicate $y] LiteralTuples given = new LiteralTuples(new Variable[] { subjectVariable }); // iterate over each row of the tuples to set up a given clause for a new query // add the objects, and remember that we've been here // get the subject and object of the statement, these names are inverted for back anchors long subject = initialTuples.getColumnValue(subjectIndex); long object = initialTuples.getColumnValue(objectIndex); Long subjectL = new Long(subject); if (logger.isDebugEnabled()) { logger.debug("Found tuple: subject=" + subject + "; object=" + object); } // remember that the subject has been visited Set<Long> visited = new HashSet<Long>(); visited.add(subjectL); // add the object to the "given" tuples long[] tupleRow = new long[] { object }; given.appendTuple(tupleRow); // go forward adding objects until the subject changes // loop while we have data while ((tuplesAvailable = initialTuples.next())) { // check that the subject has not changed, continue with the inferencing if it has if (subject != initialTuples.getColumnValue(subjectIndex)) { break; } // add the current object to the given. Take advantage that initTuplesIndex // is always 0 for anchored constraints object = initialTuples.getColumnValue(objectIndex); tupleRow[0] = object; given.appendTuple(tupleRow); if (logger.isDebugEnabled()) { logger.debug("Found subsequent tuple: *** object=" + object); } } // start inferring for the current subject, using this first set of objects // modifies the inferredResult value for us. inferTransitiveStatements(subjectL, subjectL.longValue(), workingTuples, given, visited, inferredResult); } if (logger.isDebugEnabled()) { logger.debug("Finished all inferencing"); } success = true; return inferredResult; } finally { try { try { if (initialTuples != null) initialTuples.close(); } finally { if (workingTuples != null) workingTuples.close(); } } catch (TuplesException e) { if (success) throw e; // Everything worked up to this point, re-throw this one. else logger.info("Suppressing exception closing failed tuples", e); // Log and ignore redundant exception. } } } }