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.impl; 011 012import mondrian.calc.*; 013import mondrian.mdx.MemberExpr; 014import mondrian.mdx.UnresolvedFunCall; 015import mondrian.olap.*; 016import mondrian.olap.fun.*; 017import mondrian.olap.type.*; 018import mondrian.olap.type.DimensionType; 019import mondrian.olap.type.LevelType; 020import mondrian.resource.MondrianResource; 021 022import java.util.*; 023 024/** 025 * Abstract implementation of the {@link mondrian.calc.ExpCompiler} interface. 026 * 027 * @author jhyde 028 * @since Sep 29, 2005 029 */ 030public class AbstractExpCompiler implements ExpCompiler { 031 private final Evaluator evaluator; 032 private final Validator validator; 033 private final Map<Parameter, ParameterSlotImpl> parameterSlots = 034 new HashMap<Parameter, ParameterSlotImpl>(); 035 private List<ResultStyle> resultStyles; 036 037 /** 038 * Creates an AbstractExpCompiler 039 * 040 * @param evaluator Evaluator 041 * @param validator Validator 042 */ 043 public AbstractExpCompiler(Evaluator evaluator, Validator validator) { 044 this(evaluator, validator, ResultStyle.ANY_LIST); 045 } 046 047 /** 048 * Creates an AbstractExpCompiler which is constrained to produce one of 049 * a set of result styles. 050 * 051 * @param evaluator Evaluator 052 * @param validator Validator 053 * @param resultStyles List of result styles, preferred first, must not be 054 */ 055 public AbstractExpCompiler( 056 Evaluator evaluator, 057 Validator validator, 058 List<ResultStyle> resultStyles) 059 { 060 this.evaluator = evaluator; 061 this.validator = validator; 062 this.resultStyles = (resultStyles == null) 063 ? ResultStyle.ANY_LIST : resultStyles; 064 } 065 066 public Evaluator getEvaluator() { 067 return evaluator; 068 } 069 070 public Validator getValidator() { 071 return validator; 072 } 073 074 /** 075 * {@inheritDoc} 076 * 077 * Uses the current ResultStyle to compile the expression. 078 */ 079 public Calc compile(Exp exp) { 080 return exp.accept(this); 081 } 082 083 /** 084 * {@inheritDoc} 085 * 086 * Uses a new ResultStyle to compile the expression. 087 */ 088 public Calc compileAs( 089 Exp exp, 090 Type resultType, 091 List<ResultStyle> preferredResultTypes) 092 { 093 assert preferredResultTypes != null; 094 int substitutions = 0; 095 if (Util.Retrowoven) { 096 // Copy and replace ITERABLE 097 // A number of functions declare that they can accept 098 // ITERABLEs so here is where that those are converted to innocent 099 // LISTs for jdk1.4 and other retrowoven code. 100 List<ResultStyle> tmp = 101 new ArrayList<ResultStyle>(preferredResultTypes.size()); 102 for (ResultStyle preferredResultType : preferredResultTypes) { 103 if (preferredResultType == ResultStyle.ITERABLE) { 104 preferredResultType = ResultStyle.LIST; 105 ++substitutions; 106 } 107 tmp.add(preferredResultType); 108 } 109 preferredResultTypes = tmp; 110 } 111 List<ResultStyle> save = this.resultStyles; 112 try { 113 this.resultStyles = preferredResultTypes; 114 if (resultType != null && resultType != exp.getType()) { 115 if (resultType instanceof MemberType) { 116 return compileMember(exp); 117 } else if (resultType instanceof LevelType) { 118 return compileLevel(exp); 119 } else if (resultType instanceof HierarchyType) { 120 return compileHierarchy(exp); 121 } else if (resultType instanceof DimensionType) { 122 return compileDimension(exp); 123 } else if (resultType instanceof ScalarType) { 124 return compileScalar(exp, false); 125 } 126 } 127 final Calc calc = compile(exp); 128 if (substitutions > 0) { 129 final IterCalc iterCalc = (IterCalc) calc; 130 if (iterCalc == null) { 131 this.resultStyles = 132 Collections.singletonList(ResultStyle.ITERABLE); 133 return compile(exp); 134 } else { 135 return iterCalc; 136 } 137 } 138 return calc; 139 } finally { 140 this.resultStyles = save; 141 } 142 } 143 144 public MemberCalc compileMember(Exp exp) { 145 final Type type = exp.getType(); 146 if (type instanceof HierarchyType) { 147 final HierarchyCalc hierarchyCalc = compileHierarchy(exp); 148 return hierarchyToMember(hierarchyCalc); 149 } else if (type instanceof NullType) { 150 throw MondrianResource.instance().NullNotSupported.ex(); 151 } else if (type instanceof DimensionType) { 152 final HierarchyCalc hierarchyCalc = compileHierarchy(exp); 153 return hierarchyToMember(hierarchyCalc); 154 } 155 assert type instanceof MemberType : type; 156 return (MemberCalc) compile(exp); 157 } 158 159 private MemberCalc hierarchyToMember( 160 HierarchyCalc hierarchyCalc) 161 { 162 final Hierarchy hierarchy = hierarchyCalc.getType().getHierarchy(); 163 if (hierarchy != null) { 164 return new HierarchyCurrentMemberFunDef.FixedCalcImpl( 165 new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())), 166 hierarchy); 167 } else { 168 return new HierarchyCurrentMemberFunDef.CalcImpl( 169 new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())), 170 hierarchyCalc); 171 } 172 } 173 174 public LevelCalc compileLevel(Exp exp) { 175 final Type type = exp.getType(); 176 if (type instanceof MemberType) { 177 // <Member> --> <Member>.Level 178 final MemberCalc memberCalc = compileMember(exp); 179 return new MemberLevelFunDef.CalcImpl( 180 new DummyExp(LevelType.forType(type)), 181 memberCalc); 182 } 183 assert type instanceof LevelType; 184 return (LevelCalc) compile(exp); 185 } 186 187 public DimensionCalc compileDimension(Exp exp) { 188 final Type type = exp.getType(); 189 if (type instanceof HierarchyType) { 190 final HierarchyCalc hierarchyCalc = compileHierarchy(exp); 191 return new HierarchyDimensionFunDef.CalcImpl( 192 new DummyExp(new DimensionType(type.getDimension())), 193 hierarchyCalc); 194 } 195 assert type instanceof DimensionType : type; 196 return (DimensionCalc) compile(exp); 197 } 198 199 public HierarchyCalc compileHierarchy(Exp exp) { 200 final Type type = exp.getType(); 201 if (type instanceof DimensionType) { 202 // <Dimension> --> unique Hierarchy else error 203 // Resolve at compile time if constant 204 final Dimension dimension = type.getDimension(); 205 if (dimension != null) { 206 final Hierarchy hierarchy = 207 FunUtil.getDimensionDefaultHierarchy(dimension); 208 if (hierarchy != null) { 209 return (HierarchyCalc) ConstantCalc.constantHierarchy( 210 hierarchy); 211 } else { 212 // SSAS gives error at run time (often as an error in a 213 // cell) but we prefer to give an error at validate time. 214 throw MondrianResource.instance() 215 .CannotImplicitlyConvertDimensionToHierarchy.ex( 216 dimension.getName()); 217 } 218 } 219 final DimensionCalc dimensionCalc = compileDimension(exp); 220 return new DimensionHierarchyCalc( 221 new DummyExp(HierarchyType.forType(type)), 222 dimensionCalc); 223 } 224 if (type instanceof MemberType) { 225 // <Member> --> <Member>.Hierarchy 226 final MemberCalc memberCalc = compileMember(exp); 227 return new MemberHierarchyFunDef.CalcImpl( 228 new DummyExp(HierarchyType.forType(type)), 229 memberCalc); 230 } 231 if (type instanceof LevelType) { 232 // <Level> --> <Level>.Hierarchy 233 final LevelCalc levelCalc = compileLevel(exp); 234 return new LevelHierarchyFunDef.CalcImpl( 235 new DummyExp(HierarchyType.forType(type)), 236 levelCalc); 237 } 238 assert type instanceof HierarchyType; 239 return (HierarchyCalc) compile(exp); 240 } 241 242 public IntegerCalc compileInteger(Exp exp) { 243 final Calc calc = compileScalar(exp, false); 244 final Type type = calc.getType(); 245 if (type instanceof DecimalType 246 && ((DecimalType) type).getScale() == 0) 247 { 248 return (IntegerCalc) calc; 249 } else if (type instanceof NumericType) { 250 if (calc instanceof ConstantCalc) { 251 ConstantCalc constantCalc = (ConstantCalc) calc; 252 return new ConstantCalc( 253 new DecimalType(Integer.MAX_VALUE, 0), 254 constantCalc.evaluateInteger(null)); 255 } else if (calc instanceof DoubleCalc) { 256 final DoubleCalc doubleCalc = (DoubleCalc) calc; 257 return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) { 258 public int evaluateInteger(Evaluator evaluator) { 259 return (int) doubleCalc.evaluateDouble(evaluator); 260 } 261 }; 262 } 263 } 264 return (IntegerCalc) calc; 265 } 266 267 public StringCalc compileString(Exp exp) { 268 return (StringCalc) compileScalar(exp, false); 269 } 270 271 public DateTimeCalc compileDateTime(Exp exp) { 272 return (DateTimeCalc) compileScalar(exp, false); 273 } 274 275 public ListCalc compileList(Exp exp) { 276 return compileList(exp, false); 277 } 278 279 public ListCalc compileList(Exp exp, boolean mutable) { 280 assert exp.getType() instanceof SetType : "must be a set: " + exp; 281 final List<ResultStyle> resultStyleList; 282 if (mutable) { 283 resultStyleList = ResultStyle.MUTABLELIST_ONLY; 284 } else { 285 resultStyleList = ResultStyle.LIST_ONLY; 286 } 287 Calc calc = compileAs(exp, null, resultStyleList); 288 if (calc instanceof ListCalc) { 289 return (ListCalc) calc; 290 } 291 if (calc == null) { 292 calc = compileAs(exp, null, ResultStyle.ITERABLE_ANY); 293 assert calc != null; 294 } 295 // If expression is an iterator, convert it to a list. Don't check 296 // 'calc instanceof IterCalc' because some generic calcs implement both 297 // ListCalc and IterCalc. 298 if (!(calc instanceof ListCalc)) { 299 return toList((IterCalc) calc); 300 } else { 301 // A set can only be implemented as a list or an iterable. 302 throw Util.newInternal("Cannot convert calc to list: " + calc); 303 } 304 } 305 306 /** 307 * Converts an iterable over tuples to a list of tuples. 308 * 309 * @param calc Calc 310 * @return List calculation. 311 */ 312 public ListCalc toList(IterCalc calc) { 313 return new IterableListCalc(calc); 314 } 315 316 public IterCalc compileIter(Exp exp) { 317 IterCalc calc = 318 (IterCalc) compileAs(exp, null, ResultStyle.ITERABLE_ONLY); 319 if (calc == null) { 320 calc = (IterCalc) compileAs(exp, null, ResultStyle.ANY_ONLY); 321 assert calc != null; 322 } 323 return calc; 324 } 325 326 public BooleanCalc compileBoolean(Exp exp) { 327 final Calc calc = compileScalar(exp, false); 328 if (calc instanceof BooleanCalc) { 329 if (calc instanceof ConstantCalc) { 330 final Object o = calc.evaluate(null); 331 if (!(o instanceof Boolean)) { 332 return ConstantCalc.constantBoolean( 333 CastFunDef.toBoolean(o, new BooleanType())); 334 } 335 } 336 return (BooleanCalc) calc; 337 } else if (calc instanceof DoubleCalc) { 338 final DoubleCalc doubleCalc = (DoubleCalc) calc; 339 return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) { 340 public boolean evaluateBoolean(Evaluator evaluator) { 341 return doubleCalc.evaluateDouble(evaluator) != 0; 342 } 343 }; 344 } else if (calc instanceof IntegerCalc) { 345 final IntegerCalc integerCalc = (IntegerCalc) calc; 346 return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) { 347 public boolean evaluateBoolean(Evaluator evaluator) { 348 return integerCalc.evaluateInteger(evaluator) != 0; 349 } 350 }; 351 } else { 352 return (BooleanCalc) calc; 353 } 354 } 355 356 public DoubleCalc compileDouble(Exp exp) { 357 final Calc calc = compileScalar(exp, false); 358 if (calc instanceof ConstantCalc 359 && !(calc.evaluate(null) instanceof Double)) 360 { 361 return ConstantCalc.constantDouble( 362 ((ConstantCalc) calc).evaluateDouble(null)); 363 } 364 if (calc instanceof DoubleCalc) { 365 return (DoubleCalc) calc; 366 } 367 if (calc instanceof IntegerCalc) { 368 final IntegerCalc integerCalc = (IntegerCalc) calc; 369 return new AbstractDoubleCalc(exp, new Calc[] {integerCalc}) { 370 public double evaluateDouble(Evaluator evaluator) { 371 final int result = integerCalc.evaluateInteger(evaluator); 372 return (double) result; 373 } 374 }; 375 } 376 throw Util.newInternal("cannot cast " + exp); 377 } 378 379 public TupleCalc compileTuple(Exp exp) { 380 return (TupleCalc) compile(exp); 381 } 382 383 public Calc compileScalar(Exp exp, boolean specific) { 384 final Type type = exp.getType(); 385 if (type instanceof MemberType) { 386 MemberCalc calc = compileMember(exp); 387 return memberToScalar(calc); 388 } else if (type instanceof DimensionType) { 389 HierarchyCalc hierarchyCalc = compileHierarchy(exp); 390 return hierarchyToScalar(hierarchyCalc); 391 } else if (type instanceof HierarchyType) { 392 final HierarchyCalc hierarchyCalc = compileHierarchy(exp); 393 return hierarchyToScalar(hierarchyCalc); 394 } else if (type instanceof TupleType) { 395 TupleType tupleType = (TupleType) type; 396 TupleCalc tupleCalc = compileTuple(exp); 397 final TupleValueCalc scalarCalc = 398 new TupleValueCalc( 399 new DummyExp(tupleType.getValueType()), 400 tupleCalc, 401 getEvaluator().mightReturnNullForUnrelatedDimension()); 402 return scalarCalc.optimize(); 403 } else if (type instanceof ScalarType) { 404 if (specific) { 405 if (type instanceof BooleanType) { 406 return compileBoolean(exp); 407 } else if (type instanceof NumericType) { 408 return compileDouble(exp); 409 } else if (type instanceof StringType) { 410 return compileString(exp); 411 } else { 412 return compile(exp); 413 } 414 } else { 415 return compile(exp); 416 } 417 } else { 418 return compile(exp); 419 } 420 } 421 422 private Calc hierarchyToScalar(HierarchyCalc hierarchyCalc) { 423 final MemberCalc memberCalc = hierarchyToMember(hierarchyCalc); 424 return memberToScalar(memberCalc); 425 } 426 427 private Calc memberToScalar(MemberCalc memberCalc) { 428 MemberType memberType = (MemberType) memberCalc.getType(); 429 return MemberValueCalc.create( 430 new DummyExp(memberType.getValueType()), 431 new MemberCalc[] {memberCalc}, 432 getEvaluator().mightReturnNullForUnrelatedDimension()); 433 } 434 435 public ParameterSlot registerParameter(Parameter parameter) { 436 ParameterSlot slot = parameterSlots.get(parameter); 437 if (slot != null) { 438 return slot; 439 } 440 int index = parameterSlots.size(); 441 ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index); 442 parameterSlots.put(parameter, slot2); 443 slot2.value = parameter.getValue(); 444 445 // Compile the expression only AFTER the parameter has been 446 // registered with a slot. Otherwise a cycle is possible. 447 final Type type = parameter.getType(); 448 Exp defaultExp = parameter.getDefaultExp(); 449 Calc calc; 450 if (type instanceof ScalarType) { 451 if (!defaultExp.getType().equals(type)) { 452 defaultExp = 453 new UnresolvedFunCall( 454 "Cast", 455 Syntax.Cast, 456 new Exp[] { 457 defaultExp, 458 Literal.createSymbol( 459 Category.instance.getName( 460 TypeUtil.typeToCategory(type)))}); 461 defaultExp = getValidator().validate(defaultExp, true); 462 } 463 calc = compileScalar(defaultExp, true); 464 } else { 465 calc = compileAs(defaultExp, type, resultStyles); 466 } 467 slot2.setDefaultValueCalc(calc); 468 return slot2; 469 } 470 471 public List<ResultStyle> getAcceptableResultStyles() { 472 return resultStyles; 473 } 474 475 /** 476 * Implementation of {@link ParameterSlot}. 477 */ 478 private static class ParameterSlotImpl implements ParameterSlot { 479 private final Parameter parameter; 480 private final int index; 481 private Calc defaultValueCalc; 482 private Object value; 483 private boolean assigned; 484 private Object cachedDefaultValue; 485 486 /** 487 * Creates a ParameterSlotImpl. 488 * 489 * @param parameter Parameter 490 * @param index Unique index of the slot 491 */ 492 public ParameterSlotImpl( 493 Parameter parameter, int index) 494 { 495 this.parameter = parameter; 496 this.index = index; 497 } 498 499 public int getIndex() { 500 return index; 501 } 502 503 public Calc getDefaultValueCalc() { 504 return defaultValueCalc; 505 } 506 507 public Parameter getParameter() { 508 return parameter; 509 } 510 511 /** 512 * Sets a compiled expression to compute the default value of the 513 * parameter. 514 * 515 * @param calc Compiled expression to compute default value of 516 * parameter 517 * 518 * @see #getDefaultValueCalc() 519 */ 520 private void setDefaultValueCalc(Calc calc) { 521 this.defaultValueCalc = calc; 522 } 523 524 public void setParameterValue(Object value, boolean assigned) { 525 this.value = value; 526 this.assigned = assigned; 527 528 // make sure caller called convert first 529 assert !(value instanceof List && !(value instanceof TupleList)); 530 assert !(value instanceof MemberExpr); 531 assert !(value instanceof Literal); 532 } 533 534 public Object getParameterValue() { 535 return value; 536 } 537 538 public boolean isParameterSet() { 539 return assigned; 540 } 541 542 public void unsetParameterValue() { 543 this.value = null; 544 this.assigned = false; 545 } 546 547 public void setCachedDefaultValue(Object value) { 548 this.cachedDefaultValue = value; 549 } 550 551 public Object getCachedDefaultValue() { 552 return cachedDefaultValue; 553 } 554 } 555 556 /** 557 * Computes the hierarchy of a dimension. 558 */ 559 private static class DimensionHierarchyCalc extends AbstractHierarchyCalc { 560 private final DimensionCalc dimensionCalc; 561 562 protected DimensionHierarchyCalc(Exp exp, DimensionCalc dimensionCalc) { 563 super(exp, new Calc[] {dimensionCalc}); 564 this.dimensionCalc = dimensionCalc; 565 } 566 567 public Hierarchy evaluateHierarchy(Evaluator evaluator) { 568 Dimension dimension = 569 dimensionCalc.evaluateDimension(evaluator); 570 final Hierarchy hierarchy = 571 FunUtil.getDimensionDefaultHierarchy(dimension); 572 if (hierarchy != null) { 573 return hierarchy; 574 } 575 throw FunUtil.newEvalException( 576 MondrianResource.instance() 577 .CannotImplicitlyConvertDimensionToHierarchy.ex( 578 dimension.getName())); 579 } 580 } 581} 582 583// End AbstractExpCompiler.java