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 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 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 34 35 public ItemType getItemType() { 36 switch (operation) { 37 case COUNT: 38 return super.getItemType(); 39 case SUM: { 40 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 ("Unknown aggregate operation"); 67 } 68 } 69 70 71 74 75 public Item evaluateItem(XPathContext context) throws XPathException { 76 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 ("Unknown aggregate function"); 88 } 89 } 90 91 94 95 private AtomicValue total(SequenceIterator iter, XPathContext context) throws XPathException { 96 AtomicValue sum = (AtomicValue)iter.next(); 97 if (sum == null) { 98 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 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 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 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 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 249 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 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 | Popular Tags |