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>&lt;Set&gt;.Item</code> MDX function.
024     *
025     * <p>Syntax:
026     * <blockquote><code>
027     * &lt;Set&gt;.Item(&lt;Index&gt;)<br/>
028     * &lt;Set&gt;.Item(&lt;String Expression&gt; [, ...])
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