/*
* Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com).
*
* Licensed 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 com.igormaznitsa.prol.logic;
import com.igormaznitsa.prol.data.Term;
import com.igormaznitsa.prol.data.TermFloat;
import com.igormaznitsa.prol.data.TermInteger;
import com.igormaznitsa.prol.data.Var;
import com.igormaznitsa.prol.parser.ProlTreeBuilder;
import com.igormaznitsa.prol.trace.TraceListener;
import com.igormaznitsa.prol.utils.Utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* This class allows to prepare a goal for faster multiuse, also it gives
* possibility to make parametrized goals
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
public class PreparedGoal {
/**
* The field saves prepared goal text (all {?} parameters will be changed to
* inside form)
*/
private String goalAsText;
/**
* The filed saves the work context for the goal
*/
private ProlContext context;
/**
* The term is the parsed version of the goal text
*/
private Term parsedGoal;
/**
* The list contains ordered inside names of parametrized variables
*/
private List<String> orderedVars;
/**
* The variable contains tracer for the wrapped goal, can be null
*/
private final TraceListener tracer;
/**
* Get the list of parametrized variable names, it is used for inside needs
*
* @return a list object containing ordered list of parametrized variables,
* will not be null
*/
protected final List<String> getOrderedVars() {
return this.orderedVars;
}
/**
* Set the list of parametrized var names, must not be null
*
* @param list a list containing ordered parametrized var names
*/
protected final void setOrderedVars(final List<String> list) {
this.orderedVars = list;
}
/**
* Get the term representing parsed goal
*
* @return a term pbject which containing a tree of parsed original goal text
*/
public final Term getParsedGoal() {
return this.parsedGoal;
}
/**
* Set the term representing parsed goal
*
* @param goal a term object containing parsed tree of original goal text,
* must not be null
*/
private void setParsedGoal(final Term goal) {
this.parsedGoal = goal;
}
/**
* Get the work context for the goal
*
* @return the work prol context for the goal, must not be null
*/
public final ProlContext getContext() {
return this.context;
}
/**
* Set the work prol context for the goal
*
* @param context the work prol context for the goal, must not be null
*/
private void setContext(final ProlContext context) {
this.context = context;
}
/**
* Get prepared (all {?} changed to inside var names) goal text
*
* @return the prepared goal text as String object, must not be null
*/
public final String getGoalAsText() {
return this.goalAsText;
}
/**
* Set prepared goal text
*
* @param text a String object contains prepared goal text, must not be null
*/
private void setGoalAsText(final String text) {
this.goalAsText = text;
}
/**
* A constructor allows to make the prepared goal on already parsed goal, of
* course without support any inside parametrized variables
*
* @param goal the parsed goal, must not be null
* @param workContext the work context for the goal, must not be null
* @throws NullPointerException it will be thrown if there is any null pointer
* at argument list
*/
public PreparedGoal(final Term goal, final ProlContext workContext) {
this(goal, workContext, null);
}
/**
* A constructor allows to make the prepared goal on already parsed goal, of
* course without support any inside parametrized variables
*
* @param goal the parsed goal, must not be null
* @param workContext the work context for the goal, must not be null
* @param tracer the tracer for wrapped goal, can be null
* @throws NullPointerException it will be thrown if there is any null pointer
* at argument list
*/
@SuppressWarnings("unchecked")
public PreparedGoal(final Term goal, final ProlContext workContext, final TraceListener tracer) {
if (goal == null || workContext == null) {
throw new NullPointerException("Null argument detected");
}
this.tracer = tracer;
setContext(workContext);
setParsedGoal(goal);
setOrderedVars(Collections.EMPTY_LIST);
}
/**
* A constructor. You can insert {?} parameters inside the goal text and these
* parts will be changed to named inside vars, thus you will be able make
* parametrized versions of the Goal through special functions of the class.
* Be carefully in use of {?} strings they are used as macroses by the
* constructor.
*
* @param goal the goal text, mist not be null
* @param workContext the context which will be used for the goal, must not be
* null
* @throws IOException it will be thrown if there is any exception during
* parsing
* @throws NullPointerException if either the goal or the workContext (or
* both) is null
* @see ProlContext
*/
public PreparedGoal(final String goal, final ProlContext workContext) throws IOException {
this(goal, workContext, null);
}
/**
* A constructor. You can insert {?} parameters inside the goal text and these
* parts will be changed to named inside vars, thus you will be able make
* parametrized versions of the Goal through special functions of the class.
* Be carefully in use of {?} strings they are used as macroses by the
* constructor.
*
* @param goal the goal text, mist not be null
* @param workContext the context which will be used for the goal, must not be
* null
* @param tracer the tracer to be used for wrapped goal, can be null
* @throws IOException it will be thrown if there is any exception during
* parsing
* @throws NullPointerException if either the goal or the workContext (or
* both) is null
* @see ProlContext
*/
public PreparedGoal(final String goal, final ProlContext workContext, final TraceListener tracer) throws IOException {
if (goal == null || workContext == null) {
throw new NullPointerException("Needed argument is null");
}
this.tracer = tracer;
setContext(workContext);
// find ordered vars
final List<String> parametrizedNames = new ArrayList<String>();
final StringBuilder builder = new StringBuilder(goal);
int varIndex = 1000;
while (true) {
final int indexOfOrderedVar = builder.indexOf("{?}");
if (indexOfOrderedVar < 0) {
break;
}
// found
final String varName = "_Ord" + varIndex;
varIndex++;
builder.delete(indexOfOrderedVar, indexOfOrderedVar + 3).insert(indexOfOrderedVar, ' ' + varName + ' ');
parametrizedNames.add(varName);
}
setGoalAsText(builder.toString());
setOrderedVars(parametrizedNames);
final ProlTreeBuilder treebuilder = new ProlTreeBuilder(getContext());
try {
setParsedGoal(treebuilder.readPhraseAndMakeTree(getGoalAsText()));
}
catch (InterruptedException ex) {
throw new IOException("Parsing was interrupted", ex);
}
}
/**
* Process the goal once. The goal will be called once and the result will be
* returned. If the goal fails then null will be returned.
*
* @return null if the goal fails else a resulting Term
* @throws InterruptedException it will be thrown if the executing thread is
* interrupted
* @see Goal
*/
public final Term processGoalOnce() throws InterruptedException {
final Term target = getParsedGoal().makeClone();
final Goal goal = new Goal(target, getContext(), tracer);
return goal.solve();
}
/**
* Make version of the goal for ordered integer parameters.
*
* @param parameters an integer array of values for inside goal parameters,
* must not be null
* @return the prepared goal for the parameters
* @see Goal
*/
public final Goal forIntegerParameters(final int... parameters) {
final Term[] termarray = new Term[parameters.length];
for (int li = 0; li < parameters.length; li++) {
termarray[li] = new TermInteger(parameters[li]);
}
return this.forParameters(termarray);
}
/**
* Make version of the goal for ordered String parameters.
*
* @param parameters a String array of values for inside goal parameters, must
* not be null
* @return the prepared goal for the parameters
* @see Goal
*/
public final Goal forStringParameters(final String... parameters) {
final Term[] termarray = new Term[parameters.length];
for (int li = 0; li < parameters.length; li++) {
termarray[li] = new Term(parameters[li]);
}
return this.forParameters(termarray);
}
/**
* Make version of the goal for ordered float parameters.
*
* @param parameters a float array of values for inside goal parameters, must
* not be null
* @return the prepared goal for the parameters
*/
public final Goal forFloatParameters(final float... parameters) {
final Term[] termarray = new Term[parameters.length];
for (int li = 0; li < parameters.length; li++) {
termarray[li] = new TermFloat(parameters[li]);
}
return this.forParameters(termarray);
}
/**
* Make version of the goal for ordered Term parameters.
*
* @param parameters a Term array of values for inside goal parameters, must
* not be null
* @return the prepared goal for the parameters
*/
public final Goal forParameters(final Term... parameters) {
if (orderedVars.size() != parameters.length) {
throw new IllegalArgumentException("Incompatible parameter number [" + orderedVars.size() + "!=" + parameters.length);
}
final Term goalClone = getParsedGoal().makeClone();
final List<String> varNames = getOrderedVars();
final int len = parameters.length;
for (int li = 0; li < len; li++) {
final String varName = varNames.get(li);
final Term parameter = parameters[li];
if (parameter == null) {
throw new NullPointerException();
}
final Var varTerm = Utils.findVarInsideTerm(goalClone, varName);
if (varTerm == null) {
throw new IllegalArgumentException("Can't find a variable for \'" + varName + "\' name");
}
varTerm.setValue(parameter);
}
return new Goal(goalClone, getContext(), tracer);
}
/**
* Make version of goal for named list of values
*
* @param vars a Map contains name-value pairs for inside parametrized
* variables, must not be null
* @return the prepared goal for the parameters
*/
public final Goal forParameters(final Map<String, Term> vars) {
if (vars == null) {
throw new NullPointerException();
}
final Term goalClone = getParsedGoal().makeClone();
for (Entry<String, Term> entry : vars.entrySet()) {
final Term value = entry.getValue();
final Var foundVar = Utils.findVarInsideTerm(goalClone, entry.getKey());
if (foundVar == null) {
throw new IllegalArgumentException("Can't find variable for \'" + entry.getKey() + "\' name");
}
foundVar.setValue(value);
}
return new Goal(goalClone, getContext(), tracer);
}
/**
* Get the goal instance without any parametrization
*
* @return the goal instance, must not be null
*/
public final Goal getNonparametrizedGoalInstance() {
return new Goal(getParsedGoal(), getContext(), tracer);
}
}