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) 2002-2005 Julian Hyde 008// Copyright (C) 2005-2011 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.*; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017import mondrian.olap.fun.extra.CalculatedChildFunDef; 018import mondrian.olap.fun.extra.NthQuartileFunDef; 019import mondrian.olap.fun.vba.Excel; 020import mondrian.olap.fun.vba.Vba; 021import mondrian.olap.type.LevelType; 022import mondrian.olap.type.Type; 023 024import java.io.PrintWriter; 025import java.util.*; 026 027/** 028 * <code>BuiltinFunTable</code> contains a list of all built-in MDX functions. 029 * 030 * <p>Note: Boolean expressions return {@link Boolean#TRUE}, 031 * {@link Boolean#FALSE} or null. null is returned if the expression can not be 032 * evaluated because some values have not been loaded from database yet.</p> 033 * 034 * @author jhyde 035 * @since 26 February, 2002 036 */ 037public class BuiltinFunTable extends FunTableImpl { 038 039 /** the singleton */ 040 private static BuiltinFunTable instance; 041 042 /** 043 * Creates a function table containing all of the builtin MDX functions. 044 * This method should only be called from {@link BuiltinFunTable#instance}. 045 */ 046 protected BuiltinFunTable() { 047 super(); 048 } 049 050 public void defineFunctions(Builder builder) { 051 builder.defineReserved("NULL"); 052 053 // Empty expression 054 builder.define( 055 new FunDefBase( 056 "", 057 "", 058 "Dummy function representing the empty expression", 059 Syntax.Empty, 060 Category.Empty, 061 new int[0]) 062 { 063 } 064 ); 065 066 // first char: p=Property, m=Method, i=Infix, P=Prefix 067 // 2nd: 068 069 // ARRAY FUNCTIONS 070 071 // "SetToArray(<Set>[, <Set>]...[, <Numeric Expression>])" 072 if (false) builder.define(new FunDefBase( 073 "SetToArray", 074 "SetToArray(<Set>[, <Set>]...[, <Numeric Expression>])", 075 "Converts one or more sets to an array for use in a user-defined function.", 076 "fa*") 077 { 078 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 079 { 080 throw new UnsupportedOperationException(); 081 } 082 }); 083 084 // 085 // DIMENSION FUNCTIONS 086 builder.define(HierarchyDimensionFunDef.instance); 087 088 // "<Dimension>.Dimension" 089 builder.define(DimensionDimensionFunDef.INSTANCE); 090 091 // "<Level>.Dimension" 092 builder.define(LevelDimensionFunDef.INSTANCE); 093 094 // "<Member>.Dimension" 095 builder.define(MemberDimensionFunDef.INSTANCE); 096 097 // "Dimensions(<Numeric Expression>)" 098 builder.define(DimensionsNumericFunDef.INSTANCE); 099 100 // "Dimensions(<String Expression>)" 101 builder.define(DimensionsStringFunDef.INSTANCE); 102 103 // 104 // HIERARCHY FUNCTIONS 105 builder.define(LevelHierarchyFunDef.instance); 106 builder.define(MemberHierarchyFunDef.instance); 107 108 // 109 // LEVEL FUNCTIONS 110 builder.define(MemberLevelFunDef.instance); 111 112 // "<Hierarchy>.Levels(<Numeric Expression>)" 113 builder.define( 114 new FunDefBase( 115 "Levels", 116 "Returns the level whose position in a hierarchy is specified by a numeric expression.", 117 "mlhn") 118 { 119 public Type getResultType(Validator validator, Exp[] args) { 120 final Type argType = args[0].getType(); 121 return LevelType.forType(argType); 122 } 123 124 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 125 { 126 final HierarchyCalc hierarchyCalc = 127 compiler.compileHierarchy(call.getArg(0)); 128 final IntegerCalc ordinalCalc = 129 compiler.compileInteger(call.getArg(1)); 130 return new AbstractLevelCalc( 131 call, new Calc[] {hierarchyCalc, ordinalCalc}) 132 { 133 public Level evaluateLevel(Evaluator evaluator) { 134 Hierarchy hierarchy = 135 hierarchyCalc.evaluateHierarchy(evaluator); 136 int ordinal = ordinalCalc.evaluateInteger(evaluator); 137 return nthLevel(hierarchy, ordinal); 138 } 139 }; 140 } 141 142 Level nthLevel(Hierarchy hierarchy, int n) { 143 Level[] levels = hierarchy.getLevels(); 144 145 if (n >= levels.length || n < 0) { 146 throw newEvalException( 147 this, "Index '" + n + "' out of bounds"); 148 } 149 return levels[n]; 150 } 151 }); 152 153 // "<Hierarchy>.Levels(<String Expression>)" 154 builder.define( 155 new FunDefBase( 156 "Levels", 157 "Returns the level whose name is specified by a string expression.", 158 "mlhS") 159 { 160 public Type getResultType(Validator validator, Exp[] args) { 161 final Type argType = args[0].getType(); 162 return LevelType.forType(argType); 163 } 164 165 public Calc compileCall( 166 final ResolvedFunCall call, ExpCompiler compiler) 167 { 168 final HierarchyCalc hierarchyCalc = 169 compiler.compileHierarchy(call.getArg(0)); 170 final StringCalc nameCalc = 171 compiler.compileString(call.getArg(1)); 172 return new AbstractLevelCalc( 173 call, new Calc[] {hierarchyCalc, nameCalc}) { 174 public Level evaluateLevel(Evaluator evaluator) { 175 Hierarchy hierarchy = 176 hierarchyCalc.evaluateHierarchy(evaluator); 177 String name = nameCalc.evaluateString(evaluator); 178 for (Level level : hierarchy.getLevels()) { 179 if (level.getName().equals(name)) { 180 return level; 181 } 182 } 183 throw newEvalException( 184 call.getFunDef(), 185 "Level '" + name + "' not found in hierarchy '" 186 + hierarchy + "'"); 187 } 188 }; 189 } 190 }); 191 192 // "Levels(<String Expression>)" 193 builder.define( 194 new FunDefBase( 195 "Levels", 196 "Returns the level whose name is specified by a string expression.", 197 "flS") 198 { 199 public Type getResultType(Validator validator, Exp[] args) { 200 final Type argType = args[0].getType(); 201 return LevelType.forType(argType); 202 } 203 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 204 { 205 final StringCalc stringCalc = 206 compiler.compileString(call.getArg(0)); 207 return new AbstractLevelCalc(call, new Calc[] {stringCalc}) { 208 public Level evaluateLevel(Evaluator evaluator) { 209 String levelName = 210 stringCalc.evaluateString(evaluator); 211 return findLevel(evaluator, levelName); 212 } 213 }; 214 } 215 216 Level findLevel(Evaluator evaluator, String s) { 217 Cube cube = evaluator.getCube(); 218 OlapElement o = 219 (s.startsWith("[")) 220 ? evaluator.getSchemaReader().lookupCompound( 221 cube, 222 parseIdentifier(s), 223 false, 224 Category.Level) 225 // lookupCompound barfs if "s" doesn't have matching 226 // brackets, so don't even try 227 : null; 228 229 if (o instanceof Level) { 230 return (Level) o; 231 } else if (o == null) { 232 throw newEvalException(this, "Level '" + s + "' not found"); 233 } else { 234 throw newEvalException( 235 this, "Levels('" + s + "') found " + o); 236 } 237 } 238 }); 239 240 // 241 // LOGICAL FUNCTIONS 242 builder.define(IsEmptyFunDef.FunctionResolver); 243 builder.define(IsEmptyFunDef.PostfixResolver); 244 builder.define(IsNullFunDef.Resolver); 245 builder.define(IsFunDef.Resolver); 246 builder.define(AsFunDef.RESOLVER); 247 248 // 249 // MEMBER FUNCTIONS 250 builder.define(AncestorFunDef.Resolver); 251 builder.define(AncestorsFunDef.Resolver); 252 253 builder.define( 254 new FunDefBase( 255 "Cousin", 256 "<Member> Cousin(<Member>, <Ancestor Member>)", 257 "Returns the member with the same relative position under <ancestor member> as the member specified.", 258 "fmmm") 259 { 260 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 261 { 262 final MemberCalc memberCalc = 263 compiler.compileMember(call.getArg(0)); 264 final MemberCalc ancestorMemberCalc = 265 compiler.compileMember(call.getArg(1)); 266 return new AbstractMemberCalc( 267 call, new Calc[] {memberCalc, ancestorMemberCalc}) 268 { 269 public Member evaluateMember(Evaluator evaluator) { 270 Member member = memberCalc.evaluateMember(evaluator); 271 Member ancestorMember = 272 ancestorMemberCalc.evaluateMember(evaluator); 273 return cousin( 274 evaluator.getSchemaReader(), 275 member, 276 ancestorMember); 277 } 278 }; 279 } 280 }); 281 282 builder.define(HierarchyCurrentMemberFunDef.instance); 283 builder.define(NamedSetCurrentFunDef.instance); 284 builder.define(NamedSetCurrentOrdinalFunDef.instance); 285 286 // "<Member>.DataMember" 287 builder.define( 288 new FunDefBase( 289 "DataMember", 290 "Returns the system-generated data member that is associated with a nonleaf member of a dimension.", 291 "pmm") 292 { 293 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 294 { 295 final MemberCalc memberCalc = 296 compiler.compileMember(call.getArg(0)); 297 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 298 public Member evaluateMember(Evaluator evaluator) { 299 Member member = memberCalc.evaluateMember(evaluator); 300 return member.getDataMember(); 301 } 302 }; 303 } 304 }); 305 306 // "<Dimension>.DefaultMember". The function is implemented using an 307 // implicit cast to hierarchy, and we create a FunInfo for 308 // documentation & backwards compatibility. 309 builder.define( 310 new FunInfo( 311 "DefaultMember", 312 "Returns the default member of a dimension.", 313 "pmd")); 314 315 // "<Hierarchy>.DefaultMember" 316 builder.define( 317 new FunDefBase( 318 "DefaultMember", 319 "Returns the default member of a hierarchy.", 320 "pmh") 321 { 322 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 323 { 324 final HierarchyCalc hierarchyCalc = 325 compiler.compileHierarchy(call.getArg(0)); 326 return new AbstractMemberCalc( 327 call, new Calc[] {hierarchyCalc}) 328 { 329 public Member evaluateMember(Evaluator evaluator) { 330 Hierarchy hierarchy = 331 hierarchyCalc.evaluateHierarchy(evaluator); 332 return evaluator.getSchemaReader() 333 .getHierarchyDefaultMember(hierarchy); 334 } 335 }; 336 } 337 }); 338 339 // "<Member>.FirstChild" 340 builder.define( 341 new FunDefBase( 342 "FirstChild", 343 "Returns the first child of a member.", 344 "pmm") 345 { 346 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 347 { 348 final MemberCalc memberCalc = 349 compiler.compileMember(call.getArg(0)); 350 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 351 public Member evaluateMember(Evaluator evaluator) { 352 Member member = memberCalc.evaluateMember(evaluator); 353 return firstChild(evaluator, member); 354 } 355 }; 356 } 357 358 Member firstChild(Evaluator evaluator, Member member) { 359 List<Member> children = evaluator.getSchemaReader() 360 .getMemberChildren(member); 361 return (children.size() == 0) 362 ? member.getHierarchy().getNullMember() 363 : children.get(0); 364 } 365 }); 366 367 // <Member>.FirstSibling 368 builder.define( 369 new FunDefBase( 370 "FirstSibling", 371 "Returns the first child of the parent of a member.", 372 "pmm") 373 { 374 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 375 { 376 final MemberCalc memberCalc = 377 compiler.compileMember(call.getArg(0)); 378 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 379 public Member evaluateMember(Evaluator evaluator) { 380 Member member = memberCalc.evaluateMember(evaluator); 381 return firstSibling(member, evaluator); 382 } 383 }; 384 } 385 386 Member firstSibling(Member member, Evaluator evaluator) { 387 Member parent = member.getParentMember(); 388 List<Member> children; 389 final SchemaReader schemaReader = evaluator.getSchemaReader(); 390 if (parent == null) { 391 if (member.isNull()) { 392 return member; 393 } 394 children = schemaReader.getHierarchyRootMembers( 395 member.getHierarchy()); 396 } else { 397 children = schemaReader.getMemberChildren(parent); 398 } 399 return children.get(0); 400 } 401 }); 402 403 builder.define(LeadLagFunDef.LagResolver); 404 405 // <Member>.LastChild 406 builder.define( 407 new FunDefBase( 408 "LastChild", 409 "Returns the last child of a member.", 410 "pmm") 411 { 412 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 413 { 414 final MemberCalc memberCalc = 415 compiler.compileMember(call.getArg(0)); 416 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 417 public Member evaluateMember(Evaluator evaluator) { 418 Member member = memberCalc.evaluateMember(evaluator); 419 return lastChild(evaluator, member); 420 } 421 }; 422 } 423 424 Member lastChild(Evaluator evaluator, Member member) { 425 List<Member> children = 426 evaluator.getSchemaReader().getMemberChildren(member); 427 return (children.size() == 0) 428 ? member.getHierarchy().getNullMember() 429 : children.get(children.size() - 1); 430 } 431 }); 432 433 // <Member>.LastSibling 434 builder.define( 435 new FunDefBase( 436 "LastSibling", 437 "Returns the last child of the parent of a member.", 438 "pmm") 439 { 440 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 441 { 442 final MemberCalc memberCalc = 443 compiler.compileMember(call.getArg(0)); 444 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 445 public Member evaluateMember(Evaluator evaluator) { 446 Member member = memberCalc.evaluateMember(evaluator); 447 return firstSibling(member, evaluator); 448 } 449 }; 450 } 451 452 Member firstSibling(Member member, Evaluator evaluator) { 453 Member parent = member.getParentMember(); 454 List<Member> children; 455 final SchemaReader schemaReader = evaluator.getSchemaReader(); 456 if (parent == null) { 457 if (member.isNull()) { 458 return member; 459 } 460 children = schemaReader.getHierarchyRootMembers( 461 member.getHierarchy()); 462 } else { 463 children = schemaReader.getMemberChildren(parent); 464 } 465 return children.get(children.size() - 1); 466 } 467 }); 468 469 builder.define(LeadLagFunDef.LeadResolver); 470 471 // Members(<String Expression>) 472 builder.define( 473 new FunDefBase( 474 "Members", 475 "Returns the member whose name is specified by a string expression.", 476 "fmS") 477 { 478 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 479 { 480 throw new UnsupportedOperationException(); 481 } 482 }); 483 484 // <Member>.NextMember 485 builder.define( 486 new FunDefBase( 487 "NextMember", 488 "Returns the next member in the level that contains a specified member.", 489 "pmm") 490 { 491 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 492 { 493 final MemberCalc memberCalc = 494 compiler.compileMember(call.getArg(0)); 495 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 496 public Member evaluateMember(Evaluator evaluator) { 497 Member member = memberCalc.evaluateMember(evaluator); 498 return evaluator.getSchemaReader().getLeadMember( 499 member, 1); 500 } 501 }; 502 } 503 }); 504 505 builder.define(OpeningClosingPeriodFunDef.OpeningPeriodResolver); 506 builder.define(OpeningClosingPeriodFunDef.ClosingPeriodResolver); 507 508 builder.define(MemberOrderKeyFunDef.instance); 509 510 builder.define(ParallelPeriodFunDef.Resolver); 511 512 // <Member>.Parent 513 builder.define( 514 new FunDefBase( 515 "Parent", 516 "Returns the parent of a member.", 517 "pmm") 518 { 519 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 520 { 521 final MemberCalc memberCalc = 522 compiler.compileMember(call.getArg(0)); 523 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 524 public Member evaluateMember(Evaluator evaluator) { 525 Member member = memberCalc.evaluateMember(evaluator); 526 return memberParent(evaluator, member); 527 } 528 }; 529 } 530 531 Member memberParent(Evaluator evaluator, Member member) { 532 Member parent = 533 evaluator.getSchemaReader().getMemberParent(member); 534 if (parent == null) { 535 parent = member.getHierarchy().getNullMember(); 536 } 537 return parent; 538 } 539 }); 540 541 // <Member>.PrevMember 542 builder.define( 543 new FunDefBase( 544 "PrevMember", 545 "Returns the previous member in the level that contains a specified member.", 546 "pmm") 547 { 548 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 549 { 550 final MemberCalc memberCalc = 551 compiler.compileMember(call.getArg(0)); 552 return new AbstractMemberCalc(call, new Calc[] {memberCalc}) { 553 public Member evaluateMember(Evaluator evaluator) { 554 Member member = memberCalc.evaluateMember(evaluator); 555 return evaluator.getSchemaReader().getLeadMember( 556 member, -1); 557 } 558 }; 559 } 560 }); 561 562 builder.define(StrToMemberFunDef.INSTANCE); 563 builder.define(ValidMeasureFunDef.instance); 564 565 // 566 // NUMERIC FUNCTIONS 567 builder.define(AggregateFunDef.resolver); 568 569 // Obsolete?? 570 builder.define( 571 new MultiResolver( 572 "$AggregateChildren", 573 "$AggregateChildren(<Hierarchy>)", 574 "Equivalent to 'Aggregate(<Hierarchy>.CurrentMember.Children); for internal use.", 575 new String[] {"Inh"}) { 576 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 577 return new FunDefBase(dummyFunDef) { 578 public void unparse(Exp[] args, PrintWriter pw) { 579 pw.print(getName()); 580 pw.print("("); 581 args[0].unparse(pw); 582 pw.print(")"); 583 } 584 585 public Calc compileCall( 586 ResolvedFunCall call, ExpCompiler compiler) 587 { 588 final HierarchyCalc hierarchyCalc = 589 compiler.compileHierarchy(call.getArg(0)); 590 final Calc valueCalc = new ValueCalc(call); 591 return new GenericCalc(call) { 592 public Object evaluate(Evaluator evaluator) { 593 Hierarchy hierarchy = 594 hierarchyCalc.evaluateHierarchy(evaluator); 595 return aggregateChildren( 596 evaluator, hierarchy, valueCalc); 597 } 598 599 public Calc[] getCalcs() { 600 return new Calc[] {hierarchyCalc, valueCalc}; 601 } 602 }; 603 } 604 605 Object aggregateChildren( 606 Evaluator evaluator, 607 Hierarchy hierarchy, 608 final Calc valueFunCall) 609 { 610 Member member = 611 evaluator.getPreviousContext(hierarchy); 612 List<Member> members = new ArrayList<Member>(); 613 evaluator.getSchemaReader() 614 .getParentChildContributingChildren( 615 member.getDataMember(), 616 hierarchy, 617 members); 618 Aggregator aggregator = 619 (Aggregator) evaluator.getProperty( 620 Property.AGGREGATION_TYPE.name, null); 621 if (aggregator == null) { 622 throw FunUtil.newEvalException( 623 null, 624 "Could not find an aggregator in the current " 625 + "evaluation context"); 626 } 627 Aggregator rollup = aggregator.getRollup(); 628 if (rollup == null) { 629 throw FunUtil.newEvalException( 630 null, 631 "Don't know how to rollup aggregator '" 632 + aggregator + "'"); 633 } 634 final int savepoint = evaluator.savepoint(); 635 try { 636 final Object o = rollup.aggregate( 637 evaluator, 638 new UnaryTupleList(members), 639 valueFunCall); 640 return o; 641 } finally { 642 evaluator.restore(savepoint); 643 } 644 } 645 }; 646 } 647 }); 648 649 builder.define(AvgFunDef.Resolver); 650 651 builder.define(CorrelationFunDef.Resolver); 652 653 builder.define(CountFunDef.Resolver); 654 655 // <Set>.Count 656 builder.define( 657 new FunDefBase( 658 "Count", 659 "Returns the number of tuples in a set including empty cells.", 660 "pnx") 661 { 662 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 663 { 664 final ListCalc listCalc = 665 compiler.compileList(call.getArg(0)); 666 return new AbstractIntegerCalc(call, new Calc[] {listCalc}) { 667 public int evaluateInteger(Evaluator evaluator) { 668 TupleList list = listCalc.evaluateList(evaluator); 669 return count(evaluator, list, true); 670 } 671 }; 672 } 673 }); 674 675 builder.define(CovarianceFunDef.CovarianceResolver); 676 builder.define(CovarianceFunDef.CovarianceNResolver); 677 678 builder.define(IifFunDef.STRING_INSTANCE); 679 builder.define(IifFunDef.NUMERIC_INSTANCE); 680 builder.define(IifFunDef.TUPLE_INSTANCE); 681 builder.define(IifFunDef.BOOLEAN_INSTANCE); 682 builder.define(IifFunDef.MEMBER_INSTANCE); 683 builder.define(IifFunDef.LEVEL_INSTANCE); 684 builder.define(IifFunDef.HIERARCHY_INSTANCE); 685 builder.define(IifFunDef.DIMENSION_INSTANCE); 686 builder.define(IifFunDef.SET_INSTANCE); 687 688 builder.define(LinReg.InterceptResolver); 689 builder.define(LinReg.PointResolver); 690 builder.define(LinReg.R2Resolver); 691 builder.define(LinReg.SlopeResolver); 692 builder.define(LinReg.VarianceResolver); 693 694 builder.define(MinMaxFunDef.MaxResolver); 695 builder.define(MinMaxFunDef.MinResolver); 696 697 builder.define(MedianFunDef.Resolver); 698 builder.define(PercentileFunDef.Resolver); 699 700 // <Level>.Ordinal 701 builder.define( 702 new FunDefBase( 703 "Ordinal", 704 "Returns the zero-based ordinal value associated with a level.", 705 "pnl") 706 { 707 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 708 { 709 final LevelCalc levelCalc = 710 compiler.compileLevel(call.getArg(0)); 711 return new AbstractIntegerCalc(call, new Calc[] {levelCalc}) { 712 public int evaluateInteger(Evaluator evaluator) { 713 final Level level = levelCalc.evaluateLevel(evaluator); 714 return level.getDepth(); 715 } 716 }; 717 } 718 }); 719 720 builder.define(RankFunDef.Resolver); 721 722 builder.define(CacheFunDef.Resolver); 723 724 builder.define(StdevFunDef.StdevResolver); 725 builder.define(StdevFunDef.StddevResolver); 726 727 builder.define(StdevPFunDef.StdevpResolver); 728 builder.define(StdevPFunDef.StddevpResolver); 729 730 builder.define(SumFunDef.Resolver); 731 732 // <Measure>.Value 733 builder.define( 734 new FunDefBase( 735 "Value", 736 "Returns the value of a measure.", 737 "pnm") 738 { 739 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 740 { 741 final MemberCalc memberCalc = 742 compiler.compileMember(call.getArg(0)); 743 return new GenericCalc(call) { 744 public Object evaluate(Evaluator evaluator) { 745 Member member = memberCalc.evaluateMember(evaluator); 746 final int savepoint = evaluator.savepoint(); 747 evaluator.setContext(member); 748 try { 749 Object value = evaluator.evaluateCurrent(); 750 return value; 751 } finally { 752 evaluator.restore(savepoint); 753 } 754 } 755 756 public boolean dependsOn(Hierarchy hierarchy) { 757 if (super.dependsOn(hierarchy)) { 758 return true; 759 } 760 if (memberCalc.getType().usesHierarchy( 761 hierarchy, true)) 762 { 763 return false; 764 } 765 return true; 766 } 767 public Calc[] getCalcs() { 768 return new Calc[] {memberCalc}; 769 } 770 }; 771 } 772 }); 773 774 builder.define(VarFunDef.VarResolver); 775 builder.define(VarFunDef.VarianceResolver); 776 777 builder.define(VarPFunDef.VariancePResolver); 778 builder.define(VarPFunDef.VarPResolver); 779 780 // 781 // SET FUNCTIONS 782 783 builder.define(AddCalculatedMembersFunDef.resolver); 784 785 // Ascendants(<Member>) 786 builder.define( 787 new FunDefBase( 788 "Ascendants", 789 "Returns the set of the ascendants of a specified member.", 790 "fxm") 791 { 792 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 793 { 794 final MemberCalc memberCalc = 795 compiler.compileMember(call.getArg(0)); 796 return new AbstractListCalc(call, new Calc[] {memberCalc}) 797 { 798 public TupleList evaluateList(Evaluator evaluator) { 799 Member member = memberCalc.evaluateMember(evaluator); 800 return new UnaryTupleList( 801 ascendants(evaluator.getSchemaReader(), member)); 802 } 803 }; 804 } 805 806 List<Member> ascendants(SchemaReader schemaReader, Member member) { 807 if (member.isNull()) { 808 return Collections.emptyList(); 809 } 810 final List<Member> result = new ArrayList<Member>(); 811 result.add(member); 812 schemaReader.getMemberAncestors(member, result); 813 return result; 814 } 815 }); 816 817 builder.define(TopBottomCountFunDef.BottomCountResolver); 818 builder.define(TopBottomPercentSumFunDef.BottomPercentResolver); 819 builder.define(TopBottomPercentSumFunDef.BottomSumResolver); 820 builder.define(TopBottomCountFunDef.TopCountResolver); 821 builder.define(TopBottomPercentSumFunDef.TopPercentResolver); 822 builder.define(TopBottomPercentSumFunDef.TopSumResolver); 823 824 // <Member>.Children 825 builder.define( 826 new FunDefBase( 827 "Children", 828 "Returns the children of a member.", 829 "pxm") 830 { 831 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 832 { 833 final MemberCalc memberCalc = 834 compiler.compileMember(call.getArg(0)); 835 return new AbstractListCalc( 836 call, new Calc[] {memberCalc}, false) 837 { 838 public TupleList evaluateList(Evaluator evaluator) { 839 // Return the list of children. The list is immutable, 840 // hence 'false' above. 841 Member member = memberCalc.evaluateMember(evaluator); 842 return new UnaryTupleList( 843 getNonEmptyMemberChildren(evaluator, member)); 844 } 845 }; 846 } 847 }); 848 849 builder.define(CrossJoinFunDef.Resolver); 850 builder.define(NonEmptyCrossJoinFunDef.Resolver); 851 builder.define(CrossJoinFunDef.StarResolver); 852 builder.define(DescendantsFunDef.Resolver); 853 builder.define(DescendantsFunDef.Resolver2); 854 builder.define(DistinctFunDef.instance); 855 builder.define(DrilldownLevelFunDef.Resolver); 856 857 builder.define(DrilldownLevelTopBottomFunDef.DrilldownLevelTopResolver); 858 builder.define( 859 DrilldownLevelTopBottomFunDef.DrilldownLevelBottomResolver); 860 builder.define(DrilldownMemberFunDef.Resolver); 861 862 if (false) 863 builder.define( 864 new FunDefBase( 865 "DrilldownMemberBottom", 866 "DrilldownMemberBottom(<Set1>, <Set2>, <Count>[, [<Numeric Expression>][, RECURSIVE]])", 867 "Like DrilldownMember except that it includes only the bottom N children.", 868 "fx*") 869 { 870 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 871 { 872 throw new UnsupportedOperationException(); 873 } 874 }); 875 876 if (false) 877 builder.define( 878 new FunDefBase( 879 "DrilldownMemberTop", 880 "DrilldownMemberTop(<Set1>, <Set2>, <Count>[, [<Numeric Expression>][, RECURSIVE]])", 881 "Like DrilldownMember except that it includes only the top N children.", 882 "fx*") 883 { 884 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 885 { 886 throw new UnsupportedOperationException(); 887 } 888 }); 889 890 if (false) 891 builder.define( 892 new FunDefBase( 893 "DrillupLevel", 894 "DrillupLevel(<Set>[, <Level>])", 895 "Drills up the members of a set that are below a specified level.", 896 "fx*") 897 { 898 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 899 { 900 throw new UnsupportedOperationException(); 901 } 902 }); 903 904 if (false) 905 builder.define( 906 new FunDefBase( 907 "DrillupMember", 908 "DrillupMember(<Set1>, <Set2>)", 909 "Drills up the members in a set that are present in a second specified set.", 910 "fx*") 911 { 912 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 913 { 914 throw new UnsupportedOperationException(); 915 } 916 }); 917 918 builder.define(ExceptFunDef.Resolver); 919 builder.define(ExistsFunDef.resolver); 920 builder.define(ExtractFunDef.Resolver); 921 builder.define(FilterFunDef.instance); 922 923 builder.define(GenerateFunDef.ListResolver); 924 builder.define(GenerateFunDef.StringResolver); 925 builder.define(HeadTailFunDef.HeadResolver); 926 927 builder.define(HierarchizeFunDef.Resolver); 928 929 builder.define(IntersectFunDef.resolver); 930 builder.define(LastPeriodsFunDef.Resolver); 931 932 // <Dimension>.Members is really just shorthand for <Hierarchy>.Members 933 builder.define( 934 new FunInfo( 935 "Members", 936 "Returns the set of members in a dimension.", 937 "pxd")); 938 939 // <Hierarchy>.Members 940 builder.define( 941 new FunDefBase( 942 "Members", 943 "Returns the set of members in a hierarchy.", 944 "pxh") 945 { 946 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 947 { 948 final HierarchyCalc hierarchyCalc = 949 compiler.compileHierarchy(call.getArg(0)); 950 return new AbstractListCalc( 951 call, new Calc[] {hierarchyCalc}) 952 { 953 public TupleList evaluateList(Evaluator evaluator) 954 { 955 Hierarchy hierarchy = 956 hierarchyCalc.evaluateHierarchy(evaluator); 957 return hierarchyMembers(hierarchy, evaluator, false); 958 } 959 }; 960 } 961 }); 962 963 // <Hierarchy>.AllMembers 964 builder.define( 965 new FunDefBase( 966 "AllMembers", 967 "Returns a set that contains all members, including calculated members, of the specified hierarchy.", 968 "pxh") 969 { 970 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 971 { 972 final HierarchyCalc hierarchyCalc = 973 compiler.compileHierarchy(call.getArg(0)); 974 return new AbstractListCalc( 975 call, new Calc[] {hierarchyCalc}) 976 { 977 public TupleList evaluateList(Evaluator evaluator) 978 { 979 Hierarchy hierarchy = 980 hierarchyCalc.evaluateHierarchy(evaluator); 981 return hierarchyMembers(hierarchy, evaluator, true); 982 } 983 }; 984 } 985 }); 986 987 // <Level>.Members 988 builder.define(LevelMembersFunDef.INSTANCE); 989 990 // <Level>.AllMembers 991 builder.define( 992 new FunDefBase( 993 "AllMembers", 994 "Returns a set that contains all members, including calculated members, of the specified level.", 995 "pxl") 996 { 997 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 998 { 999 final LevelCalc levelCalc = 1000 compiler.compileLevel(call.getArg(0)); 1001 return new AbstractListCalc(call, new Calc[] {levelCalc}) 1002 { 1003 public TupleList evaluateList(Evaluator evaluator) 1004 { 1005 Level level = levelCalc.evaluateLevel(evaluator); 1006 return levelMembers(level, evaluator, true); 1007 } 1008 }; 1009 } 1010 }); 1011 1012 builder.define(XtdFunDef.MtdResolver); 1013 builder.define(OrderFunDef.Resolver); 1014 builder.define(UnorderFunDef.Resolver); 1015 builder.define(PeriodsToDateFunDef.Resolver); 1016 builder.define(XtdFunDef.QtdResolver); 1017 1018 // StripCalculatedMembers(<Set>) 1019 builder.define( 1020 new FunDefBase( 1021 "StripCalculatedMembers", 1022 "Removes calculated members from a set.", 1023 "fxx") 1024 { 1025 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1026 { 1027 final ListCalc listCalc = 1028 compiler.compileList(call.getArg(0)); 1029 return new AbstractListCalc(call, new Calc[] {listCalc}) { 1030 public TupleList evaluateList(Evaluator evaluator) 1031 { 1032 TupleList list = listCalc.evaluateList(evaluator); 1033 return removeCalculatedMembers(list); 1034 } 1035 }; 1036 } 1037 }); 1038 1039 // <Member>.Siblings 1040 builder.define( 1041 new FunDefBase( 1042 "Siblings", 1043 "Returns the siblings of a specified member, including the member itself.", 1044 "pxm") 1045 { 1046 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1047 { 1048 final MemberCalc memberCalc = 1049 compiler.compileMember(call.getArg(0)); 1050 return new AbstractListCalc(call, new Calc[] {memberCalc}) 1051 { 1052 public TupleList evaluateList(Evaluator evaluator) 1053 { 1054 final Member member = 1055 memberCalc.evaluateMember(evaluator); 1056 return new UnaryTupleList( 1057 memberSiblings(member, evaluator)); 1058 } 1059 }; 1060 } 1061 1062 List<Member> memberSiblings(Member member, Evaluator evaluator) { 1063 if (member.isNull()) { 1064 // the null member has no siblings -- not even itself 1065 return Collections.emptyList(); 1066 } 1067 Member parent = member.getParentMember(); 1068 final SchemaReader schemaReader = evaluator.getSchemaReader(); 1069 if (parent == null) { 1070 return schemaReader.getHierarchyRootMembers( 1071 member.getHierarchy()); 1072 } else { 1073 return schemaReader.getMemberChildren(parent); 1074 } 1075 } 1076 }); 1077 1078 builder.define(StrToSetFunDef.Resolver); 1079 builder.define(SubsetFunDef.Resolver); 1080 builder.define(HeadTailFunDef.TailResolver); 1081 builder.define(ToggleDrillStateFunDef.Resolver); 1082 builder.define(UnionFunDef.Resolver); 1083 builder.define(VisualTotalsFunDef.Resolver); 1084 builder.define(XtdFunDef.WtdResolver); 1085 builder.define(XtdFunDef.YtdResolver); 1086 builder.define(RangeFunDef.instance); // "<member> : <member>" operator 1087 builder.define(SetFunDef.Resolver); // "{ <member> [,...] }" operator 1088 builder.define(NativizeSetFunDef.Resolver); 1089 1090 // 1091 // STRING FUNCTIONS 1092 builder.define(FormatFunDef.Resolver); 1093 1094 // <Dimension>.Caption 1095 builder.define( 1096 new FunDefBase( 1097 "Caption", 1098 "Returns the caption of a dimension.", 1099 "pSd") 1100 { 1101 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1102 { 1103 final DimensionCalc dimensionCalc = 1104 compiler.compileDimension(call.getArg(0)); 1105 return new AbstractStringCalc(call, new Calc[] {dimensionCalc}) 1106 { 1107 public String evaluateString(Evaluator evaluator) { 1108 final Dimension dimension = 1109 dimensionCalc.evaluateDimension(evaluator); 1110 return dimension.getCaption(); 1111 } 1112 }; 1113 } 1114 }); 1115 1116 // <Hierarchy>.Caption 1117 builder.define( 1118 new FunDefBase( 1119 "Caption", 1120 "Returns the caption of a hierarchy.", 1121 "pSh") 1122 { 1123 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1124 { 1125 final HierarchyCalc hierarchyCalc = 1126 compiler.compileHierarchy(call.getArg(0)); 1127 return new AbstractStringCalc(call, new Calc[] {hierarchyCalc}) 1128 { 1129 public String evaluateString(Evaluator evaluator) { 1130 final Hierarchy hierarchy = 1131 hierarchyCalc.evaluateHierarchy(evaluator); 1132 return hierarchy.getCaption(); 1133 } 1134 }; 1135 } 1136 }); 1137 1138 // <Level>.Caption 1139 builder.define( 1140 new FunDefBase( 1141 "Caption", 1142 "Returns the caption of a level.", 1143 "pSl") 1144 { 1145 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1146 { 1147 final LevelCalc levelCalc = 1148 compiler.compileLevel(call.getArg(0)); 1149 return new AbstractStringCalc(call, new Calc[] {levelCalc}) { 1150 public String evaluateString(Evaluator evaluator) { 1151 final Level level = levelCalc.evaluateLevel(evaluator); 1152 return level.getCaption(); 1153 } 1154 }; 1155 } 1156 }); 1157 1158 // <Member>.Caption 1159 builder.define( 1160 new FunDefBase( 1161 "Caption", 1162 "Returns the caption of a member.", 1163 "pSm") 1164 { 1165 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1166 { 1167 final MemberCalc memberCalc = 1168 compiler.compileMember(call.getArg(0)); 1169 return new AbstractStringCalc(call, new Calc[] {memberCalc}) { 1170 public String evaluateString(Evaluator evaluator) { 1171 final Member member = 1172 memberCalc.evaluateMember(evaluator); 1173 return member.getCaption(); 1174 } 1175 }; 1176 } 1177 }); 1178 1179 // <Dimension>.Name 1180 builder.define( 1181 new FunDefBase( 1182 "Name", 1183 "Returns the name of a dimension.", 1184 "pSd") 1185 { 1186 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1187 { 1188 final DimensionCalc dimensionCalc = 1189 compiler.compileDimension(call.getArg(0)); 1190 return new AbstractStringCalc(call, new Calc[] {dimensionCalc}) 1191 { 1192 public String evaluateString(Evaluator evaluator) { 1193 final Dimension dimension = 1194 dimensionCalc.evaluateDimension(evaluator); 1195 return dimension.getName(); 1196 } 1197 }; 1198 } 1199 }); 1200 1201 // <Hierarchy>.Name 1202 builder.define( 1203 new FunDefBase( 1204 "Name", 1205 "Returns the name of a hierarchy.", 1206 "pSh") 1207 { 1208 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1209 { 1210 final HierarchyCalc hierarchyCalc = 1211 compiler.compileHierarchy(call.getArg(0)); 1212 return new AbstractStringCalc(call, new Calc[] {hierarchyCalc}) 1213 { 1214 public String evaluateString(Evaluator evaluator) { 1215 final Hierarchy hierarchy = 1216 hierarchyCalc.evaluateHierarchy(evaluator); 1217 return hierarchy.getName(); 1218 } 1219 }; 1220 } 1221 }); 1222 1223 // <Level>.Name 1224 builder.define( 1225 new FunDefBase( 1226 "Name", 1227 "Returns the name of a level.", 1228 "pSl") 1229 { 1230 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1231 { 1232 final LevelCalc levelCalc = 1233 compiler.compileLevel(call.getArg(0)); 1234 return new AbstractStringCalc(call, new Calc[] {levelCalc}) { 1235 public String evaluateString(Evaluator evaluator) { 1236 final Level level = levelCalc.evaluateLevel(evaluator); 1237 return level.getName(); 1238 } 1239 }; 1240 } 1241 }); 1242 1243 // <Member>.Name 1244 builder.define( 1245 new FunDefBase( 1246 "Name", 1247 "Returns the name of a member.", 1248 "pSm") 1249 { 1250 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1251 { 1252 final MemberCalc memberCalc = 1253 compiler.compileMember(call.getArg(0)); 1254 return new AbstractStringCalc(call, new Calc[] {memberCalc}) { 1255 public String evaluateString(Evaluator evaluator) { 1256 final Member member = 1257 memberCalc.evaluateMember(evaluator); 1258 return member.getName(); 1259 } 1260 }; 1261 } 1262 }); 1263 1264 builder.define(SetToStrFunDef.instance); 1265 1266 builder.define(TupleToStrFunDef.instance); 1267 1268 // <Dimension>.UniqueName 1269 builder.define( 1270 new FunDefBase( 1271 "UniqueName", 1272 "Returns the unique name of a dimension.", 1273 "pSd") 1274 { 1275 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1276 { 1277 final DimensionCalc dimensionCalc = 1278 compiler.compileDimension(call.getArg(0)); 1279 return new AbstractStringCalc(call, new Calc[] {dimensionCalc}) 1280 { 1281 public String evaluateString(Evaluator evaluator) { 1282 final Dimension dimension = 1283 dimensionCalc.evaluateDimension(evaluator); 1284 return dimension.getUniqueName(); 1285 } 1286 }; 1287 } 1288 }); 1289 1290 // <Hierarchy>.UniqueName 1291 builder.define( 1292 new FunDefBase( 1293 "UniqueName", 1294 "Returns the unique name of a hierarchy.", 1295 "pSh") 1296 { 1297 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1298 { 1299 final HierarchyCalc hierarchyCalc = 1300 compiler.compileHierarchy(call.getArg(0)); 1301 return new AbstractStringCalc(call, new Calc[] {hierarchyCalc}) 1302 { 1303 public String evaluateString(Evaluator evaluator) { 1304 final Hierarchy hierarchy = 1305 hierarchyCalc.evaluateHierarchy(evaluator); 1306 return hierarchy.getUniqueName(); 1307 } 1308 }; 1309 } 1310 }); 1311 1312 // <Level>.UniqueName 1313 builder.define( 1314 new FunDefBase( 1315 "UniqueName", 1316 "Returns the unique name of a level.", 1317 "pSl") 1318 { 1319 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1320 { 1321 final LevelCalc levelCalc = 1322 compiler.compileLevel(call.getArg(0)); 1323 return new AbstractStringCalc(call, new Calc[] {levelCalc}) { 1324 public String evaluateString(Evaluator evaluator) { 1325 final Level level = levelCalc.evaluateLevel(evaluator); 1326 return level.getUniqueName(); 1327 } 1328 }; 1329 } 1330 }); 1331 1332 // <Member>.UniqueName 1333 builder.define( 1334 new FunDefBase( 1335 "UniqueName", 1336 "Returns the unique name of a member.", 1337 "pSm") 1338 { 1339 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1340 { 1341 final MemberCalc memberCalc = 1342 compiler.compileMember(call.getArg(0)); 1343 return new AbstractStringCalc(call, new Calc[] {memberCalc}) { 1344 public String evaluateString(Evaluator evaluator) { 1345 final Member member = 1346 memberCalc.evaluateMember(evaluator); 1347 return member.getUniqueName(); 1348 } 1349 }; 1350 } 1351 }); 1352 1353 // 1354 // TUPLE FUNCTIONS 1355 1356 // <Set>.Current 1357 if (false) 1358 builder.define( 1359 new FunDefBase( 1360 "Current", 1361 "Returns the current tuple from a set during an iteration.", 1362 "ptx") 1363 { 1364 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1365 { 1366 throw new UnsupportedOperationException(); 1367 } 1368 }); 1369 1370 builder.define(SetItemFunDef.intResolver); 1371 builder.define(SetItemFunDef.stringResolver); 1372 builder.define(TupleItemFunDef.instance); 1373 builder.define(StrToTupleFunDef.Resolver); 1374 1375 // special resolver for "()" 1376 builder.define(TupleFunDef.Resolver); 1377 1378 // 1379 // GENERIC VALUE FUNCTIONS 1380 builder.define(CoalesceEmptyFunDef.Resolver); 1381 builder.define(CaseTestFunDef.Resolver); 1382 builder.define(CaseMatchFunDef.Resolver); 1383 builder.define(PropertiesFunDef.Resolver); 1384 1385 // 1386 // PARAMETER FUNCTIONS 1387 builder.define(new ParameterFunDef.ParameterResolver()); 1388 builder.define(new ParameterFunDef.ParamRefResolver()); 1389 1390 // 1391 // OPERATORS 1392 1393 // <Numeric Expression> + <Numeric Expression> 1394 builder.define( 1395 new FunDefBase( 1396 "+", 1397 "Adds two numbers.", 1398 "innn") 1399 { 1400 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1401 { 1402 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1403 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1404 return new AbstractDoubleCalc(call, new Calc[] {calc0, calc1}) { 1405 public double evaluateDouble(Evaluator evaluator) { 1406 final double v0 = calc0.evaluateDouble(evaluator); 1407 final double v1 = calc1.evaluateDouble(evaluator); 1408 if (v0 == DoubleNull) { 1409 if (v1 == DoubleNull) { 1410 return DoubleNull; 1411 } else { 1412 return v1; 1413 } 1414 } else { 1415 if (v1 == DoubleNull) { 1416 return v0; 1417 } else { 1418 return v0 + v1; 1419 } 1420 } 1421 } 1422 }; 1423 } 1424 }); 1425 1426 // <Numeric Expression> - <Numeric Expression> 1427 builder.define( 1428 new FunDefBase( 1429 "-", 1430 "Subtracts two numbers.", 1431 "innn") 1432 { 1433 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1434 { 1435 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1436 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1437 return new AbstractDoubleCalc(call, new Calc[] {calc0, calc1}) { 1438 public double evaluateDouble(Evaluator evaluator) { 1439 final double v0 = calc0.evaluateDouble(evaluator); 1440 final double v1 = calc1.evaluateDouble(evaluator); 1441 if (v0 == DoubleNull) { 1442 if (v1 == DoubleNull) { 1443 return DoubleNull; 1444 } else { 1445 return - v1; 1446 } 1447 } else { 1448 if (v1 == DoubleNull) { 1449 return v0; 1450 } else { 1451 return v0 - v1; 1452 } 1453 } 1454 } 1455 }; 1456 } 1457 }); 1458 1459 // <Numeric Expression> * <Numeric Expression> 1460 builder.define( 1461 new FunDefBase( 1462 "*", 1463 "Multiplies two numbers.", 1464 "innn") 1465 { 1466 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1467 { 1468 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1469 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1470 return new AbstractDoubleCalc(call, new Calc[] {calc0, calc1}) { 1471 public double evaluateDouble(Evaluator evaluator) { 1472 final double v0 = calc0.evaluateDouble(evaluator); 1473 final double v1 = calc1.evaluateDouble(evaluator); 1474 // Multiply and divide return null if EITHER arg is 1475 // null. 1476 if (v0 == DoubleNull || v1 == DoubleNull) { 1477 return DoubleNull; 1478 } else { 1479 return v0 * v1; 1480 } 1481 } 1482 }; 1483 } 1484 }); 1485 1486 // <Numeric Expression> / <Numeric Expression> 1487 builder.define( 1488 new FunDefBase( 1489 "/", 1490 "Divides two numbers.", 1491 "innn") 1492 { 1493 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1494 { 1495 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1496 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1497 final boolean isNullDenominatorProducesNull = 1498 MondrianProperties.instance().NullDenominatorProducesNull 1499 .get(); 1500 1501 // If the mondrian property 1502 // mondrian.olap.NullOrZeroDenominatorProducesNull 1503 // is false(default), Null in denominator with numeric numerator 1504 // returns infinity. This is consistent with MSAS. 1505 // 1506 // If this property is true, Null or zero in denominator returns 1507 // Null. This is only used by certain applications and does not 1508 // conform to MSAS behavior. 1509 if (!isNullDenominatorProducesNull) { 1510 return new AbstractDoubleCalc( 1511 call, new Calc[] {calc0, calc1}) 1512 { 1513 public double evaluateDouble(Evaluator evaluator) { 1514 final double v0 = calc0.evaluateDouble(evaluator); 1515 final double v1 = calc1.evaluateDouble(evaluator); 1516 // Null in numerator always returns DoubleNull. 1517 // 1518 if (v0 == DoubleNull) { 1519 return DoubleNull; 1520 } else if (v1 == DoubleNull) { 1521 // Null only in denominator returns Infinity. 1522 return Double.POSITIVE_INFINITY; 1523 } else { 1524 return v0 / v1; 1525 } 1526 } 1527 }; 1528 } else { 1529 return new AbstractDoubleCalc( 1530 call, new Calc[] {calc0, calc1}) 1531 { 1532 public double evaluateDouble(Evaluator evaluator) { 1533 final double v0 = calc0.evaluateDouble(evaluator); 1534 final double v1 = calc1.evaluateDouble(evaluator); 1535 // Null in numerator or denominator returns 1536 // DoubleNull. 1537 if (v0 == DoubleNull || v1 == DoubleNull) { 1538 return DoubleNull; 1539 } else { 1540 return v0 / v1; 1541 } 1542 } 1543 }; 1544 } 1545 } 1546 }); 1547 1548 // - <Numeric Expression> 1549 builder.define( 1550 new FunDefBase( 1551 "-", 1552 "Returns the negative of a number.", 1553 "Pnn") 1554 { 1555 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1556 { 1557 final DoubleCalc calc = compiler.compileDouble(call.getArg(0)); 1558 return new AbstractDoubleCalc(call, new Calc[] {calc}) { 1559 public double evaluateDouble(Evaluator evaluator) { 1560 final double v = calc.evaluateDouble(evaluator); 1561 if (v == DoubleNull) { 1562 return DoubleNull; 1563 } else { 1564 return - v; 1565 } 1566 } 1567 }; 1568 } 1569 }); 1570 1571 // <String Expression> || <String Expression> 1572 builder.define( 1573 new FunDefBase( 1574 "||", 1575 "Concatenates two strings.", 1576 "iSSS") 1577 { 1578 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1579 { 1580 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1581 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1582 return new AbstractStringCalc(call, new Calc[] {calc0, calc1}) { 1583 public String evaluateString(Evaluator evaluator) { 1584 final String s0 = calc0.evaluateString(evaluator); 1585 final String s1 = calc1.evaluateString(evaluator); 1586 return s0 + s1; 1587 } 1588 }; 1589 } 1590 }); 1591 1592 // <Logical Expression> AND <Logical Expression> 1593 builder.define( 1594 new FunDefBase( 1595 "AND", 1596 "Returns the conjunction of two conditions.", 1597 "ibbb") 1598 { 1599 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1600 { 1601 final BooleanCalc calc0 = 1602 compiler.compileBoolean(call.getArg(0)); 1603 final BooleanCalc calc1 = 1604 compiler.compileBoolean(call.getArg(1)); 1605 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1606 { 1607 public boolean evaluateBoolean(Evaluator evaluator) { 1608 boolean b0 = calc0.evaluateBoolean(evaluator); 1609 // don't short-circuit evaluation if we're evaluating 1610 // the axes; that way, we can combine all measures 1611 // referenced in the AND expression in a single query 1612 if (!evaluator.isEvalAxes() && !b0) { 1613 return false; 1614 } 1615 boolean b1 = calc1.evaluateBoolean(evaluator); 1616 return b0 && b1; 1617 } 1618 }; 1619 } 1620 }); 1621 1622 // <Logical Expression> OR <Logical Expression> 1623 builder.define( 1624 new FunDefBase( 1625 "OR", 1626 "Returns the disjunction of two conditions.", 1627 "ibbb") 1628 { 1629 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1630 { 1631 final BooleanCalc calc0 = 1632 compiler.compileBoolean(call.getArg(0)); 1633 final BooleanCalc calc1 = 1634 compiler.compileBoolean(call.getArg(1)); 1635 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1636 { 1637 public boolean evaluateBoolean(Evaluator evaluator) { 1638 boolean b0 = calc0.evaluateBoolean(evaluator); 1639 // don't short-circuit evaluation if we're evaluating 1640 // the axes; that way, we can combine all measures 1641 // referenced in the OR expression in a single query 1642 if (!evaluator.isEvalAxes() && b0) { 1643 return true; 1644 } 1645 boolean b1 = calc1.evaluateBoolean(evaluator); 1646 return b0 || b1; 1647 } 1648 }; 1649 } 1650 }); 1651 1652 // <Logical Expression> XOR <Logical Expression> 1653 builder.define( 1654 new FunDefBase( 1655 "XOR", 1656 "Returns whether two conditions are mutually exclusive.", 1657 "ibbb") 1658 { 1659 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1660 { 1661 final BooleanCalc calc0 = 1662 compiler.compileBoolean(call.getArg(0)); 1663 final BooleanCalc calc1 = 1664 compiler.compileBoolean(call.getArg(1)); 1665 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1666 { 1667 public boolean evaluateBoolean(Evaluator evaluator) { 1668 final boolean b0 = calc0.evaluateBoolean(evaluator); 1669 final boolean b1 = calc1.evaluateBoolean(evaluator); 1670 return b0 != b1; 1671 } 1672 }; 1673 } 1674 }); 1675 1676 // NOT <Logical Expression> 1677 builder.define( 1678 new FunDefBase( 1679 "NOT", 1680 "Returns the negation of a condition.", 1681 "Pbb") 1682 { 1683 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1684 { 1685 final BooleanCalc calc = 1686 compiler.compileBoolean(call.getArg(0)); 1687 return new AbstractBooleanCalc(call, new Calc[] {calc}) { 1688 public boolean evaluateBoolean(Evaluator evaluator) { 1689 return !calc.evaluateBoolean(evaluator); 1690 } 1691 }; 1692 } 1693 }); 1694 1695 // <String Expression> = <String Expression> 1696 builder.define( 1697 new FunDefBase( 1698 "=", 1699 "Returns whether two expressions are equal.", 1700 "ibSS") 1701 { 1702 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1703 { 1704 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1705 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1706 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1707 { 1708 public boolean evaluateBoolean(Evaluator evaluator) { 1709 final String b0 = calc0.evaluateString(evaluator); 1710 final String b1 = calc1.evaluateString(evaluator); 1711 if (b0 == null || b1 == null) { 1712 return BooleanNull; 1713 } 1714 return b0.equals(b1); 1715 } 1716 }; 1717 } 1718 }); 1719 1720 // <Numeric Expression> = <Numeric Expression> 1721 builder.define( 1722 new FunDefBase( 1723 "=", 1724 "Returns whether two expressions are equal.", 1725 "ibnn") 1726 { 1727 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1728 { 1729 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1730 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1731 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1732 { 1733 public boolean evaluateBoolean(Evaluator evaluator) { 1734 final double v0 = calc0.evaluateDouble(evaluator); 1735 final double v1 = calc1.evaluateDouble(evaluator); 1736 if (Double.isNaN(v0) 1737 || Double.isNaN(v1) 1738 || v0 == DoubleNull 1739 || v1 == DoubleNull) 1740 { 1741 return BooleanNull; 1742 } 1743 return v0 == v1; 1744 } 1745 }; 1746 } 1747 }); 1748 1749 // <String Expression> <> <String Expression> 1750 builder.define( 1751 new FunDefBase( 1752 "<>", 1753 "Returns whether two expressions are not equal.", 1754 "ibSS") 1755 { 1756 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1757 { 1758 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1759 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1760 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1761 { 1762 public boolean evaluateBoolean(Evaluator evaluator) { 1763 final String b0 = calc0.evaluateString(evaluator); 1764 final String b1 = calc1.evaluateString(evaluator); 1765 if (b0 == null || b1 == null) { 1766 return BooleanNull; 1767 } 1768 return !b0.equals(b1); 1769 } 1770 }; 1771 } 1772 }); 1773 1774 // <Numeric Expression> <> <Numeric Expression> 1775 builder.define( 1776 new FunDefBase( 1777 "<>", 1778 "Returns whether two expressions are not equal.", 1779 "ibnn") 1780 { 1781 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1782 { 1783 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1784 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1785 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1786 { 1787 public boolean evaluateBoolean(Evaluator evaluator) { 1788 final double v0 = calc0.evaluateDouble(evaluator); 1789 final double v1 = calc1.evaluateDouble(evaluator); 1790 if (Double.isNaN(v0) 1791 || Double.isNaN(v1) 1792 || v0 == DoubleNull 1793 || v1 == DoubleNull) 1794 { 1795 return BooleanNull; 1796 } 1797 return v0 != v1; 1798 } 1799 }; 1800 } 1801 }); 1802 1803 // <Numeric Expression> < <Numeric Expression> 1804 builder.define( 1805 new FunDefBase( 1806 "<", 1807 "Returns whether an expression is less than another.", 1808 "ibnn") 1809 { 1810 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1811 { 1812 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1813 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1814 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1815 { 1816 public boolean evaluateBoolean(Evaluator evaluator) { 1817 final double v0 = calc0.evaluateDouble(evaluator); 1818 final double v1 = calc1.evaluateDouble(evaluator); 1819 if (Double.isNaN(v0) 1820 || Double.isNaN(v1) 1821 || v0 == DoubleNull 1822 || v1 == DoubleNull) 1823 { 1824 return BooleanNull; 1825 } 1826 return v0 < v1; 1827 } 1828 }; 1829 } 1830 }); 1831 1832 // <String Expression> < <String Expression> 1833 builder.define( 1834 new FunDefBase( 1835 "<", 1836 "Returns whether an expression is less than another.", 1837 "ibSS") 1838 { 1839 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1840 { 1841 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1842 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1843 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1844 { 1845 public boolean evaluateBoolean(Evaluator evaluator) { 1846 final String b0 = calc0.evaluateString(evaluator); 1847 final String b1 = calc1.evaluateString(evaluator); 1848 if (b0 == null || b1 == null) { 1849 return BooleanNull; 1850 } 1851 return b0.compareTo(b1) < 0; 1852 } 1853 }; 1854 } 1855 }); 1856 1857 // <Numeric Expression> <= <Numeric Expression> 1858 builder.define( 1859 new FunDefBase( 1860 "<=", 1861 "Returns whether an expression is less than or equal to another.", 1862 "ibnn") 1863 { 1864 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1865 { 1866 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1867 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1868 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1869 { 1870 public boolean evaluateBoolean(Evaluator evaluator) { 1871 final double v0 = calc0.evaluateDouble(evaluator); 1872 final double v1 = calc1.evaluateDouble(evaluator); 1873 if (Double.isNaN(v0) 1874 || Double.isNaN(v1) 1875 || v0 == DoubleNull 1876 || v1 == DoubleNull) 1877 { 1878 return BooleanNull; 1879 } 1880 return v0 <= v1; 1881 } 1882 }; 1883 } 1884 }); 1885 1886 // <String Expression> <= <String Expression> 1887 builder.define( 1888 new FunDefBase( 1889 "<=", 1890 "Returns whether an expression is less than or equal to another.", 1891 "ibSS") 1892 { 1893 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1894 { 1895 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1896 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1897 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1898 { 1899 public boolean evaluateBoolean(Evaluator evaluator) { 1900 final String b0 = calc0.evaluateString(evaluator); 1901 final String b1 = calc1.evaluateString(evaluator); 1902 if (b0 == null || b1 == null) { 1903 return BooleanNull; 1904 } 1905 return b0.compareTo(b1) <= 0; 1906 } 1907 }; 1908 } 1909 }); 1910 1911 // <Numeric Expression> > <Numeric Expression> 1912 builder.define( 1913 new FunDefBase( 1914 ">", 1915 "Returns whether an expression is greater than another.", 1916 "ibnn") 1917 { 1918 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1919 { 1920 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1921 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1922 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1923 { 1924 public boolean evaluateBoolean(Evaluator evaluator) { 1925 final double v0 = calc0.evaluateDouble(evaluator); 1926 final double v1 = calc1.evaluateDouble(evaluator); 1927 if (Double.isNaN(v0) 1928 || Double.isNaN(v1) 1929 || v0 == DoubleNull 1930 || v1 == DoubleNull) 1931 { 1932 return BooleanNull; 1933 } 1934 return v0 > v1; 1935 } 1936 }; 1937 } 1938 }); 1939 1940 // <String Expression> > <String Expression> 1941 builder.define( 1942 new FunDefBase( 1943 ">", 1944 "Returns whether an expression is greater than another.", 1945 "ibSS") 1946 { 1947 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1948 { 1949 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 1950 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 1951 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1952 { 1953 public boolean evaluateBoolean(Evaluator evaluator) { 1954 final String b0 = calc0.evaluateString(evaluator); 1955 final String b1 = calc1.evaluateString(evaluator); 1956 if (b0 == null || b1 == null) { 1957 return BooleanNull; 1958 } 1959 return b0.compareTo(b1) > 0; 1960 } 1961 }; 1962 } 1963 }); 1964 1965 // <Numeric Expression> >= <Numeric Expression> 1966 builder.define( 1967 new FunDefBase( 1968 ">=", 1969 "Returns whether an expression is greater than or equal to another.", 1970 "ibnn") 1971 { 1972 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 1973 { 1974 final DoubleCalc calc0 = compiler.compileDouble(call.getArg(0)); 1975 final DoubleCalc calc1 = compiler.compileDouble(call.getArg(1)); 1976 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 1977 { 1978 public boolean evaluateBoolean(Evaluator evaluator) { 1979 final double v0 = calc0.evaluateDouble(evaluator); 1980 final double v1 = calc1.evaluateDouble(evaluator); 1981 if (Double.isNaN(v0) 1982 || Double.isNaN(v1) 1983 || v0 == DoubleNull 1984 || v1 == DoubleNull) 1985 { 1986 return BooleanNull; 1987 } 1988 return v0 >= v1; 1989 } 1990 }; 1991 } 1992 }); 1993 1994 // <String Expression> >= <String Expression> 1995 builder.define( 1996 new FunDefBase( 1997 ">=", 1998 "Returns whether an expression is greater than or equal to another.", 1999 "ibSS") 2000 { 2001 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 2002 { 2003 final StringCalc calc0 = compiler.compileString(call.getArg(0)); 2004 final StringCalc calc1 = compiler.compileString(call.getArg(1)); 2005 return new AbstractBooleanCalc(call, new Calc[] {calc0, calc1}) 2006 { 2007 public boolean evaluateBoolean(Evaluator evaluator) { 2008 final String b0 = calc0.evaluateString(evaluator); 2009 final String b1 = calc1.evaluateString(evaluator); 2010 if (b0 == null || b1 == null) { 2011 return BooleanNull; 2012 } 2013 return b0.compareTo(b1) >= 0; 2014 } 2015 }; 2016 } 2017 }); 2018 2019 // NON-STANDARD FUNCTIONS 2020 2021 builder.define(NthQuartileFunDef.FirstQResolver); 2022 2023 builder.define(NthQuartileFunDef.ThirdQResolver); 2024 2025 builder.define(CalculatedChildFunDef.instance); 2026 2027 builder.define(CastFunDef.Resolver); 2028 2029 // UCase(<String Expression>) 2030 builder.define( 2031 new FunDefBase( 2032 "UCase", 2033 "Returns a string that has been converted to uppercase", 2034 "fSS") 2035 { 2036 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 2037 { 2038 final Locale locale = 2039 compiler.getEvaluator().getConnectionLocale(); 2040 final StringCalc stringCalc = 2041 compiler.compileString(call.getArg(0)); 2042 return new AbstractStringCalc(call, new Calc[]{stringCalc}) { 2043 public String evaluateString(Evaluator evaluator) { 2044 String value = stringCalc.evaluateString(evaluator); 2045 return value.toUpperCase(locale); 2046 } 2047 }; 2048 } 2049 }); 2050 2051 // Len(<String Expression>) 2052 builder.define( 2053 new FunDefBase( 2054 "Len", 2055 "Returns the number of characters in a string", 2056 "fnS") 2057 { 2058 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) 2059 { 2060 final StringCalc stringCalc = 2061 compiler.compileString(call.getArg(0)); 2062 return new AbstractIntegerCalc(call, new Calc[] {stringCalc}) { 2063 public int evaluateInteger(Evaluator evaluator) { 2064 String value = stringCalc.evaluateString(evaluator); 2065 if (value == null) { 2066 return 0; 2067 } 2068 return value.length(); 2069 } 2070 }; 2071 } 2072 }); 2073 2074 // Define VBA functions. 2075 for (FunDef funDef : JavaFunDef.scan(Vba.class)) { 2076 builder.define(funDef); 2077 } 2078 2079 // Define Excel functions. 2080 for (FunDef funDef : JavaFunDef.scan(Excel.class)) { 2081 builder.define(funDef); 2082 } 2083 } 2084 2085 /** 2086 * Returns the singleton, creating if necessary. 2087 * 2088 * @return the singleton 2089 */ 2090 public static BuiltinFunTable instance() { 2091 if (instance == null) { 2092 instance = new BuiltinFunTable(); 2093 instance.init(); 2094 } 2095 return instance; 2096 } 2097 2098} 2099 2100// End BuiltinFunTable.java