001    /*
002    // $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractCalc.java#1 $
003    // This software is subject to the terms of the Eclipse Public License v1.0
004    // Agreement, available at the following URL:
005    // http://www.eclipse.org/legal/epl-v10.html.
006    // Copyright (C) 2006-2009 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.calc.impl;
011    
012    import mondrian.olap.*;
013    import mondrian.olap.type.Type;
014    import mondrian.calc.Calc;
015    import mondrian.calc.CalcWriter;
016    import mondrian.calc.ResultStyle;
017    import mondrian.mdx.ResolvedFunCall;
018    import mondrian.rolap.RolapEvaluator;
019    import mondrian.rolap.RolapHierarchy;
020    
021    import java.io.PrintWriter;
022    import java.util.List;
023    import java.util.Collections;
024    
025    /**
026     * Abstract implementation of the {@link mondrian.calc.Calc} interface.
027     *
028     * @author jhyde
029     * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractCalc.java#1 $
030     * @since Sep 27, 2005
031     */
032    public abstract class AbstractCalc implements Calc {
033        private final Calc[] calcs;
034        protected final Type type;
035        protected final Exp exp;
036    
037        /**
038         * Creates an AbstractCalc.
039         *
040         * @param exp Source expression
041         * @param calcs Child compiled expressions
042         */
043        protected AbstractCalc(Exp exp, Calc[] calcs) {
044            assert exp != null;
045            this.exp = exp;
046            this.calcs = calcs;
047            this.type = exp.getType();
048        }
049    
050        public Type getType() {
051            return type;
052        }
053    
054        public void accept(CalcWriter calcWriter) {
055            final PrintWriter pw = calcWriter.getWriter();
056            String name = getName();
057            pw.print(name);
058            final Calc[] calcs = getCalcs();
059            final List<Object> argumentList = getArguments();
060            if (calcs.length > 0 || !argumentList.isEmpty()) {
061                pw.print("(");
062                int k = 0;
063                for (Calc calc : calcs) {
064                    if (k++ > 0) {
065                        pw.print(", ");
066                    }
067                    calc.accept(calcWriter);
068                }
069                for (Object o : argumentList) {
070                    if (k++ > 0) {
071                        pw.print(", ");
072                    }
073                    pw.print(o);
074                }
075                pw.print(")");
076            }
077        }
078    
079        /**
080         * Returns the name of this expression type, used when serializing an
081         * expression to a string.
082         *
083         * <p>The default implementation tries to extract a name from a function
084         * call, if any, then prints the last part of the class name.
085         */
086        protected String getName() {
087            String name;
088            if (exp instanceof ResolvedFunCall) {
089                ResolvedFunCall funCall = (ResolvedFunCall) exp;
090                name = funCall.getFunDef().getName();
091            } else {
092                name = getClass().getName();
093                int dot = name.lastIndexOf('.');
094                int dollar = name.lastIndexOf('$');
095                int dotDollar = Math.max(dot, dollar);
096                if (dotDollar >= 0) {
097                    name = name.substring(dotDollar + 1);
098                }
099            }
100            return name;
101        }
102    
103        /**
104         * Returns this expression's child expressions.
105         */
106        public Calc[] getCalcs() {
107            return calcs;
108        }
109    
110        public boolean dependsOn(Hierarchy hierarchy) {
111            return anyDepends(getCalcs(), hierarchy);
112        }
113    
114        /**
115         * Returns true if one of the calcs depends on the given dimension.
116         */
117        public static boolean anyDepends(Calc[] calcs, Hierarchy hierarchy) {
118            for (Calc calc : calcs) {
119                if (calc != null && calc.dependsOn(hierarchy)) {
120                    return true;
121                }
122            }
123            return false;
124        }
125    
126        /**
127         * Returns true if calc[0] depends on dimension,
128         * else false if calc[0] returns dimension,
129         * else true if any of the other calcs depend on dimension.
130         *
131         * <p>Typical application: <code>Aggregate({Set}, {Value Expression})</code>
132         * depends upon everything {Value Expression} depends upon, except the
133         * dimensions of {Set}.
134         */
135        public static boolean anyDependsButFirst(
136            Calc[] calcs, Hierarchy hierarchy)
137        {
138            if (calcs.length == 0) {
139                return false;
140            }
141            if (calcs[0].dependsOn(hierarchy)) {
142                return true;
143            }
144            if (calcs[0].getType().usesHierarchy(hierarchy, true)) {
145                return false;
146            }
147            for (int i = 1; i < calcs.length; i++) {
148                Calc calc = calcs[i];
149                if (calc != null && calc.dependsOn(hierarchy)) {
150                    return true;
151                }
152            }
153            return false;
154        }
155    
156        /**
157         * Returns true if any of the calcs depend on dimension,
158         * else false if any of the calcs return dimension,
159         * else true.
160         */
161        public static boolean butDepends(
162            Calc[] calcs, Hierarchy hierarchy)
163        {
164            boolean result = true;
165            for (Calc calc : calcs) {
166                if (calc != null) {
167                    if (calc.dependsOn(hierarchy)) {
168                        return true;
169                    }
170                    if (calc.getType().usesHierarchy(hierarchy, true)) {
171                        result = false;
172                    }
173                }
174            }
175            return result;
176        }
177    
178        /**
179         * Returns any other arguments to this calc.
180         * The default implementation returns the empty list.
181         */
182        public List<Object> getArguments() {
183            return Collections.emptyList();
184        }
185    
186        /**
187         * Returns a simplified evalator whose context is the same for every
188         * dimension which an expression depends on, and the default member for
189         * every dimension which it does not depend on.
190         *
191         * <p>The default member is often the 'all' member, so this evaluator is
192         * usually the most efficient context in which to evaluate the expression.
193         *
194         * @param calc
195         * @param evaluator
196         */
197        public static Evaluator simplifyEvaluator(Calc calc, Evaluator evaluator) {
198            if (evaluator.isNonEmpty()) {
199                // If NON EMPTY is present, we cannot simplify the context, because
200                // we have to assume that the expression depends on everything.
201                // TODO: Bug 1456418: Convert 'NON EMPTY Crossjoin' to
202                // 'NonEmptyCrossJoin'.
203                return evaluator;
204            }
205            int changeCount = 0;
206            Evaluator ev = evaluator;
207            final List<RolapHierarchy> hierarchies =
208                ((RolapEvaluator) evaluator).getCube().getHierarchies();
209            for (RolapHierarchy hierarchy : hierarchies) {
210                final Member member = ev.getContext(hierarchy);
211                if (member.isAll()) {
212                    continue;
213                }
214                if (calc.dependsOn(hierarchy)) {
215                    continue;
216                }
217                final Member unconstrainedMember =
218                    member.getHierarchy().getDefaultMember();
219                if (member == unconstrainedMember) {
220                    // This is a hierarchy without an 'all' member, and the context
221                    // is already the default member.
222                    continue;
223                }
224                if (changeCount++ == 0) {
225                    ev = evaluator.push(unconstrainedMember);
226                } else {
227                    ev.setContext(unconstrainedMember);
228                }
229            }
230            return ev;
231        }
232    
233        public ResultStyle getResultStyle() {
234            return ResultStyle.VALUE;
235        }
236    }
237    
238    // End AbstractCalc.java