package org.commcare.tasks;
import android.util.Pair;
import org.commcare.activities.EntitySelectActivity;
import org.commcare.android.logging.ForceCloseLogger;
import org.commcare.cases.entity.Entity;
import org.commcare.cases.entity.NodeEntityFactory;
import org.commcare.logging.AndroidLogger;
import org.commcare.logging.XPathErrorLogger;
import org.commcare.models.AsyncNodeEntityFactory;
import org.commcare.preferences.DeveloperPreferences;
import org.commcare.suite.model.Detail;
import org.commcare.tasks.templates.ManagedAsyncTask;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.services.Logger;
import org.javarosa.xpath.XPathException;
import java.util.ArrayList;
import java.util.List;
/**
* @author ctsims
*/
public class EntityLoaderTask
extends ManagedAsyncTask<TreeReference, Integer, Pair<List<Entity<TreeReference>>, List<TreeReference>>> {
private final static Object lock = new Object();
private static EntityLoaderTask pendingTask = null;
private final NodeEntityFactory factory;
private EntityLoaderListener listener;
private Exception mException = null;
private int focusTargetIndex;
public EntityLoaderTask(Detail detail, EvaluationContext evalCtx) {
evalCtx.addFunctionHandler(EntitySelectActivity.getHereFunctionHandler());
if (detail.useAsyncStrategy()) {
this.factory = new AsyncNodeEntityFactory(detail, evalCtx);
} else {
this.factory = new NodeEntityFactory(detail, evalCtx);
if(DeveloperPreferences.collectAndDisplayEntityTrances()) {
this.factory.activateDebugTraceOutput();
}
}
}
@Override
protected Pair<List<Entity<TreeReference>>, List<TreeReference>> doInBackground(TreeReference... nodeset) {
try {
List<TreeReference> references = factory.expandReferenceList(nodeset[0]);
List<Entity<TreeReference>> full = new ArrayList<>();
focusTargetIndex = -1;
int indexInFullList = 0;
for (TreeReference ref : references) {
if (this.isCancelled()) {
return null;
}
Entity<TreeReference> e = factory.getEntity(ref);
if (e != null) {
full.add(e);
if (e.shouldReceiveFocus()) {
focusTargetIndex = indexInFullList;
}
indexInFullList++;
}
}
factory.prepareEntities();
factory.printAndClearTraces("build");
return new Pair<>(full, references);
} catch (XPathException xe) {
XPathErrorLogger.INSTANCE.logErrorToCurrentApp(xe);
XPathException me = new XPathException("Encountered an xpath error while trying to load and filter the list.");
me.setSource(xe.getSource());
xe.printStackTrace();
Logger.log(AndroidLogger.TYPE_ERROR_DESIGN, ForceCloseLogger.getStackTrace(me));
mException = me;
return null;
}
}
@Override
protected void onPostExecute(Pair<List<Entity<TreeReference>>, List<TreeReference>> result) {
super.onPostExecute(result);
long waitingTime = System.currentTimeMillis();
//Ok. So. time to try to deliver the result
while (true) {
synchronized (lock) {
if (listener != null) {
pendingTask = null;
if (mException != null) {
listener.deliverLoadError(mException);
return;
}
listener.deliverLoadResult(result.first, result.second, factory, focusTargetIndex);
return;
}
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// If this is pending for more than about a second, drop it, we
// never know if it's going to get reattached
if (System.currentTimeMillis() - waitingTime > 1000) {
pendingTask = null;
return;
}
}
}
public void detachActivity() {
synchronized (lock) {
pendingTask = this;
}
}
public static boolean attachToActivity(EntityLoaderListener listener) {
synchronized (lock) {
if (pendingTask == null) {
return false;
}
EntityLoaderTask task = pendingTask;
task.attachListener(listener);
pendingTask = null;
return true;
}
}
public void attachListener(EntityLoaderListener listener) {
this.listener = listener;
listener.attachLoader(this);
}
}