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.*;
014import mondrian.mdx.ResolvedFunCall;
015import mondrian.olap.*;
016import mondrian.olap.type.*;
017
018import java.util.*;
019
020/**
021 * Definition of the <code>Generate</code> MDX function.
022 *
023 * @author jhyde
024 * @since Mar 23, 2006
025 */
026class GenerateFunDef extends FunDefBase {
027    static final ReflectiveMultiResolver ListResolver =
028        new ReflectiveMultiResolver(
029            "Generate",
030            "Generate(<Set1>, <Set2>[, ALL])",
031            "Applies a set to each member of another set and joins the resulting sets by union.",
032            new String[] {"fxxx", "fxxxy"},
033            GenerateFunDef.class);
034
035    static final ReflectiveMultiResolver StringResolver =
036        new ReflectiveMultiResolver(
037            "Generate",
038            "Generate(<Set>, <String>[, <String>])",
039            "Applies a set to a string expression and joins resulting sets by string concatenation.",
040            new String[] {"fSxS", "fSxSS"},
041            GenerateFunDef.class);
042
043    private static final String[] ReservedWords = new String[] {"ALL"};
044
045    public GenerateFunDef(FunDef dummyFunDef) {
046        super(dummyFunDef);
047    }
048
049    public Type getResultType(Validator validator, Exp[] args) {
050        final Type type = args[1].getType();
051        if (type instanceof StringType) {
052            // Generate(<Set>, <String>[, <String>])
053            return type;
054        } else {
055            final Type memberType = TypeUtil.toMemberOrTupleType(type);
056            return new SetType(memberType);
057        }
058    }
059
060    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
061        final IterCalc iterCalc = compiler.compileIter(call.getArg(0));
062        if (call.getArg(1).getType() instanceof StringType) {
063            final StringCalc stringCalc =
064                compiler.compileString(call.getArg(1));
065            final StringCalc delimCalc;
066            if (call.getArgCount() == 3) {
067                delimCalc = compiler.compileString(call.getArg(2));
068            } else {
069                delimCalc = ConstantCalc.constantString("");
070            }
071
072            return new GenerateStringCalcImpl(
073                call, (IterCalc) iterCalc, stringCalc, delimCalc);
074        } else {
075            final ListCalc listCalc2 =
076                compiler.compileList(call.getArg(1));
077            final String literalArg = getLiteralArg(call, 2, "", ReservedWords);
078            final boolean all = literalArg.equalsIgnoreCase("ALL");
079            final int arityOut = call.getType().getArity();
080            return new GenerateListCalcImpl(
081                call, iterCalc, listCalc2, arityOut, all);
082        }
083    }
084
085    private static class GenerateListCalcImpl extends AbstractListCalc {
086        private final IterCalc iterCalc1;
087        private final ListCalc listCalc2;
088        private final int arityOut;
089        private final boolean all;
090
091        public GenerateListCalcImpl(
092            ResolvedFunCall call,
093            IterCalc iterCalc,
094            ListCalc listCalc2,
095            int arityOut,
096            boolean all)
097        {
098            super(call, new Calc[]{iterCalc, listCalc2});
099            this.iterCalc1 = iterCalc;
100            this.listCalc2 = listCalc2;
101            this.arityOut = arityOut;
102            this.all = all;
103        }
104
105        public TupleList evaluateList(Evaluator evaluator) {
106            final int savepoint = evaluator.savepoint();
107            try {
108                evaluator.setNonEmpty(false);
109                final TupleIterable iterable1 =
110                        iterCalc1.evaluateIterable(evaluator);
111                evaluator.restore(savepoint);
112                TupleList result = TupleCollections.createList(arityOut);
113                if (all) {
114                    final TupleCursor cursor = iterable1.tupleCursor();
115                    while (cursor.forward()) {
116                        cursor.setContext(evaluator);
117                        final TupleList result2 =
118                            listCalc2.evaluateList(evaluator);
119                        result.addAll(result2);
120                    }
121                } else {
122                    final Set<List<Member>> emitted =
123                            new HashSet<List<Member>>();
124                    final TupleCursor cursor = iterable1.tupleCursor();
125                    while (cursor.forward()) {
126                        cursor.setContext(evaluator);
127                        final TupleList result2 =
128                                listCalc2.evaluateList(evaluator);
129                        addDistinctTuples(result, result2, emitted);
130                    }
131                }
132                return result;
133            } finally {
134                evaluator.restore(savepoint);
135            }
136        }
137
138        private static void addDistinctTuples(
139            TupleList result,
140            TupleList result2,
141            Set<List<Member>> emitted)
142        {
143            for (List<Member> row : result2) {
144                // wrap array for correct distinctness test
145                if (emitted.add(row)) {
146                    result.add(row);
147                }
148            }
149        }
150
151        public boolean dependsOn(Hierarchy hierarchy) {
152            return anyDependsButFirst(getCalcs(), hierarchy);
153        }
154    }
155
156    private static class GenerateStringCalcImpl extends AbstractStringCalc {
157        private final IterCalc iterCalc;
158        private final StringCalc stringCalc;
159        private final StringCalc sepCalc;
160
161        public GenerateStringCalcImpl(
162            ResolvedFunCall call,
163            IterCalc iterCalc,
164            StringCalc stringCalc,
165            StringCalc sepCalc)
166        {
167            super(call, new Calc[]{iterCalc, stringCalc});
168            this.iterCalc = iterCalc;
169            this.stringCalc = stringCalc;
170            this.sepCalc = sepCalc;
171        }
172
173        public String evaluateString(Evaluator evaluator) {
174            final int savepoint = evaluator.savepoint();
175            try {
176                StringBuilder buf = new StringBuilder();
177                int k = 0;
178                final TupleIterable iter11 =
179                    iterCalc.evaluateIterable(evaluator);
180                final TupleCursor cursor = iter11.tupleCursor();
181                while (cursor.forward()) {
182                    cursor.setContext(evaluator);
183                    if (k++ > 0) {
184                        String sep = sepCalc.evaluateString(evaluator);
185                        buf.append(sep);
186                    }
187                    final String result2 =
188                        stringCalc.evaluateString(evaluator);
189                    buf.append(result2);
190                }
191                return buf.toString();
192            } finally {
193                evaluator.restore(savepoint);
194            }
195        }
196
197        public boolean dependsOn(Hierarchy hierarchy) {
198            return anyDependsButFirst(getCalcs(), hierarchy);
199        }
200    }
201}
202
203// End GenerateFunDef.java