001    /*
002    // $Id: //open/mondrian-release/3.1/src/main/mondrian/calc/impl/AbstractExpCompiler.java#4 $
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.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    import mondrian.mdx.UnresolvedFunCall;
020    
021    import java.util.*;
022    
023    /**
024     * Abstract implementation of the {@link mondrian.calc.ExpCompiler} interface.
025     *
026     * @author jhyde
027     * @version $Id: //open/mondrian-release/3.1/src/main/mondrian/calc/impl/AbstractExpCompiler.java#4 $
028     * @since Sep 29, 2005
029     */
030    public class AbstractExpCompiler implements ExpCompiler {
031        private final Evaluator evaluator;
032        private final Validator validator;
033        private final Map<Parameter, ParameterSlotImpl> parameterSlots =
034            new HashMap<Parameter, ParameterSlotImpl>();
035        private List<ResultStyle> resultStyles;
036    
037        /**
038         * Creates an AbstractExpCompiler
039         *
040         * @param evaluator Evaluator
041         * @param validator Validator
042         */
043        public AbstractExpCompiler(Evaluator evaluator, Validator validator) {
044            this(evaluator, validator, ResultStyle.ANY_LIST);
045        }
046    
047        /**
048         * Creates an AbstractExpCompiler which is constrained to produce one of
049         * a set of result styles.
050         *
051         * @param evaluator Evaluator
052         * @param validator Validator
053         * @param resultStyles List of result styles, preferred first, must not be
054         */
055        public AbstractExpCompiler(
056            Evaluator evaluator,
057            Validator validator,
058            List<ResultStyle> resultStyles)
059        {
060            this.evaluator = evaluator;
061            this.validator = validator;
062            this.resultStyles = (resultStyles == null)
063                ? ResultStyle.ANY_LIST : resultStyles;
064        }
065    
066        public Evaluator getEvaluator() {
067            return evaluator;
068        }
069    
070        public Validator getValidator() {
071            return validator;
072        }
073    
074        /**
075         * {@inheritDoc}
076         *
077         * Uses the current ResultStyle to compile the expression.
078         */
079        public Calc compile(Exp exp) {
080            return exp.accept(this);
081        }
082    
083        /**
084         * {@inheritDoc}
085         *
086         * Uses a new ResultStyle to compile the expression.
087         */
088        public Calc compileAs(
089            Exp exp,
090            Type resultType,
091            List<ResultStyle> preferredResultTypes)
092        {
093            assert preferredResultTypes != null;
094            int substitutions = 0;
095            if (Util.Retrowoven) {
096                // Copy and replace ITERABLE
097                // A number of functions declare that they can accept
098                // ITERABLEs so here is where that those are converted to innocent
099                // LISTs for jdk1.4 and other retrowoven code.
100                List<ResultStyle> tmp =
101                    new ArrayList<ResultStyle>(preferredResultTypes.size());
102                for (ResultStyle preferredResultType : preferredResultTypes) {
103                    if (preferredResultType == ResultStyle.ITERABLE) {
104                        preferredResultType = ResultStyle.LIST;
105                        ++substitutions;
106                    }
107                    tmp.add(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                    } else if (resultType instanceof ScalarType) {
124                        return compileScalar(exp, false);
125                    }
126                }
127                final Calc calc = compile(exp);
128                if (substitutions > 0) {
129                    if (calc == null) {
130                        this.resultStyles =
131                            Collections.singletonList(ResultStyle.ITERABLE);
132                        return compile(exp);
133                    } else if (calc instanceof IterCalc) {
134                        return calc;
135                    } else {
136                        assert calc instanceof ListCalc;
137                        if (((SetType) calc.getType()).getArity() == 1) {
138                            return toIter((MemberListCalc) calc);
139                        } else {
140                            return toIter((TupleListCalc) calc);
141                        }
142                    }
143                }
144                return calc;
145            } finally {
146                this.resultStyles = save;
147            }
148        }
149    
150        public MemberCalc compileMember(Exp exp) {
151            final Type type = exp.getType();
152            if (type instanceof DimensionType) {
153                final DimensionCalc dimensionCalc = compileDimension(exp);
154                return new DimensionCurrentMemberCalc(
155                    new DummyExp(TypeUtil.toMemberType(type)), dimensionCalc);
156            } else if (type instanceof HierarchyType) {
157                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
158                return new HierarchyCurrentMemberFunDef.CalcImpl(
159                    new DummyExp(TypeUtil.toMemberType(type)), hierarchyCalc);
160            } else if (type instanceof NullType) {
161                throw MondrianResource.instance().NullNotSupported.ex();
162            }
163            assert type instanceof MemberType;
164            return (MemberCalc) compile(exp);
165        }
166    
167        public LevelCalc compileLevel(Exp exp) {
168            final Type type = exp.getType();
169            if (type instanceof MemberType) {
170                // <Member> --> <Member>.Level
171                final MemberCalc memberCalc = compileMember(exp);
172                return new MemberLevelFunDef.CalcImpl(
173                    new DummyExp(LevelType.forType(type)),
174                    memberCalc);
175            }
176            assert type instanceof LevelType;
177            return (LevelCalc) compile(exp);
178        }
179    
180        public DimensionCalc compileDimension(Exp exp) {
181            final Type type = exp.getType();
182            if (type instanceof HierarchyType) {
183                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
184                return new HierarchyDimensionFunDef.CalcImpl(
185                    new DummyExp(new DimensionType(type.getDimension())),
186                    hierarchyCalc);
187            }
188            assert type instanceof DimensionType : type;
189            return (DimensionCalc) compile(exp);
190        }
191    
192        public HierarchyCalc compileHierarchy(Exp exp) {
193            final Type type = exp.getType();
194            if (type instanceof DimensionType) {
195                // <Dimension> --> unique Hierarchy else error
196                // Resolve at compile time if constant
197                final Dimension dimension = type.getDimension();
198                if (dimension != null) {
199                    final Hierarchy hierarchy =
200                        FunUtil.getDimensionDefaultHierarchy(dimension);
201                    if (hierarchy != null) {
202                        return (HierarchyCalc) ConstantCalc.constantHierarchy(
203                            hierarchy);
204                    }
205                }
206                final DimensionCalc dimensionCalc = compileDimension(exp);
207                return new DimensionHierarchyCalc(
208                    new DummyExp(HierarchyType.forType(type)),
209                    dimensionCalc);
210            }
211            if (type instanceof MemberType) {
212                // <Member> --> <Member>.Hierarchy
213                final MemberCalc memberCalc = compileMember(exp);
214                return new MemberHierarchyFunDef.CalcImpl(
215                    new DummyExp(HierarchyType.forType(type)),
216                    memberCalc);
217            }
218            if (type instanceof LevelType) {
219                // <Level> --> <Level>.Hierarchy
220                final LevelCalc levelCalc = compileLevel(exp);
221                return new LevelHierarchyFunDef.CalcImpl(
222                    new DummyExp(HierarchyType.forType(type)),
223                    levelCalc);
224            }
225            assert type instanceof HierarchyType;
226            return (HierarchyCalc) compile(exp);
227        }
228    
229        public IntegerCalc compileInteger(Exp exp) {
230            final Calc calc = compileScalar(exp, false);
231            final Type type = calc.getType();
232            if (type instanceof DecimalType
233                && ((DecimalType) type).getScale() == 0)
234            {
235                return (IntegerCalc) calc;
236            } else if (type instanceof NumericType) {
237                if (calc instanceof ConstantCalc) {
238                    ConstantCalc constantCalc = (ConstantCalc) calc;
239                    return new ConstantCalc(
240                        new DecimalType(Integer.MAX_VALUE, 0),
241                        constantCalc.evaluateInteger(null));
242                } else if (calc instanceof DoubleCalc) {
243                    final DoubleCalc doubleCalc = (DoubleCalc) calc;
244                    return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) {
245                        public int evaluateInteger(Evaluator evaluator) {
246                            return (int) doubleCalc.evaluateDouble(evaluator);
247                        }
248                    };
249                }
250            }
251            return (IntegerCalc) calc;
252        }
253    
254        public StringCalc compileString(Exp exp) {
255            return (StringCalc) compileScalar(exp, false);
256        }
257    
258        public DateTimeCalc compileDateTime(Exp exp) {
259            return (DateTimeCalc) compileScalar(exp, false);
260        }
261    
262        public ListCalc compileList(Exp exp) {
263            return compileList(exp, false);
264        }
265    
266        public ListCalc compileList(Exp exp, boolean mutable) {
267            assert exp.getType() instanceof SetType : "must be a set: " + exp;
268            final List<ResultStyle> resultStyleList;
269            if (mutable) {
270                resultStyleList = ResultStyle.MUTABLELIST_ONLY;
271            } else {
272                resultStyleList = ResultStyle.LIST_ONLY;
273            }
274            Calc calc = compileAs(exp, null, resultStyleList);
275            if (calc instanceof ListCalc) {
276                return (ListCalc) calc;
277            }
278            if (calc == null) {
279                calc = compileAs(exp, null, ResultStyle.ITERABLE_ANY);
280                assert calc != null;
281            }
282            if (calc instanceof IterCalc) {
283                if (((SetType) calc.getType()).getArity() == 1) {
284                    return toList((MemberIterCalc) calc);
285                } else {
286                    return toList((TupleIterCalc) calc);
287                }
288            } else {
289                // A set can only be implemented as a list or an iterable.
290                throw Util.newInternal("Cannot convert calc to list: " + calc);
291            }
292        }
293    
294        /**
295         * Converts an iterable over members to a list of members.
296         *
297         * @param calc Calc
298         * @return List calculation.
299         */
300        public MemberListCalc toList(MemberIterCalc calc) {
301            return new IterableMemberListCalc(calc);
302        }
303    
304        /**
305         * Converts an iterable over tuples to a list of tuples.
306         *
307         * @param calc Calc
308         * @return List calculation.
309         */
310        public TupleListCalc toList(TupleIterCalc calc) {
311            return new IterableTupleListCalc(calc);
312        }
313    
314        public IterCalc compileIter(Exp exp) {
315            Calc calc = compileAs(exp, null, ResultStyle.ITERABLE_ONLY);
316            if (calc == null) {
317                calc = compileAs(exp, null, ResultStyle.ANY_ONLY);
318                assert calc != null;
319            }
320            if (calc instanceof IterCalc) {
321                return (IterCalc) calc;
322            } else {
323                if (((SetType) calc.getType()).getArity() == 1) {
324                    return toIter((MemberListCalc) calc);
325                } else {
326                    return toIter((TupleListCalc) calc);
327                }
328            }
329        }
330    
331        /**
332         * Converts a list of members to an iterable over members.
333         *
334         * @param memberListCalc Calc
335         * @return Iterable calculation
336         */
337        public MemberIterCalc toIter(final MemberListCalc memberListCalc) {
338            return new MemberListIterCalc(memberListCalc);
339        }
340    
341        /**
342         * Converts a list of tuples to an iterable over tuples.
343         *
344         * @param tupleListCalc Calc
345         * @return Iterable calculation
346         */
347        public TupleIterCalc toIter(final TupleListCalc tupleListCalc) {
348            return new TupleListIterCalc(tupleListCalc);
349        }
350    
351        public BooleanCalc compileBoolean(Exp exp) {
352            final Calc calc = compileScalar(exp, false);
353            if (calc instanceof BooleanCalc) {
354                if (calc instanceof ConstantCalc) {
355                    final Object o = calc.evaluate(null);
356                    if (!(o instanceof Boolean)) {
357                        return ConstantCalc.constantBoolean(
358                            CastFunDef.toBoolean(o, new BooleanType()));
359                    }
360                }
361                return (BooleanCalc) calc;
362            } else if (calc instanceof DoubleCalc) {
363                final DoubleCalc doubleCalc = (DoubleCalc) calc;
364                return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) {
365                    public boolean evaluateBoolean(Evaluator evaluator) {
366                        return doubleCalc.evaluateDouble(evaluator) != 0;
367                    }
368                };
369            } else if (calc instanceof IntegerCalc) {
370                final IntegerCalc integerCalc = (IntegerCalc) calc;
371                return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) {
372                    public boolean evaluateBoolean(Evaluator evaluator) {
373                        return integerCalc.evaluateInteger(evaluator) != 0;
374                    }
375                };
376            } else {
377                return (BooleanCalc) calc;
378            }
379        }
380    
381        public DoubleCalc compileDouble(Exp exp) {
382            final DoubleCalc calc = (DoubleCalc) compileScalar(exp, false);
383            if (calc instanceof ConstantCalc
384                && !(calc.evaluate(null) instanceof Double))
385            {
386                return ConstantCalc.constantDouble(
387                    calc.evaluateDouble(null));
388            }
389            return calc;
390        }
391    
392        public TupleCalc compileTuple(Exp exp) {
393            return (TupleCalc) compile(exp);
394        }
395    
396        public Calc compileScalar(Exp exp, boolean specific) {
397            final Type type = exp.getType();
398            if (type instanceof MemberType) {
399                MemberType memberType = (MemberType) type;
400                MemberCalc calc = compileMember(exp);
401                return new MemberValueCalc(
402                    new DummyExp(memberType.getValueType()),
403                    new MemberCalc[] {calc});
404            } else if (type instanceof DimensionType) {
405                final DimensionCalc dimensionCalc = compileDimension(exp);
406                MemberType memberType = MemberType.forType(type);
407                final MemberCalc dimensionCurrentMemberCalc =
408                    new DimensionCurrentMemberCalc(
409                        new DummyExp(memberType),
410                        dimensionCalc);
411                return new MemberValueCalc(
412                    new DummyExp(memberType.getValueType()),
413                    new MemberCalc[] {dimensionCurrentMemberCalc});
414            } else if (type instanceof HierarchyType) {
415                HierarchyType hierarchyType = (HierarchyType) type;
416                MemberType memberType =
417                    MemberType.forHierarchy(hierarchyType.getHierarchy());
418                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
419                final MemberCalc hierarchyCurrentMemberCalc =
420                    new HierarchyCurrentMemberFunDef.CalcImpl(
421                        new DummyExp(memberType), hierarchyCalc);
422                return new MemberValueCalc(
423                    new DummyExp(memberType.getValueType()),
424                    new MemberCalc[] {hierarchyCurrentMemberCalc});
425            } else if (type instanceof TupleType) {
426                TupleType tupleType = (TupleType) type;
427                TupleCalc tupleCalc = compileTuple(exp);
428                final TupleValueCalc scalarCalc = new TupleValueCalc(
429                    new DummyExp(tupleType.getValueType()), tupleCalc);
430                return scalarCalc.optimize();
431            } else if (type instanceof ScalarType) {
432                if (specific) {
433                    if (type instanceof BooleanType) {
434                        return compileBoolean(exp);
435                    } else if (type instanceof NumericType) {
436                        return compileDouble(exp);
437                    } else if (type instanceof StringType) {
438                        return compileString(exp);
439                    } else {
440                        return compile(exp);
441                    }
442                } else {
443                    return compile(exp);
444                }
445            } else {
446                return compile(exp);
447            }
448        }
449    
450        public ParameterSlot registerParameter(Parameter parameter) {
451            ParameterSlot slot = parameterSlots.get(parameter);
452            if (slot != null) {
453                return slot;
454            }
455            int index = parameterSlots.size();
456            ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index);
457            parameterSlots.put(parameter, slot2);
458            slot2.value = parameter.getValue();
459    
460            // Compile the expression only AFTER the parameter has been
461            // registered with a slot. Otherwise a cycle is possible.
462            final Type type = parameter.getType();
463            Exp defaultExp = parameter.getDefaultExp();
464            Calc calc;
465            if (type instanceof ScalarType) {
466                if (!defaultExp.getType().equals(type)) {
467                    defaultExp =
468                        new UnresolvedFunCall(
469                            "Cast",
470                            Syntax.Cast,
471                            new Exp[] {
472                                defaultExp,
473                                Literal.createSymbol(
474                                    Category.instance.getName(
475                                        TypeUtil.typeToCategory(type)))});
476                    defaultExp = getValidator().validate(defaultExp, true);
477                }
478                calc = compileScalar(defaultExp, true);
479            } else {
480                calc = compileAs(defaultExp, type, resultStyles);
481            }
482            slot2.setDefaultValueCalc(calc);
483            return slot2;
484        }
485    
486        public List<ResultStyle> getAcceptableResultStyles() {
487            return resultStyles;
488        }
489    
490        /**
491         * Implementation of {@link ParameterSlot}.
492         */
493        private static class ParameterSlotImpl implements ParameterSlot {
494            private final Parameter parameter;
495            private final int index;
496            private Calc defaultValueCalc;
497            private Object value;
498            private Object cachedDefaultValue;
499    
500            /**
501             * Creates a ParameterSlotImpl.
502             *
503             * @param parameter Parameter
504             * @param index Unique index of the slot
505             */
506            public ParameterSlotImpl(
507                Parameter parameter, int index)
508            {
509                this.parameter = parameter;
510                this.index = index;
511            }
512    
513            public int getIndex() {
514                return index;
515            }
516    
517            public Calc getDefaultValueCalc() {
518                return defaultValueCalc;
519            }
520    
521            public Parameter getParameter() {
522                return parameter;
523            }
524    
525            /**
526             * Sets a compiled expression to compute the default value of the
527             * parameter.
528             *
529             * @param calc Compiled expression to compute default value of
530             * parameter
531             *
532             * @see #getDefaultValueCalc()
533             */
534            private void setDefaultValueCalc(Calc calc) {
535                this.defaultValueCalc = calc;
536            }
537    
538            public void setParameterValue(Object value) {
539                this.value = value;
540            }
541    
542            public Object getParameterValue() {
543                return value;
544            }
545    
546            public void setCachedDefaultValue(Object value) {
547                this.cachedDefaultValue = value;
548            }
549    
550            public Object getCachedDefaultValue() {
551                return cachedDefaultValue;
552            }
553        }
554    
555        /**
556         * Adapter that converts a member list calc into a member iter calc.
557         */
558        private static class MemberListIterCalc extends AbstractMemberIterCalc {
559            private final MemberListCalc memberListCalc;
560    
561            public MemberListIterCalc(MemberListCalc memberListCalc) {
562                super(
563                    new DummyExp(memberListCalc.getType()),
564                    new Calc[]{memberListCalc});
565                this.memberListCalc = memberListCalc;
566            }
567    
568            public Iterable<Member> evaluateMemberIterable(Evaluator evaluator) {
569                return memberListCalc.evaluateMemberList(evaluator);
570            }
571        }
572    
573        /**
574         * Adapter that converts a tuple list calc into a tuple iter calc.
575         */
576        private static class TupleListIterCalc extends AbstractTupleIterCalc {
577            private final TupleListCalc tupleListCalc;
578    
579            public TupleListIterCalc(TupleListCalc tupleListCalc) {
580                super(
581                    new DummyExp(tupleListCalc.getType()),
582                    new Calc[]{tupleListCalc});
583                this.tupleListCalc = tupleListCalc;
584            }
585    
586            public Iterable<Member[]> evaluateTupleIterable(Evaluator evaluator) {
587                return tupleListCalc.evaluateTupleList(evaluator);
588            }
589        }
590    
591        /**
592         * Computes the hierarchy of a dimension
593         */
594        private static class DimensionHierarchyCalc extends AbstractHierarchyCalc {
595            private final DimensionCalc dimensionCalc;
596    
597            protected DimensionHierarchyCalc(Exp exp, DimensionCalc dimensionCalc) {
598                super(exp, new Calc[] {dimensionCalc});
599                this.dimensionCalc = dimensionCalc;
600            }
601    
602            public Hierarchy evaluateHierarchy(Evaluator evaluator) {
603                Dimension dimension =
604                    dimensionCalc.evaluateDimension(evaluator);
605                final Hierarchy hierarchy =
606                    FunUtil.getDimensionDefaultHierarchy(dimension);
607                if (hierarchy != null) {
608                    return hierarchy;
609                }
610                throw FunUtil.newEvalException(
611                    MondrianResource.instance()
612                        .CannotImplicitlyConvertDimensionToHierarchy
613                        .ex(
614                        dimension.getName()));
615            }
616        }
617    
618        /**
619         * Computation that returns the current member of a dimension.
620         */
621        public static class DimensionCurrentMemberCalc extends AbstractMemberCalc {
622            private final DimensionCalc dimensionCalc;
623    
624            public DimensionCurrentMemberCalc(
625                Exp exp,
626                DimensionCalc dimensionCalc)
627            {
628                super(exp, new Calc[] {dimensionCalc});
629                this.dimensionCalc = dimensionCalc;
630            }
631    
632            protected String getName() {
633                return "CurrentMember";
634            }
635    
636            public Member evaluateMember(Evaluator evaluator) {
637                Dimension dimension =
638                    dimensionCalc.evaluateDimension(evaluator);
639                return evaluator.getContext(dimension);
640            }
641    
642            public boolean dependsOn(Dimension dimension) {
643                return dimensionCalc.getType().usesDimension(dimension, true);
644            }
645        }
646    }
647    
648    // End AbstractExpCompiler.java