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.AbstractMemberCalc; 014import mondrian.calc.impl.AbstractTupleCalc; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017import mondrian.olap.type.*; 018 019import java.util.ArrayList; 020import java.util.List; 021 022/** 023 * Definition of the <code><Set>.Item</code> MDX function. 024 * 025 * <p>Syntax: 026 * <blockquote><code> 027 * <Set>.Item(<Index>)<br/> 028 * <Set>.Item(<String Expression> [, ...]) 029 * </code></blockquote> 030 * 031 * @author jhyde 032 * @since Mar 23, 2006 033 */ 034class SetItemFunDef extends FunDefBase { 035 static final Resolver intResolver = 036 new ReflectiveMultiResolver( 037 "Item", 038 "<Set>.Item(<Index>)", 039 "Returns a tuple from the set specified in <Set>. The tuple to be returned is specified by the zero-based position of the tuple in the set in <Index>.", 040 new String[] {"mmxn"}, 041 SetItemFunDef.class); 042 043 static final Resolver stringResolver = 044 new ResolverBase( 045 "Item", 046 "<Set>.Item(<String> [, ...])", 047 "Returns a tuple from the set specified in <Set>. The tuple to be returned is specified by the member name (or names) in <String>.", 048 Syntax.Method) 049 { 050 public FunDef resolve( 051 Exp[] args, 052 Validator validator, 053 List<Conversion> conversions) 054 { 055 if (args.length < 1) { 056 return null; 057 } 058 final Exp setExp = args[0]; 059 if (!(setExp.getType() instanceof SetType)) { 060 return null; 061 } 062 final SetType setType = (SetType) setExp.getType(); 063 final int arity = setType.getArity(); 064 // All args must be strings. 065 for (int i = 1; i < args.length; i++) { 066 if (!validator.canConvert( 067 i, args[i], Category.String, conversions)) 068 { 069 return null; 070 } 071 } 072 if (args.length - 1 != arity) { 073 throw Util.newError( 074 "Argument count does not match set's cardinality " + arity); 075 } 076 final int category = arity == 1 ? Category.Member : Category.Tuple; 077 FunDef dummy = createDummyFunDef(this, category, args); 078 return new SetItemFunDef(dummy); 079 } 080 }; 081 082 public SetItemFunDef(FunDef dummyFunDef) { 083 super(dummyFunDef); 084 } 085 086 public Type getResultType(Validator validator, Exp[] args) { 087 SetType setType = (SetType) args[0].getType(); 088 return setType.getElementType(); 089 } 090 091 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 092 final ListCalc listCalc = 093 compiler.compileList(call.getArg(0)); 094 final Type elementType = 095 ((SetType) listCalc.getType()).getElementType(); 096 final boolean isString = 097 call.getArgCount() < 2 098 || call.getArg(1).getType() instanceof StringType; 099 final IntegerCalc indexCalc; 100 final StringCalc[] stringCalcs; 101 List<Calc> calcList = new ArrayList<Calc>(); 102 calcList.add(listCalc); 103 if (isString) { 104 indexCalc = null; 105 stringCalcs = new StringCalc[call.getArgCount() - 1]; 106 for (int i = 0; i < stringCalcs.length; i++) { 107 stringCalcs[i] = compiler.compileString(call.getArg(i + 1)); 108 calcList.add(stringCalcs[i]); 109 } 110 } else { 111 stringCalcs = null; 112 indexCalc = compiler.compileInteger(call.getArg(1)); 113 calcList.add(indexCalc); 114 } 115 Calc[] calcs = calcList.toArray(new Calc[calcList.size()]); 116 if (elementType instanceof TupleType) { 117 final TupleType tupleType = (TupleType) elementType; 118 final Member[] nullTuple = makeNullTuple(tupleType); 119 if (isString) { 120 return new AbstractTupleCalc(call, calcs) { 121 public Member[] evaluateTuple(Evaluator evaluator) { 122 final int savepoint = evaluator.savepoint(); 123 final TupleList list; 124 try { 125 evaluator.setNonEmpty(false); 126 list = listCalc.evaluateList(evaluator); 127 assert list != null; 128 } finally { 129 evaluator.restore(savepoint); 130 } 131 try { 132 String[] results = new String[stringCalcs.length]; 133 for (int i = 0; i < stringCalcs.length; i++) { 134 results[i] = 135 stringCalcs[i].evaluateString(evaluator); 136 } 137 listLoop: 138 for (List<Member> members : list) { 139 for (int j = 0; j < results.length; j++) { 140 String result = results[j]; 141 final Member member = members.get(j); 142 if (!matchMember(member, result)) { 143 continue listLoop; 144 } 145 } 146 // All members match. Return the current one. 147 return members.toArray( 148 new Member[members.size()]); 149 } 150 } finally { 151 evaluator.restore(savepoint); 152 } 153 // We use 'null' to represent the null tuple. Don't 154 // know why. 155 return null; 156 } 157 }; 158 } else { 159 return new AbstractTupleCalc(call, calcs) { 160 public Member[] evaluateTuple(Evaluator evaluator) { 161 final int savepoint = evaluator.savepoint(); 162 final TupleList list; 163 try { 164 evaluator.setNonEmpty(false); 165 list = 166 listCalc.evaluateList(evaluator); 167 } finally { 168 evaluator.restore(savepoint); 169 } 170 assert list != null; 171 try { 172 final int index = 173 indexCalc.evaluateInteger(evaluator); 174 int listSize = list.size(); 175 if (index >= listSize || index < 0) { 176 return nullTuple; 177 } else { 178 final List<Member> members = 179 list.get(index); 180 return members.toArray( 181 new Member[members.size()]); 182 } 183 } finally { 184 evaluator.restore(savepoint); 185 } 186 } 187 }; 188 } 189 } else { 190 final MemberType memberType = (MemberType) elementType; 191 final Member nullMember = makeNullMember(memberType); 192 if (isString) { 193 return new AbstractMemberCalc(call, calcs) { 194 public Member evaluateMember(Evaluator evaluator) { 195 final int savepoint = evaluator.savepoint(); 196 final List<Member> list; 197 try { 198 evaluator.setNonEmpty(false); 199 list = 200 listCalc.evaluateList(evaluator).slice(0); 201 assert list != null; 202 } finally { 203 evaluator.restore(savepoint); 204 } 205 try { 206 final String result = 207 stringCalcs[0].evaluateString(evaluator); 208 for (Member member : list) { 209 if (matchMember(member, result)) { 210 return member; 211 } 212 } 213 return nullMember; 214 } finally { 215 evaluator.restore(savepoint); 216 } 217 } 218 }; 219 } else { 220 return new AbstractMemberCalc(call, calcs) { 221 public Member evaluateMember(Evaluator evaluator) { 222 final int savepoint = evaluator.savepoint(); 223 final List<Member> list; 224 try { 225 evaluator.setNonEmpty(false); 226 list = 227 listCalc.evaluateList(evaluator).slice(0); 228 assert list != null; 229 } finally { 230 evaluator.restore(savepoint); 231 } 232 try { 233 final int index = 234 indexCalc.evaluateInteger(evaluator); 235 int listSize = list.size(); 236 if (index >= listSize || index < 0) { 237 return nullMember; 238 } else { 239 return list.get(index); 240 } 241 } finally { 242 evaluator.restore(savepoint); 243 } 244 } 245 }; 246 } 247 } 248 } 249 250 private static boolean matchMember(final Member member, String name) { 251 return member.getName().equals(name); 252 } 253} 254 255// End SetItemFunDef.java