001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/GenerateFunDef.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.*;
013    import mondrian.olap.type.*;
014    import mondrian.calc.*;
015    import mondrian.calc.impl.AbstractListCalc;
016    import mondrian.calc.impl.ConstantCalc;
017    import mondrian.calc.impl.AbstractStringCalc;
018    import mondrian.mdx.ResolvedFunCall;
019    
020    import java.util.List;
021    import java.util.Arrays;
022    import java.util.ArrayList;
023    import java.util.HashSet;
024    import java.util.Set;
025    
026    /**
027     * Definition of the <code>Generate</code> MDX function.
028     *
029     * @author jhyde
030     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/GenerateFunDef.java#7 $
031     * @since Mar 23, 2006
032     */
033    class GenerateFunDef extends FunDefBase {
034        static final ReflectiveMultiResolver ListResolver =
035            new ReflectiveMultiResolver(
036                "Generate",
037                "Generate(<Set1>, <Set2>[, ALL])",
038                "Applies a set to each member of another set and joins the resulting sets by union.",
039                new String[] {"fxxx", "fxxxy"},
040                GenerateFunDef.class);
041    
042        static final ReflectiveMultiResolver StringResolver =
043            new ReflectiveMultiResolver(
044                "Generate",
045                "Generate(<Set>, <String>[, <String>])",
046                "Applies a set to a string expression and joins resulting sets by string concatenation.",
047                new String[] {"fSxS", "fSxSS"},
048                GenerateFunDef.class);
049    
050        private static final String[] ReservedWords = new String[] {"ALL"};
051    
052        public GenerateFunDef(FunDef dummyFunDef) {
053            super(dummyFunDef);
054        }
055    
056        public Type getResultType(Validator validator, Exp[] args) {
057            final Type type = args[1].getType();
058            if (type instanceof StringType) {
059                // Generate(<Set>, <String>[, <String>])
060                return type;
061            } else {
062                final Type memberType = TypeUtil.toMemberOrTupleType(type);
063                return new SetType(memberType);
064            }
065        }
066    
067        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
068            final ListCalc listCalc = compiler.compileList(call.getArg(0));
069            final boolean tupleIn = ((SetType) listCalc.getType()).getElementType()
070                instanceof TupleType;
071            if (call.getArg(1).getType() instanceof StringType) {
072                final StringCalc stringCalc = compiler.compileString(call.getArg(1));
073                final StringCalc delimCalc;
074                if (call.getArgCount() == 3) {
075                    delimCalc = compiler.compileString(call.getArg(2));
076                } else {
077                    delimCalc = ConstantCalc.constantString("");
078                }
079    
080                return new GenerateStringCalcImpl(
081                    call, listCalc, stringCalc, tupleIn, delimCalc);
082            } else {
083                final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
084                final String literalArg = getLiteralArg(call, 2, "", ReservedWords);
085                final boolean all = literalArg.equalsIgnoreCase("ALL");
086                final boolean tupleOut =
087                    ((SetType) call.getType()).getElementType()
088                    instanceof TupleType;
089                return new GenerateListCalcImpl(
090                    call, listCalc, listCalc2, tupleIn, tupleOut, all);
091            }
092        }
093    
094        private static class GenerateListCalcImpl extends AbstractListCalc {
095            private final MemberListCalc memberListCalc1;
096            private final TupleListCalc tupleListCalc1;
097            private final MemberListCalc memberListCalc2;
098            private final TupleListCalc tupleListCalc2;
099            private final boolean tupleIn;
100            private final boolean tupleOut;
101            private final boolean all;
102    
103            public GenerateListCalcImpl(
104                ResolvedFunCall call,
105                ListCalc listCalc1,
106                ListCalc listCalc2,
107                boolean tupleIn,
108                boolean tupleOut,
109                boolean all)
110            {
111                super(call, new Calc[]{listCalc1, listCalc2});
112                if (tupleIn) {
113                    this.memberListCalc1 = null;
114                    this.tupleListCalc1 = (TupleListCalc) listCalc1;
115                } else {
116                    this.memberListCalc1 = (MemberListCalc) listCalc1;
117                    this.tupleListCalc1 = null;
118                }
119                if (tupleOut) {
120                    this.memberListCalc2 = null;
121                    this.tupleListCalc2 = (TupleListCalc) listCalc2;
122                } else {
123                    this.memberListCalc2 = (MemberListCalc) listCalc2;
124                    this.tupleListCalc2 = null;
125                }
126                this.tupleIn = tupleIn;
127                this.tupleOut = tupleOut;
128                this.all = all;
129            }
130    
131            public List evaluateList(Evaluator evaluator) {
132                // 8 cases - all of the combinations of tupleIn x tupleOut x all
133                final Evaluator evaluator2 = evaluator.push();
134                if (tupleIn) {
135                    final List<Member[]> list1 =
136                        tupleListCalc1.evaluateTupleList(evaluator);
137                    if (tupleOut) {
138                        List<Member[]> result = new ArrayList<Member[]>();
139                        if (all) {
140                            for (Member[] members : list1) {
141                                evaluator2.setContext(members);
142                                final List<Member[]> result2 =
143                                    tupleListCalc2.evaluateTupleList(evaluator2);
144                                result.addAll(result2);
145                            }
146                        } else {
147                            final Set<List<Member>> emitted =
148                                new HashSet<List<Member>>();
149                            for (Member[] members : list1) {
150                                evaluator2.setContext(members);
151                                final List<Member[]> result2 =
152                                    tupleListCalc2.evaluateTupleList(evaluator2);
153                                addDistinctTuples(result, result2, emitted);
154                            }
155                        }
156                        return result;
157                    } else {
158                        List<Member> result = new ArrayList<Member>();
159                        final Set<Member> emitted =
160                            all ? null : new HashSet<Member>();
161                        for (Member[] members : list1) {
162                            evaluator2.setContext(members);
163                            final List<Member> result2 =
164                                memberListCalc2.evaluateMemberList(evaluator2);
165                            if (emitted != null) {
166                                addDistinctMembers(result, result2, emitted);
167                            } else {
168                                result.addAll(result2);
169                            }
170                        }
171                        return result;
172                    }
173                } else {
174                    final List<Member> list1 =
175                        memberListCalc1.evaluateMemberList(evaluator);
176                    if (tupleOut) {
177                        List<Member[]> result = new ArrayList<Member[]>();
178                        if (all) {
179                            for (Member member : list1) {
180                                evaluator2.setContext(member);
181                                final List<Member[]> result2 =
182                                    tupleListCalc2.evaluateTupleList(evaluator2);
183                                result.addAll(result2);
184                            }
185                        } else {
186                            final Set<List<Member>> emitted =
187                                new HashSet<List<Member>>();
188                            for (Member member : list1) {
189                                evaluator2.setContext(member);
190                                final List<Member[]> result2 =
191                                    tupleListCalc2.evaluateTupleList(evaluator2);
192                                addDistinctTuples(result, result2, emitted);
193                            }
194                        }
195                        return result;
196                    } else {
197                        List<Member> result = new ArrayList<Member>();
198                        if (all) {
199                            for (Member member : list1) {
200                                evaluator2.setContext(member);
201                                final List<Member> result2 =
202                                    memberListCalc2.evaluateMemberList(evaluator2);
203                                result.addAll(result2);
204                            }
205                        } else {
206                            final Set<Member> emitted = new HashSet<Member>();
207                            for (Member member : list1) {
208                                evaluator2.setContext(member);
209                                final List<Member> result2 =
210                                    memberListCalc2.evaluateMemberList(evaluator2);
211                                addDistinctMembers(result, result2, emitted);
212                            }
213                        }
214                        return result;
215                    }
216                }
217            }
218    
219            private static void addDistinctMembers(
220                List<Member> result,
221                List<Member> result2,
222                Set<Member> emitted)
223            {
224                for (Member row : result2) {
225                    if (emitted.add(row)) {
226                        result.add(row);
227                    }
228                }
229            }
230    
231            private static void addDistinctTuples(
232                List<Member[]> result,
233                List<Member[]> result2,
234                Set<List<Member>> emitted)
235            {
236                for (Member[] row : result2) {
237                    // wrap array for correct distinctness test
238                    if (emitted.add(Arrays.asList(row))) {
239                        result.add(row);
240                    }
241                }
242            }
243    
244            public boolean dependsOn(Dimension dimension) {
245                return anyDependsButFirst(getCalcs(), dimension);
246            }
247        }
248    
249        private static class GenerateStringCalcImpl extends AbstractStringCalc {
250            private final MemberListCalc memberListCalc;
251            private final TupleListCalc tupleListCalc;
252            private final StringCalc stringCalc;
253            private final boolean tuple;
254            private final StringCalc sepCalc;
255    
256            public GenerateStringCalcImpl(
257                ResolvedFunCall call,
258                ListCalc listCalc,
259                StringCalc stringCalc,
260                boolean tuple,
261                StringCalc sepCalc)
262            {
263                super(call, new Calc[]{listCalc, stringCalc});
264                if (listCalc instanceof MemberListCalc) {
265                    this.memberListCalc = (MemberListCalc) listCalc;
266                    this.tupleListCalc = null;
267                } else {
268                    this.memberListCalc = null;
269                    this.tupleListCalc = (TupleListCalc) listCalc;
270                }
271                this.stringCalc = stringCalc;
272                this.tuple = tuple;
273                this.sepCalc = sepCalc;
274            }
275    
276            public String evaluateString(Evaluator evaluator) {
277                StringBuilder buf = new StringBuilder();
278                int k = 0;
279                if (tuple) {
280                    final List<Member[]> list1 =
281                        tupleListCalc.evaluateTupleList(evaluator);
282                    final Evaluator evaluator2 = evaluator.push();
283                    for (Member[] members : list1) {
284                        evaluator2.setContext(members);
285                        if (k++ > 0) {
286                            String sep = sepCalc.evaluateString(evaluator2);
287                            buf.append(sep);
288                        }
289                        final String result2 =
290                            stringCalc.evaluateString(evaluator2);
291                        buf.append(result2);
292                    }
293                } else {
294                    final List<Member> list1 =
295                        memberListCalc.evaluateMemberList(evaluator);
296                    final Evaluator evaluator2 = evaluator.push();
297                    for (Member member : list1) {
298                        evaluator2.setContext(member);
299                        if (k++ > 0) {
300                            String sep = sepCalc.evaluateString(evaluator2);
301                            buf.append(sep);
302                        }
303                        final String result2 =
304                            stringCalc.evaluateString(evaluator2);
305                        buf.append(result2);
306                    }
307                }
308                return buf.toString();
309            }
310    
311            public boolean dependsOn(Dimension dimension) {
312                return anyDependsButFirst(getCalcs(), dimension);
313            }
314        }
315    }
316    
317    // End GenerateFunDef.java