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