001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/SetItemFunDef.java#7 $ 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) 2006-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.olap.type.*; 013 import mondrian.olap.*; 014 import mondrian.calc.*; 015 import mondrian.calc.impl.AbstractTupleCalc; 016 import mondrian.calc.impl.AbstractMemberCalc; 017 import mondrian.mdx.ResolvedFunCall; 018 019 import java.util.List; 020 import java.util.ArrayList; 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 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/SetItemFunDef.java#7 $ 033 * @since Mar 23, 2006 034 */ 035 class SetItemFunDef extends FunDefBase { 036 static final Resolver intResolver = 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 = new ResolverBase( 044 "Item", 045 "<Set>.Item(<String> [, ...])", 046 "Returns a tuple from the set specified in <Set>. The tuple to be returned is specified by the member name (or names) in <String>.", 047 Syntax.Method) { 048 049 public FunDef resolve( 050 Exp[] args, Validator validator, int[] conversionCount) { 051 if (args.length < 1) { 052 return null; 053 } 054 final Exp setExp = args[0]; 055 if (!(setExp.getType() instanceof SetType)) { 056 return null; 057 } 058 final SetType setType = (SetType) setExp.getType(); 059 int arity; 060 if (setType.getElementType() instanceof TupleType) { 061 arity = ((TupleType) setType.getElementType()).elementTypes.length; 062 } else { 063 arity = 1; 064 } 065 // All args must be strings. 066 for (int i = 1; i < args.length; i++) { 067 if (!validator.canConvert( 068 args[i], Category.String, conversionCount)) { 069 return null; 070 } 071 } 072 if (args.length - 1 != arity) { 073 throw Util.newError("Argument count does not match set's cardinality " + arity); 074 } 075 final int category = arity == 1 ? Category.Member : Category.Tuple; 076 FunDef dummy = createDummyFunDef(this, category, args); 077 return new SetItemFunDef(dummy); 078 } 079 }; 080 081 public SetItemFunDef(FunDef dummyFunDef) { 082 super(dummyFunDef); 083 } 084 085 public Type getResultType(Validator validator, Exp[] args) { 086 SetType setType = (SetType) args[0].getType(); 087 return setType.getElementType(); 088 } 089 090 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 091 final ListCalc listCalc = 092 compiler.compileList(call.getArg(0)); 093 final Type elementType = ((SetType) listCalc.getType()).getElementType(); 094 final boolean isString = call.getArgCount() < 2 || 095 call.getArg(1).getType() instanceof StringType; 096 final IntegerCalc indexCalc; 097 final StringCalc[] stringCalcs; 098 List<Calc> calcList = new ArrayList<Calc>(); 099 calcList.add(listCalc); 100 if (isString) { 101 indexCalc = null; 102 stringCalcs = new StringCalc[call.getArgCount() - 1]; 103 for (int i = 0; i < stringCalcs.length; i++) { 104 stringCalcs[i] = compiler.compileString(call.getArg(i + 1)); 105 calcList.add(stringCalcs[i]); 106 } 107 } else { 108 stringCalcs = null; 109 indexCalc = compiler.compileInteger(call.getArg(1)); 110 calcList.add(indexCalc); 111 } 112 Calc[] calcs = calcList.toArray(new Calc[calcList.size()]); 113 if (elementType instanceof TupleType) { 114 final TupleType tupleType = (TupleType) elementType; 115 final Member[] nullTuple = makeNullTuple(tupleType); 116 if (isString) { 117 return new AbstractTupleCalc(call, calcs) { 118 public Member[] evaluateTuple(Evaluator evaluator) { 119 final List<Member[]> list = listCalc.evaluateList(evaluator); 120 assert list != null; 121 String[] results = new String[stringCalcs.length]; 122 for (int i = 0; i < stringCalcs.length; i++) { 123 results[i] = 124 stringCalcs[i].evaluateString(evaluator); 125 } 126 listLoop: 127 for (Member[] members : list) { 128 for (int j = 0; j < results.length; j++) { 129 String result = results[j]; 130 final Member member = members[j]; 131 if (!matchMember(member, result)) { 132 continue listLoop; 133 } 134 } 135 // All members match. Return the current one. 136 return members; 137 } 138 // We use 'null' to represent the null tuple. Don't 139 // know why. 140 return null; 141 } 142 }; 143 } else { 144 return new AbstractTupleCalc(call, calcs) { 145 public Member[] evaluateTuple(Evaluator evaluator) { 146 final List<Member[]> list = listCalc.evaluateList(evaluator); 147 assert list != null; 148 final int index = indexCalc.evaluateInteger(evaluator); 149 int listSize = list.size(); 150 if (index >= listSize || index < 0) { 151 return nullTuple; 152 } else { 153 return (Member[]) list.get(index); 154 } 155 } 156 }; 157 } 158 } else { 159 final MemberType memberType = (MemberType) elementType; 160 final Member nullMember = makeNullMember(memberType); 161 if (isString) { 162 return new AbstractMemberCalc(call, calcs) { 163 public Member evaluateMember(Evaluator evaluator) { 164 final List<Member> list = listCalc.evaluateList(evaluator); 165 assert list != null; 166 final String result = 167 stringCalcs[0].evaluateString(evaluator); 168 for (Member member : list) { 169 if (matchMember(member, result)) { 170 return member; 171 } 172 } 173 return nullMember; 174 } 175 }; 176 } else { 177 return new AbstractMemberCalc(call, calcs) { 178 public Member evaluateMember(Evaluator evaluator) { 179 final List list = listCalc.evaluateList(evaluator); 180 assert list != null; 181 final int index = indexCalc.evaluateInteger(evaluator); 182 int listSize = list.size(); 183 if (index >= listSize || index < 0) { 184 return nullMember; 185 } else { 186 return (Member) list.get(index); 187 } 188 } 189 }; 190 } 191 } 192 } 193 194 private static boolean matchMember(final Member member, String name) { 195 return member.getName().equals(name); 196 } 197 198 Object makeNullMember(Evaluator evaluator, Exp[] args) { 199 final Type elementType = ((SetType) args[0].getType()).getElementType(); 200 return makeNullMemberOrTuple(elementType); 201 } 202 203 Object makeNullMemberOrTuple(final Type elementType) { 204 if (elementType instanceof MemberType) { 205 MemberType memberType = (MemberType) elementType; 206 return makeNullMember(memberType); 207 } else if (elementType instanceof TupleType) { 208 return makeNullTuple((TupleType) elementType); 209 } else { 210 throw Util.newInternal("bad type " + elementType); 211 } 212 } 213 } 214 215 // End SetItemFunDef.java