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>&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 * @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