23
23
24
24
import org .exist .dom .QName ;
25
25
import org .exist .xquery .util .ExpressionDumper ;
26
+ import org .exist .xquery .value .IntegerValue ;
26
27
import org .exist .xquery .value .Item ;
27
28
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 ;
28
32
29
33
/**
30
34
* Implements a count clause inside a FLWOR expressions.
34
38
*/
35
39
public class CountClause extends AbstractFLWORClause {
36
40
41
+ private static final SequenceType countVarType = new SequenceType (Type .INTEGER , Cardinality .EXACTLY_ONE );
42
+
37
43
final QName varName ;
38
44
45
+ // the count itself
46
+ private long count = 0 ;
47
+ private int step = 1 ;
48
+
39
49
public CountClause (final XQueryContext context , final QName varName ) {
40
50
super (context );
41
51
this .varName = varName ;
@@ -52,12 +62,131 @@ public QName getVarName() {
52
62
53
63
@ Override
54
64
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
+ }
55
103
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 ;
56
126
}
57
127
58
128
@ Override
59
129
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 );
61
190
}
62
191
63
192
@ Override
@@ -67,4 +196,9 @@ public void dump(final ExpressionDumper dumper) {
67
196
dumper .display (this .varName );
68
197
dumper .endIndent ().nl ();
69
198
}
199
+
200
+ @ Override
201
+ public void accept (final ExpressionVisitor visitor ) {
202
+ visitor .visitCountClause (this );
203
+ }
70
204
}
0 commit comments