Skip to content

Commit ad6a38b

Browse files
committed
[feature] Implementation of count clause
1 parent 2b4e691 commit ad6a38b

File tree

4 files changed

+382
-1
lines changed

4 files changed

+382
-1
lines changed

exist-core/src/main/java/org/exist/xquery/BasicExpressionVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ public void visitOrderByClause(final OrderByClause orderBy) {
180180
// Nothing to do
181181
}
182182

183+
@Override
184+
public void visitCountClause(final CountClause count) {
185+
// Nothing to do
186+
}
187+
183188
@Override
184189
public void visitGroupByClause(final GroupByClause groupBy) {
185190
// Nothing to do

exist-core/src/main/java/org/exist/xquery/CountClause.java

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323

2424
import org.exist.dom.QName;
2525
import org.exist.xquery.util.ExpressionDumper;
26+
import org.exist.xquery.value.IntegerValue;
2627
import org.exist.xquery.value.Item;
2728
import org.exist.xquery.value.Sequence;
29+
import org.exist.xquery.value.SequenceType;
30+
import org.exist.xquery.value.Type;
31+
import org.exist.xquery.value.ValueSequence;
2832

2933
/**
3034
* Implements a count clause inside a FLWOR expressions.
@@ -34,8 +38,14 @@
3438
*/
3539
public class CountClause extends AbstractFLWORClause {
3640

41+
private static final SequenceType countVarType = new SequenceType(Type.INTEGER, Cardinality.EXACTLY_ONE);
42+
3743
final QName varName;
3844

45+
// the count itself
46+
private long count = 0;
47+
private int step = 1;
48+
3949
public CountClause(final XQueryContext context, final QName varName) {
4050
super(context);
4151
this.varName = varName;
@@ -52,12 +62,131 @@ public QName getVarName() {
5262

5363
@Override
5464
public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException {
65+
contextInfo.setParent(this);
66+
unordered = (contextInfo.getFlags() & UNORDERED) > 0;
67+
68+
// Save the local variable stack
69+
final LocalVariable mark = context.markLocalVariables(false);
70+
try {
71+
final AnalyzeContextInfo varContextInfo = new AnalyzeContextInfo(contextInfo);
72+
73+
// Declare the count variable
74+
final LocalVariable countVar = new LocalVariable(varName);
75+
countVar.setSequenceType(countVarType);
76+
countVar.setStaticType(varContextInfo.getStaticReturnType());
77+
context.declareVariableBinding(countVar);
78+
79+
// analyze the return expression
80+
final AnalyzeContextInfo newContextInfo = new AnalyzeContextInfo(contextInfo);
81+
returnExpr.analyze(newContextInfo);
82+
83+
} finally {
84+
// restore the local variable stack
85+
context.popLocalVariables(mark);
86+
}
87+
}
88+
89+
@Override
90+
public Sequence preEval(final Sequence seq) throws XPathException {
91+
// determine whether to count down or up
92+
this.step = hasPreviousOrderByDescending() ? -1 : 1;
93+
94+
// get the count start position
95+
if (this.step == 1) {
96+
this.count = 0;
97+
} else {
98+
this.count = seq.getItemCountLong() + 1;
99+
}
100+
101+
return super.preEval(seq);
102+
}
55103

104+
private boolean hasPreviousOrderByDescending() {
105+
FLWORClause prev = getPreviousClause();
106+
while (prev != null) {
107+
switch (prev.getType()) {
108+
case LET:
109+
case GROUPBY:
110+
case FOR:
111+
return false;
112+
case ORDERBY:
113+
return isDescending(((OrderByClause) prev).getOrderSpecs());
114+
}
115+
prev = prev.getPreviousClause();
116+
}
117+
return true;
118+
}
119+
private boolean isDescending(final OrderSpec[] orderSpecs) {
120+
for (final OrderSpec orderSpec : orderSpecs) {
121+
if ((orderSpec.getModifiers() & OrderSpec.DESCENDING_ORDER) == OrderSpec.DESCENDING_ORDER) {
122+
return true;
123+
}
124+
}
125+
return false;
56126
}
57127

58128
@Override
59129
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
60-
return null;
130+
if (context.getProfiler().isEnabled()) {
131+
context.getProfiler().start(this);
132+
context.getProfiler().message(this, Profiler.DEPENDENCIES,
133+
"DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
134+
if (contextSequence != null) {
135+
context.getProfiler().message(this, Profiler.START_SEQUENCES,
136+
"CONTEXT SEQUENCE", contextSequence);
137+
}
138+
if (contextItem != null) {
139+
context.getProfiler().message(this, Profiler.START_SEQUENCES,
140+
"CONTEXT ITEM", contextItem.toSequence());
141+
}
142+
}
143+
144+
context.expressionStart(this);
145+
146+
final Sequence resultSequence = new ValueSequence(unordered);
147+
148+
// update the count
149+
count = count + step;
150+
151+
// Save the local variable stack
152+
final LocalVariable mark = context.markLocalVariables(false);
153+
try {
154+
155+
// Declare the count variable
156+
final LocalVariable countVar = createVariable(varName);
157+
countVar.setSequenceType(countVarType);
158+
context.declareVariableBinding(countVar);
159+
160+
// set the binding for the count
161+
countVar.setValue(new IntegerValue(count));
162+
163+
// eval the return expression on the window binding
164+
resultSequence.addAll(returnExpr.eval(null, null));
165+
166+
// free resources
167+
countVar.destroy(context, resultSequence);
168+
169+
} finally {
170+
// restore the local variable stack
171+
context.popLocalVariables(mark, resultSequence);
172+
}
173+
174+
setActualReturnType(resultSequence.getItemType());
175+
176+
context.expressionEnd(this);
177+
if (context.getProfiler().isEnabled()) {
178+
context.getProfiler().end(this, "", resultSequence);
179+
}
180+
181+
return resultSequence;
182+
}
183+
184+
@Override
185+
public Sequence postEval(Sequence seq) throws XPathException {
186+
if (returnExpr instanceof FLWORClause) {
187+
seq = ((FLWORClause)returnExpr).postEval(seq);
188+
}
189+
return super.postEval(seq);
61190
}
62191

63192
@Override
@@ -67,4 +196,9 @@ public void dump(final ExpressionDumper dumper) {
67196
dumper.display(this.varName);
68197
dumper.endIndent().nl();
69198
}
199+
200+
@Override
201+
public void accept(final ExpressionVisitor visitor) {
202+
visitor.visitCountClause(this);
203+
}
70204
}

exist-core/src/main/java/org/exist/xquery/ExpressionVisitor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public interface ExpressionVisitor {
7474

7575
void visitLetExpression(LetExpr letExpr);
7676

77+
void visitCountClause(CountClause count);
78+
7779
void visitOrderByClause(OrderByClause orderBy);
7880

7981
void visitGroupByClause(GroupByClause groupBy);

0 commit comments

Comments
 (0)