001    /*
002    // $Id: //open/mondrian/src/main/mondrian/calc/ExpCompiler.java#14 $
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-2007 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/src/main/mondrian/calc/ExpCompiler.java#14 $
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                return getExpCompiler(evaluator, validator, ResultStyle.ANY_LIST);
233            }
234    
235            /**
236             *
237             *
238             * @param evaluator the <code>Evaluator</code> to use with the compiler
239             * @param validator the <code>Validator</code> to use with the compiler
240             * @param resultStyles the initial <code>ResultStyle</code> array
241             * for the compiler
242             * @return the new <code>ExpCompiler</code> compiler
243             * @throws CreationException if the compiler can not be created
244             */
245            public static ExpCompiler getExpCompiler(
246                final Evaluator evaluator,
247                final Validator validator,
248                final List<ResultStyle> resultStyles)
249                    throws CreationException
250            {
251                return factory.getObject(
252                    CLASS_ARRAY,
253                    new Object[] {
254                        evaluator,
255                        validator,
256                        resultStyles
257                    });
258            }
259    
260            /**
261             * <code>ThreadLocal</code> used to hold the class name of an
262             * <code>ExpCompiler</code> implementation.
263             * Generally, this should only be used for testing.
264             */
265            private static final ThreadLocal<String> ClassName =
266                new ThreadLocal<String>();
267    
268            /**
269             * Get the class name of a <code>ExpCompiler</code> implementation
270             * or null.
271             *
272             * @return the class name or null.
273            */
274            public static String getThreadLocalClassName() {
275                return ClassName.get();
276            }
277            /**
278             * Sets the class name of a  <code>ExpCompiler</code> implementation.
279             * This should be called (obviously) before calling the
280             * <code>ExpCompiler.Factory</code> <code>getExpCompiler</code>
281             * method to get the <code>ExpCompiler</code> implementation.
282             * Generally, this is only used for testing.
283             *
284             * @param className Class name
285             */
286            public static void setThreadLocalClassName(String className) {
287                ClassName.set(className);
288            }
289            /**
290             * Clears the class name (regardless of whether a class name was set).
291             * When a class name is set using <code>setThreadLocalClassName</code>,
292             * the setting whould be done in a try-block and a call to this
293             * clear method should be in the finally-clause of that try-block.
294             */
295            public static void clearThreadLocalClassName() {
296                ClassName.set(null);
297            }
298    
299            /**
300             * The constructor for the <code>ExpCompiler.Factory</code>.
301             * This passes the <code>ExpCompiler</code> class to the
302             * <code>ObjectFactory</code> base class.
303            */
304            private Factory() {
305                super(ExpCompiler.class);
306            }
307            /**
308             * Get the class name set in the <code>ThreadLocal</code> or null.
309             *
310             * @return class name or null.
311             */
312            protected String getClassName() {
313                return getThreadLocalClassName();
314            }
315    
316            /**
317             * Return the <code>ExpCompiler.Factory</code property name.
318             *
319             * @return <code>ExpCompiler.Factory</code> property name
320             */
321            protected StringProperty getStringProperty() {
322                return MondrianProperties.instance().ExpCompilerClass;
323            }
324            /**
325             * The <code>ExpCompiler.Factory</code>'s implementation of the
326             * <code>ObjectFactory</code>'s abstract method which returns
327             * the default <code>ExpCompiler</code> instance.
328             *
329             * @param parameterTypes array of classes: Evaluator, Validator and
330             *  ResultStyle
331             * @param parameterValues  the Evaluator, Validator and ResultStyle
332             * values
333             * @return <code>ExpCompiler</code> instance
334             * @throws CreationException if the <code>ExpCompiler</code> can not be
335             * created.
336             */
337            protected ExpCompiler getDefault(
338                final Class[] parameterTypes,
339                final Object[] parameterValues)
340                throws CreationException
341            {
342                // Strong typed above so don't need to check here
343                Evaluator evaluator = (Evaluator) parameterValues[0];
344                Validator validator = (Validator) parameterValues[1];
345                List<ResultStyle> resultStyles =
346                    (List<ResultStyle>) parameterValues[2];
347    
348                // Here there is bleed-through from the "calc.impl" implementation
349                // directory into the "calc" interface definition directory.
350                // This can be avoided if we were to use reflection to
351                // create this the default ExpCompiler implementation.
352                return new BetterExpCompiler(
353                    evaluator, validator, resultStyles);
354            }
355    
356            /**
357             * Get the underlying Factory object.
358             * <p>
359             * This is for testing only.
360             *
361             * @return the <code>ExpCompiler.Factory</code> object
362             */
363            public static Factory getFactory() {
364                return factory;
365            }
366    
367            /**
368             * Get the current override contect.
369             * <p>
370             * This is for testing only.
371             *
372             * @return the override context object.
373             */
374            public Object removeContext() {
375                return new Context();
376            }
377    
378            /**
379             * Restore the current overrides.
380             * <p>
381             * This is for testing only.
382             *
383             * @param context the current override object.
384             */
385            public void restoreContext(final Object context) {
386                if (context instanceof Context) {
387                    ((Context) context).restore();
388                }
389            }
390    
391            /**
392             * The <code>ExpCompiler</code> only has two override mechanisms: the
393             * <code>ThreadLocal</code> and <code>System</code>
394             * <code>Properties</code>. This class captures and clears the current
395             * values for both in the constructor and then replaces them
396             * in the <code>restore</code> method.
397             * <p>
398             * This is for testing only.
399             */
400            public static class Context implements ObjectFactory.Context {
401                private final String threadLocalClassName;
402                private final String systemPropertyClassName;
403    
404                /**
405                 * Creates a Context.
406                 */
407                Context() {
408                    this.threadLocalClassName =
409                            ExpCompiler.Factory.getThreadLocalClassName();
410                    if (this.threadLocalClassName != null) {
411                        ExpCompiler.Factory.clearThreadLocalClassName();
412                    }
413    
414                    this.systemPropertyClassName =
415                            System.getProperty(ExpCompiler.class.getName());
416                    if (this.systemPropertyClassName != null) {
417                        System.getProperties().remove(ExpCompiler.class.getName());
418                    }
419                }
420    
421                /**
422                 * Restores the previous context.
423                 */
424                private void restore() {
425                    if (this.threadLocalClassName != null) {
426                        ExpCompiler.Factory.setThreadLocalClassName(
427                            this.threadLocalClassName);
428                    }
429                    if (this.systemPropertyClassName != null) {
430                        System.setProperty(ExpCompiler.class.getName(),
431                                this.systemPropertyClassName);
432                    }
433                }
434            }
435        }
436    }
437    
438    // End ExpCompiler.java