001    /*
002    // $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/ExpCompiler.java#1 $
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;
011    
012    import org.eigenbase.util.property.StringProperty;
013    import mondrian.olap.*;
014    import mondrian.olap.type.Type;
015    import mondrian.util.ObjectFactory;
016    import mondrian.util.CreationException;
017    import mondrian.calc.impl.BetterExpCompiler;
018    
019    import java.util.List;
020    
021    /**
022     * Mediates the compilation of an expression ({@link mondrian.olap.Exp})
023     * into a compiled expression ({@link Calc}).
024     *
025     * @author jhyde
026     * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/ExpCompiler.java#1 $
027     * @since Sep 28, 2005
028     */
029    public 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 java.util.List}
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 java.util.List} 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