001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.calc;
011
012import mondrian.calc.impl.BetterExpCompiler;
013import mondrian.olap.*;
014import mondrian.olap.type.Type;
015import mondrian.util.CreationException;
016import mondrian.util.ObjectFactory;
017
018import org.eigenbase.util.property.StringProperty;
019
020import java.util.List;
021
022/**
023 * Mediates the compilation of an expression ({@link mondrian.olap.Exp})
024 * into a compiled expression ({@link Calc}).
025 *
026 * @author jhyde
027 * @since Sep 28, 2005
028 */
029public interface ExpCompiler {
030
031    /**
032     * Returns the evaluator to be used for evaluating expressions during the
033     * compilation process.
034     */
035    Evaluator getEvaluator();
036
037    /**
038     * Returns the validator which was used to validate this expression.
039     *
040     * @return validator
041     */
042    Validator getValidator();
043
044    /**
045     * Compiles an expression.
046     *
047     * @param exp Expression
048     * @return Compiled expression
049     */
050    Calc compile(Exp exp);
051
052    /**
053     * Compiles an expression to a given result type.
054     *
055     * <p>If <code>resultType</code> is not null, casts the expression to that
056     * type. Throws an exception if that conversion is not allowed by the
057     * type system.
058     *
059     * <p>The <code>preferredResultStyles</code> parameter specifies a list
060     * of desired result styles. It must not be null, but may be empty.
061     *
062     * @param exp Expression
063     *
064     * @param resultType Desired result type, or null to use expression's
065     *                   current type
066     *
067     * @param preferredResultStyles List of result types, in descending order
068     *                   of preference. Never null.
069     *
070     * @return Compiled expression, or null if none can satisfy
071     */
072    Calc compileAs(
073        Exp exp,
074        Type resultType,
075        List<ResultStyle> preferredResultStyles);
076
077    /**
078     * Compiles an expression which yields a {@link Member} result.
079     */
080    MemberCalc compileMember(Exp exp);
081
082    /**
083     * Compiles an expression which yields a {@link Level} result.
084     */
085    LevelCalc compileLevel(Exp exp);
086
087    /**
088     * Compiles an expression which yields a {@link Dimension} result.
089     */
090    DimensionCalc compileDimension(Exp exp);
091
092    /**
093     * Compiles an expression which yields a {@link Hierarchy} result.
094     */
095    HierarchyCalc compileHierarchy(Exp exp);
096
097    /**
098     * Compiles an expression which yields an <code>int</code> result.
099     * The expression is implicitly converted into a scalar.
100     */
101    IntegerCalc compileInteger(Exp exp);
102
103    /**
104     * Compiles an expression which yields a {@link String} result.
105     * The expression is implicitly converted into a scalar.
106     */
107    StringCalc compileString(Exp exp);
108
109    /**
110     * Compiles an expression which yields a {@link java.util.Date} result.
111     * The expression is implicitly converted into a scalar.
112     */
113    DateTimeCalc compileDateTime(Exp exp);
114
115    /**
116     * Compiles an expression which yields an immutable {@link TupleList}
117     * result.
118     *
119     * <p>Always equivalent to <code>{@link #compileList}(exp, false)</code>.
120     */
121    ListCalc compileList(Exp exp);
122
123    /**
124     * Compiles an expression which yields {@link TupleList} result.
125     *
126     * <p>Such an expression is generally a list of {@link Member} objects or a
127     * list of tuples (each represented by a {@link Member} array).
128     *
129     * <p>See {@link #compileList(mondrian.olap.Exp)}.
130     *
131     * @param exp Expression
132     * @param mutable Whether resulting list is mutable
133     */
134    ListCalc compileList(Exp exp, boolean mutable);
135
136    /**
137     * Compiles an expression which yields an immutable {@link Iterable} result.
138     *
139     * @param exp Expression
140     * @return Calculator which yields an Iterable
141     */
142    IterCalc compileIter(Exp exp);
143
144    /**
145     * Compiles an expression which yields a <code>boolean</code> result.
146     *
147     * @param exp Expression
148     * @return Calculator which yields a boolean
149     */
150    BooleanCalc compileBoolean(Exp exp);
151
152    /**
153     * Compiles an expression which yields a <code>double</code> result.
154     *
155     * @param exp Expression
156     * @return Calculator which yields a double
157     */
158    DoubleCalc compileDouble(Exp exp);
159
160    /**
161     * Compiles an expression which yields a tuple result.
162     *
163     * @param exp Expression
164     * @return Calculator which yields a tuple
165     */
166    TupleCalc compileTuple(Exp exp);
167
168    /**
169     * Compiles an expression to yield a scalar result.
170     *
171     * <p>If the expression yields a member or tuple, the calculator will
172     * automatically apply that member or tuple to the current dimensional
173     * context and return the value of the current measure.
174     *
175     * @param exp Expression
176     * @param specific Whether to try to use the specific compile method for
177     *   scalar types. For example, if <code>specific</code> is true and
178     *   <code>exp</code> is a string expression, calls
179     *   {@link #compileString(mondrian.olap.Exp)}
180     * @return Calculation which returns the scalar value of the expression
181     */
182    Calc compileScalar(Exp exp, boolean specific);
183
184    /**
185     * Implements a parameter, returning a unique slot which will hold the
186     * parameter's value.
187     *
188     * @param parameter Parameter
189     * @return Slot
190     */
191    ParameterSlot registerParameter(Parameter parameter);
192
193    /**
194     * Returns a list of the {@link ResultStyle}s
195     * acceptable to the caller.
196     */
197    List<ResultStyle> getAcceptableResultStyles();
198
199    /**
200     * The <code>ExpCompiler.Factory</code> is used to access
201     * <code>ExpCompiler</code> implementations. Each call returns
202     * a new instance. This factory supports overriding the default
203     * instance by use of a <code>ThreadLocal</code> and by defining a
204     * <code>System</code> property with the <code>ExpCompiler</code>
205     * class name.
206     */
207    public static final class Factory extends ObjectFactory<ExpCompiler> {
208        private static final Factory factory;
209        private static final Class[] CLASS_ARRAY;
210        static {
211            factory = new Factory();
212            CLASS_ARRAY = new Class[] {
213                Evaluator.class,
214                Validator.class,
215                ResultStyle[].class,
216            };
217        }
218
219        /**
220         * Create a <code>ExpCompiler</code> instance, each call returns a
221         * new compiler.
222         *
223         * @param evaluator the <code>Evaluator</code> to use with the compiler
224         * @param validator the <code>Validator</code> to use with the compiler
225         * @return the new <code>ExpCompiler</code> compiler
226         * @throws CreationException if the compiler can not be created
227         */
228        public static ExpCompiler getExpCompiler(
229            final Evaluator evaluator,
230            final Validator validator)
231            throws CreationException
232        {
233            return getExpCompiler(evaluator, validator, ResultStyle.ANY_LIST);
234        }
235
236        /**
237         *
238         *
239         * @param evaluator the <code>Evaluator</code> to use with the compiler
240         * @param validator the <code>Validator</code> to use with the compiler
241         * @param resultStyles the initial <code>ResultStyle</code> array
242         * for the compiler
243         * @return the new <code>ExpCompiler</code> compiler
244         * @throws CreationException if the compiler can not be created
245         */
246        public static ExpCompiler getExpCompiler(
247            final Evaluator evaluator,
248            final Validator validator,
249            final List<ResultStyle> resultStyles)
250                throws CreationException
251        {
252            return factory.getObject(
253                CLASS_ARRAY,
254                new Object[] {
255                    evaluator,
256                    validator,
257                    resultStyles
258                });
259        }
260
261        /**
262         * <code>ThreadLocal</code> used to hold the class name of an
263         * <code>ExpCompiler</code> implementation.
264         * Generally, this should only be used for testing.
265         */
266        private static final ThreadLocal<String> ClassName =
267            new ThreadLocal<String>();
268
269        /**
270         * Get the class name of a <code>ExpCompiler</code> implementation
271         * or null.
272         *
273         * @return the class name or null.
274        */
275        public static String getThreadLocalClassName() {
276            return ClassName.get();
277        }
278        /**
279         * Sets the class name of a  <code>ExpCompiler</code> implementation.
280         * This should be called (obviously) before calling the
281         * <code>ExpCompiler.Factory</code> <code>getExpCompiler</code>
282         * method to get the <code>ExpCompiler</code> implementation.
283         * Generally, this is only used for testing.
284         *
285         * @param className Class name
286         */
287        public static void setThreadLocalClassName(String className) {
288            ClassName.set(className);
289        }
290        /**
291         * Clears the class name (regardless of whether a class name was set).
292         * When a class name is set using <code>setThreadLocalClassName</code>,
293         * the setting whould be done in a try-block and a call to this
294         * clear method should be in the finally-clause of that try-block.
295         */
296        public static void clearThreadLocalClassName() {
297            ClassName.set(null);
298        }
299
300        /**
301         * The constructor for the <code>ExpCompiler.Factory</code>.
302         * This passes the <code>ExpCompiler</code> class to the
303         * <code>ObjectFactory</code> base class.
304        */
305        private Factory() {
306            super(ExpCompiler.class);
307        }
308        /**
309         * Get the class name set in the <code>ThreadLocal</code> or null.
310         *
311         * @return class name or null.
312         */
313        protected String getClassName() {
314            return getThreadLocalClassName();
315        }
316
317        /**
318         * Return the <code>ExpCompiler.Factory</code property name.
319         *
320         * @return <code>ExpCompiler.Factory</code> property name
321         */
322        protected StringProperty getStringProperty() {
323            return MondrianProperties.instance().ExpCompilerClass;
324        }
325        /**
326         * The <code>ExpCompiler.Factory</code>'s implementation of the
327         * <code>ObjectFactory</code>'s abstract method which returns
328         * the default <code>ExpCompiler</code> instance.
329         *
330         * @param parameterTypes array of classes: Evaluator, Validator and
331         *  ResultStyle
332         * @param parameterValues  the Evaluator, Validator and ResultStyle
333         * values
334         * @return <code>ExpCompiler</code> instance
335         * @throws CreationException if the <code>ExpCompiler</code> can not be
336         * created.
337         */
338        protected ExpCompiler getDefault(
339            final Class[] parameterTypes,
340            final Object[] parameterValues)
341            throws CreationException
342        {
343            // Strong typed above so don't need to check here
344            Evaluator evaluator = (Evaluator) parameterValues[0];
345            Validator validator = (Validator) parameterValues[1];
346            List<ResultStyle> resultStyles =
347                (List<ResultStyle>) parameterValues[2];
348
349            // Here there is bleed-through from the "calc.impl" implementation
350            // directory into the "calc" interface definition directory.
351            // This can be avoided if we were to use reflection to
352            // create this the default ExpCompiler implementation.
353            return new BetterExpCompiler(
354                evaluator, validator, resultStyles);
355        }
356
357        /**
358         * Get the underlying Factory object.
359         * <p>
360         * This is for testing only.
361         *
362         * @return the <code>ExpCompiler.Factory</code> object
363         */
364        public static Factory getFactory() {
365            return factory;
366        }
367
368        /**
369         * Get the current override contect.
370         * <p>
371         * This is for testing only.
372         *
373         * @return the override context object.
374         */
375        public Object removeContext() {
376            return new Context();
377        }
378
379        /**
380         * Restore the current overrides.
381         * <p>
382         * This is for testing only.
383         *
384         * @param context the current override object.
385         */
386        public void restoreContext(final Object context) {
387            if (context instanceof Context) {
388                ((Context) context).restore();
389            }
390        }
391
392        /**
393         * The <code>ExpCompiler</code> only has two override mechanisms: the
394         * <code>ThreadLocal</code> and <code>System</code>
395         * <code>Properties</code>. This class captures and clears the current
396         * values for both in the constructor and then replaces them
397         * in the <code>restore</code> method.
398         * <p>
399         * This is for testing only.
400         */
401        public static class Context implements ObjectFactory.Context {
402            private final String threadLocalClassName;
403            private final String systemPropertyClassName;
404
405            /**
406             * Creates a Context.
407             */
408            Context() {
409                this.threadLocalClassName =
410                        ExpCompiler.Factory.getThreadLocalClassName();
411                if (this.threadLocalClassName != null) {
412                    ExpCompiler.Factory.clearThreadLocalClassName();
413                }
414
415                this.systemPropertyClassName =
416                        System.getProperty(ExpCompiler.class.getName());
417                if (this.systemPropertyClassName != null) {
418                    System.getProperties().remove(ExpCompiler.class.getName());
419                }
420            }
421
422            /**
423             * Restores the previous context.
424             */
425            private void restore() {
426                if (this.threadLocalClassName != null) {
427                    ExpCompiler.Factory.setThreadLocalClassName(
428                        this.threadLocalClassName);
429                }
430                if (this.systemPropertyClassName != null) {
431                    System.setProperty(
432                        ExpCompiler.class.getName(),
433                        this.systemPropertyClassName);
434                }
435            }
436        }
437    }
438}
439
440// End ExpCompiler.java