iot.jcypher.domainquery.internal.QueryRecorder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcypher Show documentation
Show all versions of jcypher Show documentation
Provides seamlessly integrated Java access to graph databases (Neo4J)
at different levels of abstraction.
The newest version!
/************************************************************************
* Copyright (c) 2015-2016 IoT-Solutions e.U.
*
* 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 iot.jcypher.domainquery.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import iot.jcypher.domainquery.AbstractDomainQuery;
import iot.jcypher.domainquery.GDomainQuery;
import iot.jcypher.domainquery.InternalAccess;
import iot.jcypher.domainquery.api.APIAccess;
import iot.jcypher.domainquery.api.DomainObjectMatch;
import iot.jcypher.domainquery.internal.RecordedQuery.Invocation;
import iot.jcypher.domainquery.internal.RecordedQuery.Statement;
import iot.jcypher.query.values.MathFunctions;
import iot.jcypher.query.values.ValueAccess;
import iot.jcypher.query.values.ValueElement;
public class QueryRecorder {
public static final String QUERY_ID = "q";
private static ThreadLocal queriesPerThread =
new ThreadLocal();
public static ThreadLocal blockRecording =
new ThreadLocal() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};
/**
* invocations on domainQuery (q)
* but stacked within e.g. a SELECT_FROM(...).ELEMENTS(...) statement.
* Must not be directly added to the root RecordedQuery
* but must be encapsulated in a sub statement
* @param on
* @param method
* @param result
* @param params
*/
public static void recordStackedInvocation(Object on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
recordInvocation(false, true, false, on, method, result, params);
}
/**
* invocations on domainQuery (q)
* but stacked within e.g. a SELECT_FROM(...).ELEMENTS(...) statement.
* Must not be directly added to the root RecordedQuery
* but must be encapsulated in a sub statement
* @param on
* @param method
* @param result
* @param params
*/
public static void recordStackedAssignment(Object on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
recordInvocation(true, true, false, on, method, result, params);
}
public static void recordInvocation(Object on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
recordInvocation(false, false, false, on, method, result, params);
}
public static void recordInvocationNoConcat(Object on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
recordInvocation(false, false, true, on, method, result, params);
}
public static void recordAssignment(Object on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
recordInvocation(true, false, false, on, method, result, params);
}
private static void recordInvocation(boolean assign, boolean subRoot, boolean noConcat, Object on, String method,
Object result, Object... params) {
// subRoot true means invocations on domainQuery (q)
// but stacked within e.g. a SELECT_FROM(...).ELEMENTS(...) statement.
// Must not be directly added to the root RecordedQuery
// but must be encapsulated in a sub statement
QueriesPerThread qpt = getCreateQueriesPerThread();
RecQueryHolder rqh = null;
if (noConcat) {
rqh = createRecQueryHolder();
} else {
if (on instanceof AbstractDomainQuery)
rqh = getRecQueryHolder((AbstractDomainQuery)on);
else {
rqh = qpt.getHolderRef(on);
if (rqh == null)
rqh = createRecQueryHolder();
}
}
if (subRoot && rqh.root) {
// the parameters have already been adopted to the root
rqh.stackLastNStatements(assign, params == null ? 0 : params.length, on, method, result);
} else {
List parameters = new ArrayList(params.length);
for (int i = 0; i < params.length; i++) {
Object param = params[i];
if (param instanceof Literal)
parameters.add(rqh.recordedQuery.literal(((Literal)param).value));
else if (param instanceof PlaceHolder) {
Object val = ((PlaceHolder)param).value;
if (val instanceof DomainObjectMatch>) {
String oid = rqh.object2IdMap.get(val);
parameters.add(rqh.recordedQuery.doMatchRef(oid));
} else {
RecQueryHolder trqh = qpt.getHolderRef(val);
if (trqh != null) {
List adopted = adoptStatements(trqh, rqh);
parameters.addAll(adopted);
qpt.removeHolderRef(val);
} else { // assume it is a literal (or a parameter)
parameters.add(rqh.recordedQuery.literal(val));
}
}
} else if (param instanceof Reference) {
Object val = ((Reference)param).value;
parameters.add(rqh.recordedQuery.reference(val, rqh.getNextRefId()));
}
}
rqh.recordInvocation(assign, on, method, result, parameters);
qpt.putHolderRef(result, rqh);
if (rqh.root) {
qpt.removeFromQuery2HolderMap(rqh, null, false, null); // don't remove root
}
}
return;
}
private static List adoptStatements(RecQueryHolder from,
RecQueryHolder to) {
List params = from.recordedQuery.getStatements();
for (Statement s : params) {
adoptStatement(from, to, s);
}
return params;
}
private static void adoptStatement(RecQueryHolder from,
RecQueryHolder to, Statement s) {
if (s instanceof Invocation) {
String or = ((RecordedQuery.Invocation)s).getOnObjectRef();
Object o = from.id2ObjectMap.get(or);
String id = to.object2IdMap.get(o);
if (id == null) {
id = to.getNextId();
to.object2IdMap.put(o, id);
to.id2ObjectMap.put(id, o);
}
((RecordedQuery.Invocation) s).setOnObjectRef(id);
or = ((RecordedQuery.Invocation)s).getReturnObjectRef();
o = from.id2ObjectMap.get(or);
id = to.object2IdMap.get(o);
if (id == null) {
id = to.getNextId();
to.object2IdMap.put(o, id);
to.id2ObjectMap.put(id, o);
}
((RecordedQuery.Invocation) s).setReturnObjectRef(id);
QueriesPerThread qpt = getCreateQueriesPerThread();
List params = ((RecordedQuery.Invocation) s).getParams();
for (Statement stmt : params) {
RecQueryHolder nextFrom = qpt.getHolderForQuery(stmt.getRecordedQuery());
if (nextFrom != null)
adoptStatement(nextFrom, to, stmt);
}
}
to.addAdopted(from);
}
public static void recordInvocationConditional(ValueElement on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
QueriesPerThread qpt = queriesPerThread.get();
if (qpt != null && !qpt.isEmpty()) {
Object dom = ValueAccess.getAnyHint(on, APIAccess.hintKey_dom);
if (dom != null)
recordInvocation(on, method, result, params);
}
}
public static void recordInvocationConditional(MathFunctions on, String method, Object result, Object... params) {
if (blockRecording.get())
return;
QueriesPerThread qpt = queriesPerThread.get();
if (qpt != null && !qpt.isEmpty()) {
Object dom = ValueAccess.getAnyHint(ValueAccess.getArgument(on), APIAccess.hintKey_dom);
if (dom != null)
recordInvocation(on, method, result, params);
}
}
public static void recordInvocationReplace(DomainObjectMatch> on, Object toReplace,
String methodName) {
if (blockRecording.get())
return;
QueriesPerThread qpt = getCreateQueriesPerThread();
RecQueryHolder trqh = qpt.getHolderRef(toReplace);
RecQueryHolder rqh = qpt.getHolderRef(on);
if (trqh != null) {
List stmts = trqh.recordedQuery.getStatements();
for (Statement stmt : stmts) {
if (stmt instanceof Invocation) {
String onId = trqh.object2IdMap.get(on);
if (onId == null) {
onId = trqh.getNextId();
trqh.object2IdMap.put(on, onId);
trqh.id2ObjectMap.put(onId, on);
}
((RecordedQuery.Invocation)stmt).setOnObjectRef(onId);
((RecordedQuery.Invocation)stmt).setMethod(methodName);
}
}
//qpt.removeHolderRef(toReplace);
if (rqh == null)
qpt.putHolderRef(on, trqh);
else
rqh.addReplaced(trqh);
}
}
public static void recordCreateQuery(AbstractDomainQuery query) {
if (blockRecording.get())
return;
RecordedQuery rq = new RecordedQuery(query instanceof GDomainQuery);
RecQueryHolder rqh = new RecQueryHolder(rq);
rqh.root = true;
getCreateQueriesPerThread().put(query, rqh);
}
public static void queryCompleted(AbstractDomainQuery query) {
if (blockRecording.get())
return;
QueryExecutor qe = InternalAccess.getQueryExecutor((AbstractDomainQuery) query);
boolean done = qe.queryCreationCompleted(false);
if (!done) {
QueriesPerThread qpt = queriesPerThread.get();
if (qpt != null) {
qpt.queryCompleted(query);
}
}
}
public static RecordedQuery getRecordedQuery(AbstractDomainQuery query) {
if (blockRecording.get())
return null;
RecQueryHolder rqh = getRecQueryHolder(query);
if (rqh != null)
return rqh.recordedQuery;
return null;
}
public static Literal literal(Object value) {
return new Literal(value);
}
public static PlaceHolder placeHolder(Object value) {
return new PlaceHolder(value);
}
public static Reference reference(Object value) {
return new Reference(value);
}
private static RecQueryHolder getRecQueryHolder(AbstractDomainQuery on) {
QueriesPerThread qpt = getCreateQueriesPerThread();
return qpt.get(on);
}
private static RecQueryHolder createRecQueryHolder() {
RecQueryHolder rqh = new RecQueryHolder(new RecordedQuery(false));
return rqh;
}
public static QueriesPerThread getCreateQueriesPerThread() {
QueriesPerThread qpt = queriesPerThread.get();
if (qpt == null) {
qpt = new QueriesPerThread();
queriesPerThread.set(qpt);
}
return qpt;
}
public static QueriesPerThread getQueriesPerThread() {
return queriesPerThread.get();
}
/*******************************/
public static class QueriesPerThread {
private Map queries;
private Map