/* * 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.jena.reasoner.rulesys.impl; import java.util.*; import org.apache.jena.graph.* ; import org.apache.jena.reasoner.rulesys.Node_RuleVariable ; /** * Frame in the LPInterpreter's control stack used to represent matching * to the results of a tabled predicate. Conventionally the system state which * finds and tables the goal results is called the generator and states which * require those results are called consumers. * <p> * This is used in the inner loop of the interpreter and so is a pure data structure * not an abstract data type and assumes privileged access to the interpreter state. * </p> */ public class ConsumerChoicePointFrame extends GenericTripleMatchFrame implements LPAgendaEntry, LPInterpreterState { /** The generator whose tabled results we are selecting over */ protected Generator generator; /** The index in the generator's result set that we have reached so far. */ protected int resultIndex; /** The preserved permanent registers for the pickled interpreter */ protected Node[] pVars; /** The preserved trail variables for the picked interpreter */ protected Node_RuleVariable[] trailVars; /** The preserved trail bound values for the picked interpreter */ protected Node[] trailValues; /** The length of the preserved trail */ protected int trailLength; /** The generator or top iterator we are producting results for */ protected LPInterpreterContext context; /** * Constructor. * @param interpreter the parent interpreter whose state is to be preserved here, its arg stack * defines the parameters for the target goal */ public ConsumerChoicePointFrame(LPInterpreter interpreter) { init(interpreter); } /** * Initialize the choice point state. * @param interpreter the parent interpreter whose state is to be preserved here, its arg stack * defines the parameters for the target goal */ @Override public void init(LPInterpreter interpreter) { super.init(interpreter); context = interpreter.getContext(); generator = interpreter.getEngine().generatorFor(goal); generator.addConsumer(this); resultIndex = 0; } /** * Preserve the state of an interpreter into this frame. */ public void preserveState( List<Node> trail ) { // Save the trail state int trailLen = trail.size(); if (trailLen > trailLength) { trailValues = new Node[trailLen]; trailVars = new Node_RuleVariable[trailLen]; } trailLength = trailLen; for (int i = 0; i < trailLen; i++) { Node_RuleVariable var = (Node_RuleVariable) trail.get(i); trailVars[i] = var; trailValues[i] = var.getRawBoundValue(); } // Save the permanent variables Node[] currentPVars = envFrame.pVars; if (currentPVars != null) { if (pVars == null || pVars.length < currentPVars.length) { pVars = new Node[currentPVars.length]; } System.arraycopy(currentPVars, 0, pVars, 0, currentPVars.length); } } /** * Restore the state of an interpreter from this frame */ public void restoreState(LPInterpreter interp) { interp.unwindTrail(0); for (int i = 0; i < trailLength; i++) { interp.bind(trailVars[i], trailValues[i]); } if (pVars != null) { System.arraycopy(pVars, 0, envFrame.pVars, 0, pVars.length); } } /** * Find the next result triple and bind the result vars appropriately. * @param interpreter the calling interpreter whose trail should be used * @return FAIL if there are no more matches and the generator is closed, SUSPEND if * there are no more matches but the generator could generate more, SATISFIED if * a match has been found. */ public synchronized StateFlag nextMatch(LPInterpreter interpreter) { while (resultIndex < generator.results.size()) { Triple result = (Triple) generator.results.get(resultIndex++); // Check if we have finished with this generator if (resultIndex >= generator.results.size() && generator.isComplete()) { generator.removeConsumer(this); } if (bindResult(result, interpreter)) { return StateFlag.SATISFIED; } } if (generator.isComplete()) { setFinished(); generator.removeConsumer(this); return StateFlag.FAIL; } else { return StateFlag.SUSPEND; } } /** * Return true if this choice point could usefully be restarted. */ @Override public boolean isReady() { return generator.numResults() > resultIndex; } /** * Called by generator when there are more results available. */ public void setReady() { context.setReady(this); } @Override public void close() { super.close(); if (generator != null) { if(generator.interpreter != null) { generator.interpreter.close(); } generator.removeConsumer(this); // .. but NOT //generator.setComplete(); // as it seems to cause other tests to fail } } /** * Notify that this consumer choice point has finished consuming all * the results of a closed generator. */ public void setFinished() { context.notifyFinished(this); } /** * Reactivate this choice point to return new results. */ @Override public void pump() { if (context instanceof Generator) { ((Generator)context).pump(this); } else { // The top level iterator is in charge and will restore and run this choice point itself } } /** * Return the generator associated with this entry (might be the entry itself) */ @Override public Generator getGenerator() { return generator; } /** * Return the interpeter context which is reading the results of this consumer. */ public LPInterpreterContext getConsumingContext() { return context; } }