001    /*
002    // $Id: //open/mondrian/src/main/mondrian/calc/impl/AbstractExpCompiler.java#25 $
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.calc.impl;
011    
012    import mondrian.olap.*;
013    import mondrian.olap.fun.*;
014    import mondrian.olap.type.*;
015    import mondrian.olap.type.DimensionType;
016    import mondrian.olap.type.LevelType;
017    import mondrian.resource.MondrianResource;
018    import mondrian.calc.*;
019    
020    import java.util.HashMap;
021    import java.util.Map;
022    import java.util.List;
023    import java.util.ArrayList;
024    
025    /**
026     * Abstract implementation of the {@link mondrian.calc.ExpCompiler} interface.
027     *
028     * @author jhyde
029     * @version $Id: //open/mondrian/src/main/mondrian/calc/impl/AbstractExpCompiler.java#25 $
030     * @since Sep 29, 2005
031     */
032    public class AbstractExpCompiler implements ExpCompiler {
033        private final Evaluator evaluator;
034        private final Validator validator;
035        private final Map<Parameter, ParameterSlotImpl> parameterSlots =
036            new HashMap<Parameter, ParameterSlotImpl>();
037        private List<ResultStyle> resultStyles;
038    
039        /**
040         * Creates an AbstractExpCompiler
041         *
042         * @param evaluator Evaluator
043         * @param validator Validator
044         */
045        public AbstractExpCompiler(Evaluator evaluator, Validator validator) {
046            this(evaluator, validator, ResultStyle.ANY_LIST);
047        }
048    
049        /**
050         * Creates an AbstractExpCompiler which is constrained to produce one of
051         * a set of result styles.
052         *
053         * @param evaluator Evaluator
054         * @param validator Validator
055         * @param resultStyles List of result styles, preferred first, must not be
056         */
057        public AbstractExpCompiler(
058            Evaluator evaluator,
059            Validator validator,
060            List<ResultStyle> resultStyles)
061        {
062            this.evaluator = evaluator;
063            this.validator = validator;
064            this.resultStyles = (resultStyles == null)
065                ? ResultStyle.ANY_LIST : resultStyles;
066        }
067    
068        public Evaluator getEvaluator() {
069            return evaluator;
070        }
071    
072        public Validator getValidator() {
073            return validator;
074        }
075    
076        /**
077         * {@inheritDoc}
078         *
079         * Uses the current ResultStyle to compile the expression.
080         */
081        public Calc compile(Exp exp) {
082            return exp.accept(this);
083        }
084    
085        /**
086         * {@inheritDoc}
087         *
088         * Uses a new ResultStyle to compile the expression.
089         */
090        public Calc compileAs(
091            Exp exp,
092            Type resultType,
093            List<ResultStyle> preferredResultTypes)
094        {
095            assert preferredResultTypes != null;
096            if (Util.Retrowoven) {
097                // Copy and replace ITERABLE
098                // A number of functions declare that they can accept
099                // ITERABLEs so here is where that those are converted to innocent
100                // LISTs for jdk1.4 and other retrowoven code.
101                List<ResultStyle> tmp =
102                    new ArrayList<ResultStyle>(preferredResultTypes.size());
103                for (ResultStyle preferredResultType : preferredResultTypes) {
104                    tmp.add(
105                        (preferredResultType == ResultStyle.ITERABLE)
106                            ? ResultStyle.LIST
107                            : preferredResultType);
108                }
109                preferredResultTypes = tmp;
110            }
111            List<ResultStyle> save = this.resultStyles;
112            try {
113                this.resultStyles = preferredResultTypes;
114                if (resultType != null && resultType != exp.getType()) {
115                    if (resultType instanceof MemberType) {
116                        return compileMember(exp);
117                    } else if (resultType instanceof LevelType) {
118                        return compileLevel(exp);
119                    } else if (resultType instanceof HierarchyType) {
120                        return compileHierarchy(exp);
121                    } else if (resultType instanceof DimensionType) {
122                        return compileDimension(exp);
123                    }
124                }
125                return compile(exp);
126            } finally {
127                this.resultStyles = save;
128            }
129        }
130    
131        public MemberCalc compileMember(Exp exp) {
132            final Type type = exp.getType();
133            if (type instanceof DimensionType) {
134                final DimensionCalc dimensionCalc = compileDimension(exp);
135                return new DimensionCurrentMemberFunDef.CalcImpl(
136                        new DummyExp(TypeUtil.toMemberType(type)), dimensionCalc);
137            } else if (type instanceof HierarchyType) {
138                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
139                return new HierarchyCurrentMemberFunDef.CalcImpl(
140                        new DummyExp(TypeUtil.toMemberType(type)), hierarchyCalc);
141            } else if (type instanceof NullType) {
142                throw MondrianResource.instance().NullNotSupported.ex();
143            }
144            assert type instanceof MemberType;
145            return (MemberCalc) compile(exp);
146        }
147    
148        public LevelCalc compileLevel(Exp exp) {
149            final Type type = exp.getType();
150            if (type instanceof MemberType) {
151                // <Member> --> <Member>.Level
152                final MemberCalc memberCalc = compileMember(exp);
153                return new MemberLevelFunDef.CalcImpl(
154                        new DummyExp(LevelType.forType(type)),
155                        memberCalc);
156            }
157            assert type instanceof LevelType;
158            return (LevelCalc) compile(exp);
159        }
160    
161        public DimensionCalc compileDimension(Exp exp) {
162            final Type type = exp.getType();
163            if (type instanceof HierarchyType) {
164                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
165                return new HierarchyDimensionFunDef.CalcImpl(
166                    new DummyExp(new DimensionType(type.getDimension())),
167                        hierarchyCalc);
168            }
169            assert type instanceof DimensionType : type;
170            return (DimensionCalc) compile(exp);
171        }
172    
173        public HierarchyCalc compileHierarchy(Exp exp) {
174            final Type type = exp.getType();
175            if (type instanceof DimensionType ||
176                    type instanceof MemberType) {
177                // <Dimension> --> <Dimension>.CurrentMember.Hierarchy
178                final MemberCalc memberCalc = compileMember(exp);
179                return new MemberHierarchyFunDef.CalcImpl(
180                        new DummyExp(HierarchyType.forType(type)),
181                        memberCalc);
182            }
183            if (type instanceof LevelType) {
184                // <Level> --> <Level>.Hierarchy
185                final LevelCalc levelCalc = compileLevel(exp);
186                return new LevelHierarchyFunDef.CalcImpl(
187                        new DummyExp(HierarchyType.forType(type)),
188                        levelCalc);
189            }
190            assert type instanceof HierarchyType;
191            return (HierarchyCalc) compile(exp);
192        }
193    
194        public IntegerCalc compileInteger(Exp exp) {
195            final Calc calc = compileScalar(exp, false);
196            final Type type = calc.getType();
197            if (type instanceof DecimalType
198                && ((DecimalType) type).getScale() == 0) {
199                return (IntegerCalc) calc;
200            } else if (type instanceof NumericType) {
201                if (calc instanceof ConstantCalc) {
202                    ConstantCalc constantCalc = (ConstantCalc) calc;
203                    return new ConstantCalc(
204                        new DecimalType(Integer.MAX_VALUE, 0),
205                        constantCalc.evaluateInteger(null));
206                } else if (calc instanceof DoubleCalc) {
207                    final DoubleCalc doubleCalc = (DoubleCalc) calc;
208                    return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) {
209                        public int evaluateInteger(Evaluator evaluator) {
210                            return (int) doubleCalc.evaluateDouble(evaluator);
211                        }
212                    };
213                }
214            }
215            return (IntegerCalc) calc;
216        }
217    
218        public StringCalc compileString(Exp exp) {
219            return (StringCalc) compile(exp);
220        }
221    
222        public DateTimeCalc compileDateTime(Exp exp) {
223            return (DateTimeCalc) compile(exp);
224        }
225    
226        public ListCalc compileList(Exp exp) {
227            return compileList(exp, false);
228        }
229    
230        public ListCalc compileList(Exp exp, boolean mutable) {
231            if (mutable) {
232                return (ListCalc) compileAs(exp, null, ResultStyle.MUTABLELIST_ONLY);
233            } else {
234                return (ListCalc) compileAs(exp, null, ResultStyle.LIST_ONLY);
235            }
236        }
237    
238        public IterCalc compileIter(Exp exp) {
239            return (IterCalc) compileAs(exp, null, ResultStyle.ITERABLE_ONLY);
240        }
241    
242        public BooleanCalc compileBoolean(Exp exp) {
243            final Calc calc = compileScalar(exp, false);
244            if (calc instanceof BooleanCalc) {
245                if (calc instanceof ConstantCalc) {
246                    final Object o = calc.evaluate(null);
247                    if (!(o instanceof Boolean)) {
248                        return ConstantCalc.constantBoolean(
249                            CastFunDef.toBoolean(o, new BooleanType()));
250                    }
251                }
252                return (BooleanCalc) calc;
253            } else if (calc instanceof DoubleCalc) {
254                final DoubleCalc doubleCalc = (DoubleCalc) calc;
255                return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) {
256                    public boolean evaluateBoolean(Evaluator evaluator) {
257                        return doubleCalc.evaluateDouble(evaluator) != 0;
258                    }
259                };
260            } else if (calc instanceof IntegerCalc) {
261                final IntegerCalc integerCalc = (IntegerCalc) calc;
262                return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) {
263                    public boolean evaluateBoolean(Evaluator evaluator) {
264                        return integerCalc.evaluateInteger(evaluator) != 0;
265                    }
266                };
267            } else {
268                return (BooleanCalc) calc;
269            }
270        }
271    
272        public DoubleCalc compileDouble(Exp exp) {
273            final DoubleCalc calc = (DoubleCalc) compileScalar(exp, false);
274            if (calc instanceof ConstantCalc
275                && !(calc.evaluate(null) instanceof Double)) {
276                return ConstantCalc.constantDouble(
277                    calc.evaluateDouble(null));
278            }
279            return calc;
280        }
281    
282        public TupleCalc compileTuple(Exp exp) {
283            return (TupleCalc) compile(exp);
284        }
285    
286        public Calc compileScalar(Exp exp, boolean specific) {
287            final Type type = exp.getType();
288            if (type instanceof MemberType) {
289                MemberType memberType = (MemberType) type;
290                MemberCalc calc = compileMember(exp);
291                return new MemberValueCalc(
292                        new DummyExp(memberType.getValueType()),
293                        new MemberCalc[] {calc});
294            } else if (type instanceof DimensionType) {
295                final DimensionCalc dimensionCalc = compileDimension(exp);
296                MemberType memberType = MemberType.forType(type);
297                final MemberCalc dimensionCurrentMemberCalc =
298                        new DimensionCurrentMemberFunDef.CalcImpl(
299                                new DummyExp(memberType),
300                                dimensionCalc);
301                return new MemberValueCalc(
302                        new DummyExp(memberType.getValueType()),
303                        new MemberCalc[] {dimensionCurrentMemberCalc});
304            } else if (type instanceof HierarchyType) {
305                HierarchyType hierarchyType = (HierarchyType) type;
306                MemberType memberType =
307                        MemberType.forHierarchy(hierarchyType.getHierarchy());
308                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
309                final MemberCalc hierarchyCurrentMemberCalc =
310                        new HierarchyCurrentMemberFunDef.CalcImpl(
311                                new DummyExp(memberType), hierarchyCalc);
312                return new MemberValueCalc(
313                        new DummyExp(memberType.getValueType()),
314                        new MemberCalc[] {hierarchyCurrentMemberCalc});
315            } else if (type instanceof TupleType) {
316                TupleType tupleType = (TupleType) type;
317                TupleCalc tupleCalc = compileTuple(exp);
318                final TupleValueCalc scalarCalc = new TupleValueCalc(
319                        new DummyExp(tupleType.getValueType()), tupleCalc);
320                return scalarCalc.optimize();
321            } else if (type instanceof ScalarType) {
322                if (specific) {
323                    if (type instanceof BooleanType) {
324                        return compileBoolean(exp);
325                    } else if (type instanceof NumericType) {
326                        return compileDouble(exp);
327                    } else if (type instanceof StringType) {
328                        return compileString(exp);
329                    } else {
330                        return compile(exp);
331                    }
332                } else {
333                    return compile(exp);
334                }
335            } else {
336                return compile(exp);
337            }
338        }
339    
340        public ParameterSlot registerParameter(Parameter parameter) {
341            ParameterSlot slot = parameterSlots.get(parameter);
342            if (slot != null) {
343                return slot;
344            }
345            int index = parameterSlots.size();
346            ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index);
347            parameterSlots.put(parameter, slot2);
348            slot2.value = parameter.getValue();
349    
350            // Compile the expression only AFTER the parameter has been
351            // registered with a slot. Otherwise a cycle is possible.
352            Calc calc = parameter.getDefaultExp().accept(this);
353            slot2.setDefaultValueCalc(calc);
354            return slot2;
355        }
356    
357        public List<ResultStyle> getAcceptableResultStyles() {
358            return resultStyles;
359        }
360    
361        /**
362         * Implementation of {@link ParameterSlot}.
363         */
364        private static class ParameterSlotImpl implements ParameterSlot {
365            private final Parameter parameter;
366            private final int index;
367            private Calc defaultValueCalc;
368            private Object value;
369            private Object cachedDefaultValue;
370    
371            /**
372             * Creates a ParameterSlotImpl.
373             *
374             * @param parameter Parameter
375             * @param index Unique index of the slot
376             */
377            public ParameterSlotImpl(
378                Parameter parameter, int index)
379            {
380                this.parameter = parameter;
381                this.index = index;
382            }
383    
384            public int getIndex() {
385                return index;
386            }
387    
388            public Calc getDefaultValueCalc() {
389                return defaultValueCalc;
390            }
391    
392            public Parameter getParameter() {
393                return parameter;
394            }
395    
396            /**
397             * Sets a compiled expression to compute the default value of the
398             * parameter.
399             *
400             * @param calc Compiled expression to compute default value of
401             * parameter
402             *
403             * @see #getDefaultValueCalc()
404             */
405            private void setDefaultValueCalc(Calc calc) {
406                this.defaultValueCalc = calc;
407            }
408    
409            public void setParameterValue(Object value) {
410                this.value = value;
411            }
412    
413            public Object getParameterValue() {
414                return value;
415            }
416    
417            public void setCachedDefaultValue(Object value) {
418                this.cachedDefaultValue = value;
419            }
420    
421            public Object getCachedDefaultValue() {
422                return cachedDefaultValue;
423            }
424        }
425    }
426    
427    // End AbstractExpCompiler.java