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