Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.cyc.query.QueryImpl Maven / Gradle / Ivy
Go to download
Query API implementation for requesting and handling answers to arbitrarily complex questions
posed to a Cyc server.
package com.cyc.query;
/*
* #%L
* File: QueryImpl.java
* Project: Query Client
* %%
* Copyright (C) 2013 - 2017 Cycorp, Inc.
* %%
* 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.
* #L%
*/
import com.cyc.base.CycAccess;
import com.cyc.base.CycAccessManager;
import com.cyc.base.cycobject.CycList;
import com.cyc.base.cycobject.CycObject;
import com.cyc.base.cycobject.CycVariable;
import com.cyc.base.cycobject.DenotationalTerm;
import com.cyc.base.cycobject.ElMt;
import com.cyc.base.cycobject.FormulaSentence;
import com.cyc.base.exception.CycApiException;
import com.cyc.base.exception.CycConnectionException;
import com.cyc.base.exception.CycTimeOutException;
import com.cyc.base.inference.InferenceAnswer;
import com.cyc.base.inference.InferenceResultSet;
import com.cyc.base.inference.InferenceWorker;
import com.cyc.base.inference.InferenceWorkerListener;
import com.cyc.baseclient.CycObjectFactory;
import com.cyc.baseclient.connection.SublApiHelper;
import com.cyc.baseclient.cycobject.ArgPositionImpl;
import com.cyc.baseclient.cycobject.CycFormulaSentence;
import com.cyc.baseclient.inference.CycBackedInferenceAnswer;
import com.cyc.baseclient.inference.DefaultInferenceWorkerSynch;
import com.cyc.baseclient.inference.DefaultResultSet;
import com.cyc.baseclient.inference.ResultSetInferenceAnswer;
import com.cyc.baseclient.inference.SpecifiedInferenceAnswerIdentifier;
import com.cyc.baseclient.inference.metrics.InferenceMetricsValuesImpl;
import com.cyc.baseclient.inference.params.DefaultInferenceParameters;
import com.cyc.baseclient.parser.CyclParserUtil;
import com.cyc.baseclient.parser.InvalidConstantGuidException;
import com.cyc.baseclient.parser.InvalidConstantNameException;
import com.cyc.baseclient.parser.ParseException;
import com.cyc.baseclient.parser.UnsupportedVocabularyException;
import com.cyc.kb.ArgPosition;
import com.cyc.kb.Context;
import com.cyc.kb.KbIndividual;
import com.cyc.kb.KbObject;
import com.cyc.kb.Sentence;
import com.cyc.kb.Variable;
import com.cyc.kb.client.Constants;
import com.cyc.kb.client.ContextImpl;
import com.cyc.kb.client.KbIndividualImpl;
import com.cyc.kb.client.KbTermImpl;
import com.cyc.kb.client.KbUtils;
import com.cyc.kb.client.SentenceImpl;
import com.cyc.kb.client.config.KbConfiguration;
import com.cyc.kb.exception.CreateException;
import com.cyc.kb.exception.KbException;
import com.cyc.kb.exception.KbTypeException;
import com.cyc.nl.Paraphraser;
import com.cyc.query.exception.QueryConstructionException;
import com.cyc.query.exception.QueryRuntimeException;
import com.cyc.query.metrics.InferenceMetricsValues;
import com.cyc.query.parameters.InferenceMode;
import com.cyc.query.parameters.InferenceParameters;
import com.cyc.session.CycServerReleaseType;
import com.cyc.session.CycSession;
import com.cyc.session.CycSessionManager;
import com.cyc.session.compatibility.CycSessionRequirementList;
import com.cyc.session.compatibility.MinimumPatchRequirement;
import com.cyc.session.compatibility.NotOpenCycRequirement;
import com.cyc.session.exception.OpenCycUnsupportedFeatureException;
import com.cyc.session.exception.SessionCommandException;
import com.cyc.session.exception.SessionCommunicationException;
import com.cyc.session.exception.SessionConfigurationException;
import com.cyc.session.exception.SessionInitializationException;
import com.cyc.session.exception.UnsupportedCycOperationException;
import java.io.Closeable;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.JAXBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* QueryImpl
is designed to represent queries posed to Cyc and provide access to their
* results.
*
* In general, the process of getting an answer from Cyc is:
*
* Create a QueryImpl
object and set relevant fields on it.
* Access the answers via methods like {@link #getAnswersCyc()}, {@link #getResultSet()}, or by
* adding a listener via {@link #addListener(com.cyc.query.QueryListener)} and starting the query
* via {@link #performInference()}.
* To avoid filling up memory on the Cyc server, {@link #close()} the QueryImpl when you are
* done with it, which will free up any lingering associated inference resources on the Cyc image.
* Queries are also closed when their {@link #finalize()} method is invoked, notably when they are
* garbage collected.
*
*
* @author Vijay Raj
* @author David Baxter
*
*/
public class QueryImpl implements Query, Closeable {
/* == QueryInference ======================================================================== */
/**
* Inner class to hold the aspects of a QueryImpl that it acquires when run.
*/
private class QueryInference implements InferenceWorkerListener {
protected long closeTimeoutMS = 5000;
private QueryResultSet rs = null;
private boolean hasBeenStarted = false;
private QueryWorker worker;
private InferenceStatus inferenceStatus = InferenceStatus.NOT_STARTED;
private InferenceIdentifier inferenceIdentifier = null;
private final List cycAnswers = new ArrayList<>();
private QueryInference() {
}
/**
*
* @param inferenceWorker
*/
@Override
public void notifyInferenceCreated(InferenceWorker inferenceWorker) {
this.hasBeenStarted = true;
}
/**
*
* @param oldStatus
* @param newStatus
* @param suspendReason
* @param inferenceWorker
*/
@Override
public void notifyInferenceStatusChanged(InferenceStatus oldStatus,
InferenceStatus newStatus, InferenceSuspendReason suspendReason,
InferenceWorker inferenceWorker) {
this.inferenceStatus = newStatus;
}
/**
*
* @param inferenceWorker
* @param newAnswers
*/
@Override
public void notifyInferenceAnswersAvailable(InferenceWorker inferenceWorker,
List newAnswers) {
cycAnswers.addAll(newAnswers);
}
/**
*
* @param inferenceWorker
* @param e
*/
@Override
public void notifyInferenceTerminated(InferenceWorker inferenceWorker,
Exception e) {
if (isContinuingInference) {
isContinuingInference = false;
synchronized (continueInferenceLock) {
continueInferenceLock.notify();
}
}
}
private InferenceIdentifier getInferenceIdentifier() throws SessionCommunicationException {
if (inferenceIdentifier == null && hasBeenStarted) {
try {
if (worker != null) {
inferenceIdentifier = worker.getInferenceIdentifier();
}
if (getResultSet() != null && inferenceIdentifier == null) {
inferenceIdentifier = rs.getInferenceIdentifier();
}
} catch (CycConnectionException ex) {
throw new SessionCommunicationException(ex);
}
}
return inferenceIdentifier;
}
private InferenceSuspendReason getSuspendReason() {
if (worker != null) {
return worker.getSuspendReason();
} else {
return null;
}
}
private void start() throws IOException, CycConnectionException {
worker = createInferenceWorker();
worker.start();
hasBeenStarted = true;
}
private void stop(final Integer patience) {
if (patience == null) {
worker.interruptInference();
} else {
worker.interruptInference(patience);
}
}
private void close() {
if (worker != null) {
try {
worker.releaseInferenceResources(closeTimeoutMS);
return;
} catch (CycConnectionException | CycTimeOutException | CycApiException ex) {
LOGGER.error("Got exception trying to free inference resources for " + this, ex);
ex.printStackTrace(System.err);
}
}
if (rs != null) {
((QueryResultSetImpl) rs).close();
}
}
private QueryResultSet getResultSet() throws CycConnectionException {
if (rs == null) {
if (!hasBeenStarted) {
performInference();
}
if (rs == null) {
setResultSet(new QueryResultSetImpl(cycAnswers));
}
}
if (rs.getCurrentRowCount() < cycAnswers.size()) {
setResultSet(new QueryResultSetImpl(cycAnswers));
}
return rs;
}
private QueryResultSet performInference() throws CycConnectionException {
if (requiresInferenceWorker()) {
createInferenceWorker().executeQuery();
} else {
final InferenceResultSet inferenceResultSet = getCycAccess().getInferenceTool().executeQuery(getQuerySentenceCyc(),
ContextImpl.asELMt(getContext()), getInferenceParameters());
this.setResultSet(new QueryResultSetImpl(inferenceResultSet));
hasBeenStarted = true;
this.inferenceStatus = InferenceStatus.SUSPENDED;
}
return rs;
}
private final Object continueInferenceLock = new Object();
private boolean isContinuingInference = false;
private void continueInference() throws CycConnectionException {
if (!isContinuable()) {
throw new UnsupportedOperationException("This query is not continuable.");
} else if (!hasBeenStarted) {
performInference();
} else if (worker != null) {
try {
isContinuingInference = true;
worker.continueInference(params);
synchronized (continueInferenceLock) {
while (isContinuingInference) {
continueInferenceLock.wait();
}
}
} catch (InterruptedException ex) {
} finally {
isContinuingInference = false;
}
} else {
throw new UnsupportedOperationException("This query cannot be continued.");
}
}
private QueryWorker createInferenceWorker() {
final ElMt elmt = ContextImpl.asELMt(ctx);
worker = new QueryWorker(elmt, getCycAccess());
// We get to be the first listener, so we can be sure we're up to date
// when other listeners are called.
worker.addInferenceListener(this);
for (final QueryListener listener : listeners) {
worker.addQueryListener(listener);
}
return (QueryWorker) worker;
}
private void setResultSet(QueryResultSet rs) {
this.rs = rs;
}
private void clear() {
close();
rs = null;
hasBeenStarted = false;
worker = null;
inferenceStatus = InferenceStatus.NOT_STARTED;
inferenceIdentifier = null;
cycAnswers.clear();
}
protected CycList getSublCommand() {
return (worker != null) ? worker.getSubLCommand() : null;
}
}
/* == QueryWorker =========================================================================== */
class QueryWorker extends DefaultInferenceWorkerSynch {
public QueryWorker(ElMt mt, CycAccess access) {
super(querySentence, mt, params, access, getQueryWorkerTimeoutMillis());
}
QueryImpl getQuery() {
return QueryImpl.this;
}
public void addQueryListener(QueryListener listener) {
if (listener instanceof InferenceWorkerListener) {
addInferenceListener((InferenceWorkerListener) listener);
} else {
addInferenceListener(new QueryListenerAdaptor(listener));
}
}
}
/* == Fields ================================================================================ */
public static final CycSessionRequirementList QUERY_LOADER_REQUIREMENTS
= CycSessionRequirementList.fromList(
new MinimumPatchRequirement(
"Query loading is unsupported on ResearchCyc 4.0q and earlier",
CycServerReleaseType.RESEARCHCYC, 154917));
public static final CycSessionRequirementList QUERY_COMPARISON_REQUIREMENTS
= CycSessionRequirementList.fromList(
NotOpenCycRequirement.NOT_OPENCYC);
/**
* Do not use this value directly; instead, call {@link QueryImpl#getQueryWorkerTimeoutMillis()}.
* It is currently set to 3 minutes.
*/
public static final long DEFAULT_QUERY_WORKER_TIMEOUT_MILLIS = 3 * 60 * 1000;
private static final Logger LOGGER = LoggerFactory.getLogger(QueryImpl.class);
/**
* Do not use this value directly; instead, call {@link QueryImpl#getQueryWorkerTimeoutMillis()}.
*/
private long defaultQueryWorkerTimeoutMillis = DEFAULT_QUERY_WORKER_TIMEOUT_MILLIS;
private static final String RESULT_SORT_ORDER = ":RESULT-SORT-ORDER";
private static final String RETURN = ":RETURN";
private static final String COMPUTE_ANSWER_JUSTIFICATIONS = ":COMPUTE-ANSWER-JUSTIFICATIONS?";
private static final Set UNCLOSED_QUERIES = Collections.newSetFromMap(new ConcurrentHashMap());
private final CycSession session;
private final FormulaSentence originalQuerySentence;
private final CycAccess cyc;
private final Set listeners = new HashSet<>();
private final Set categories = new HashSet<>();
private final QueryInference inference = new QueryInference();
private Context ctx = null;
private FormulaSentence querySentence = null;
private InferenceParameters params = null;
private KbIndividual id = null;
private Map substitutions = null;
private List answers = new ArrayList<>();
private QueryRulesImpl rules = null;
private boolean retainInference = false;
/* == Static methods ======================================================================== */
// Public
/**
* Returns a QueryImpl object defined by a query term (i.e. an instance of
* #$CycLQuerySpecification). Use of this method is equivalent to calling
* {@link Query#Query(com.cyc.kb.KBIndividual)}.
*
*
* @param id an instance of #$CycLQuerySpecification
* @return a QueryImpl object defined by queryTerm
* @throws KbException if there is a problem creating the QueryImpl object
* @throws QueryConstructionException if there is some other kind of problem
*
* Note: {@link QueryConstructionException} is thrown if the specified query term has a
* sentence whose outermost operator is #$ist and the query is loaded from a Cyc server with a
* system level under 10.154917 (Nov. 2014). A workaround is to edit the query in the KB, removing
* the #$ist from the query's sentence, and specifying it as the query mt using
* #$microtheoryParameterValueInSpecification.
*/
public static Query load(KbIndividual id)
throws UnsupportedCycOperationException, QueryConstructionException, KbException {
try {
QUERY_LOADER_REQUIREMENTS.throwExceptionIfIncompatible();
final Query q = new QueryReader().queryFromTerm(id);
// FIXME: This shouldn't be necessary, as explained in SNC-267 - nwinant, 2017-04-23
q.getRules().useOnlySpecifiedRules(false);
return q;
} catch (UnsupportedCycOperationException | KbException ex) {
throw ex;
} catch (JAXBException ex) {
throw new RuntimeException(ex);
} catch (NullPointerException ex) {
throw new QueryConstructionException("Could not load a query for " + id, ex);
} catch (SessionCommunicationException | SessionCommandException | SessionConfigurationException | SessionInitializationException | QueryConstructionException | QueryRuntimeException ex) {
throw new QueryConstructionException(ex);
}
}
/**
* Returns a new QueryImpl loaded from a term in Cyc specifying its properties. Terms in the
* specified query can be replaced with others by providing a non-empty indexicals
* map.
*
* @param id the Cyc term
* @param indexicals A map of substitutions to be made.
* @return the QueryImpl specified by id
* @throws QueryRuntimeException
*
* Note: {@link QueryConstructionException} is thrown if the specified query term has a
* sentence whose outermost operator is #$ist and the query is loaded from a Cyc server with a
* system level under 10.154917 (Nov. 2014). A workaround is to edit the query in the KB, removing
* the #$ist from the query's sentence, and specifying it as the query mt using
* #$microtheoryParameterValueInSpecification.
*/
public static QueryImpl load(KbIndividual id, Map indexicals) {
final Map kboToCoMap = KbUtils.convertKBObjectMapToCoObjectMap(indexicals);
final QueryImpl q;
try {
q = loadCycObjectMap(id, kboToCoMap);
} catch (UnsupportedCycOperationException | QueryConstructionException | KbException ex) {
throw new QueryRuntimeException(ex);
}
return q;
}
/**
* Returns a QueryImpl object defined by a CycLQuerySpecification term, and substitutes in
* relevant values from the indexicals Map.
*
* @param idStr The instance of CycLQuerySpecification
* @param indexicals A map from terms in the query (as loaded from the KB) to the actual values
* that should be queried with.
* @throws QueryConstructionException
*
* Note: {@link QueryConstructionException} is thrown if the specified query term has a
* sentence whose outermost operator is #$ist and the query is loaded from a Cyc server with a
* system level under 10.154917 (Nov. 2014). A workaround is to edit the query in the KB, removing
* the #$ist from the query's sentence, and specifying it as the query mt using
* #$microtheoryParameterValueInSpecification.
*
* @throws KbTypeException if idStr
does not identify a KBIndividual.
*
* @return a QueryImpl object defined by idStr
*/
public static QueryImpl load(String idStr, Map indexicals)
throws QueryConstructionException, KbTypeException, UnsupportedCycOperationException {
try {
final QueryImpl q = (QueryImpl) load(KbIndividualImpl.get(idStr));
replaceIndexicals(indexicals, q);
return q;
} catch (KbException e) {
throw new QueryRuntimeException("Exception thrown while trying to load " + idStr, e);
}
}
/**
*
* @return the number of queries that were closed.
*/
public static synchronized int closeAllUnclosedQueries() {
int count = 0;
for (final QueryImpl q : UNCLOSED_QUERIES) {
q.close();
count++;
}
if (count > 0) {
LOGGER.info("Closed {} queries.", count);
}
return count;
}
/**
* Returns a QueryImpl object defined by a CycLQuerySpecification term, and substitutes in
* relevant values from the indexicals Map. This is the equivalent of calling
* {@link #load(com.cyc.kb.KBIndividual)} and then calling
* {@link #substituteTermsWithCycObject(java.util.Map)} on it.
*
*
* @param id The instance of CycLQuerySpecification
* @param indexicals A map from terms in the query (as loaded from the KB) to the actual values
* that should be queried with.
*
* @return a QueryImpl object defined by id
* @throws com.cyc.query.exception.QueryConstructionException
* @throws com.cyc.kb.exception.KbException
*
* @deprecated Use {@link #load(KBIndividual,Map)}
*/
@Deprecated
static QueryImpl loadCycObjectMap(KbIndividual id, Map indexicals) throws UnsupportedCycOperationException, QueryConstructionException, KbException {
QueryImpl q = (QueryImpl) load(id);
replaceIndexicalsWithCycObject(q, indexicals);
return q;
}
// Private
@Deprecated
private static void replaceIndexicalsWithCycObject(QueryImpl q,
Map indexicals) {
try {
FormulaSentence cfs = q.getQuerySentenceCyc().treeSubstitute(
getAccess(), indexicals);
q.setQuerySentence(cfs);
q.substituteTermsWithCycObject(indexicals);
} catch (SessionConfigurationException | SessionCommunicationException | SessionInitializationException | CycApiException | CycConnectionException e) {
throw new QueryRuntimeException("Exception thrown during indexical replacement", e);
}
}
private static void replaceIndexicals(QueryImpl q, Map indexicals) {
Map kboToCoMap = KbUtils.convertKBObjectMapToCoObjectMap(indexicals);
replaceIndexicalsWithCycObject(q, kboToCoMap);
}
private static void replaceIndexicals(Map indexicals, QueryImpl q) {
Map idx = new HashMap<>();
for (Map.Entry kv : indexicals.entrySet()) {
//this.indexicals.put(cyc.getKnownFortByName(kv.getKey()), CycObjectFactory.makeCycVariable(kv.getValue()));
idx.put((CycObject) getCycObject(kv.getKey()), getCycObject(kv.getValue()));
}
replaceIndexicalsWithCycObject(q, idx);
}
private static Object getCycObject(String str) {
if (str.startsWith("?")) {
return CycObjectFactory.makeCycVariable(str);
} else if (str.startsWith(":")) {
return CycObjectFactory.makeCycSymbol(str);
} else {
try {
return getAccess().getLookupTool().getKnownFortByName(getAccess().cyclifyString(
str));
} catch (SessionConfigurationException | SessionCommunicationException | SessionInitializationException | CycConnectionException | CycApiException e) {
return str;
}
}
}
private static CycAccess getAccess() throws SessionConfigurationException, SessionCommunicationException, SessionInitializationException {
return CycAccessManager.getCurrentAccess();
}
private static ContextImpl constructContext(String ctxStr) throws QueryConstructionException {
try {
return ContextImpl.get(ctxStr);
} catch (KbTypeException | CreateException ex) {
throw new QueryConstructionException(ex);
}
}
private static DefaultInferenceParameters constructParams(String queryParams) throws QueryConstructionException {
try {
return queryParams.isEmpty() ? null : new DefaultInferenceParameters(
getAccess(), queryParams);
} catch (SessionConfigurationException | SessionCommunicationException | SessionInitializationException ex) {
throw new QueryConstructionException(ex);
}
}
private static CycFormulaSentence constructSentence(String queryStr) throws QueryConstructionException {
try {
return CyclParserUtil.parseCycLSentence(queryStr, true,
getAccess());
} catch (SessionConfigurationException | SessionCommunicationException | SessionInitializationException | ParseException | CycConnectionException | CycApiException | InvalidConstantNameException | InvalidConstantGuidException | UnsupportedVocabularyException | IOException ex) {
throw new QueryConstructionException(ex);
}
}
private static ContextImpl constructContext(CycObject ctx) throws QueryConstructionException {
try {
return ContextImpl.get(ctx);
} catch (KbTypeException | CreateException ex) {
throw new QueryConstructionException(ex);
}
}
private static KbIndividualImpl constructQueryIdTerm(final DenotationalTerm id)
throws QueryConstructionException {
try {
return KbIndividualImpl.get(id.cyclify());
} catch (KbTypeException | CreateException ex) {
throw new QueryConstructionException(ex);
}
}
/* == Constructors ========================================================================== */
/**
* Returns a new query with the specified context, sentence, and parameters.
*
* @param sent The CycL sentence to be queried
* @param ctx the Context in which to run the query
* @param params
* @throws com.cyc.query.exception.QueryConstructionException
*/
@Deprecated
private QueryImpl(FormulaSentence sent, Context ctx, InferenceParameters params) throws QueryConstructionException {
try {
this.session = CycSessionManager.getCurrentSession();
this.cyc = CycAccessManager.getAccessManager().fromSession(session);
this.ctx = ctx;
this.originalQuerySentence = sent;
this.querySentence = sent.deepCopy();
if (params == null) {
this.params = getDefaultInferenceParameters();
} else {
this.params = params;
}
UNCLOSED_QUERIES.add(this);
} catch (SessionConfigurationException | SessionCommunicationException | SessionInitializationException ex) {
throw new QueryConstructionException(ex);
}
}
/**
* constructs a Query working with the string queryStr.
*
* The query is executed in InferencePSC with a default timeout and default inference parameters.
*
* @param queryStr the string representing the CycL query
* @see com.cyc.query.Query#TIMEOUT
*
* @throws QueryConstructionException
*/
public QueryImpl(String queryStr) throws QueryConstructionException {
this(constructSentence(queryStr), Constants.inferencePSCMt());
}
/**
* Returns a query object defined by queryStr asked in Microtheory ctxStr, with default inference
* parameters.
*
* @param queryStr The query string.
* @param ctxStr The Microtheory where the query is asked.
*
* @throws QueryConstructionException
*
*/
public QueryImpl(String queryStr, String ctxStr) throws QueryConstructionException {
this(queryStr, ctxStr, "");
}
/**
* Returns a query object defined by queryStr asked in Microtheory ctxStr, with inference
* parameters, queryParams.
*
* @param queryStr The query string.
* @param ctxStr The Microtheory where the query is asked.
* @param queryParams The inference parameters to use for the query. This string should consist of
* a series of keywords followed by the values for those keywords. The keywords can be found by
* looking for the #$sublIdentifier for the desired instance of InferenceParameter in the Cyc KB.
* For example, to limit a query to single-depth transformation and to allow at most 5 seconds per
* query, use the string ":max-transformation-depth 1 :max-time 5".
*
* @throws QueryConstructionException
*
*/
public QueryImpl(String queryStr, String ctxStr, String queryParams) throws QueryConstructionException {
this(constructSentence(queryStr), constructContext(ctxStr), constructParams(queryParams));
}
/**
* Construct a new Query with the specified sentence and context.
*
* @param sent
* @param ctx
* @throws QueryConstructionException
*/
public QueryImpl(Sentence sent, ElMt ctx) throws QueryConstructionException {
this((FormulaSentence) sent.getCore(), ctx);
}
/**
*
* @param sent
* @param ctx
* @param params
* @throws com.cyc.query.exception.QueryConstructionException
*/
public QueryImpl(Sentence sent, Context ctx, InferenceParameters params) throws QueryConstructionException {
this((FormulaSentence) sent.getCore(), ctx, params);
}
/**
*
* @param sent
* @param ctx
* @throws QueryConstructionException
*/
public QueryImpl(Sentence sent, Context ctx) throws QueryConstructionException {
this((FormulaSentence) sent.getCore(), ctx);
}
/**
* Construct a new Query with the specified sentence, context, and inference parameters.
*
* @param sent
* @param ctx
* @param params
* @throws QueryConstructionException
*/
public QueryImpl(Sentence sent, ElMt ctx, InferenceParameters params) throws QueryConstructionException {
this((FormulaSentence) sent.getCore(), ctx, params);
}
/**
* Returns a new query with the specified context and sentence, and default parameters.
*
* @param sent The CycL sentence to be queried
* @param ctx the Context in which to run the query
*
* @throws QueryConstructionException
*
*/
@Deprecated
private QueryImpl(FormulaSentence sent, Context ctx) throws QueryConstructionException {
this(sent, ctx, null);
}
/**
* Returns a new query with the specified context, sentence, and parameters.
*
* @param sent The CycL sentence to be queried
* @param ctx the Context in which to run the query
* @param params
* @throws QueryConstructionException
*
* @deprecated Use {@link #Query(FormulaSentence,Context,InferenceParameters)}
*
*/
@Deprecated
private QueryImpl(FormulaSentence sent, ElMt ctx, InferenceParameters params) throws QueryConstructionException {
this(sent, constructContext(ctx), params);
}
/**
* Returns a new query with the specified context and sentence, and default parameters.
*
* @param sent The CycL sentence to be queried
* @param ctx the Context in which to run the query
*
* @throws QueryConstructionException
* @deprecated Use {@link #Query(FormulaSentence,Context)}
*
*/
@Deprecated
private QueryImpl(FormulaSentence sent, ElMt ctx) throws QueryConstructionException {
this(sent, constructContext(ctx));
}
/* == Public methods ======================================================================== */
/**
* Run this query and return the results.
*
* @return the results of running the query.
* @throws QueryRuntimeException if an exception is thrown during inference.
*
*/
@Override
public com.cyc.query.QueryResultSet performInference() throws QueryRuntimeException {
try {
return inference.performInference();
} catch (Exception e) {
throw new QueryRuntimeException("Exception thrown during inference", e);
}
}
/**
* Get the Cyc term that defines this query. To change the Id of an existing query, see
* {@link #saveAs(String)}
*
* @return the id term.
*/
@Override
public KbIndividual getId() {
return id;
}
/**
* Set the Cyc term that underlies this query in the KB. Note that setting a new ID will not be
* reflected immediately in the KB. Instead, the change will only be reflected in the KB when the
* query is saved. Setting a new Id on a query is similar to calling {@link #saveAs(String)}, in
* that neither method will change the original QueryImpl specification in the KB.
*
* In general, this method should be avoided. QueryImpl
is saved.
*
* @param id
*/
@Deprecated
void setId(KbIndividual id) {
this.id = id;
}
/**
* Ensure that any required Semantic Knowledge Source removal modules for this query have been
* registered on the Cyc Server and made available for inference.
*
* This should be done prior to running a query or set of queries that relies on real-time access
* to external knowledge sources.
*
* Required knowledge sources are noted in the KB using the predicate sksiModulesNeeded.
*/
@Override
public void registerRequiredSksModules() {
try {
getCycAccess().converse().converseVoid(
"(ensure-sksi-modules-needed " + getId().stringApiValue() + ")");
} catch (CycConnectionException | CycApiException e) {
throw new QueryRuntimeException("Exception thrown during SKS module registration", e);
}
}
/**
* Saves this QueryImpl as the term which is its current ID.
*
* @see Query#saveAs(String)
* @see Query#getId()
*/
@Override
public void save() {
final String fn = KbConfiguration.getShouldTranscriptOperations()
? "update-kbq-definition" : "update-kbq-definition-silent";
try {
final DenotationalTerm idCycTerm = (DenotationalTerm) getCycAccess().getLookupTool().getKnownFortByName(
getId().stringApiValue());
final String command = SublApiHelper.makeSubLStmt(fn, idCycTerm,
getQuerySentenceCyc(), ContextImpl.asELMt(getContext()), params);
getCycAccess().converse().converseVoid(command);
} catch (CycConnectionException | CycApiException e) {
throw new QueryRuntimeException("Exception thrown while saving query", e);
}
}
/**
* Saves this QueryImpl as a new query term with the specified name.
*
* @param name The name by which to save the query.
*
* @return the new term
* @throws com.cyc.kb.exception.KbException
* @throws com.cyc.query.exception.QueryConstructionException
* @throws com.cyc.session.exception.SessionCommunicationException
* @see Query#save()
*/
@Override
public KbIndividual saveAs(String name) throws KbException, SessionCommunicationException,
QueryConstructionException {
if (KbTermImpl.existsAsType(name)) {
throw new CreateException("The name " + name + " is already used.");
}
KbIndividual newID = KbIndividualImpl.findOrCreate(name);
newID.instantiates(QueryConstants.getInstance().CYCL_QUERY_SPECIFICATION, Constants.uvMt());
setId(newID);
save();
return newID;
}
/**
* Returns the categories to which this query belongs. Categories are associated with queries via
* {@link #addCategory(com.cyc.query.Query.Category)}.
*
* @return the categories to which this query belongs.
*/
@Override
public Collection getCategories() {
return Collections.unmodifiableCollection(categories);
}
/**
* Add a new category to which this query belongs.
*
* @param category
*/
@Override
public void addCategory(String category) {
categories.add(category);
}
/**
* Get the inference identifier for this query.
*
* @return the identifier, or null if inference has not been started.
* @throws com.cyc.session.exception.SessionCommunicationException if there is a problem
* communicating with Cyc.
*/
@Override
public InferenceIdentifier getInferenceIdentifier() throws SessionCommunicationException {
return inference.getInferenceIdentifier();
}
/**
* Get the metrics values for this QueryImpl.
*
* @throws com.cyc.session.exception.SessionCommunicationException if there is a problem
* communicating with Cyc.
* @return the metrics values.
*/
@Override
public InferenceMetricsValues getMetricsValues() throws SessionCommunicationException {
try {
return InferenceMetricsValuesImpl.fromInference(getInferenceIdentifier());
} catch (CycConnectionException ex) {
throw new SessionCommunicationException(ex);
}
}
/**
* Return the inference parameters for this query.
*
* @return the inference parameters.
*/
@Override
public synchronized InferenceParameters getInferenceParameters() {
return params;
}
/**
* Adds a listener to this query.
*
* @param listener
* @return this query.
*/
@Override
public QueryImpl addListener(QueryListener listener) {
listeners.add(listener);
return this;
}
/**
* Designates var as a variable to return bindings for.
*
* @param var
* @return this query.
* @throws IllegalArgumentException if var is not found in this query.
* @throws IllegalStateException if query has already been started.
*/
@Override
public QueryImpl addQueryVariable(Variable var) {
QueryImpl q = addQueryVariable((CycVariable) var.getCore());
return q;
}
/**
* Bind a query variable to a specified value. All occurrences of the variable in this query's
* sentence will be replaced with the specified value.
*
* @param var must be a query variable in this query.
* @param replacement the value to substitute for var.
*/
@Override
public void bindVariable(Variable var, Object replacement) {
bindVariable((CycVariable) var.getCore(), replacement);
}
/**
* Bind a query variable to a specified value.
*
* @param varName The name of the variable, with or without the '?' prefix.
* @param replacement
*/
@Override
public void bindVariable(String varName, Object replacement) {
bindVariable(CycObjectFactory.makeCycVariable(varName), replacement);
}
/**
* Designates var as a variable to not return bindings for.
*
* @param var
* @return this QueryImpl
* @throws IllegalArgumentException if var is not found in this query.
* @throws IllegalStateException if query has already been started.
*/
@Override
public QueryImpl removeQueryVariable(Variable var) {
QueryImpl q = removeQueryVariable((CycVariable) var.getCore());
return q;
}
@Override
public Set getQueryVariables() throws KbException {
return new HashSet(this.getQuerySentence().getVariables(true));
}
/**
* Continues the query. Can be used if a query has not been started yet, has stopped due to
* reaching the specified number of answers, or has used its alloted time or other resources and
* is continuable.
*
* Any resource constraints, e.g. time or number, get to "start over," so if the inference has
* already used its alloted 5 seconds, or found the specified three answers, continuing it will
* allow it to run for up to
* another
* 5 seconds, or until it finds up to another three answers.
*
* Returns when the inference has stopped running.
*
* @see #setMaxAnswerCount(Integer)
* @see #setMaxTime(Integer)
* @see #isContinuable()
* @see #setContinuable(boolean)
*
* @see InferenceWorker#continueInference(com.cyc.base.inference.InferenceParameters)
*/
@Override
public void continueQuery() {
try {
inference.continueInference();
} catch (Exception e) {
throw new QueryRuntimeException("Exception thrown while continuing query", e);
}
}
/**
* Identifies redundant clauses in this query.
*
* For instance, if one clause isa (isa ?X Dog) and another is (isa ?X GreatDane), that pair is
* considered redundant. This method provides no guidance as to what can or should be done to
* resolve the redundancy, and in fact it may be virtually harmless, as Cyc can often solve such a
* query almost as efficiently as it can solve the more specific clause of the pair.
*
* @return a collection of pairs of any such clauses
* @throws KbException
* @throws com.cyc.session.exception.SessionCommunicationException if there is a problem
* communicating with Cyc
* @throws com.cyc.session.exception.OpenCycUnsupportedFeatureException when run against an
* OpenCyc server
*/
@Override
public Collection> findRedundantClauses() throws KbException,
SessionCommunicationException, OpenCycUnsupportedFeatureException {
try {
QUERY_COMPARISON_REQUIREMENTS.throwRuntimeExceptionIfIncompatible();
Collection> cycClauseCollections = findRedundantClausesCFS();
Collection> clauses = new HashSet<>();
Collection clauseCollections = new HashSet<>();
for (Collection c : cycClauseCollections) {
for (FormulaSentence s : c) {
clauseCollections.add(new SentenceImpl(s));
}
clauses.add(clauseCollections);
}
return clauses;
} catch (CycConnectionException ex) {
throw new SessionCommunicationException(ex);
}
}
/**
* Identifies unconnected clauses in this query. Generally, all clauses of a query will be
* connected by a chain of variables that connect them together. Queries with unconnected clauses
* are effectively separate queries, and running queries with disconnected clauses generally
* results in a cartesian product of the answer sets of the two separate queries.
*
* @return a collection of the arg positions of any such clauses
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
* @throws com.cyc.session.exception.OpenCycUnsupportedFeatureException when run against an
* OpenCyc server
*/
@Override
public Collection findUnconnectedClauses() throws SessionCommunicationException,
OpenCycUnsupportedFeatureException {
try {
QUERY_COMPARISON_REQUIREMENTS.throwRuntimeExceptionIfIncompatible();
final Set argPositions = new HashSet<>();
for (final Object obj : getCycAccess().converse().converseList(SublApiHelper.makeSubLStmt(
"find-unconnected-literals", querySentence))) {
argPositions.add(new ArgPositionImpl((List) obj));
}
return argPositions;
} catch (CycConnectionException | CycApiException ex) {
throw new SessionCommunicationException(ex);
}
}
/**
* Conjoin this sentence with otherQuery, attempting to unify and rename variables. Typically, two
* different variables will unify into a single variable, causing all the uses of one of the
* variables to be renamed with the name of the other. In some cases, additional renaming may
* happen (e.g. if the queries contain mnemonic variables that become more tightly constrained as
* a result of the unification, a new mnemonic variable may be used in place of both of the
* original variables).
*
* @param otherQuery
* @return the new query
* @throws QueryConstructionException if there was a problem constructing the new query.
* @throws com.cyc.session.exception.SessionCommunicationException
* @throws com.cyc.session.exception.OpenCycUnsupportedFeatureException when run against an
* OpenCyc server
*/
@Override
public Query merge(Query otherQuery) throws QueryConstructionException,
SessionCommunicationException,
OpenCycUnsupportedFeatureException {
try {
QUERY_COMPARISON_REQUIREMENTS.throwRuntimeExceptionIfIncompatible();
QueryImpl otherQueryImpl = (QueryImpl) otherQuery;
final String command = SublApiHelper.makeSubLStmt("combine-queries",
querySentence, ContextImpl.asELMt(ctx), params, otherQueryImpl.querySentence,
ContextImpl.asELMt(otherQueryImpl.ctx), otherQueryImpl.params);
final CycList newStuff = getCycAccess().converse().converseList(command);
final Object paramsObj = newStuff.third();
final List paramsList = CycObjectFactory.nil.equals(paramsObj) ? Collections.emptyList() : (List) paramsObj;
final List sentenceAsList = (List) newStuff.first();
return new QueryImpl(
new CycFormulaSentence(sentenceAsList),
constructContext((CycObject) newStuff.second()),
DefaultInferenceParameters.fromPlist(getCycAccess(), paramsList));
} catch (CycConnectionException | CycApiException ex) {
throw new SessionCommunicationException(ex);
}
}
/**
* Set the inference mode for this QueryImpl.
*
* @param mode
* @return this QueryImpl
*
* @see
* com.cyc.base.inference.InferenceParameters#setInferenceMode(com.cyc.base.inference.InferenceMode)
*/
@Override
public QueryImpl setInferenceMode(InferenceMode mode) {
getInferenceParameters().setInferenceMode(mode);
return this;
}
/**
* Check whether this QueryImpl is continuable. Queries that have not yet been run are considered
* continuable, as well as ones whose parameters have the continuable flag set to
* true
.
*
* @see InferenceParameters#setContinuable(boolean)
* @see #continueQuery()
*
* @return true iff it can be continued.
*/
@Override
public boolean isContinuable() {
return !inference.hasBeenStarted
|| (getInferenceParameters().isContinuable() != null
&& getInferenceParameters().isContinuable());
}
/**
* Set the maximum number of answers (or sets of answers) that Cyc will attempt to find for this
* QueryImpl. In some cases (such as when a set of answers is retrieved in a batch), more answers
* than this may actually be returned. Once this number of answers has been reached, Cyc will not
* actively look for additional answers.
*
* @param max
* @return this QueryImpl
*
* @see com.cyc.base.inference.InferenceParameters#setMaxNumber(Integer)
*/
@Override
public QueryImpl setMaxAnswerCount(Integer max) {
getInferenceParameters().setMaxAnswerCount(max);
return this;
}
/**
* Set the Context of this QueryImpl.
*
* @param ctx
* @return this object.
*/
@Override
public QueryImpl setContext(final Context ctx) {
this.ctx = ctx;
return this;
}
/**
* Set the soft timeout for this QueryImpl in seconds.
*
* @param max
* @return this QueryImpl
*
* @see com.cyc.base.inference.InferenceParameters#setMaxTime(Integer)
*/
@Override
public QueryImpl setMaxTime(Integer max) {
getInferenceParameters().setMaxTime(max);
return this;
}
@Override
public Integer getMaxTransformationDepth() {
return getInferenceParameters().getMaxTransformationDepth();
}
@Override
public QueryImpl setMaxTransformationDepth(Integer i) {
getInferenceParameters().setMaxTransformationDepth(i);
return this;
}
/**
* Set the inference parameters for this query.
*
* @param params the inference parameters
* @return this QueryImpl object.
*/
@Override
public QueryImpl setInferenceParameters(final InferenceParameters params) {
if (params != null) {
this.params = params;
} else {
this.params = getDefaultInferenceParameters();
}
return this;
}
/**
* Sets the hypothesized clause of this QueryImpl. When the query is run, Cyc will assume that
* this clause is true. If the clause is independently known to be false in the query context, the
* query will be considered tautologous, and will fail.
*
* @param sentence
* @return this QueryImpl.
* @see Query#getQuerySentenceHypothesizedClause()
* @throws IllegalStateException if query has already been started.
*/
@Override
public QueryImpl setQuerySentenceHypothesizedClause(Sentence sentence) {
QueryImpl q = setQuerySentenceHypothesizedClause((CycFormulaSentence) sentence.getCore());
return q;
}
/**
* Sets the main (i.e. non-hypothesized) clause of this QueryImpl
*
* @param sentence
* @return this QueryImpl.
* @see Query#getQuerySentenceMainClause()
* @throws IllegalStateException if query has already been started.
*/
@Override
public QueryImpl setQuerySentenceMainClause(Sentence sentence) {
QueryImpl q = setQuerySentenceMainClause((CycFormulaSentence) sentence.getCore());
return q;
}
/**
* Designates vars as the variables to return bindings for.
*
* @param vars
* @return this query.
* @throws IllegalArgumentException if any of vars is not found in this query.
* @throws IllegalStateException if query has already been started.
*/
@Override
public QueryImpl setQueryVariables(Collection vars) {
Collection cycvars = new HashSet<>();
for (Variable v : vars) {
cycvars.add((CycVariable) v.getCore());
}
QueryImpl q = setQueryVariablesCyc(cycvars);
return q;
}
@Override
public Query setSubstitutions(Map substitutions) {
this.substitutions = Collections.unmodifiableMap(substitutions);
final Map kboToCoMap = KbUtils.convertKBObjectMapToCoObjectMap(this.substitutions);
this.querySentence = this.originalQuerySentence.deepCopy();
this.querySentence.applySubstitutionsDestructive(kboToCoMap);
return this;
}
@Override
public Query addSubstitutions(Map addedSubstitutions) {
final Map newSubs = new LinkedHashMap();
if (this.substitutions instanceof Map) {
newSubs.putAll(this.substitutions);
}
if (addedSubstitutions instanceof Map) {
newSubs.putAll(addedSubstitutions);
}
return setSubstitutions(newSubs);
}
/**
* Starts the query.
*
*
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Override
public void start() throws SessionCommunicationException {
try {
inference.start();
} catch (IOException | CycConnectionException e) {
throw new SessionCommunicationException(e);
}
}
/**
* Issues a request that the query stop immediately.
*
* @param patience If non-null, the query will be forcibly aborted if it does not stop before this
* many seconds have elapsed.
*/
@Override
public void stop(final Integer patience) {
inference.stop(patience);
}
/**
* Get the Cyc session to be used for this query.
*
* @return a CycSession for this query.
*/
@Override
public final CycSession getCycSession() {
return this.session;
}
/**
* Specify that this inference should be retained by Cyc until the QueryImpl is closed. This can
* be called before the query has been started, and must be called before the query has finished
* running.
*
* @see Query#close()
*/
@Override
public void retainInference() {
this.retainInference = true;
}
/**
* Returns the number of answers found for this query. For running queries, the value returned by
* this method may change as additional answers are found.
*
* @return the number of answers found for this query.
*/
@Override
public int getAnswerCount() {
if (!getQueryVariablesCyc().isEmpty()) {
return getResultSet().getCurrentRowCount();
} else if (isTrue()) {
return 1;
} else {
return 0;
}
}
/**
* Returns the list of answers for this query. For running queries, the value returned by this
* method may change as additional answers are found.
*
* @return the list of answers
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Override
public QueryAnswers getAnswers() throws SessionCommunicationException {
final QueryAnswers localAnswers = new QueryAnswersImpl<>(answers);
for (int i = localAnswers.size(); i < getAnswerCount(); i++) {
localAnswers.add(getAnswer(i));
}
return localAnswers;
}
@Override
public QueryAnswers getAnswers(Paraphraser paraphraser) throws SessionCommunicationException {
final QueryAnswers localAnswers = new QueryAnswersImpl<>();
for (int i = 0; i < getAnswerCount(); i++) {
localAnswers.add(getAnswer(i, paraphraser));
}
return localAnswers;
}
/**
* Returns the nth answer for this query. For the first answer, n == 0.
*
* @param n
* @return the answer.
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Override
public QueryAnswer getAnswer(final int n) throws SessionCommunicationException {
if (answers.size() > n) {
return answers.get(n);
}
final InferenceAnswer answerCyc = getAnswerCyc(n);
return (answerCyc == null) ? null : new InferenceAnswerBackedQueryAnswer(answerCyc) {
//Inner class is used so pointer to parent QueryImpl is maintained.
// That prevents the QueryImpl from being closed and the inference from being destroyed
// so long as this answer stays around.
};
}
/**
* Returns the nth answer for this query. For the first answer, n == 0.
*
* @param n
* @return the answer.
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Override
public ParaphrasedQueryAnswer getAnswer(final int n, final Paraphraser paraphraser) throws SessionCommunicationException {
if (answers.size() > n
&& answers.get(n) instanceof ParaphrasedQueryAnswer
&& ((ParaphrasedQueryAnswer) answers.get(n)).getParaphraser().equals(paraphraser)) {
return (ParaphrasedQueryAnswer) answers.get(n);
}
final InferenceAnswer answerCyc = getAnswerCyc(n);
if (answerCyc == null) {
return null;
}
ParaphrasedQueryAnswer answer = new InferenceAnswerBackedQueryAnswer(answerCyc, paraphraser) {
//Inner class is used so pointer to parent QueryImpl is maintained.
// That prevents the QueryImpl from being closed and the inference from being destroyed
// so long as this answer stays around.
};
//make sure the list is big enough.
for (int index = answers.size(); index < n; index++) {
answers.add(index, null);
}
answers.add(n, answer);
return answer;
}
/**
* Returns the Context of this QueryImpl.
*
* @return the Context of this QueryImpl
*/
@Override
public Context getContext() {
return ctx;
}
/**
*
* @return @throws KbException
*/
@Override
public Sentence getQuerySentence() throws KbException {
Sentence s = new SentenceImpl(getQuerySentenceCyc());
return s;
}
/**
*
* @param querySentence
*/
@Override
public QueryImpl setQuerySentence(Sentence querySentence) {
setQuerySentence((FormulaSentence) querySentence.getCore());
return this;
}
/**
*
* @return @throws KbException
*/
@Override
public Sentence getQuerySentenceMainClause() throws KbException {
Sentence s = new SentenceImpl(getQuerySentenceMainClauseCyc());
return s;
}
/**
*
* @return @throws KbException
*/
@Override
public Sentence getQuerySentenceHypothesizedClause() throws KbException {
Sentence s = new SentenceImpl(getQuerySentenceHypothesizedClauseCyc());
return s;
}
@Override
public Map getSubstitutions() {
return substitutions;
}
@Override
public Sentence getOriginalQuerySentence() throws KbException {
return new SentenceImpl(this.originalQuerySentence);
}
/**
* Get the CycL sentence from the specified answer to this query. Substitutes the set of bindings
* from answer into the query sentence.
*
* @param answer
* @return the answer sentence
* @throws KbException
*/
@Override
public Sentence getAnswerSentence(QueryAnswer answer) throws KbException {
Sentence sentence = getQuerySentenceMainClause();
final List from = new ArrayList<>();
final List to = new ArrayList<>();
for (final Variable var : getQueryVariables()) {
from.add(var);
to.add(answer.getBinding(var));
}
return sentence.replaceTerms(from, to);
}
/**
* Forget all results for this query. All settings on the QueryImpl are retained, including the
* query sentence, context, and inference parameters. After a QueryImpl has been cleared, it can
* be re-run, with possibly different results.
*
* @return this QueryImpl
*/
@Override
public QueryImpl clearResults() {
inference.clear();
return this;
}
/**
* Returns the soft timeout for this QueryImpl in seconds.
*
* @return the soft timeout for this QueryImpl in seconds.
*
* @see com.cyc.base.inference.InferenceParameters#getMaxTime()
*/
@Override
public Integer getMaxTime() {
if (params == null) {
return null;
} else {
return params.getMaxTime();
}
}
/**
* Returns the maximum number of answers (or sets of answers) that Cyc will attempt to find for
* this QueryImpl. In some cases (such as when a set of answers is retrieved in a batch), more
* answers than this may actually be returned. Once this number of answers has been reached, Cyc
* will not actively look for additional answers.
*
* @return the number cutoff for this QueryImpl.
*
* @see com.cyc.base.inference.InferenceParameters#getMaxNumber()
*/
@Override
public Integer getMaxAnswerCount() {
if (params == null) {
return null;
} else {
return params.getMaxAnswerCount();
}
}
/**
* Returns the inference mode for this QueryImpl.
*
* @return the inference mode for this QueryImpl.
*
* @see com.cyc.base.inference.InferenceParameters#getInferenceMode()
*/
@Override
public InferenceMode getInferenceMode() {
if (params == null) {
return null;
} else {
return params.getInferenceMode();
}
}
/**
* Return the InferenceStatus for this QueryImpl.
*
* @return the InferenceStatus for this QueryImpl.
*/
@Override
public InferenceStatus getStatus() {
return this.inference.inferenceStatus;
}
/**
* Return the reason why this QueryImpl was suspended (if it was).
*
* @return the reason, or null if this QueryImpl was not suspended.
* @see DefaultInferenceSuspendReasonforexamples.
*/
@Override
public InferenceSuspendReason getSuspendReason() {
return this.inference.getSuspendReason();
}
/**
*
* @return true iff this query has been proven true.
* @throws RuntimeException if the query has open variables.
* @see com.cyc.base.inference.InferenceResultSet#getTruthValue()
*/
@Override
public boolean isTrue() {
return getResultSet().getTruthValue();
}
/**
* Is this query either True (if a boolean query) or does it have bindings (if non-boolean)
*
* @return True if there are bindings (or it's a true boolean query), false if there are no
* bindings (or it's a false boolean query).
*
*/
@Override
public boolean isProvable() {
return getAnswerCount() > 0;
}
/**
* Closes this query's result set, and releases resources on the Cyc server. See
* {@link com.cyc.query.KBInferenceResultSet#close()} for more details on what happens when a
* query is closed.
*
* It is good practice to always invoke this method explicitly as soon as a QueryImpl is no longer
* needed, as they can take up significant amounts of memory on the Cyc server.
*
* @see com.cyc.query.KBInferenceResultSet#close()
*/
@Override
public void close() {
inference.close();
UNCLOSED_QUERIES.remove(this);
}
/**
* Returns the timeout for the {@link #close()} method.
*
* @return timeout in milliseconds
*/
@Override
public long getCloseTimeout() {
return inference.closeTimeoutMS;
}
/**
* Sets the timeout for the {@link #close()} method.
*
* @param timeoutMS timeout in milliseconds
*/
@Override
public void setCloseTimeout(long timeoutMS) {
inference.closeTimeoutMS = timeoutMS;
}
/**
*
* @return this query's result set. The QueryResultSetImpl is an object that may be updated
* dynamically for running queries. This contrasts with {@link #getAnswersCyc()} which returns a
* static list of the answers at the time is was called.
*
* @see com.cyc.query.QueryResultSetImpl
*/
@Override
public synchronized QueryResultSet getResultSet() {
try {
return inference.getResultSet();
} catch (Exception e) {
throw new QueryRuntimeException("Exception thrown while getting result set", e);
}
}
@Override
public Set getUnresolvedIndexicals() throws KbException, SessionCommunicationException {
return new HashSet(getQuerySentence().getIndexicals());
}
@Override
public QueryRulesImpl getRules() throws QueryConstructionException, KbException {
if (this.rules == null) {
this.rules = new QueryRulesImpl(this);
}
return this.rules;
}
@Override
public String toString() {
return "Query: " + querySentence;
}
@Override
public int hashCode() {
int hash = 1;
hash = hash * 17 + (ctx == null ? 0 : ctx.hashCode());
hash = hash * 31 + (params == null ? 0 : params.hashCode());
hash = hash * 13 + (querySentence == null ? 0 : querySentence.hashCode());
return hash;
}
@Override
public boolean equals(Object o) {
if ((o == null) || !(o instanceof QueryImpl)) {
return false;
}
return this.hashCode() == o.hashCode();
}
/**
* Do not use this value directly; instead, call {@link QueryImpl#getQueryWorkerTimeoutMillis()}.
*
* @return the number of milliseconds that a QueryWorker will wait before throwing an exception if
* no soft timeout is specified in the query's inference parameters.
*/
public long getDefaultQueryWorkerTimeoutMillis() {
return this.defaultQueryWorkerTimeoutMillis;
}
public void setDefaultQueryWorkerTimeoutMillis(long queryWorkerTimeoutMillis) {
this.defaultQueryWorkerTimeoutMillis = queryWorkerTimeoutMillis;
}
/**
* Returns the number of milliseconds that a QueryWorker will wait, once started, before throwing
* an exception. A value of 0 milliseconds means to wait forever.
*
*
* If a soft timeout is specified in the inference parameters, the QueryWorker's hard timeout will
* be twice that duration to allow for additional overhead on top of the time required for the
* actual inference. If no soft timeout is specified in the inference parameters, this method will
* return the value of {@link QueryImpl#getDefaultQueryWorkerTimeoutMillis()}.
*
* @return the number of milliseconds that a QueryWorker will wait before throwing an exception.
*/
public long getQueryWorkerTimeoutMillis() {
if (getInferenceParameters() != null) {
final Integer maxTime = getInferenceParameters().getMaxTime();
if (maxTime != null) {
return maxTime * 2 * 1000;
}
}
return getDefaultQueryWorkerTimeoutMillis();
}
String getSublCommand() {
// TODO: revise & javadoc - nwinant, 2016-03-11
CycList list = inference.getSublCommand();
System.out.println("===========================================");
System.out.println();
System.out.println(list.toPrettyString(" "));
System.out.println();
System.out.println("===========================================");
return "" + list;
}
/* == Protected & package-private methods =================================================== */
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
/**
* Get the Cyc image to be used for this query.
*
* @return a CycAccess for the Cyc image.
*/
protected final CycAccess getCycAccess() {
return this.cyc;
}
/**
* Returns the nth answer for this query. For the first answer, n == 0.
*
* @param n
* @return the answer.
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Deprecated
InferenceAnswer getAnswerCyc(final int n) throws SessionCommunicationException {
if (n >= getAnswerCount()) {
throw new IllegalArgumentException("Can't get answer " + n
+ ". Query has only " + getAnswerCount() + " answers.");
}
final InferenceIdentifier inferenceIdentifier = getInferenceIdentifier();
if (inferenceIdentifier != null) {
return new CycBackedInferenceAnswer(
new SpecifiedInferenceAnswerIdentifier(inferenceIdentifier, n));
} else if (getResultSet() != null) {
final QueryResultSetImpl resultSet = (QueryResultSetImpl) getResultSet();
return new ResultSetInferenceAnswer((DefaultResultSet) resultSet.getInferenceResultSet(), n);
} else {
return null;
}
}
/**
* Get the CycL sentence of this query.
*
* @return the query sentence
*/
@Deprecated
FormulaSentence getQuerySentenceCyc() {
return querySentence;
}
/**
* Returns the main (that is, non-hypothesized) clause of this QueryImpl. All valid queries have a
* main sentence clause.
*
* @return the main (i.e. non-hypothesized) clause of this query.
*/
@Deprecated
FormulaSentence getQuerySentenceMainClauseCyc() {
if (querySentence.isConditionalSentence()) {
return (FormulaSentence) querySentence.getArg2();
} else {
return querySentence;
}
}
/**
* Returns the hypothesized clause of this QueryImpl, if any. Most queries have no hypothesized
* clause, in which case this method will return null.
*
* @return the hypothesized clause of this query, if any, or null if there is none.
*/
@Deprecated
FormulaSentence getQuerySentenceHypothesizedClauseCyc() {
if (querySentence.isConditionalSentence()) {
return (FormulaSentence) querySentence.getArg1();
} else {
return null;
}
}
/* == Private methods ======================================================================= */
private boolean requiresInferenceWorker() {
final InferenceParameters inferenceParameters = getInferenceParameters();
//result-sort-ordering requires that all answers be returned at once instead of dribbling in as they do with async queries.
//@todo add requiresSynchronousQuery (which might check Cyc to see which params require synchronicity. @daves 2017-03-30
if (inferenceParameters.containsKey(RESULT_SORT_ORDER)) {
return false;
} else if (inferenceParameters.containsKey(RETURN)) {
return true;
} else if (Boolean.TRUE.equals(inferenceParameters.isContinuable())) {
return true;
} else if (Boolean.TRUE.equals(inferenceParameters.isBrowsable())) {
return true;
} else if (!listeners.isEmpty()) {
return true;
} else if (retainInference == true) {
return true;
} else {
return CycObjectFactory.t.equals(inferenceParameters.get(COMPUTE_ANSWER_JUSTIFICATIONS));
}
}
private QueryImpl addQueryVariable(CycVariable var) {
if (!querySentence.treeContains(var)) {
throw new IllegalArgumentException(
var + " is not contained in " + querySentence);
}
if (inference.hasBeenStarted) {
throw new IllegalStateException("Query has already been started.");
}
if (!getQueryVariablesCyc().contains(var)) {
querySentence.existentiallyUnbind(var);
}
return this;
}
private QueryImpl removeQueryVariable(CycVariable var) {
if (!querySentence.treeContains(var)) {
throw new IllegalArgumentException(
var + " is not contained in " + querySentence);
}
if (inference.hasBeenStarted) {
throw new IllegalStateException("Query has already been started.");
}
if (getQueryVariablesCyc().contains(var)) {
querySentence.existentiallyBind(var);
}
return this;
}
private List getQueryVariablesCyc() {
return querySentence.findQueryableVariables();
}
/**
* Designates vars as the variables to return bindings for.
*
* @param vars
* @return this query.
* @throws IllegalArgumentException if any of vars is not found in this query.
* @throws IllegalStateException if query has already been started.
*/
private QueryImpl setQueryVariablesCyc(Collection vars) {
for (final CycVariable var : getQueryVariablesCyc()) {
removeQueryVariable(var);
}
for (final CycVariable var : vars) {
addQueryVariable(var);
}
return this;
}
private void bindVariable(CycVariable var, Object replacement) {
if (!getQueryVariablesCyc().contains(var)) {
throw new IllegalArgumentException(
var + " is not a query variable in " + getQuerySentenceCyc());
}
if (replacement instanceof KbObject) {
replacement = ((KbObject) replacement).getCore();
}
getQuerySentenceCyc().substituteDestructive(var, replacement);
}
private DefaultInferenceParameters getDefaultInferenceParameters() {
return new DefaultInferenceParameters(getCycAccess(), true);
}
/**
* Substitute specified terms for specified indexicals in the query sentence.
*
* @param indexicals - a map from indexicals to their replacements.
*/
@Deprecated
private void substituteTermsWithCycObject(Map indexicals) {
querySentence.applySubstitutionsDestructive(indexicals);
}
/**
* Identifies redundant clauses in this query.
*
* For instance, if one clause isa (isa ?X Dog) and another is (isa ?X GreatDane), that pair is
* considered redundant. This method provides no guidance as to what can or should be done to
* resolve the redundancy, and in fact it may be virtually harmless, as Cyc can often solve such a
* query almost as efficiently as it can solve the more specific clause of the pair.
*
* @return a collection of pairs of any such clauses
* @throws com.cyc.base.exception.CycConnectionException
*/
@Deprecated
private Collection> findRedundantClausesCFS()
throws CycConnectionException {
final Set> clausePairs = new HashSet<>();
for (final Object obj : getCycAccess().converse().converseList(SublApiHelper.makeSubLStmt(
"find-redundant-literals",
querySentence, ContextImpl.asELMt(ctx)))) {
final CycList dottedPair = (CycList) obj;
final FormulaSentence sentence1 = new CycFormulaSentence(
(CycList) dottedPair.first());
final FormulaSentence sentence2 = new CycFormulaSentence(
(CycList) dottedPair.rest());
clausePairs.add(Arrays.asList(sentence1, sentence2));
}
return clausePairs;
}
/**
* Sets the hypothesized clause of this QueryImpl. When the query is run, Cyc will assume that
* this clause is true. If the clause is independently known to be false in the query context, the
* query will be considered tautologous, and will fail.
*
* @param sentence
* @return this QueryImpl.
* @see Query#getQuerySentenceHypothesizedClause()
* @throws IllegalStateException if query has already been started.
*/
@Deprecated
private QueryImpl setQuerySentenceHypothesizedClause(
CycFormulaSentence sentence) {
if (querySentence.isConditionalSentence()) {
querySentence.setSpecifiedObject(ArgPositionImpl.ARG1, sentence);
} else {
querySentence = CycFormulaSentence.makeConditional(sentence, querySentence);
}
return this;
}
/**
* Sets the main (i.e. non-hypothesized) clause of this QueryImpl
*
* @param sentence
* @return this QueryImpl.
* @see Query#getQuerySentenceMainClause()
* @throws IllegalStateException if query has already been started.
*/
@Deprecated
private QueryImpl setQuerySentenceMainClause(
CycFormulaSentence sentence) {
if (querySentence.isConditionalSentence()) {
querySentence.setSpecifiedObject(ArgPositionImpl.ARG2, sentence);
} else {
querySentence = sentence;
}
return this;
}
/**
* Get the CycL sentence from the specified answer to this query. Substitutes the set of bindings
* from answer into the query sentence.
*
* @param answer
* @throws CycConnectionException if there is a problem communicating with Cyc.
* @return the answer sentence
*/
@Deprecated
private FormulaSentence getAnswerSentenceCyc(InferenceAnswer answer)
throws CycConnectionException {
final FormulaSentence sentence = getQuerySentenceMainClauseCyc().deepCopy();
for (final CycVariable var : getQueryVariablesCyc()) {
sentence.substituteDestructive(var, answer.getBinding(var));
}
return sentence;
}
/**
* Returns the list of answers for this query. For running queries, the value returned by this
* method may change as additional answers are found.
*
* @return the list of answers
* @throws SessionCommunicationException if there is a problem communicating with Cyc.
*/
@Deprecated
private List getAnswersCyc() throws SessionCommunicationException {
final List localAnswers = new ArrayList<>(
getAnswerCount());
for (int i = 0; i < getAnswerCount(); i++) {
localAnswers.add(getAnswerCyc(i));
}
return localAnswers;
}
/**
* Set the CycL sentence of this query.
*
* @param querySentence
*/
@Deprecated
private void setQuerySentence(FormulaSentence querySentence) {
this.querySentence = querySentence;
}
}