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