001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/ExtractFunDef.java#2 $ 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) 2007-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.olap.fun; 011 012 import mondrian.calc.*; 013 import mondrian.calc.impl.AbstractListCalc; 014 import mondrian.mdx.ResolvedFunCall; 015 import mondrian.mdx.DimensionExpr; 016 import mondrian.olap.*; 017 import mondrian.olap.type.*; 018 019 import java.util.*; 020 021 /** 022 * Definition of the <code>Extract</code> MDX function. 023 * 024 * <p>Syntax: 025 * <blockquote><code>Extract(<Set>, <Dimension>[, 026 * <Dimension>...])</code></blockquote> 027 * 028 * @author jhyde 029 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/ExtractFunDef.java#2 $ 030 * @since Jun 10, 2007 031 */ 032 class ExtractFunDef extends FunDefBase { 033 static final ResolverBase Resolver = new ResolverBase( 034 "Extract", 035 "Extract(<Set>, <Dimension>[, <Dimension>...])", 036 "Returns a set of tuples from extracted dimension elements. The opposite of Crossjoin.", 037 Syntax.Function) { 038 039 public FunDef resolve( 040 Exp[] args, Validator validator, int[] conversionCount) { 041 if (args.length < 2) { 042 return null; 043 } 044 if (!validator.canConvert( 045 args[0], Category.Set, conversionCount)) { 046 return null; 047 } 048 for (int i = 1; i < args.length; ++i) { 049 if (!validator.canConvert( 050 args[i], Category.Dimension, conversionCount)) { 051 return null; 052 } 053 } 054 055 // Find the dimensionality of the set expression. 056 057 // Form a list of ordinals of the dimensions being extracted. 058 // For example, in 059 // Extract(X.Members * Y.Members * Z.Members, Z, X) 060 // the dimension ordinals are X=0, Y=1, Z=2, and the extracted 061 // ordinals are {2, 0}. 062 // 063 // Each dimension extracted must exist in the LHS, 064 // and no dimension may be extracted more than once. 065 List<Integer> extractedOrdinals = new ArrayList<Integer>(); 066 final List<Dimension> extractedDimensions = new ArrayList<Dimension>(); 067 findExtractedDimensions(args, extractedDimensions, extractedOrdinals); 068 int[] parameterTypes = new int[args.length]; 069 parameterTypes[0] = Category.Set; 070 Arrays.fill(parameterTypes, 1, parameterTypes.length, Category.Dimension); 071 return new ExtractFunDef(this, Category.Set, parameterTypes); 072 } 073 }; 074 075 private ExtractFunDef( 076 Resolver resolver, int returnType, int[] parameterTypes) 077 { 078 super(resolver, returnType, parameterTypes); 079 } 080 081 public Type getResultType(Validator validator, Exp[] args) { 082 final List<Dimension> extractedDimensions = 083 new ArrayList<Dimension>(); 084 final List<Integer> extractedOrdinals = new ArrayList<Integer>(); 085 findExtractedDimensions(args, extractedDimensions, extractedOrdinals); 086 if (extractedDimensions.size() == 1) { 087 return new SetType( 088 new MemberType(extractedDimensions.get(0), null, null, null)); 089 } else { 090 List<Type> typeList = new ArrayList<Type>(); 091 for (Dimension extractedDimension : extractedDimensions) { 092 typeList.add( 093 new MemberType( 094 extractedDimension, null, null, null)); 095 } 096 return new SetType( 097 new TupleType( 098 typeList.toArray(new Type[typeList.size()]))); 099 } 100 } 101 102 private static void findExtractedDimensions( 103 Exp[] args, 104 List<Dimension> extractedDimensions, 105 List<Integer> extractedOrdinals) 106 { 107 SetType type = (SetType) args[0].getType(); 108 final List<Dimension> dimensions = new ArrayList<Dimension>(); 109 if (type.getElementType() instanceof TupleType) { 110 for (Type elementType : ((TupleType) type 111 .getElementType()).elementTypes) { 112 Dimension dimension = elementType.getDimension(); 113 if (dimension == null) { 114 throw new RuntimeException("dimension of argument not known"); 115 } 116 dimensions.add(dimension); 117 } 118 } else { 119 Dimension dimension = type.getDimension(); 120 if (dimension == null) { 121 throw new RuntimeException("dimension of argument not known"); 122 } 123 dimensions.add(dimension); 124 } 125 126 for (int i = 1; i < args.length; i++) { 127 Exp arg = args[i]; 128 if (arg instanceof DimensionExpr) { 129 DimensionExpr dimensionExpr = (DimensionExpr) arg; 130 final Dimension extractedDimension = 131 dimensionExpr.getDimension(); 132 int ordinal = dimensions.indexOf(extractedDimension); 133 if (ordinal == -1) { 134 throw new RuntimeException( 135 "dimension " + 136 extractedDimension.getUniqueName() + 137 " is not a dimension of the expression " + args[0]); 138 } 139 if (extractedOrdinals.indexOf(ordinal) >= 0) { 140 throw new RuntimeException( 141 "dimension " + 142 extractedDimension.getUniqueName() + 143 " is extracted more than once"); 144 } 145 extractedOrdinals.add(ordinal); 146 extractedDimensions.add(extractedDimension); 147 } else { 148 throw new RuntimeException("not a constant dimension: " + arg); 149 } 150 } 151 } 152 153 private static int[] toIntArray(List<Integer> integerList) { 154 final int[] ints = new int[integerList.size()]; 155 for (int i = 0; i < ints.length; i++) { 156 ints[i] = integerList.get(i); 157 } 158 return ints; 159 } 160 161 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 162 List<Dimension> extractedDimensionList = new ArrayList<Dimension>(); 163 List<Integer> extractedOrdinalList = new ArrayList<Integer>(); 164 findExtractedDimensions( 165 call.getArgs(), 166 extractedDimensionList, 167 extractedOrdinalList); 168 Util.assertTrue( 169 extractedOrdinalList.size() == extractedDimensionList.size()); 170 Exp arg = call.getArg(0); 171 final TupleListCalc listCalc = 172 (TupleListCalc) compiler.compileList(arg, false); 173 int inArity = ((SetType) arg.getType()).getArity(); 174 final int outArity = extractedOrdinalList.size(); 175 if (inArity == 1) { 176 // LHS is a set of members, RHS is the same dimension. Extract boils 177 // down to eliminating duplicate members. 178 Util.assertTrue(outArity == 1); 179 return new DistinctFunDef.CalcImpl(call, listCalc); 180 } 181 final int[] extractedOrdinals = toIntArray(extractedOrdinalList); 182 if (outArity == 1) { 183 return new AbstractListCalc(call, new Calc[] {listCalc}) { 184 public List evaluateList(Evaluator evaluator) { 185 List<Member> result = new ArrayList<Member>(); 186 List<Member[]> list = listCalc.evaluateTupleList(evaluator); 187 Set<Member> emittedMembers = new HashSet<Member>(); 188 for (Member[] members : list) { 189 Member outMember = members[extractedOrdinals[0]]; 190 if (emittedMembers.add(outMember)) { 191 result.add(outMember); 192 } 193 } 194 return result; 195 } 196 }; 197 } else { 198 return new AbstractListCalc(call, new Calc[] {listCalc}) { 199 public List evaluateList(Evaluator evaluator) { 200 List<Member[]> result = new ArrayList<Member[]>(); 201 List<Member[]> list = listCalc.evaluateTupleList(evaluator); 202 Set<List<Member>> emittedTuples = new HashSet<List<Member>>(); 203 for (Member[] members : list) { 204 Member[] outMembers = new Member[outArity]; 205 for (int i = 0; i < outMembers.length; i++) { 206 outMembers[i] = members[extractedOrdinals[i]]; 207 } 208 final List<Member> outTuple = Arrays.asList(outMembers); 209 if (emittedTuples.add(outTuple)) { 210 result.add(outMembers); 211 } 212 } 213 return result; 214 } 215 }; 216 } 217 } 218 } 219 220 // End ExtractFunDef.java