Skip to content

Commit

Permalink
Merge pull request #1403 from dimagi/master_update
Browse files Browse the repository at this point in the history
Duplicates Formplayer PRs for Master
  • Loading branch information
shubham1g5 authored Apr 12, 2024
2 parents cfe3f9b + 0c7656a commit 6063ea0
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 39 deletions.
36 changes: 35 additions & 1 deletion src/cli/java/org/commcare/util/screen/EntityScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,45 @@ protected EvaluationContext getAutoLaunchEvaluationContext(String nextInput) {

@Trace
public void init(SessionWrapper session) throws CommCareSessionException {
if (initReferences(session)) {
initListSubScreen();
}
}

/**
* Initialises EntityListSubscreen for current entity screen
* Should only be called after initReferences
* @throws CommCareSessionException
*/
public void initListSubScreen() throws CommCareSessionException {
if (!initialized) {
throw new CommCareSessionException("trying to initialise subscreen before initialising references");
}

// if readyToSkip, entity screen will not be displayed. We don't need to init the subscreen
if (!readyToSkip) {
// if isDetailScreen or needsFullInit is not set,
// sub screen is needed to handle actions but we can skip eval refs
Vector<TreeReference> entityListReferences =
!needsFullInit || isDetailScreen() ? new Vector<>() : references;
mCurrentScreen = new EntityListSubscreen(mShortDetail, entityListReferences, evalContext,
handleCaseIndex, entityScreenContext);
}
}

/**
* Initialises references and referenceMap for current entity screen
* @param session Current CommCare Session to initialise the screen with
* @throws CommCareSessionException
* @return whether we initialised references as part of this call
*/
public boolean initReferences(SessionWrapper session) throws CommCareSessionException {
if (initialized) {
if (session != this.mSession) {
throw new CommCareSessionException(
"Entity screen initialized with two different session wrappers");
}
return;
return false;
}

this.setSession(session);
Expand Down Expand Up @@ -164,6 +197,7 @@ public void init(SessionWrapper session) throws CommCareSessionException {
handleCaseIndex, entityScreenContext);
}
initialized = true;
return true;
}

protected boolean shouldAutoSelect() {
Expand Down
10 changes: 9 additions & 1 deletion src/cli/java/org/commcare/util/screen/EntityScreenHelper.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.commcare.util.screen;

import org.commcare.cases.entity.AsyncEntity;
import org.commcare.cases.entity.AsyncNodeEntityFactory;
import org.commcare.cases.entity.Entity;
import org.commcare.cases.entity.EntitySortNotificationInterface;
import org.commcare.cases.entity.EntitySorter;
import org.commcare.cases.entity.EntityStorageCache;
import org.commcare.cases.entity.EntityStringFilterer;
import org.commcare.cases.entity.NodeEntityFactory;
import org.commcare.suite.model.Detail;
Expand Down Expand Up @@ -32,7 +35,12 @@ public class EntityScreenHelper {
*/
public static List<Entity<TreeReference>> initEntities(EvaluationContext context, Detail detail,
EntityScreenContext entityScreenContext, TreeReference[] entitiesRefs) {
NodeEntityFactory nodeEntityFactory = new NodeEntityFactory(detail, context);
NodeEntityFactory nodeEntityFactory;
if (detail.isLazyLoading()) {
nodeEntityFactory = new AsyncNodeEntityFactory(detail, context, null);
} else {
nodeEntityFactory = new NodeEntityFactory(detail, context);
}
List<Entity<TreeReference>> entities = new ArrayList<>();
for (TreeReference reference : entitiesRefs) {
entities.add(nodeEntityFactory.getEntity(reference));
Expand Down
108 changes: 79 additions & 29 deletions src/main/java/org/commcare/cases/entity/AsyncEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import org.commcare.cases.util.StringUtils;
import org.commcare.suite.model.DetailField;
import org.commcare.suite.model.DetailGroup;
import org.commcare.suite.model.Text;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
Expand All @@ -17,6 +18,8 @@
import java.util.Enumeration;
import java.util.Hashtable;

import javax.annotation.Nullable;

/**
* An AsyncEntity is an entity reference which is capable of building its
* values (evaluating all Text elements/background data elements) lazily
Expand All @@ -38,12 +41,17 @@ public class AsyncEntity extends Entity<TreeReference> {
private final String[] sortData;
private final boolean[] relevancyData;
private final String[][] sortDataPieces;

private final String[] altTextData;
private final EvaluationContext context;
private final Hashtable<String, XPathExpression> mVariableDeclarations;
private final DetailGroup mDetailGroup;

private boolean mVariableContextLoaded = false;
private final String mCacheIndex;
private final String mDetailId;

@Nullable
private final EntityStorageCache mEntityStorageCache;

/*
Expand All @@ -61,15 +69,15 @@ public class AsyncEntity extends Entity<TreeReference> {

public AsyncEntity(DetailField[] fields, EvaluationContext ec,
TreeReference t, Hashtable<String, XPathExpression> variables,
EntityStorageCache cache, String cacheIndex, String detailId,
String extraKey) {
@Nullable EntityStorageCache cache, String cacheIndex, String detailId,
String extraKey, DetailGroup detailGroup) {
super(t, extraKey);

this.fields = fields;
this.data = new Object[fields.length];
this.sortData = new String[fields.length];
this.sortDataPieces = new String[fields.length][];
this.relevancyData = new boolean[fields.length];
this.altTextData = new String[fields.length];
this.context = ec;
this.mVariableDeclarations = variables;
this.mEntityStorageCache = cache;
Expand All @@ -79,6 +87,7 @@ public AsyncEntity(DetailField[] fields, EvaluationContext ec,
this.mCacheIndex = cacheIndex;

this.mDetailId = detailId;
this.mDetailGroup = detailGroup;
}

private void loadVariableContext() {
Expand Down Expand Up @@ -123,7 +132,7 @@ public String getNormalizedField(int i) {

@Override
public String getSortField(int i) {
try (Closeable ignored = mEntityStorageCache.lockCache()) {
try (Closeable ignored = mEntityStorageCache != null ? mEntityStorageCache.lockCache() : null) {
//get our second lock.
synchronized (mAsyncLock) {
if (sortData[i] == null) {
Expand All @@ -132,35 +141,38 @@ public String getSortField(int i) {
if (sortText == null) {
return null;
}
String cacheKey = null;
if (mEntityStorageCache != null) {
cacheKey = mEntityStorageCache.getCacheKey(mDetailId, String.valueOf(i));
if (mCacheIndex != null) {
//Check the cache!
String value = mEntityStorageCache.retrieveCacheValue(mCacheIndex, cacheKey);
if (value != null) {
this.setSortData(i, value);
return sortData[i];
}

String cacheKey = mEntityStorageCache.getCacheKey(mDetailId, String.valueOf(i));

if (mCacheIndex != null) {
//Check the cache!
String value = mEntityStorageCache.retrieveCacheValue(mCacheIndex, cacheKey);
if (value != null) {
this.setSortData(i, value);
return sortData[i];
}
}

loadVariableContext();
try {
sortText = fields[i].getSort();
if (sortText == null) {
this.setSortData(i, getFieldString(i));
} else {
this.setSortData(i, StringUtils.normalize(sortText.evaluate(context)));
loadVariableContext();
try {
sortText = fields[i].getSort();
if (sortText == null) {
this.setSortData(i, getFieldString(i));
} else {
this.setSortData(i, StringUtils.normalize(sortText.evaluate(context)));
}
if (mEntityStorageCache != null) {
mEntityStorageCache.cache(mCacheIndex, cacheKey, sortData[i]);
}
} catch (XPathException xpe) {
Logger.exception("Error while evaluating sort field", xpe);
xpe.printStackTrace();
sortData[i] = "<invalid xpath: " + xpe.getMessage() + ">";
}
}

mEntityStorageCache.cache(mCacheIndex, cacheKey, sortData[i]);
} catch (XPathException xpe) {
Logger.exception("Error while evaluating sort field", xpe);
xpe.printStackTrace();
sortData[i] = "<invalid xpath: " + xpe.getMessage() + ">";
return sortData[i];
}
return sortData[i];
}
return sortData[i];
}
} catch (IOException e) {
Logger.exception("Error while getting sort field", e);
Expand Down Expand Up @@ -219,6 +231,9 @@ private void setSortData(int i, String val) {
}

public void setSortData(String cacheKey, String val) {
if (mEntityStorageCache == null) {
throw new IllegalStateException("No entity cache defined");
}
int sortIndex = mEntityStorageCache.getSortFieldIdFromCacheKey(mDetailId, cacheKey);
if (sortIndex != -1) {
setSortData(sortIndex, val);
Expand All @@ -234,4 +249,39 @@ private static String[] breakUpField(String input) {
return input.split("\\s+");
}
}

@Nullable
@Override
public String getGroupKey() {
if (mDetailGroup != null) {
return (String)mDetailGroup.getFunction().eval(context);
}
return null;
}

@Nullable
public String getAltTextData(int i) {
synchronized (mAsyncLock) {
loadVariableContext();
Text altText = fields[i].getAltText();
if (altText != null) {
try {
altTextData[i] = altText.evaluate(context);
} catch (XPathException xpe) {
Logger.exception("Error while evaluating field for case list ", xpe);
xpe.printStackTrace();
altTextData[i] = "<invalid xpath: " + xpe.getMessage() + ">";
}
}
return altTextData[i];
}
}

@Override
public String[] getAltText() {
for (int i = 0; i < this.getNumFields(); ++i) {
this.getAltTextData(i);
}
return altTextData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.Hashtable;
import java.util.List;

import javax.annotation.Nullable;
/**
* @author ctsims
*/
Expand All @@ -19,6 +20,7 @@ public class AsyncNodeEntityFactory extends NodeEntityFactory {
private final OrderedHashtable<String, XPathExpression> mVariableDeclarations;

private final Hashtable<String, AsyncEntity> mEntitySet = new Hashtable<>();
@Nullable
private final EntityStorageCache mEntityCache;

private CacheHost mCacheHost = null;
Expand All @@ -29,9 +31,9 @@ public class AsyncNodeEntityFactory extends NodeEntityFactory {
// Don't show entity list until we primeCache and caches all fields
private final boolean isBlockingAsyncMode;

public AsyncNodeEntityFactory(Detail d, EvaluationContext ec, EntityStorageCache entityStorageCache) {
public AsyncNodeEntityFactory(Detail d, EvaluationContext ec,
@Nullable EntityStorageCache entityStorageCache) {
super(d, ec);

mVariableDeclarations = detail.getVariableDeclarations();
mEntityCache = entityStorageCache;
isBlockingAsyncMode = detail.hasSortField();
Expand All @@ -54,7 +56,7 @@ public Entity<TreeReference> getEntity(TreeReference data) {
String entityKey = loadCalloutDataMapKey(nodeContext);
AsyncEntity entity =
new AsyncEntity(detail.getFields(), nodeContext, data, mVariableDeclarations,
mEntityCache, mCacheIndex, detail.getId(), entityKey);
mEntityCache, mCacheIndex, detail.getId(), entityKey, detail.getGroup());

if (mCacheIndex != null) {
mEntitySet.put(mCacheIndex, entity);
Expand All @@ -77,7 +79,7 @@ protected void setEvaluationContextDefaultQuerySet(EvaluationContext ec,
* Note that the cache is lazily built upon first case list search.
*/
private void primeCache() {
if (mTemplateIsCachable == null || !mTemplateIsCachable || mCacheHost == null) {
if (mEntityCache == null || mTemplateIsCachable == null || !mTemplateIsCachable || mCacheHost == null) {
return;
}

Expand Down
Loading

0 comments on commit 6063ea0

Please sign in to comment.