001    /*
002    // $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractExpCompiler.java#3 $
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-2010 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.*;
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.2/src/main/mondrian/calc/impl/AbstractExpCompiler.java#3 $
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 HierarchyType) {
153                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
154                return hierarchyToMember(hierarchyCalc);
155            } else if (type instanceof NullType) {
156                throw MondrianResource.instance().NullNotSupported.ex();
157            } else if (type instanceof DimensionType) {
158                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
159                return hierarchyToMember(hierarchyCalc);
160            }
161            assert type instanceof MemberType : type;
162            return (MemberCalc) compile(exp);
163        }
164    
165        private MemberCalc hierarchyToMember(
166            HierarchyCalc hierarchyCalc)
167        {
168            final Hierarchy hierarchy = hierarchyCalc.getType().getHierarchy();
169            if (hierarchy != null) {
170                return new HierarchyCurrentMemberFunDef.FixedCalcImpl(
171                    new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())),
172                    hierarchy);
173            } else {
174                return new HierarchyCurrentMemberFunDef.CalcImpl(
175                    new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())),
176                    hierarchyCalc);
177            }
178        }
179    
180        public LevelCalc compileLevel(Exp exp) {
181            final Type type = exp.getType();
182            if (type instanceof MemberType) {
183                // <Member> --> <Member>.Level
184                final MemberCalc memberCalc = compileMember(exp);
185                return new MemberLevelFunDef.CalcImpl(
186                    new DummyExp(LevelType.forType(type)),
187                    memberCalc);
188            }
189            assert type instanceof LevelType;
190            return (LevelCalc) compile(exp);
191        }
192    
193        public DimensionCalc compileDimension(Exp exp) {
194            final Type type = exp.getType();
195            if (type instanceof HierarchyType) {
196                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
197                return new HierarchyDimensionFunDef.CalcImpl(
198                    new DummyExp(new DimensionType(type.getDimension())),
199                    hierarchyCalc);
200            }
201            assert type instanceof DimensionType : type;
202            return (DimensionCalc) compile(exp);
203        }
204    
205        public HierarchyCalc compileHierarchy(Exp exp) {
206            final Type type = exp.getType();
207            if (type instanceof DimensionType) {
208                // <Dimension> --> unique Hierarchy else error
209                // Resolve at compile time if constant
210                final Dimension dimension = type.getDimension();
211                if (dimension != null) {
212                    final Hierarchy hierarchy =
213                        FunUtil.getDimensionDefaultHierarchy(dimension);
214                    if (hierarchy != null) {
215                        return (HierarchyCalc) ConstantCalc.constantHierarchy(
216                            hierarchy);
217                    } else {
218                        // SSAS gives error at run time (often as an error in a
219                        // cell) but we prefer to give an error at validate time.
220                        throw MondrianResource.instance()
221                            .CannotImplicitlyConvertDimensionToHierarchy.ex(
222                                dimension.getName());
223                    }
224                }
225                final DimensionCalc dimensionCalc = compileDimension(exp);
226                return new DimensionHierarchyCalc(
227                    new DummyExp(HierarchyType.forType(type)),
228                    dimensionCalc);
229            }
230            if (type instanceof MemberType) {
231                // <Member> --> <Member>.Hierarchy
232                final MemberCalc memberCalc = compileMember(exp);
233                return new MemberHierarchyFunDef.CalcImpl(
234                    new DummyExp(HierarchyType.forType(type)),
235                    memberCalc);
236            }
237            if (type instanceof LevelType) {
238                // <Level> --> <Level>.Hierarchy
239                final LevelCalc levelCalc = compileLevel(exp);
240                return new LevelHierarchyFunDef.CalcImpl(
241                    new DummyExp(HierarchyType.forType(type)),
242                    levelCalc);
243            }
244            assert type instanceof HierarchyType;
245            return (HierarchyCalc) compile(exp);
246        }
247    
248        public IntegerCalc compileInteger(Exp exp) {
249            final Calc calc = compileScalar(exp, false);
250            final Type type = calc.getType();
251            if (type instanceof DecimalType
252                && ((DecimalType) type).getScale() == 0)
253            {
254                return (IntegerCalc) calc;
255            } else if (type instanceof NumericType) {
256                if (calc instanceof ConstantCalc) {
257                    ConstantCalc constantCalc = (ConstantCalc) calc;
258                    return new ConstantCalc(
259                        new DecimalType(Integer.MAX_VALUE, 0),
260                        constantCalc.evaluateInteger(null));
261                } else if (calc instanceof DoubleCalc) {
262                    final DoubleCalc doubleCalc = (DoubleCalc) calc;
263                    return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) {
264                        public int evaluateInteger(Evaluator evaluator) {
265                            return (int) doubleCalc.evaluateDouble(evaluator);
266                        }
267                    };
268                }
269            }
270            return (IntegerCalc) calc;
271        }
272    
273        public StringCalc compileString(Exp exp) {
274            return (StringCalc) compileScalar(exp, false);
275        }
276    
277        public DateTimeCalc compileDateTime(Exp exp) {
278            return (DateTimeCalc) compileScalar(exp, false);
279        }
280    
281        public ListCalc compileList(Exp exp) {
282            return compileList(exp, false);
283        }
284    
285        public ListCalc compileList(Exp exp, boolean mutable) {
286            assert exp.getType() instanceof SetType : "must be a set: " + exp;
287            final List<ResultStyle> resultStyleList;
288            if (mutable) {
289                resultStyleList = ResultStyle.MUTABLELIST_ONLY;
290            } else {
291                resultStyleList = ResultStyle.LIST_ONLY;
292            }
293            Calc calc = compileAs(exp, null, resultStyleList);
294            if (calc instanceof ListCalc) {
295                return (ListCalc) calc;
296            }
297            if (calc == null) {
298                calc = compileAs(exp, null, ResultStyle.ITERABLE_ANY);
299                assert calc != null;
300            }
301            // If expression is an iterator, convert it to a list. Don't check
302            // 'calc instanceof IterCalc' because some generic calcs implement both
303            // ListCalc and IterCalc.
304            if (!(calc instanceof ListCalc)) {
305                if (((SetType) calc.getType()).getArity() == 1) {
306                    return toList((MemberIterCalc) calc);
307                } else {
308                    return toList((TupleIterCalc) calc);
309                }
310            } else {
311                // A set can only be implemented as a list or an iterable.
312                throw Util.newInternal("Cannot convert calc to list: " + calc);
313            }
314        }
315    
316        /**
317         * Converts an iterable over members to a list of members.
318         *
319         * @param calc Calc
320         * @return List calculation.
321         */
322        public MemberListCalc toList(MemberIterCalc calc) {
323            return new IterableMemberListCalc(calc);
324        }
325    
326        /**
327         * Converts an iterable over tuples to a list of tuples.
328         *
329         * @param calc Calc
330         * @return List calculation.
331         */
332        public TupleListCalc toList(TupleIterCalc calc) {
333            return new IterableTupleListCalc(calc);
334        }
335    
336        public IterCalc compileIter(Exp exp) {
337            Calc calc = compileAs(exp, null, ResultStyle.ITERABLE_ONLY);
338            if (calc == null) {
339                calc = compileAs(exp, null, ResultStyle.ANY_ONLY);
340                assert calc != null;
341            }
342            if (calc instanceof IterCalc) {
343                return (IterCalc) calc;
344            } else {
345                if (((SetType) calc.getType()).getArity() == 1) {
346                    return toIter((MemberListCalc) calc);
347                } else {
348                    return toIter((TupleListCalc) calc);
349                }
350            }
351        }
352    
353        /**
354         * Converts a list of members to an iterable over members.
355         *
356         * @param memberListCalc Calc
357         * @return Iterable calculation
358         */
359        public MemberIterCalc toIter(final MemberListCalc memberListCalc) {
360            return new MemberListIterCalc(memberListCalc);
361        }
362    
363        /**
364         * Converts a list of tuples to an iterable over tuples.
365         *
366         * @param tupleListCalc Calc
367         * @return Iterable calculation
368         */
369        public TupleIterCalc toIter(final TupleListCalc tupleListCalc) {
370            return new TupleListIterCalc(tupleListCalc);
371        }
372    
373        public BooleanCalc compileBoolean(Exp exp) {
374            final Calc calc = compileScalar(exp, false);
375            if (calc instanceof BooleanCalc) {
376                if (calc instanceof ConstantCalc) {
377                    final Object o = calc.evaluate(null);
378                    if (!(o instanceof Boolean)) {
379                        return ConstantCalc.constantBoolean(
380                            CastFunDef.toBoolean(o, new BooleanType()));
381                    }
382                }
383                return (BooleanCalc) calc;
384            } else if (calc instanceof DoubleCalc) {
385                final DoubleCalc doubleCalc = (DoubleCalc) calc;
386                return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) {
387                    public boolean evaluateBoolean(Evaluator evaluator) {
388                        return doubleCalc.evaluateDouble(evaluator) != 0;
389                    }
390                };
391            } else if (calc instanceof IntegerCalc) {
392                final IntegerCalc integerCalc = (IntegerCalc) calc;
393                return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) {
394                    public boolean evaluateBoolean(Evaluator evaluator) {
395                        return integerCalc.evaluateInteger(evaluator) != 0;
396                    }
397                };
398            } else {
399                return (BooleanCalc) calc;
400            }
401        }
402    
403        public DoubleCalc compileDouble(Exp exp) {
404            final DoubleCalc calc = (DoubleCalc) compileScalar(exp, false);
405            if (calc instanceof ConstantCalc
406                && !(calc.evaluate(null) instanceof Double))
407            {
408                return ConstantCalc.constantDouble(
409                    calc.evaluateDouble(null));
410            }
411            return calc;
412        }
413    
414        public TupleCalc compileTuple(Exp exp) {
415            return (TupleCalc) compile(exp);
416        }
417    
418        public Calc compileScalar(Exp exp, boolean specific) {
419            final Type type = exp.getType();
420            if (type instanceof MemberType) {
421                MemberCalc calc = compileMember(exp);
422                return memberToScalar(calc);
423            } else if (type instanceof DimensionType) {
424                HierarchyCalc hierarchyCalc = compileHierarchy(exp);
425                return hierarchyToScalar(hierarchyCalc);
426            } else if (type instanceof HierarchyType) {
427                final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
428                return hierarchyToScalar(hierarchyCalc);
429            } else if (type instanceof TupleType) {
430                TupleType tupleType = (TupleType) type;
431                TupleCalc tupleCalc = compileTuple(exp);
432                final TupleValueCalc scalarCalc =
433                    new TupleValueCalc(
434                    new DummyExp(tupleType.getValueType()), tupleCalc);
435                return scalarCalc.optimize();
436            } else if (type instanceof ScalarType) {
437                if (specific) {
438                    if (type instanceof BooleanType) {
439                        return compileBoolean(exp);
440                    } else if (type instanceof NumericType) {
441                        return compileDouble(exp);
442                    } else if (type instanceof StringType) {
443                        return compileString(exp);
444                    } else {
445                        return compile(exp);
446                    }
447                } else {
448                    return compile(exp);
449                }
450            } else {
451                return compile(exp);
452            }
453        }
454    
455        private Calc hierarchyToScalar(HierarchyCalc hierarchyCalc) {
456            final MemberCalc memberCalc = hierarchyToMember(hierarchyCalc);
457            return memberToScalar(memberCalc);
458        }
459    
460        private Calc memberToScalar(MemberCalc memberCalc) {
461            MemberType memberType = (MemberType) memberCalc.getType();
462            return new MemberValueCalc(
463                new DummyExp(memberType.getValueType()),
464                new MemberCalc[] {memberCalc});
465        }
466    
467        public ParameterSlot registerParameter(Parameter parameter) {
468            ParameterSlot slot = parameterSlots.get(parameter);
469            if (slot != null) {
470                return slot;
471            }
472            int index = parameterSlots.size();
473            ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index);
474            parameterSlots.put(parameter, slot2);
475            slot2.value = parameter.getValue();
476    
477            // Compile the expression only AFTER the parameter has been
478            // registered with a slot. Otherwise a cycle is possible.
479            final Type type = parameter.getType();
480            Exp defaultExp = parameter.getDefaultExp();
481            Calc calc;
482            if (type instanceof ScalarType) {
483                if (!defaultExp.getType().equals(type)) {
484                    defaultExp =
485                        new UnresolvedFunCall(
486                            "Cast",
487                            Syntax.Cast,
488                            new Exp[] {
489                                defaultExp,
490                                Literal.createSymbol(
491                                    Category.instance.getName(
492                                        TypeUtil.typeToCategory(type)))});
493                    defaultExp = getValidator().validate(defaultExp, true);
494                }
495                calc = compileScalar(defaultExp, true);
496            } else {
497                calc = compileAs(defaultExp, type, resultStyles);
498            }
499            slot2.setDefaultValueCalc(calc);
500            return slot2;
501        }
502    
503        public List<ResultStyle> getAcceptableResultStyles() {
504            return resultStyles;
505        }
506    
507        /**
508         * Implementation of {@link ParameterSlot}.
509         */
510        private static class ParameterSlotImpl implements ParameterSlot {
511            private final Parameter parameter;
512            private final int index;
513            private Calc defaultValueCalc;
514            private Object value;
515            private boolean assigned;
516            private Object cachedDefaultValue;
517    
518            /**
519             * Creates a ParameterSlotImpl.
520             *
521             * @param parameter Parameter
522             * @param index Unique index of the slot
523             */
524            public ParameterSlotImpl(
525                Parameter parameter, int index)
526            {
527                this.parameter = parameter;
528                this.index = index;
529            }
530    
531            public int getIndex() {
532                return index;
533            }
534    
535            public Calc getDefaultValueCalc() {
536                return defaultValueCalc;
537            }
538    
539            public Parameter getParameter() {
540                return parameter;
541            }
542    
543            /**
544             * Sets a compiled expression to compute the default value of the
545             * parameter.
546             *
547             * @param calc Compiled expression to compute default value of
548             * parameter
549             *
550             * @see #getDefaultValueCalc()
551             */
552            private void setDefaultValueCalc(Calc calc) {
553                this.defaultValueCalc = calc;
554            }
555    
556            public void setParameterValue(Object value, boolean assigned) {
557                this.value = value;
558                this.assigned = assigned;
559            }
560    
561            public Object getParameterValue() {
562                return value;
563            }
564    
565            public boolean isParameterSet() {
566                return assigned;
567            }
568    
569            public void unsetParameterValue() {
570                this.value = null;
571                this.assigned = false;
572            }
573    
574            public void setCachedDefaultValue(Object value) {
575                this.cachedDefaultValue = value;
576            }
577    
578            public Object getCachedDefaultValue() {
579                return cachedDefaultValue;
580            }
581        }
582    
583        /**
584         * Adapter that converts a member list calc into a member iter calc.
585         */
586        private static class MemberListIterCalc extends AbstractMemberIterCalc {
587            private final MemberListCalc memberListCalc;
588    
589            public MemberListIterCalc(MemberListCalc memberListCalc) {
590                super(
591                    new DummyExp(memberListCalc.getType()),
592                    new Calc[]{memberListCalc});
593                this.memberListCalc = memberListCalc;
594            }
595    
596            public Iterable<Member> evaluateMemberIterable(Evaluator evaluator) {
597                return memberListCalc.evaluateMemberList(evaluator);
598            }
599        }
600    
601        /**
602         * Adapter that converts a tuple list calc into a tuple iter calc.
603         */
604        private static class TupleListIterCalc extends AbstractTupleIterCalc {
605            private final TupleListCalc tupleListCalc;
606    
607            public TupleListIterCalc(TupleListCalc tupleListCalc) {
608                super(
609                    new DummyExp(tupleListCalc.getType()),
610                    new Calc[]{tupleListCalc});
611                this.tupleListCalc = tupleListCalc;
612            }
613    
614            public Iterable<Member[]> evaluateTupleIterable(Evaluator evaluator) {
615                return tupleListCalc.evaluateTupleList(evaluator);
616            }
617        }
618    
619        /**
620         * Computes the hierarchy of a dimension.
621         */
622        private static class DimensionHierarchyCalc extends AbstractHierarchyCalc {
623            private final DimensionCalc dimensionCalc;
624    
625            protected DimensionHierarchyCalc(Exp exp, DimensionCalc dimensionCalc) {
626                super(exp, new Calc[] {dimensionCalc});
627                this.dimensionCalc = dimensionCalc;
628            }
629    
630            public Hierarchy evaluateHierarchy(Evaluator evaluator) {
631                Dimension dimension =
632                    dimensionCalc.evaluateDimension(evaluator);
633                final Hierarchy hierarchy =
634                    FunUtil.getDimensionDefaultHierarchy(dimension);
635                if (hierarchy != null) {
636                    return hierarchy;
637                }
638                throw FunUtil.newEvalException(
639                    MondrianResource.instance()
640                        .CannotImplicitlyConvertDimensionToHierarchy.ex(
641                        dimension.getName()));
642            }
643        }
644    }
645    
646    // End AbstractExpCompiler.java