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) 2006-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.*; 014import mondrian.mdx.ResolvedFunCall; 015import mondrian.olap.*; 016import mondrian.olap.type.*; 017 018import java.util.*; 019 020/** 021 * Definition of the <code>Generate</code> MDX function. 022 * 023 * @author jhyde 024 * @since Mar 23, 2006 025 */ 026class GenerateFunDef extends FunDefBase { 027 static final ReflectiveMultiResolver ListResolver = 028 new ReflectiveMultiResolver( 029 "Generate", 030 "Generate(<Set1>, <Set2>[, ALL])", 031 "Applies a set to each member of another set and joins the resulting sets by union.", 032 new String[] {"fxxx", "fxxxy"}, 033 GenerateFunDef.class); 034 035 static final ReflectiveMultiResolver StringResolver = 036 new ReflectiveMultiResolver( 037 "Generate", 038 "Generate(<Set>, <String>[, <String>])", 039 "Applies a set to a string expression and joins resulting sets by string concatenation.", 040 new String[] {"fSxS", "fSxSS"}, 041 GenerateFunDef.class); 042 043 private static final String[] ReservedWords = new String[] {"ALL"}; 044 045 public GenerateFunDef(FunDef dummyFunDef) { 046 super(dummyFunDef); 047 } 048 049 public Type getResultType(Validator validator, Exp[] args) { 050 final Type type = args[1].getType(); 051 if (type instanceof StringType) { 052 // Generate(<Set>, <String>[, <String>]) 053 return type; 054 } else { 055 final Type memberType = TypeUtil.toMemberOrTupleType(type); 056 return new SetType(memberType); 057 } 058 } 059 060 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 061 final IterCalc iterCalc = compiler.compileIter(call.getArg(0)); 062 if (call.getArg(1).getType() instanceof StringType) { 063 final StringCalc stringCalc = 064 compiler.compileString(call.getArg(1)); 065 final StringCalc delimCalc; 066 if (call.getArgCount() == 3) { 067 delimCalc = compiler.compileString(call.getArg(2)); 068 } else { 069 delimCalc = ConstantCalc.constantString(""); 070 } 071 072 return new GenerateStringCalcImpl( 073 call, (IterCalc) iterCalc, stringCalc, delimCalc); 074 } else { 075 final ListCalc listCalc2 = 076 compiler.compileList(call.getArg(1)); 077 final String literalArg = getLiteralArg(call, 2, "", ReservedWords); 078 final boolean all = literalArg.equalsIgnoreCase("ALL"); 079 final int arityOut = call.getType().getArity(); 080 return new GenerateListCalcImpl( 081 call, iterCalc, listCalc2, arityOut, all); 082 } 083 } 084 085 private static class GenerateListCalcImpl extends AbstractListCalc { 086 private final IterCalc iterCalc1; 087 private final ListCalc listCalc2; 088 private final int arityOut; 089 private final boolean all; 090 091 public GenerateListCalcImpl( 092 ResolvedFunCall call, 093 IterCalc iterCalc, 094 ListCalc listCalc2, 095 int arityOut, 096 boolean all) 097 { 098 super(call, new Calc[]{iterCalc, listCalc2}); 099 this.iterCalc1 = iterCalc; 100 this.listCalc2 = listCalc2; 101 this.arityOut = arityOut; 102 this.all = all; 103 } 104 105 public TupleList evaluateList(Evaluator evaluator) { 106 final int savepoint = evaluator.savepoint(); 107 try { 108 evaluator.setNonEmpty(false); 109 final TupleIterable iterable1 = 110 iterCalc1.evaluateIterable(evaluator); 111 evaluator.restore(savepoint); 112 TupleList result = TupleCollections.createList(arityOut); 113 if (all) { 114 final TupleCursor cursor = iterable1.tupleCursor(); 115 while (cursor.forward()) { 116 cursor.setContext(evaluator); 117 final TupleList result2 = 118 listCalc2.evaluateList(evaluator); 119 result.addAll(result2); 120 } 121 } else { 122 final Set<List<Member>> emitted = 123 new HashSet<List<Member>>(); 124 final TupleCursor cursor = iterable1.tupleCursor(); 125 while (cursor.forward()) { 126 cursor.setContext(evaluator); 127 final TupleList result2 = 128 listCalc2.evaluateList(evaluator); 129 addDistinctTuples(result, result2, emitted); 130 } 131 } 132 return result; 133 } finally { 134 evaluator.restore(savepoint); 135 } 136 } 137 138 private static void addDistinctTuples( 139 TupleList result, 140 TupleList result2, 141 Set<List<Member>> emitted) 142 { 143 for (List<Member> row : result2) { 144 // wrap array for correct distinctness test 145 if (emitted.add(row)) { 146 result.add(row); 147 } 148 } 149 } 150 151 public boolean dependsOn(Hierarchy hierarchy) { 152 return anyDependsButFirst(getCalcs(), hierarchy); 153 } 154 } 155 156 private static class GenerateStringCalcImpl extends AbstractStringCalc { 157 private final IterCalc iterCalc; 158 private final StringCalc stringCalc; 159 private final StringCalc sepCalc; 160 161 public GenerateStringCalcImpl( 162 ResolvedFunCall call, 163 IterCalc iterCalc, 164 StringCalc stringCalc, 165 StringCalc sepCalc) 166 { 167 super(call, new Calc[]{iterCalc, stringCalc}); 168 this.iterCalc = iterCalc; 169 this.stringCalc = stringCalc; 170 this.sepCalc = sepCalc; 171 } 172 173 public String evaluateString(Evaluator evaluator) { 174 final int savepoint = evaluator.savepoint(); 175 try { 176 StringBuilder buf = new StringBuilder(); 177 int k = 0; 178 final TupleIterable iter11 = 179 iterCalc.evaluateIterable(evaluator); 180 final TupleCursor cursor = iter11.tupleCursor(); 181 while (cursor.forward()) { 182 cursor.setContext(evaluator); 183 if (k++ > 0) { 184 String sep = sepCalc.evaluateString(evaluator); 185 buf.append(sep); 186 } 187 final String result2 = 188 stringCalc.evaluateString(evaluator); 189 buf.append(result2); 190 } 191 return buf.toString(); 192 } finally { 193 evaluator.restore(savepoint); 194 } 195 } 196 197 public boolean dependsOn(Hierarchy hierarchy) { 198 return anyDependsButFirst(getCalcs(), hierarchy); 199 } 200 } 201} 202 203// End GenerateFunDef.java