001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomPercentSumFunDef.java#6 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2002-2002 Kana Software, Inc. 007 // Copyright (C) 2002-2007 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 */ 011 package mondrian.olap.fun; 012 013 import mondrian.calc.*; 014 import mondrian.calc.impl.AbstractListCalc; 015 import mondrian.mdx.ResolvedFunCall; 016 import mondrian.olap.*; 017 018 import java.util.List; 019 import java.util.Map; 020 021 /** 022 * Definition of the <code>TopPercent</code>, <code>BottomPercent</code>, 023 * <code>TopSum</code> and <code>BottomSum</code> MDX builtin functions. 024 * 025 * @author jhyde 026 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomPercentSumFunDef.java#6 $ 027 * @since Mar 23, 2006 028 */ 029 class TopBottomPercentSumFunDef extends FunDefBase { 030 /** 031 * Whether to calculate top (as opposed to bottom). 032 */ 033 final boolean top; 034 /** 035 * Whether to calculate percent (as opposed to sum). 036 */ 037 final boolean percent; 038 039 static final ResolverImpl TopPercentResolver = new ResolverImpl( 040 "TopPercent", 041 "TopPercent(<Set>, <Percentage>, <Numeric Expression>)", 042 "Sorts a set and returns the top N elements whose cumulative total is at least a specified percentage.", 043 new String[]{"fxxnn"}, true, true); 044 045 static final ResolverImpl BottomPercentResolver = new ResolverImpl( 046 "BottomPercent", 047 "BottomPercent(<Set>, <Percentage>, <Numeric Expression>)", 048 "Sorts a set and returns the bottom N elements whose cumulative total is at least a specified percentage.", 049 new String[]{"fxxnn"}, false, true); 050 051 static final ResolverImpl TopSumResolver = new ResolverImpl( 052 "TopSum", 053 "TopSum(<Set>, <Value>, <Numeric Expression>)", 054 "Sorts a set and returns the top N elements whose cumulative total is at least a specified value.", 055 new String[]{"fxxnn"}, true, false); 056 057 static final ResolverImpl BottomSumResolver = new ResolverImpl( 058 "BottomSum", 059 "BottomSum(<Set>, <Value>, <Numeric Expression>)", 060 "Sorts a set and returns the bottom N elements whose cumulative total is at least a specified value.", 061 new String[]{"fxxnn"}, false, false); 062 063 public TopBottomPercentSumFunDef( 064 FunDef dummyFunDef, boolean top, boolean percent) { 065 super(dummyFunDef); 066 this.top = top; 067 this.percent = percent; 068 } 069 070 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 071 final ListCalc listCalc = (ListCalc) compiler.compileAs(call.getArg(0), 072 null, ResultStyle.MUTABLELIST_ONLY); 073 final DoubleCalc doubleCalc = compiler.compileDouble(call.getArg(1)); 074 final Calc calc = compiler.compileScalar(call.getArg(2), true); 075 return new CalcImpl(call, listCalc, doubleCalc, calc); 076 } 077 078 private static class ResolverImpl extends MultiResolver { 079 private final boolean top; 080 private final boolean percent; 081 082 public ResolverImpl( 083 final String name, final String signature, 084 final String description, final String[] signatures, 085 boolean top, boolean percent) { 086 super(name, signature, description, signatures); 087 this.top = top; 088 this.percent = percent; 089 } 090 091 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 092 return new TopBottomPercentSumFunDef(dummyFunDef, top, percent); 093 } 094 } 095 096 private class CalcImpl extends AbstractListCalc { 097 private final ListCalc listCalc; 098 private final DoubleCalc doubleCalc; 099 private final Calc calc; 100 101 public CalcImpl(ResolvedFunCall call, ListCalc listCalc, DoubleCalc doubleCalc, Calc calc) { 102 super(call, new Calc[]{listCalc, doubleCalc, calc}); 103 this.listCalc = listCalc; 104 this.doubleCalc = doubleCalc; 105 this.calc = calc; 106 } 107 108 public List evaluateList(Evaluator evaluator) { 109 List list = listCalc.evaluateList(evaluator); 110 double target = doubleCalc.evaluateDouble(evaluator); 111 if (list.isEmpty()) { 112 return list; 113 } 114 Map mapMemberToValue; 115 Object first = list.get(0); 116 boolean isMember = true; 117 if (first instanceof Member) { 118 List<Member> memberList = (List<Member>) list; 119 mapMemberToValue = 120 evaluateMembers(evaluator, calc, memberList, false); 121 sortMembers(evaluator, memberList, calc, top, true); 122 } else { 123 isMember = false; 124 List<Member[]> tupleList = (List<Member[]>) list; 125 mapMemberToValue = 126 evaluateTuples(evaluator, calc, tupleList); 127 int arity = ((Member[]) first).length; 128 sortTuples(evaluator, tupleList, calc, top, true, arity); 129 } 130 if (percent) { 131 toPercent(list, mapMemberToValue, isMember); 132 } 133 double runningTotal = 0; 134 int memberCount = list.size(); 135 int nullCount = 0; 136 for (int i = 0; i < memberCount; i++) { 137 if (runningTotal >= target) { 138 list = list.subList(0, i); 139 break; 140 } 141 Object o = (isMember) 142 ? mapMemberToValue.get(list.get(i)) 143 : mapMemberToValue.get( 144 new ArrayHolder<Member>((Member []) list.get(i))); 145 if (o == Util.nullValue) { 146 nullCount++; 147 } else if (o instanceof Number) { 148 runningTotal += ((Number) o).doubleValue(); 149 } else if (o instanceof Exception) { 150 // ignore the error 151 } else { 152 throw Util.newInternal("got " + o + " when expecting Number"); 153 } 154 } 155 156 // MSAS exhibits the following behavior. If the value of all members is 157 // null, then the first (or last) member of the set is returned for percent 158 // operations. 159 if (memberCount > 0 && percent && nullCount == memberCount) { 160 return top ? 161 list.subList(0, 1) : 162 list.subList(memberCount - 1, memberCount); 163 } 164 return list; 165 } 166 167 public boolean dependsOn(Dimension dimension) { 168 return anyDependsButFirst(getCalcs(), dimension); 169 } 170 } 171 } 172 173 // End TopBottomPercentSumFunDef.java