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