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