/* * 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.*; // Third party packages import org.apache.log4j.Logger; // Locally written packages import org.mulgara.query.*; import org.mulgara.store.tuples.LiteralTuples; import org.mulgara.store.tuples.Tuples; import org.mulgara.store.tuples.TuplesOperations; /** * The base class for transitive functions. * * @created 2004-06-07 * * @author Andrew Newman * * @version $Revision: 1.8 $ * * @modified $Date: 2005/01/05 04:58:24 $ * * @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 TransitiveFunction { /** * Logger. */ private static final Logger logger = Logger.getLogger(TransitiveFunction.class.getName()); /** * This method finds all statements that match a <code>given</code> tuples, * and adds new statements based on these results to the collection of * <code>inferredTuples</code>. It then chains these results * (via iteration) into a new set of inferences. * * @param baseSubject The subject that this chain is inferencing for. * <code>null</code> if this method is to produce only a single column. * @param subject the internal value of the original subject. * @param initialTuples All existing statements in the model which have the * required predicate. * @param given The current base of the chain, as a set of starting subjects * for statements. Closed by this method. * @param visited A set of all nodes which have been inferred from for * <code>subject</code>. This is to prevent loops. * @param inferredResult An in/out parameter which holds all the inferred * statements so far. This method will append to it. * @throws TuplesException If there was a problem while moving around in * results of the query resolutions. */ protected static void inferTransitiveStatements( Long baseSubject, long subject, Tuples initialTuples, Tuples given, Set<Long> visited, LiteralTuples inferredResult) throws TuplesException { assert initialTuples.getNumberOfVariables() == 2; assert given.getNumberOfVariables() == 1; assert baseSubject == null && inferredResult.getNumberOfVariables() == 1 || inferredResult.getNumberOfVariables() == 2; // get out the column variable being used by given Variable givenColumn = given.getVariables()[0]; if (logger.isDebugEnabled()) { logger.debug("Inferring for subject: " + baseSubject); logger.debug("Given is on column: " + givenColumn.getName()); Variable[] vars = initialTuples.getVariables(); String varnames = ""; for (int v = 0; v < vars.length; v++) { varnames += " " + vars[v]; } logger.debug("InitialTuples has columns: " + varnames); } // True if we should search in s, o boolean searchInOrder = given.getVariables()[0]. equals(initialTuples.getVariables()[0]); LiteralTuples newGiven = null; // Each iteration of this loop steps forward in the inference chain do { // join the current "given" on all the predicates Tuples joinResult = TuplesOperations.join(given, initialTuples); if (logger.isDebugEnabled()) { given.beforeFirst(); String gs = ""; while (given.next()) { gs += " " + given.getColumnValue(0); } if (logger.isDebugEnabled()) logger.debug("Joined with a given of: " + gs); } TuplesException te = null; try { // check if there is any data from the join joinResult.beforeFirst(); if (!joinResult.next()) { break; } // determine the column numbers for the variables int[] tuplesIndex = new int[2]; tuplesIndex[0] = joinResult.getColumnIndex(givenColumn); tuplesIndex[1] = 1 - tuplesIndex[0]; // Create a new given tuples, with the same column variable as the original newGiven = new LiteralTuples(new Variable[] {givenColumn}); // Iterate through all the results, we are already on the first line do { // get the object long object = joinResult.getColumnValue(tuplesIndex[1]); Long objectL = new Long(object); if (logger.isDebugEnabled()) { long s = joinResult.getColumnValue(tuplesIndex[0]); logger.debug("** found subject=" + s + "; object=" + object); logger.debug("** inserting [" + baseSubject + "," + object + "]"); } // add this row to the final result if it doesn't exist in tuples if (baseSubject == null) { // Search for existing s, o or o, s in initial tuples. if ((searchInOrder && !tuplesExist(initialTuples, subject, object)) || (!searchInOrder && !tuplesExist(initialTuples, object, subject))) { inferredResult.appendTuple(new long[] { object }); } } else { // Search for existing s, o or o, s in initial tuples. if ((searchInOrder && !tuplesExist(initialTuples, subject, object)) || (!searchInOrder && !tuplesExist(initialTuples, object, subject))) { inferredResult.appendTuple(new long[] { subject, object }); } } // nothing more to do with this object if we have already seen it if (visited.contains(objectL)) { continue; } // add object to the new given newGiven.appendTuple(new long[] { object }); // tell the visited set that we have been here visited.add(objectL); } while (joinResult.next()); } catch (TuplesException e) { te = e; } finally { // clean up the tuples objects, either by falling through, or from the break above try { if (joinResult != null) joinResult.close(); } catch (TuplesException e) { if (te == null) te = e; else logger.info("Suppressing exception closing tuples on failed transitive function", e); } finally { try { if (given != null) given.close(); } catch (TuplesException e) { if (te == null) te = e; else logger.info("Suppressing exception closing tuples on failed transitive function", e); } } if (te != null) throw te; } // use this new given, and iterate given = newGiven; } while (newGiven.getRowCardinality() != Cursor.ZERO); // close the empty tuples if it exists if (newGiven != null) { newGiven.close(); } } /** * Returns true if the given s, o already exists in the initial tuples. * * @return true if the given s, o already exists in the initial tuples. * @throws TuplesException if there's an error accessing the tuples. */ protected static boolean tuplesExist(Tuples initialTuples, long s, long o) throws TuplesException { initialTuples.beforeFirst(new long[] {s, o}, 0); return initialTuples.next(); } }