Skip to content

Commit

Permalink
Merge pull request #727 from seadowg/extra-expressions
Browse files Browse the repository at this point in the history
Support optimizations for more expressions
  • Loading branch information
lognaturel committed Dec 5, 2023
2 parents bee6652 + 204ff22 commit 349239d
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 183 deletions.

This file was deleted.

119 changes: 119 additions & 0 deletions src/main/java/org/javarosa/core/model/CompareToNodeExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.javarosa.core.model;

import kotlin.Pair;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.DataInstance;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.xpath.expr.XPathCmpExpr;
import org.javarosa.xpath.expr.XPathEqExpr;
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathFuncExpr;
import org.javarosa.xpath.expr.XPathNumericLiteral;
import org.javarosa.xpath.expr.XPathPathExpr;
import org.javarosa.xpath.expr.XPathStringLiteral;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**
* Convenience class for identifying and dealing with expressions that compare a child node to something else in the
* form like:
* <p/>
* name = /data/search
* <p/>
* In the example above, "name" would be the "node side" (a relative expression) and "/data/search" would be the
* "context side" (an absolute or context expression). These expressions are useful to be able to identify for caching
* evaluations as nodes with the same node side evaluation and context side expression will ultimately evaluate the same
* result.
* <p/>
* This class does not support expressions comparing two relative expressions as there would be two nodes sides.
*/
public class CompareToNodeExpression {

private final XPathPathExpr nodeSide;
private final XPathExpression contextSide;
private final XPathExpression original;

public CompareToNodeExpression(XPathPathExpr nodeSide, XPathExpression contextSide, XPathExpression original) {
this.nodeSide = nodeSide;
this.contextSide = contextSide;
this.original = original;
}

public Object evalNodeSide(DataInstance sourceInstance, EvaluationContext evaluationContext, TreeReference child, int childIndex) {
EvaluationContext rescopedContext = evaluationContext.rescope(child, childIndex);
return getNodeSide().eval(sourceInstance, rescopedContext).unpack();
}

public Object evalContextSide(DataInstance sourceInstance, EvaluationContext evaluationContext) {
if (contextSide instanceof XPathPathExpr) {
return ((XPathPathExpr) getContextSide()).eval(sourceInstance, evaluationContext).unpack();
} else {
return contextSide.eval(sourceInstance, evaluationContext);
}
}

public XPathPathExpr getNodeSide() {
return nodeSide;
}

public XPathExpression getContextSide() {
return contextSide;
}

public XPathExpression getOriginal() {
return original;
}

@Nullable
public static CompareToNodeExpression parse(XPathExpression expression) {
XPathExpression a = null;
XPathExpression b = null;

if (expression instanceof XPathCmpExpr) {
a = ((XPathCmpExpr) expression).a;
b = ((XPathCmpExpr) expression).b;
} else if (expression instanceof XPathEqExpr) {
a = ((XPathEqExpr) expression).a;
b = ((XPathEqExpr) expression).b;
} else if (expression instanceof XPathFuncExpr && expression.isIdempotent()
&& ((XPathFuncExpr) expression).args.length == 2) {
a = ((XPathFuncExpr) expression).args[0];
b = ((XPathFuncExpr) expression).args[1];
}

Pair<XPathPathExpr, XPathExpression> nodeAndContextSides = getNodeAndContextSides(a, b);
if (nodeAndContextSides != null) {
return new CompareToNodeExpression(nodeAndContextSides.getFirst(), nodeAndContextSides.getSecond(), expression);
} else {
return null;
}
}

private static Pair<XPathPathExpr, XPathExpression> getNodeAndContextSides(XPathExpression a, XPathExpression b) {
XPathPathExpr node = null;
XPathExpression context = null;

Queue<XPathExpression> subExpressions = new LinkedList<>(Arrays.asList(a, b));
while (!subExpressions.isEmpty()) {
XPathExpression subExpression = subExpressions.poll();
if (subExpression instanceof XPathPathExpr) {
if (((XPathPathExpr) subExpression).init_context == XPathPathExpr.INIT_CONTEXT_RELATIVE)
node = (XPathPathExpr) subExpression;
else {
context = subExpression;
}
} else if (subExpression instanceof XPathNumericLiteral || subExpression instanceof XPathStringLiteral) {
context = subExpression;
}
}

if (node != null && context != null) {
return new Pair<>(node, context);
} else {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
return next.get();
}

CompareChildToAbsoluteExpression candidate = CompareChildToAbsoluteExpression.parse(predicate);
CompareToNodeExpression candidate = CompareToNodeExpression.parse(predicate);
if (candidate != null) {
Object absoluteValue = candidate.evalAbsolute(sourceInstance, evaluationContext);
String key = nodeSet.toString() + predicate + candidate.getRelativeSide() + absoluteValue.toString();
Object absoluteValue = candidate.evalContextSide(sourceInstance, evaluationContext);
String key = nodeSet.toString() + predicate + candidate.getNodeSide() + absoluteValue.toString();

if (cachedEvaluations.containsKey(key)) {
return cachedEvaluations.get(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
return next.get();
}

CompareChildToAbsoluteExpression candidate = CompareChildToAbsoluteExpression.parse(predicate);
CompareToNodeExpression candidate = CompareToNodeExpression.parse(predicate);
if (candidate != null) {
XPathEqExpr original = (XPathEqExpr) candidate.getOriginal();
if (original.isEqual()) {
String section = nodeSet + candidate.getRelativeSide().toString();
String section = nodeSet + candidate.getNodeSide().toString();
if (!index.contains(section)) {
buildIndex(sourceInstance, candidate, children, evaluationContext, section);
}

Object absoluteValue = candidate.evalAbsolute(sourceInstance, evaluationContext);
Object absoluteValue = candidate.evalContextSide(sourceInstance, evaluationContext);
return index.lookup(section, absoluteValue.toString());
} else {
return next.get();
Expand All @@ -52,12 +52,12 @@ public List<TreeReference> filter(@NotNull DataInstance sourceInstance, @NotNull
}
}

private void buildIndex(DataInstance sourceInstance, CompareChildToAbsoluteExpression predicate, List<TreeReference> children, EvaluationContext evaluationContext, String section) {
private void buildIndex(DataInstance sourceInstance, CompareToNodeExpression predicate, List<TreeReference> children, EvaluationContext evaluationContext, String section) {
for (int i = 0; i < children.size(); i++) {
TreeReference child = children.get(i);

Measure.log("IndexEvaluation");
String relativeValue = predicate.evalRelative(sourceInstance, evaluationContext, child, i).toString();
String relativeValue = predicate.evalNodeSide(sourceInstance, evaluationContext, child, i).toString();
index.add(section, relativeValue, child);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/javarosa/xpath/expr/XPathFuncExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ public boolean containsFunc(@NotNull String name) {
return name.equals(id.name) || Arrays.stream(args).anyMatch(expression -> expression.containsFunc(name));
}

private static final String[] IDEMPOTENT_FUNCTIONS = new String[]{
public static final String[] IDEMPOTENT_FUNCTIONS = new String[]{
"regex",
"starts-with",
"ends-with",
Expand Down

This file was deleted.

Loading

0 comments on commit 349239d

Please sign in to comment.