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