KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > functions > Aggregate


1 package net.sf.saxon.functions;
2 import net.sf.saxon.expr.*;
3 import net.sf.saxon.om.Item;
4 import net.sf.saxon.om.SequenceIterator;
5 import net.sf.saxon.trans.DynamicError;
6 import net.sf.saxon.trans.XPathException;
7 import net.sf.saxon.type.ItemType;
8 import net.sf.saxon.type.Type;
9 import net.sf.saxon.value.*;
10
11 /**
12 * This class implements the sum(), avg(), count() functions,
13 */

14
15 public class Aggregate extends SystemFunction {
16
17     public static final int SUM = 0;
18     public static final int AVG = 1;
19     public static final int COUNT = 4;
20
21     /**
22      * Static analysis: prevent sorting of the argument
23      */

24
25     public void checkArguments(StaticContext env) throws XPathException {
26         super.checkArguments(env);
27         Optimizer opt = env.getConfiguration().getOptimizer();
28         argument[0] = ExpressionTool.unsorted(opt, argument[0], true);
29     }
30
31     /**
32      * Determine the item type of the value returned by the function
33      */

34
35     public ItemType getItemType() {
36         switch (operation) {
37             case COUNT:
38                 return super.getItemType();
39             case SUM: {
40                 //ItemType base = argument[0].getItemType();
41
ItemType base = Atomizer.getAtomizedItemType(argument[0], false);
42                 if (base == Type.UNTYPED_ATOMIC_TYPE) {
43                     base = Type.DOUBLE_TYPE;
44                 }
45                 if (Cardinality.allowsZero(argument[0].getCardinality())) {
46                     if (argument.length == 1) {
47                         return Type.getCommonSuperType(base, Type.INTEGER_TYPE);
48                     } else {
49                         return Type.getCommonSuperType(base, argument[1].getItemType());
50                     }
51                 } else {
52                     return base;
53                 }
54             }
55             case AVG: {
56                 ItemType base = Atomizer.getAtomizedItemType(argument[0], false);
57                 if (base == Type.UNTYPED_ATOMIC_TYPE) {
58                     return Type.DOUBLE_TYPE;
59                 } else if (base.getPrimitiveType() == Type.INTEGER) {
60                     return Type.DECIMAL_TYPE;
61                 } else {
62                     return base;
63                 }
64             }
65             default:
66                 throw new AssertionError JavaDoc("Unknown aggregate operation");
67         }
68     }
69
70
71     /**
72     * Evaluate the function
73     */

74
75     public Item evaluateItem(XPathContext context) throws XPathException {
76         // Note: these functions do not need to sort the underlying sequence,
77
// but they do need to de-duplicate it
78
switch (operation) {
79             case COUNT:
80                 SequenceIterator iter = argument[0].iterate(context);
81                 return new IntegerValue(count(iter));
82             case SUM:
83                 return total(argument[0].iterate(context), context);
84             case AVG:
85                 return average(argument[0].iterate(context), context);
86             default:
87                 throw new UnsupportedOperationException JavaDoc("Unknown aggregate function");
88         }
89     }
90
91     /**
92     * Calculate total
93     */

94
95     private AtomicValue total(SequenceIterator iter, XPathContext context) throws XPathException {
96         AtomicValue sum = (AtomicValue)iter.next();
97         if (sum == null) {
98             // the sequence is empty
99
if (argument.length == 2) {
100                 return (AtomicValue)argument[1].evaluateItem(context);
101             } else {
102                 return IntegerValue.ZERO;
103             }
104         }
105         if (!sum.hasBuiltInType()) {
106             sum = sum.getPrimitiveValue();
107         }
108         if (sum instanceof UntypedAtomicValue) {
109             sum = sum.convert(Type.DOUBLE, context);
110         }
111         if (sum instanceof NumericValue) {
112             while (true) {
113                 AtomicValue nextVal = (AtomicValue)iter.next();
114                 if (nextVal == null) {
115                     return sum;
116                 }
117                 AtomicValue next = nextVal.getPrimitiveValue();
118                 if (next instanceof UntypedAtomicValue) {
119                     next = next.convert(Type.DOUBLE, context);
120                 } else if (!(next instanceof NumericValue)) {
121                     DynamicError err =
122                             new DynamicError("Input to sum() contains a mix of numeric and non-numeric values");
123                     err.setXPathContext(context);
124                     err.setErrorCode("FORG0007");
125                     throw err;
126                 }
127                 sum = ((NumericValue)sum).arithmetic(Token.PLUS, (NumericValue)next, context);
128                 if (((NumericValue)sum).isNaN()) {
129                     // take an early bath, once we've got a NaN it's not going to change
130
return sum;
131                 }
132             }
133         } else if (sum instanceof DurationValue) {
134             while (true) {
135                 AtomicValue nextVal = (AtomicValue)iter.next();
136                 if (nextVal == null) {
137                     return sum;
138                 }
139                 AtomicValue next = nextVal.getPrimitiveValue();
140                 if (!(next instanceof DurationValue)) {
141                     DynamicError err =
142                             new DynamicError("Input to sum() contains a mix of duration and non-duration values");
143                     err.setXPathContext(context);
144                     err.setErrorCode("FORG0007");
145                     throw err;
146                 }
147                 sum = ((DurationValue)sum).add((DurationValue)next, context);
148             }
149         } else {
150             DynamicError err =
151                     new DynamicError("Input to sum() contains a value that is neither numeric, nor a duration");
152             err.setXPathContext(context);
153             err.setErrorCode("FORG0007");
154             throw err;
155         }
156     }
157
158     /**
159     * Calculate average
160     */

161
162     private AtomicValue average(SequenceIterator iter, XPathContext context) throws XPathException {
163         int count = 0;
164         AtomicValue sum = (AtomicValue)iter.next();
165         if (sum == null) {
166             // the sequence is empty
167
return null;
168         }
169         count++;
170         if (!sum.hasBuiltInType()) {
171             sum = sum.getPrimitiveValue();
172         }
173         if (sum instanceof UntypedAtomicValue) {
174             sum = sum.convert(Type.DOUBLE, context);
175         }
176         if (sum instanceof NumericValue) {
177             while (true) {
178                 AtomicValue nextVal = (AtomicValue)iter.next();
179                 if (nextVal == null) {
180                     return ((NumericValue)sum).arithmetic(Token.DIV, new IntegerValue(count), context);
181                 }
182                 count++;
183                 AtomicValue next = nextVal.getPrimitiveValue();
184                 if (next instanceof UntypedAtomicValue) {
185                     next = next.convert(Type.DOUBLE, context);
186                 } else if (!(next instanceof NumericValue)) {
187                     DynamicError err =
188                             new DynamicError("Input to avg() contains a mix of numeric and non-numeric values");
189                     err.setXPathContext(context);
190                     err.setErrorCode("FORG0007");
191                     throw err;
192                 }
193                 sum = ((NumericValue)sum).arithmetic(Token.PLUS, (NumericValue)next, context);
194                 if (((NumericValue)sum).isNaN()) {
195                     // take an early bath, once we've got a NaN it's not going to change
196
return sum;
197                 }
198             }
199         } else if (sum instanceof DurationValue) {
200             while (true) {
201                 AtomicValue nextVal = (AtomicValue)iter.next();
202                 if (nextVal == null) {
203                     return ((DurationValue)sum).multiply(1.0/count, context);
204                 }
205                 count++;
206                 AtomicValue next = nextVal.getPrimitiveValue();
207                 if (!(next instanceof DurationValue)) {
208                     DynamicError err =
209                             new DynamicError("Input to avg() contains a mix of duration and non-duration values");
210                     err.setXPathContext(context);
211                     err.setErrorCode("FORG0007");
212                     throw err;
213                 }
214                 sum = ((DurationValue)sum).add((DurationValue)next, context);
215             }
216         } else {
217             DynamicError err =
218                     new DynamicError("Input to avg() contains a value that is neither numeric, nor a duration");
219             err.setXPathContext(context);
220             err.setErrorCode("FORG0007");
221             throw err;
222         }
223     }
224
225
226 // private NumericValue average(SequenceIterator iter, XPathContext context) throws XPathException {
227
// NumericValue sum = new IntegerValue(0);
228
// int count = 0;
229
// while (true) {
230
// AtomicValue nextVal = (AtomicValue)iter.next();
231
// if (nextVal == null) {
232
// break;
233
// }
234
// NumericValue next = (NumericValue)nextVal.getPrimitiveValue();
235
// sum = sum.arithmetic(Token.PLUS, next, context);
236
// count++;
237
// }
238
//
239
// if (count == 0) {
240
// return null;
241
// }
242
//
243
// // divide sum by count, following the exact rules in the spec.
244
//
245
// return sum.arithmetic(Token.DIV, new IntegerValue(count), context);
246
//
247
// }
248

249     /**
250      * Get the number of items in a sequence identified by a SequenceIterator
251      * @param iter The SequenceIterator. This method moves the current position
252      * of the supplied iterator; if this isn't safe, make a copy of the iterator
253      * first by calling getAnother(). The supplied iterator must be positioned
254      * before the first item (there must have been no call on next()).
255      * @return the number of items in the underlying sequence
256      * @throws XPathException if a failure occurs reading the input sequence
257      */

258
259     public static int count(SequenceIterator iter) throws XPathException {
260         if ((iter.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
261             return ((LastPositionFinder)iter).getLastPosition();
262         } else {
263             int n = 0;
264             while (iter.next() != null) {
265                 n++;
266             }
267             return n;
268         }
269     }
270
271     /**
272      * Determine whether a given expression is a call to the count() function
273      */

274
275     public static boolean isCountFunction(Expression exp) {
276         if (!(exp instanceof Aggregate)) return false;
277         Aggregate ag = (Aggregate)exp;
278         return ag.getNumberOfArguments() == 1 && ag.operation == COUNT;
279     }
280
281 }
282
283
284 //
285
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
286
// you may not use this file except in compliance with the License. You may obtain a copy of the
287
// License at http://www.mozilla.org/MPL/
288
//
289
// Software distributed under the License is distributed on an "AS IS" basis,
290
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
291
// See the License for the specific language governing rights and limitations under the License.
292
//
293
// The Original Code is: all this file.
294
//
295
// The Initial Developer of the Original Code is Michael H. Kay.
296
//
297
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
298
//
299
// Contributor(s): none.
300
//
301
Popular Tags