KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > olap > fun > FunUtil


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#92 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2002-2002 Kana Software, Inc.
7 // Copyright (C) 2002-2007 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 3 March, 2002
12 */

13 package mondrian.olap.fun;
14
15 import mondrian.olap.*;
16 import mondrian.olap.type.*;
17 import mondrian.resource.MondrianResource;
18 import mondrian.calc.Calc;
19 import mondrian.calc.ExpCompiler.ResultStyle;
20 import mondrian.calc.DoubleCalc;
21 import mondrian.mdx.*;
22
23 import org.apache.log4j.Logger;
24
25 import java.util.*;
26 import java.util.Set JavaDoc;
27 import java.io.PrintWriter JavaDoc;
28
29 /**
30  * <code>FunUtil</code> contains a set of methods useful within the
31  * <code>mondrian.olap.fun</code> package.
32  *
33  * @author jhyde
34  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#92 $
35  * @since 1.0
36  */

37 public class FunUtil extends Util {
38
39     static final String JavaDoc[] emptyStringArray = new String JavaDoc[0];
40     private static final boolean debug = false;
41     public static final NullMember NullMember = new NullMember();
42
43     /**
44      * Special value which indicates that a <code>double</code> computation
45      * has returned the MDX null value. See {@link DoubleCalc}.
46      */

47     public static final double DoubleNull = 0.000000012345;
48
49     /**
50      * Special value which indicates that a <code>double</code> computation
51      * has returned the MDX EMPTY value. See {@link DoubleCalc}.
52      */

53     public static final double DoubleEmpty = -0.000000012345;
54
55     /**
56      * Special value which indicates that an <code>int</code> computation
57      * has returned the MDX null value. See {@link mondrian.calc.IntegerCalc}.
58      */

59     public static final int IntegerNull = Integer.MIN_VALUE + 1;
60
61     /**
62      * Null value in three-valued boolean logic.
63      * Actually, a placeholder until we actually implement 3VL.
64      */

65     public static final boolean BooleanNull = false;
66
67     /**
68      * Creates an exception which indicates that an error has occurred while
69      * executing a given function.
70      */

71     public static RuntimeException JavaDoc newEvalException(
72             FunDef funDef,
73             String JavaDoc message) {
74         Util.discard(funDef); // TODO: use this
75
return new MondrianEvaluationException(message);
76     }
77
78     /**
79      * Creates an exception which indicates that an error has occurred while
80      * executing a given function.
81      */

82     public static RuntimeException JavaDoc newEvalException(Throwable JavaDoc throwable) {
83         return new MondrianEvaluationException(throwable.getMessage());
84     }
85
86     public static final boolean isMemberType(Calc calc) {
87         Type type = calc.getType();
88         return (type instanceof SetType) &&
89           (((SetType) type).getElementType() instanceof MemberType);
90     }
91
92     public static final void checkIterListResultStyles(Calc calc) {
93         switch (calc.getResultStyle()) {
94         case ITERABLE:
95         case LIST:
96         case MUTABLE_LIST:
97             break;
98         default:
99             throw ResultStyleException.generateBadType(
100                 new ResultStyle[] {
101                     ResultStyle.ITERABLE,
102                     ResultStyle.LIST,
103                     ResultStyle.MUTABLE_LIST
104                 },
105                 calc.getResultStyle());
106         }
107     }
108
109     public static final void checkListResultStyles(Calc calc) {
110         switch (calc.getResultStyle()) {
111         case LIST:
112         case MUTABLE_LIST:
113             break;
114         default:
115             throw ResultStyleException.generateBadType(
116                 new ResultStyle[] {
117                     ResultStyle.LIST,
118                     ResultStyle.MUTABLE_LIST
119                 },
120                 calc.getResultStyle());
121         }
122     }
123
124
125     /**
126      * Returns an argument whose value is a literal.
127      */

128     static String JavaDoc getLiteralArg(
129             ResolvedFunCall call,
130             int i,
131             String JavaDoc defaultValue,
132             String JavaDoc[] allowedValues) {
133         if (i >= call.getArgCount()) {
134             if (defaultValue == null) {
135                 throw newEvalException(call.getFunDef(),
136                         "Required argument is missing");
137             } else {
138                 return defaultValue;
139             }
140         }
141         Exp arg = call.getArg(i);
142         if (!(arg instanceof Literal) ||
143                 arg.getCategory() != Category.Symbol) {
144             throw newEvalException(call.getFunDef(),
145                     "Expected a symbol, found '" + arg + "'");
146         }
147         String JavaDoc s = (String JavaDoc) ((Literal) arg).getValue();
148         StringBuilder JavaDoc sb = new StringBuilder JavaDoc(64);
149         for (int j = 0; j < allowedValues.length; j++) {
150             String JavaDoc allowedValue = allowedValues[j];
151             if (allowedValue.equalsIgnoreCase(s)) {
152                 return allowedValue;
153             }
154             if (j > 0) {
155                 sb.append(", ");
156             }
157             sb.append(allowedValue);
158         }
159         throw newEvalException(call.getFunDef(),
160                 "Allowed values are: {" + sb + "}");
161     }
162
163     /**
164      * Returns the ordinal of a literal argument. If the argument does not
165      * belong to the supplied enumeration, returns -1.
166      */

167     static <E extends Enum JavaDoc<E>> E getLiteralArg(
168             ResolvedFunCall call,
169             int i,
170             E defaultValue,
171             Class JavaDoc<E> allowedValues) {
172         if (i >= call.getArgCount()) {
173             if (defaultValue == null) {
174                 throw newEvalException(call.getFunDef(),
175                         "Required argument is missing");
176             } else {
177                 return defaultValue;
178             }
179         }
180         Exp arg = call.getArg(i);
181         if (!(arg instanceof Literal) ||
182                 arg.getCategory() != Category.Symbol) {
183             throw newEvalException(call.getFunDef(),
184                     "Expected a symbol, found '" + arg + "'");
185         }
186         String JavaDoc s = (String JavaDoc) ((Literal) arg).getValue();
187         for (E e : allowedValues.getEnumConstants()) {
188             if (e.name().equalsIgnoreCase(s)) {
189                 return e;
190             }
191         }
192         StringBuilder JavaDoc buf = new StringBuilder JavaDoc(64);
193         int k = 0;
194         for (E e : allowedValues.getEnumConstants()) {
195             if (k++ > 0) {
196                 buf.append(", ");
197             }
198             buf.append(e.name());
199         }
200         throw newEvalException(call.getFunDef(),
201                 "Allowed values are: {" + buf + "}");
202     }
203
204     /**
205      * Throws an error if the expressions don't have the same hierarchy.
206      * @param left
207      * @param right
208      * @throws MondrianEvaluationException if expressions don't have the same
209      * hierarchy
210      */

211     static void checkCompatible(Exp left, Exp right, FunDef funDef) {
212         final Type leftType = TypeUtil.stripSetType(left.getType());
213         final Type rightType = TypeUtil.stripSetType(right.getType());
214         if (!TypeUtil.isUnionCompatible(leftType, rightType)) {
215             throw newEvalException(funDef, "Expressions must have the same hierarchy");
216         }
217     }
218
219     /**
220      * Returns <code>true</code> if the mask in <code>flag</code> is set.
221      * @param value The value to check.
222      * @param mask The mask within value to look for.
223      * @param strict If <code>true</code> all the bits in mask must be set. If
224      * <code>false</code> the method will return <code>true</code> if any of the
225      * bits in <code>mask</code> are set.
226      * @return <code>true</code> if the correct bits in <code>mask</code> are set.
227      */

228     static boolean checkFlag(int value, int mask, boolean strict) {
229         return (strict)
230             ? ((value & mask) == mask)
231             : ((value & mask) != 0);
232     }
233
234     /**
235      * Adds every element of <code>right</code> which is not in <code>set</code>
236      * to both <code>set</code> and <code>left</code>.
237      */

238     static void addUnique(List left, List right, Set JavaDoc set) {
239         assert left != null;
240         assert right != null;
241         if (right.isEmpty()) {
242             return;
243         }
244         for (int i = 0, n = right.size(); i < n; i++) {
245             Object JavaDoc o = right.get(i),
246                     p = o;
247             if (o instanceof Object JavaDoc[]) {
248                 p = new ArrayHolder((Object JavaDoc[]) o);
249             }
250             if (set.add(p)) {
251                 left.add(o);
252             }
253         }
254     }
255
256     static List<Member> addMembers(
257             SchemaReader schemaReader,
258             List<Member> members,
259             Hierarchy hierarchy) {
260         // only add accessible levels
261
Level[] levels = schemaReader.getHierarchyLevels(hierarchy);
262         for (int i = 0; i < levels.length; i++) {
263             addMembers(schemaReader, members, levels[i]);
264         }
265         return members;
266     }
267
268     static List<Member> addMembers(
269             SchemaReader schemaReader,
270             List<Member> members,
271             Level level) {
272         Member[] levelMembers = schemaReader.getLevelMembers(level, true);
273         addAll(members, levelMembers);
274         return members;
275     }
276
277     /**
278      * Removes every member from a list which is calculated.
279      * The list must not be null, and must consist only of members.
280      */

281     static void removeCalculatedMembers(List<Member> memberList)
282     {
283         for (int i = 0; i < memberList.size(); i++) {
284             Member member = (Member) memberList.get(i);
285             if (member.isCalculated()) {
286                 memberList.remove(i);
287                 --i;
288             }
289         }
290     }
291
292     /**
293      * Returns whether <code>m0</code> is an ancestor of <code>m1</code>.
294      *
295      * @param strict if true, a member is not an ancestor of itself
296      */

297     static boolean isAncestorOf(Member m0, Member m1, boolean strict) {
298         if (strict) {
299             if (m1 == null) {
300                 return false;
301             }
302             m1 = m1.getParentMember();
303         }
304         while (m1 != null) {
305             if (m1 == m0) {
306                 return true;
307             }
308             m1 = m1.getParentMember();
309         }
310         return false;
311     }
312
313     /**
314      * For each member in a list, evaluate an expression and create a map
315      * from members to values.
316      *
317      * <p>If the list contains tuples, use
318      * {@link #evaluateTuples(mondrian.olap.Evaluator, mondrian.calc.Calc, java.util.List)}.
319      *
320      * @param evaluator Evaluation context
321      * @param exp Expression to evaluate
322      * @param members List of members
323      * @param parentsToo If true, evaluate the expression for all ancestors
324      * of the members as well
325      *
326      * @pre exp != null
327      * @pre exp.getType() instanceof ScalarType
328      */

329     static Map<Member, Object JavaDoc> evaluateMembers(
330             Evaluator evaluator,
331             Calc exp,
332             List<Member> members,
333             boolean parentsToo) {
334         // RME
335
evaluator= evaluator.push();
336
337         assert exp.getType() instanceof ScalarType;
338         Map<Member, Object JavaDoc> mapMemberToValue = new HashMap<Member, Object JavaDoc>();
339         for (int i = 0, count = members.size(); i < count; i++) {
340             Member member = members.get(i);
341             while (true) {
342                 evaluator.setContext(member);
343                 Object JavaDoc result = exp.evaluate(evaluator);
344                 if (result == null) {
345                     result = Util.nullValue;
346                 }
347                 mapMemberToValue.put(member, result);
348                 if (!parentsToo) {
349                     break;
350                 }
351                 member = member.getParentMember();
352                 if (member == null) {
353                     break;
354                 }
355                 if (mapMemberToValue.containsKey(member)) {
356                     break;
357                 }
358             }
359         }
360         return mapMemberToValue;
361     }
362
363     /**
364      * For each tuple in a list, evaluates an expression and creates a map
365      * from tuples to values.
366      *
367      * @param evaluator Evaluation context
368      * @param exp Expression to evaluate
369      * @param members List of members (or List of Member[] tuples)
370      *
371      * @pre exp != null
372      * @pre exp.getType() instanceof ScalarType
373      */

374     static Map<Object JavaDoc, Object JavaDoc> evaluateTuples(
375             Evaluator evaluator,
376             Calc exp,
377             List<Member[]> members) {
378         // RME
379
evaluator= evaluator.push();
380
381         assert exp.getType() instanceof ScalarType;
382         Map<Object JavaDoc, Object JavaDoc> mapMemberToValue = new HashMap<Object JavaDoc, Object JavaDoc>();
383         for (int i = 0, count = members.size(); i < count; i++) {
384             Member[] tuples = members.get(i);
385             evaluator.setContext(tuples);
386             Object JavaDoc result = exp.evaluate(evaluator);
387             if (result == null) {
388                 result = Util.nullValue;
389             }
390             mapMemberToValue.put(new ArrayHolder<Member>(tuples), result);
391         }
392         return mapMemberToValue;
393     }
394
395     static Map<Member, Object JavaDoc> evaluateMembers(
396             Evaluator evaluator,
397             List<Member> members,
398             boolean parentsToo) {
399         Map<Member, Object JavaDoc> mapMemberToValue = new HashMap<Member, Object JavaDoc>();
400         for (int i = 0, count = members.size(); i < count; i++) {
401             Member member = members.get(i);
402             while (true) {
403                 evaluator.setContext(member);
404                 Object JavaDoc result = evaluator.evaluateCurrent();
405                 mapMemberToValue.put(member, result);
406                 if (!parentsToo) {
407                     break;
408                 }
409                 member = member.getParentMember();
410                 if (member == null) {
411                     break;
412                 }
413                 if (mapMemberToValue.containsKey(member)) {
414                     break;
415                 }
416             }
417         }
418         return mapMemberToValue;
419     }
420
421     /**
422      * Helper function to sortMembers a list of members according to an expression.
423      *
424      * <p>NOTE: This function does not preserve the contents of the validator.
425      */

426     static void sortMembers(
427             Evaluator evaluator,
428             List<Member> members,
429             Calc exp,
430             boolean desc,
431             boolean brk) {
432         if (members.isEmpty()) {
433             return;
434         }
435         Object JavaDoc first = members.get(0);
436         Comparator comparator;
437         Map<Member, Object JavaDoc> mapMemberToValue;
438         if (first instanceof Member) {
439             final boolean parentsToo = !brk;
440             mapMemberToValue = evaluateMembers(evaluator, exp, members, parentsToo);
441             if (brk) {
442                 comparator = new BreakMemberComparator(mapMemberToValue, desc);
443             } else {
444                 comparator = new HierarchicalMemberComparator(mapMemberToValue, desc);
445             }
446         } else {
447             Util.assertTrue(first instanceof Member[]);
448             final int arity = ((Member[]) first).length;
449             if (brk) {
450                 comparator = new BreakArrayComparator(evaluator, exp, arity);
451                 if (desc) {
452                     comparator = new ReverseComparator(comparator);
453                 }
454             } else {
455                 comparator = new HierarchicalArrayComparator(evaluator, exp, arity, desc);
456             }
457         }
458         Collections.sort(members, comparator);
459         if (debug) {
460             final PrintWriter JavaDoc pw = new PrintWriter JavaDoc(System.out);
461             for (int i = 0; i < members.size(); i++) {
462                 Object JavaDoc o = members.get(i);
463                 pw.print(i);
464                 pw.print(": ");
465                 if (mapMemberToValue != null) {
466                     pw.print(mapMemberToValue.get(o));
467                     pw.print(": ");
468                 }
469                 pw.println(o);
470             }
471             pw.flush();
472         }
473     }
474
475     /**
476      * Helper function to sortMembers a list of members according to an expression.
477      *
478      * <p>NOTE: This function does not preserve the contents of the validator.
479      */

480     static void sortTuples(
481         Evaluator evaluator,
482         List<Member[]> tuples,
483         Calc exp,
484         boolean desc,
485         boolean brk,
486         int arity)
487     {
488         if (tuples.isEmpty()) {
489             return;
490         }
491         Comparator comparator;
492         if (brk) {
493             comparator = new BreakArrayComparator(evaluator, exp, arity);
494             if (desc) {
495                 comparator = new ReverseComparator(comparator);
496             }
497         } else {
498             comparator = new HierarchicalArrayComparator(evaluator, exp, arity, desc);
499         }
500         Collections.sort(tuples, comparator);
501         if (debug) {
502             final PrintWriter JavaDoc pw = new PrintWriter JavaDoc(System.out);
503             for (int i = 0; i < tuples.size(); i++) {
504                 Object JavaDoc o = tuples.get(i);
505                 pw.print(i);
506                 pw.print(": ");
507                 pw.println(o);
508             }
509             pw.flush();
510         }
511     }
512
513     public static void hierarchize(List members, boolean post) {
514         if (members.isEmpty()) {
515             return;
516         }
517         Object JavaDoc first = members.get(0);
518         Comparator comparator;
519         if (first instanceof Member) {
520             comparator = new HierarchizeComparator(post);
521         } else {
522             Util.assertTrue(first instanceof Member[]);
523             final int arity = ((Member[]) first).length;
524             comparator = new HierarchizeArrayComparator(arity, post);
525         }
526         Collections.sort(members, comparator);
527     }
528
529     static int sign(double d) {
530         return (d == 0)
531             ? 0
532             : (d < 0)
533                 ? -1
534                 : 1;
535     }
536
537     /**
538      * Compares double-precision values according to MDX semantics.
539      *
540      * <p>MDX requires a total order:
541      * <pre>
542      * -inf &lt; NULL &lt; ... &lt; -1 &lt; ... &lt; 0 &lt; ... &lt; NaN &lt; +inf
543      * </pre>
544      * but this is different than Java semantics, specifically with regard
545      * to {@link Double#NaN}.
546      */

547     static int compareValues(double d1, double d2) {
548         if (Double.isNaN(d1)) {
549             if (d2 == Double.POSITIVE_INFINITY) {
550                 return -1;
551             } else if (Double.isNaN(d2)) {
552                 return 0;
553             } else {
554                 return 1;
555             }
556         } else if (Double.isNaN(d2)) {
557             if (d1 == Double.POSITIVE_INFINITY) {
558                 return 1;
559             } else {
560                 return -1;
561             }
562         } else if (d1 == d2) {
563             return 0;
564         } else if (d1 == FunUtil.DoubleNull) {
565             if (d2 == Double.NEGATIVE_INFINITY) {
566                 return 1;
567             } else {
568                 return -1;
569             }
570         } else if (d2 == FunUtil.DoubleNull) {
571             if (d1 == Double.NEGATIVE_INFINITY) {
572                 return -1;
573             } else {
574                 return 1;
575             }
576         } else if (d1 < d2) {
577             return -1;
578         } else {
579             return 1;
580         }
581     }
582
583     static int compareValues(int i, int j) {
584         return (i == j)
585             ? 0
586             : (i < j)
587                 ? -1
588                 : 1;
589     }
590
591     /**
592      * Compares two cell values.
593      *
594      * <p>Nulls compare last, exceptions (including the
595      * object which indicates the the cell is not in the cache yet) next,
596      * then numbers and strings are compared by value.
597      *
598      * @param value0 First cell value
599      * @param value1 Second cell value
600      * @return -1, 0, or 1, depending upon whether first cell value is less
601      * than, equal to, or greater than the second
602      */

603     static int compareValues(Object JavaDoc value0, Object JavaDoc value1) {
604         if (value0 == value1) {
605             return 0;
606         }
607         // null is less than anything else
608
if (value0 == null) {
609             return -1;
610         }
611         if (value1 == null) {
612             return 1;
613         }
614         if (value0 instanceof RuntimeException JavaDoc ||
615             value1 instanceof RuntimeException JavaDoc) {
616             // one of the values is not in cache; continue as best as we can
617
return 0;
618         } else if (value0 == Util.nullValue) {
619             return -1; // null == -infinity
620
} else if (value1 == Util.nullValue) {
621             return 1; // null == -infinity
622
} else if (value0 instanceof String JavaDoc) {
623             return ((String JavaDoc) value0).compareTo((String JavaDoc) value1);
624         } else if (value0 instanceof Number JavaDoc) {
625             return FunUtil.compareValues(
626                     ((Number JavaDoc) value0).doubleValue(),
627                     ((Number JavaDoc) value1).doubleValue());
628         } else {
629             throw Util.newInternal("cannot compare " + value0);
630         }
631     }
632
633     /**
634      * Turns the mapped values into relative values (percentages) for easy
635      * use by the general topOrBottom function. This might also be a useful
636      * function in itself.
637      */

638     static void toPercent(List members, Map mapMemberToValue, boolean isMember) {
639         double total = 0;
640         int memberCount = members.size();
641         for (int i = 0; i < memberCount; i++) {
642             Object JavaDoc o = (isMember)
643                     ? mapMemberToValue.get(members.get(i))
644                     : mapMemberToValue.get(
645                         new ArrayHolder<Member>((Member []) members.get(i)));
646             if (o instanceof Number JavaDoc) {
647                 total += ((Number JavaDoc) o).doubleValue();
648             }
649         }
650         for (int i = 0; i < memberCount; i++) {
651             Object JavaDoc mo = members.get(i);
652             Object JavaDoc o = (isMember) ?
653                 mapMemberToValue.get(mo) :
654                 mapMemberToValue.get(new ArrayHolder<Member>((Member []) mo));
655             if (o instanceof Number JavaDoc) {
656                 double d = ((Number JavaDoc) o).doubleValue();
657                 if (isMember) {
658                     mapMemberToValue.put(
659                         mo,
660                         d / total * (double) 100);
661                 } else {
662                     mapMemberToValue.put(
663                         new ArrayHolder<Member>((Member []) mo),
664                         d / total * (double) 100);
665                 }
666             }
667         }
668     }
669
670
671     /**
672      * Decodes the syntactic type of an operator.
673      *
674      * @param flags A encoded string which represents an operator signature,
675      * as used by the <code>flags</code> parameter used to construct a
676      * {@link FunDefBase}.
677      *
678      * @return A {@link Syntax}
679      */

680     public static Syntax decodeSyntacticType(String JavaDoc flags) {
681         char c = flags.charAt(0);
682         switch (c) {
683         case 'p':
684             return Syntax.Property;
685         case 'f':
686             return Syntax.Function;
687         case 'm':
688             return Syntax.Method;
689         case 'i':
690             return Syntax.Infix;
691         case 'P':
692             return Syntax.Prefix;
693         case 'Q':
694             return Syntax.Postfix;
695         case 'I':
696             return Syntax.Internal;
697         default:
698             throw newInternal(
699                     "unknown syntax code '" + c + "' in string '" + flags + "'");
700         }
701     }
702
703     /**
704      * Decodes the signature of a function into a category code which describes
705      * the return type of the operator.
706      *
707      * <p>For example, <code>decodeReturnType("fnx")</code> returns
708      * <code>{@link Category#Numeric}</code>, indicating this function has a
709      * numeric return value.
710      *
711      * @param flags The signature of an operator,
712      * as used by the <code>flags</code> parameter used to construct a
713      * {@link FunDefBase}.
714      *
715      * @return An array {@link Category} codes.
716      */

717     public static int decodeReturnCategory(String JavaDoc flags) {
718         final int returnCategory = decodeCategory(flags, 1);
719         if ((returnCategory & Category.Mask) != returnCategory) {
720             throw newInternal("bad return code flag in flags '" + flags + "'");
721         }
722         return returnCategory;
723     }
724
725     /**
726      * Decodes the <code>offset</code>th character of an encoded method
727      * signature into a type category.
728      *
729      * <p>The codes are:
730      * <table border="1">
731      *
732      * <tr><td>a</td><td>{@link Category#Array}</td></tr>
733      *
734      * <tr><td>d</td><td>{@link Category#Dimension}</td></tr>
735      *
736      * <tr><td>h</td><td>{@link Category#Hierarchy}</td></tr>
737      *
738      * <tr><td>l</td><td>{@link Category#Level}</td></tr>
739      *
740      * <tr><td>b</td><td>{@link Category#Logical}</td></tr>
741      *
742      * <tr><td>m</td><td>{@link Category#Member}</td></tr>
743      *
744      * <tr><td>N</td><td>Constant {@link Category#Numeric}</td></tr>
745      *
746      * <tr><td>n</td><td>{@link Category#Numeric}</td></tr>
747      *
748      * <tr><td>x</td><td>{@link Category#Set}</td></tr>
749      *
750      * <tr><td>#</td><td>Constant {@link Category#String}</td></tr>
751      *
752      * <tr><td>S</td><td>{@link Category#String}</td></tr>
753      *
754      * <tr><td>t</td><td>{@link Category#Tuple}</td></tr>
755      *
756      * <tr><td>v</td><td>{@link Category#Value}</td></tr>
757      *
758      * <tr><td>y</td><td>{@link Category#Symbol}</td></tr>
759      *
760      * </table>
761      *
762      * @param flags Encoded signature string
763      * @param offset 0-based offset of character within string
764      * @return A {@link Category}
765      */

766     public static int decodeCategory(String JavaDoc flags, int offset) {
767         char c = flags.charAt(offset);
768         switch (c) {
769         case 'a':
770             return Category.Array;
771         case 'd':
772             return Category.Dimension;
773         case 'h':
774             return Category.Hierarchy;
775         case 'l':
776             return Category.Level;
777         case 'b':
778             return Category.Logical;
779         case 'm':
780             return Category.Member;
781         case 'N':
782             return Category.Numeric | Category.Constant;
783         case 'n':
784             return Category.Numeric;
785         case 'I':
786             return Category.Numeric | Category.Integer | Category.Constant;
787         case 'i':
788             return Category.Numeric | Category.Integer;
789         case 'x':
790             return Category.Set;
791         case '#':
792             return Category.String | Category.Constant;
793         case 'S':
794             return Category.String;
795         case 't':
796             return Category.Tuple;
797         case 'v':
798             return Category.Value;
799         case 'y':
800             return Category.Symbol;
801         case 'U':
802             return Category.Null;
803         default:
804             throw newInternal(
805                     "unknown type code '" + c + "' in string '" + flags + "'");
806         }
807     }
808
809     /**
810      * Decodes a string of parameter types into an array of type codes.
811      *
812      * <p>Each character is decoded using {@link #decodeCategory(String, int)}.
813      * For example, <code>decodeParameterTypes("nx")</code> returns
814      * <code>{{@link Category#Numeric}, {@link Category#Set}}</code>.
815      *
816      * @param flags The signature of an operator,
817      * as used by the <code>flags</code> parameter used to construct a
818      * {@link FunDefBase}.
819      *
820      * @return An array {@link Category} codes.
821      */

822     public static int[] decodeParameterCategories(String JavaDoc flags) {
823         int[] parameterCategories = new int[flags.length() - 2];
824         for (int i = 0; i < parameterCategories.length; i++) {
825             parameterCategories[i] = decodeCategory(flags, i + 2);
826         }
827         return parameterCategories;
828     }
829
830     /**
831      * Sorts an array of values.
832      */

833     public static void sortValuesDesc(Object JavaDoc[] values) {
834         Arrays.sort(values, DescendingValueComparator.instance);
835     }
836
837     /**
838      * Binary searches an array of values.
839      */

840     public static int searchValuesDesc(Object JavaDoc[] values, Object JavaDoc value) {
841         return Arrays.binarySearch(
842                 values, value, DescendingValueComparator.instance);
843     }
844
845     static Object JavaDoc median(Evaluator evaluator, List members, Calc exp) {
846         SetWrapper sw = evaluateSet(evaluator, members, exp);
847         if (sw.errorCount > 0) {
848             return new Double JavaDoc(Double.NaN);
849         } else if (sw.v.size() == 0) {
850             return Util.nullValue;
851         }
852         double[] asArray = new double[sw.v.size()];
853         for (int i = 0; i < asArray.length; i++) {
854             asArray[i] = ((Double JavaDoc) sw.v.get(i)).doubleValue();
855         }
856         Arrays.sort(asArray);
857
858         /*
859          * The median is defined as the value that has exactly the same
860          * number of entries before it in the sorted list as after.
861          * So, if the number of entries in the list is odd, the
862          * median is the entry at (length-1)/2 (using zero-based indexes).
863          * If the number of entries is even, the median is defined as the
864          * arithmetic mean of the two numbers in the middle of the list, or
865          * (entries[length/2 - 1] + entries[length/2]) / 2.
866          */

867         int length = asArray.length;
868         Double JavaDoc result = ((length & 1) == 1)
869             // The length is odd. Note that length/2 is an integer expression,
870
// and it's positive so we save ourselves a divide...
871
? new Double JavaDoc(asArray[length >> 1])
872             : new Double JavaDoc((asArray[(length >> 1) - 1] + asArray[length >> 1]) / 2.0);
873
874         return result;
875     }
876
877     /**
878      * Returns the member which lies upon a particular quartile according to a
879      * given expression.
880      *
881      * @param evaluator Evaluator
882      * @param members List of members
883      * @param exp Expression to rank members
884      * @param range Quartile (1, 2 or 3)
885      *
886      * @pre range >= 1 && range <= 3
887      */

888     protected static double quartile(
889             Evaluator evaluator,
890             List members,
891             Calc exp,
892             int range) {
893         Util.assertPrecondition(range >= 1 && range <= 3, "range >= 1 && range <= 3");
894
895         SetWrapper sw = evaluateSet(evaluator, members, exp);
896         if (sw.errorCount > 0) {
897             return Double.NaN;
898         } else if (sw.v.size() == 0) {
899             return DoubleNull;
900         }
901
902         double[] asArray = new double[sw.v.size()];
903         for (int i = 0; i < asArray.length; i++) {
904             asArray[i] = ((Double JavaDoc) sw.v.get(i)).doubleValue();
905         }
906
907         Arrays.sort(asArray);
908         // get a quartile, median is a second q
909
double dm = (asArray.length * range) / 4;
910         int median = (int) Math.floor(dm);
911         return dm == median && median < asArray.length - 1 ?
912                 (asArray[median] + asArray[median + 1]) / 2 :
913                 asArray[median];
914     }
915
916     public static Object JavaDoc min(Evaluator evaluator, List members, Calc calc) {
917         SetWrapper sw = evaluateSet(evaluator, members, calc);
918         if (sw.errorCount > 0) {
919             return new Double JavaDoc(Double.NaN);
920         } else if (sw.v.size() == 0) {
921             return Util.nullValue;
922         } else {
923             double min = Double.MAX_VALUE;
924             for (int i = 0; i < sw.v.size(); i++) {
925                 double iValue = ((Double JavaDoc) sw.v.get(i)).doubleValue();
926                 if (iValue < min) {
927                     min = iValue;
928                 }
929             }
930             return new Double JavaDoc(min);
931         }
932     }
933
934     public static Object JavaDoc max(Evaluator evaluator, List members, Calc exp) {
935         SetWrapper sw = evaluateSet(evaluator, members, exp);
936         if (sw.errorCount > 0) {
937             return new Double JavaDoc(Double.NaN);
938         } else if (sw.v.size() == 0) {
939             return Util.nullValue;
940         } else {
941             double max = Double.MIN_VALUE;
942             for (int i = 0; i < sw.v.size(); i++) {
943                 double iValue = ((Double JavaDoc) sw.v.get(i)).doubleValue();
944                 if (iValue > max) {
945                     max = iValue;
946                 }
947             }
948             return new Double JavaDoc(max);
949         }
950     }
951
952     static Object JavaDoc var(
953             Evaluator evaluator,
954             List members,
955             Calc exp,
956             boolean biased) {
957         SetWrapper sw = evaluateSet(evaluator, members, exp);
958         return _var(sw, biased);
959     }
960
961     private static Object JavaDoc _var(SetWrapper sw, boolean biased) {
962         if (sw.errorCount > 0) {
963             return new Double JavaDoc(Double.NaN);
964         } else if (sw.v.size() == 0) {
965             return Util.nullValue;
966         } else {
967             double stdev = 0.0;
968             double avg = _avg(sw);
969             for (int i = 0; i < sw.v.size(); i++) {
970                 stdev += Math.pow((((Double JavaDoc) sw.v.get(i)).doubleValue() - avg),2);
971             }
972             int n = sw.v.size();
973             if (!biased) {
974                 n--;
975             }
976             return new Double JavaDoc(stdev / (double) n);
977         }
978     }
979
980     static double correlation(
981             Evaluator evaluator,
982             List memberList,
983             Calc exp1,
984             Calc exp2) {
985         SetWrapper sw1 = evaluateSet(evaluator, memberList, exp1);
986         SetWrapper sw2 = evaluateSet(evaluator, memberList, exp2);
987         Object JavaDoc covar = _covariance(sw1, sw2, false);
988         Object JavaDoc var1 = _var(sw1, false); //this should be false, yes?
989
Object JavaDoc var2 = _var(sw2, false);
990         if ((covar instanceof Double JavaDoc) &&
991             (var1 instanceof Double JavaDoc) &&
992             (var2 instanceof Double JavaDoc)) {
993             return ((Double JavaDoc) covar).doubleValue() /
994                     Math.sqrt(((Double JavaDoc) var1).doubleValue() *
995                     ((Double JavaDoc) var2).doubleValue());
996         } else {
997             return DoubleNull;
998         }
999     }
1000
1001    static Object JavaDoc covariance(Evaluator evaluator, List members,
1002            Calc exp1, Calc exp2, boolean biased) {
1003        SetWrapper sw1 = evaluateSet(evaluator.push(), members, exp1);
1004        SetWrapper sw2 = evaluateSet(evaluator.push(), members, exp2);
1005        // todo: because evaluateSet does not add nulls to the SetWrapper, this
1006
// solution may lead to mismatched lists and is therefore not robust
1007
return _covariance(sw1, sw2, biased);
1008    }
1009
1010
1011    private static Object JavaDoc _covariance(SetWrapper sw1,
1012                                      SetWrapper sw2,
1013                                      boolean biased) {
1014        if (sw1.v.size() != sw2.v.size()) {
1015            return Util.nullValue;
1016        }
1017        double avg1 = _avg(sw1);
1018        double avg2 = _avg(sw2);
1019        double covar = 0.0;
1020        for (int i = 0; i < sw1.v.size(); i++) {
1021            //all of this casting seems inefficient - can we make SetWrapper
1022
//contain an array of double instead?
1023
double diff1 = (((Double JavaDoc) sw1.v.get(i)).doubleValue() - avg1);
1024            double diff2 = (((Double JavaDoc) sw2.v.get(i)).doubleValue() - avg2);
1025            covar += (diff1 * diff2);
1026        }
1027        int n = sw1.v.size();
1028        if (!biased) {
1029            n--;
1030        }
1031        return new Double JavaDoc(covar / (double) n);
1032    }
1033
1034    static Object JavaDoc stdev(
1035            Evaluator evaluator,
1036            List members,
1037            Calc exp,
1038            boolean biased) {
1039        Object JavaDoc o = var(evaluator, members, exp, biased);
1040        return (o instanceof Double JavaDoc)
1041            ? new Double JavaDoc(Math.sqrt(((Double JavaDoc) o).doubleValue()))
1042            : o;
1043    }
1044
1045    public static Object JavaDoc avg(Evaluator evaluator, List members, Calc calc) {
1046        SetWrapper sw = evaluateSet(evaluator, members, calc);
1047        return (sw.errorCount > 0) ?
1048                new Double JavaDoc(Double.NaN) :
1049                (sw.v.size() == 0) ?
1050                Util.nullValue :
1051                new Double JavaDoc(_avg(sw));
1052    }
1053
1054    //todo: parameterize inclusion of nulls
1055
//also, maybe make _avg a method of setwrapper, so we can cache the result (i.e. for correl)
1056
private static double _avg(SetWrapper sw) {
1057        double sum = 0.0;
1058        for (int i = 0; i < sw.v.size(); i++) {
1059            sum += ((Double JavaDoc) sw.v.get(i)).doubleValue();
1060        }
1061        //todo: should look at context and optionally include nulls
1062
return sum / (double) sw.v.size();
1063    }
1064
1065    public static Object JavaDoc sum(Evaluator evaluator, List members, Calc exp) {
1066        double d = sumDouble(evaluator, members, exp);
1067        return d == DoubleNull ? Util.nullValue : new Double JavaDoc(d);
1068    }
1069
1070    public static double sumDouble(Evaluator evaluator, List members, Calc exp) {
1071        SetWrapper sw = evaluateSet(evaluator, members, exp);
1072        if (sw.errorCount > 0) {
1073            return Double.NaN;
1074        } else if (sw.v.size() == 0) {
1075            return DoubleNull;
1076        } else {
1077            double sum = 0.0;
1078            for (int i = 0; i < sw.v.size(); i++) {
1079                sum += ((Double JavaDoc) sw.v.get(i)).doubleValue();
1080            }
1081            return sum;
1082        }
1083    }
1084    public static double sumDouble(Evaluator evaluator, Iterable JavaDoc iterable, Calc exp) {
1085        SetWrapper sw = evaluateSet(evaluator, iterable, exp);
1086        if (sw.errorCount > 0) {
1087            return Double.NaN;
1088        } else if (sw.v.size() == 0) {
1089            return DoubleNull;
1090        } else {
1091            double sum = 0.0;
1092            for (int i = 0; i < sw.v.size(); i++) {
1093                sum += ((Double JavaDoc) sw.v.get(i)).doubleValue();
1094            }
1095            return sum;
1096        }
1097    }
1098    public static int count(
1099            Evaluator evaluator,
1100            Iterable JavaDoc iterable,
1101            boolean includeEmpty) {
1102        if (iterable == null) {
1103            return 0;
1104        }
1105        if (includeEmpty) {
1106            if (iterable instanceof Collection) {
1107                return ((Collection) iterable).size();
1108            } else {
1109                int retval = 0;
1110                Iterator it = iterable.iterator();
1111                while (it.hasNext()) {
1112                    // must get the next one
1113
it.next();
1114                    retval++;
1115                }
1116                return retval;
1117            }
1118        } else {
1119            int retval = 0;
1120            for (Object JavaDoc object: iterable) {
1121                if (object instanceof Member) {
1122                    evaluator.setContext((Member) object);
1123                } else {
1124                    evaluator.setContext((Member[]) object);
1125                }
1126                Object JavaDoc o = evaluator.evaluateCurrent();
1127                if (o != Util.nullValue && o != null) {
1128                    retval++;
1129                }
1130            }
1131            return retval;
1132        }
1133    }
1134
1135/*
1136    public static int countOld(
1137            Evaluator evaluator,
1138            List members,
1139            boolean includeEmpty) {
1140        if (members == null) {
1141System.out.println("FunUtil.count List: null 0");
1142            return 0;
1143        }
1144        if (includeEmpty) {
1145System.out.println("FunUtil.count List: "+members.size());
1146            return members.size();
1147        } else {
1148            int retval = 0;
1149            for (int i = 0; i < members.size(); i++) {
1150                final Object member = members.get(i);
1151                if (member instanceof Member) {
1152                    evaluator.setContext((Member) member);
1153                } else {
1154                    evaluator.setContext((Member[]) member);
1155                }
1156                Object o = evaluator.evaluateCurrent();
1157                if (o != Util.nullValue && o != null) {
1158                    retval++;
1159                }
1160            }
1161System.out.println("FunUtil.count List: "+retval);
1162            return retval;
1163        }
1164    }
1165    public static int countIterable(
1166            Evaluator evaluator,
1167            Iterable iterable,
1168            boolean includeEmpty) {
1169        if (iterable == null) {
1170System.out.println("FunUtil.countIterable Iterable: null 0");
1171            return 0;
1172        }
1173        int retval = 0;
1174        Iterator it = iterable.iterator();
1175        while (it.hasNext()) {
1176            final Object member = it.next();
1177            if (member instanceof Member) {
1178                evaluator.setContext((Member) member);
1179            } else if (member instanceof Member[]) {
1180                evaluator.setContext((Member[]) member);
1181            }
1182            if (includeEmpty) {
1183                retval++;
1184            } else {
1185                Object o = evaluator.evaluateCurrent();
1186                if (o != Util.nullValue && o != null) {
1187                    retval++;
1188                }
1189            }
1190        }
1191System.out.println("FunUtil.countIterable Iterable: "+retval);
1192        return retval;
1193    }
1194*/

1195
1196    /**
1197     * Evaluates <code>exp</code> (if defined) over <code>members</code> to
1198     * generate a {@link List} of {@link SetWrapper} objects, which contains
1199     * a {@link Double} value and meta information, unlike
1200     * {@link #evaluateMembers}, which only produces values.
1201     *
1202     * @pre exp != null
1203     */

1204    static SetWrapper evaluateSet(
1205            Evaluator evaluator,
1206            Iterable JavaDoc members,
1207            Calc calc) {
1208        Util.assertPrecondition(members != null, "members != null");
1209        Util.assertPrecondition(calc != null, "calc != null");
1210        Util.assertPrecondition(calc.getType() instanceof ScalarType);
1211
1212        // todo: treat constant exps as evaluateMembers() does
1213
SetWrapper retval = new SetWrapper();
1214        for (Iterator it = members.iterator(); it.hasNext();) {
1215            Object JavaDoc obj = it.next();
1216            if (obj instanceof Member[]) {
1217                evaluator.setContext((Member[])obj);
1218            } else {
1219                evaluator.setContext((Member)obj);
1220            }
1221            Object JavaDoc o = calc.evaluate(evaluator);
1222            if (o == null || o == Util.nullValue) {
1223                retval.nullCount++;
1224            } else if (o instanceof Throwable JavaDoc) {
1225                // Carry on summing, so that if we are running in a
1226
// BatchingCellReader, we find out all the dependent cells we
1227
// need
1228
retval.errorCount++;
1229            } else if (o instanceof Double JavaDoc) {
1230                retval.v.add(o);
1231            } else if (o instanceof Number JavaDoc) {
1232                retval.v.add(((Number JavaDoc) o).doubleValue());
1233            } else {
1234                retval.v.add(o);
1235            }
1236        }
1237        return retval;
1238    }
1239
1240    /**
1241     * Evaluates one or more expressions against the member list returning
1242     * a SetWrapper array. Where this differs very significantly from the
1243     * above evaluateSet methods is how it count null values and Throwables;
1244     * this method adds nulls to the SetWrapper Vector rather than not adding
1245     * anything - as the above method does. The impact of this is that if, for
1246     * example, one was creating a list of x,y values then each list will have
1247     * the same number of values (though some might be null) - this allows
1248     * higher level code to determine how to handle the lack of data rather than
1249     * having a non-equal number (if one is plotting x,y values it helps to
1250     * have the same number and know where a potential gap is the data is.
1251     */

1252    static SetWrapper[] evaluateSet(
1253            Evaluator evaluator,
1254            List members,
1255            DoubleCalc[] calcs,
1256            boolean isTuples) {
1257        Util.assertPrecondition(calcs != null, "calcs != null");
1258
1259        // todo: treat constant exps as evaluateMembers() does
1260
SetWrapper[] retvals = new SetWrapper[calcs.length];
1261        for (int i = 0; i < calcs.length; i++) {
1262            retvals[i] = new SetWrapper();
1263        }
1264        for (int j = 0; j < members.size(); j++) {
1265            if (isTuples) {
1266                evaluator.setContext((Member[]) members.get(j));
1267            } else {
1268                evaluator.setContext((Member) members.get(j));
1269            }
1270            for (int i = 0; i < calcs.length; i++) {
1271                DoubleCalc calc = calcs[i];
1272                SetWrapper retval = retvals[i];
1273                double o = calc.evaluateDouble(evaluator);
1274                if (o == FunUtil.DoubleNull) {
1275                    retval.nullCount++;
1276                    retval.v.add(null);
1277                } else {
1278                    retval.v.add(o);
1279                }
1280                // TODO: If the expression yielded an error, carry on
1281
// summing, so that if we are running in a
1282
// BatchingCellReader, we find out all the dependent cells
1283
// we need
1284
}
1285        }
1286        return retvals;
1287    }
1288
1289    static List<Member> periodsToDate(
1290            Evaluator evaluator,
1291            Level level,
1292            Member member) {
1293        if (member == null) {
1294            member = evaluator.getContext(level.getHierarchy().getDimension());
1295        }
1296        Member m = member;
1297        while (m != null) {
1298            if (m.getLevel() == level) {
1299                break;
1300            }
1301            m = m.getParentMember();
1302        }
1303        // If m == null, then "level" was lower than member's level.
1304
// periodsToDate( [Time].[Quarter], [Time].[1997] is valid,
1305
// but will return an empty List
1306
List<Member> members = new ArrayList<Member>();
1307        if (m != null) {
1308            // e.g. m is [Time].[1997] and member is [Time].[1997].[Q1].[3]
1309
// we now have to make m to be the first member of the range,
1310
// so m becomes [Time].[1997].[Q1].[1]
1311
SchemaReader reader = evaluator.getSchemaReader();
1312            m = Util.getFirstDescendantOnLevel(reader, m, member.getLevel());
1313            reader.getMemberRange(level, m, member, members);
1314        }
1315        return members;
1316    }
1317
1318    static List<Member> memberRange(
1319        Evaluator evaluator,
1320        Member startMember,
1321        Member endMember)
1322    {
1323        final Level level = startMember.getLevel();
1324        assertTrue(level == endMember.getLevel());
1325        List<Member> members = new ArrayList<Member>();
1326        evaluator.getSchemaReader().getMemberRange(
1327            level, startMember, endMember, members);
1328
1329        if (members.isEmpty()) {
1330            // The result is empty, so maybe the members are reversed. This is
1331
// cheaper than comparing the members before we call getMemberRange.
1332
evaluator.getSchemaReader().getMemberRange(
1333                level, endMember, startMember, members);
1334        }
1335        return members;
1336    }
1337
1338    /**
1339     * Returns the member under ancestorMember having the same relative position
1340     * under member's parent.
1341     * <p>For exmaple, cousin([Feb 2001], [Q3 2001]) is [August 2001].
1342     * @param schemaReader The reader to use
1343     * @param member The member for which we'll find the cousin.
1344     * @param ancestorMember The cousin's ancestor.
1345     * @return The child of <code>ancestorMember</code> in the same position under
1346     * <code>ancestorMember</code> as <code>member</code> is under its parent.
1347     */

1348    static Member cousin(SchemaReader schemaReader,
1349                         Member member,
1350                         Member ancestorMember) {
1351        if (ancestorMember.isNull()) {
1352            return ancestorMember;
1353        }
1354        if (member.getHierarchy() != ancestorMember.getHierarchy()) {
1355            throw MondrianResource.instance().CousinHierarchyMismatch.ex(
1356                member.getUniqueName(), ancestorMember.getUniqueName());
1357        }
1358        if (member.getLevel().getDepth() < ancestorMember.getLevel().getDepth()) {
1359            return member.getHierarchy().getNullMember();
1360        }
1361
1362        Member cousin = cousin2(schemaReader, member, ancestorMember);
1363        if (cousin == null) {
1364            cousin = member.getHierarchy().getNullMember();
1365        }
1366
1367        return cousin;
1368    }
1369
1370    static private Member cousin2(SchemaReader schemaReader,
1371                                  Member member1,
1372                                  Member member2) {
1373        if (member1.getLevel() == member2.getLevel()) {
1374            return member2;
1375        }
1376        Member uncle = cousin2(schemaReader, member1.getParentMember(), member2);
1377        if (uncle == null) {
1378            return null;
1379        }
1380        int ordinal = Util.getMemberOrdinalInParent(schemaReader, member1);
1381        Member[] cousins = schemaReader.getMemberChildren(uncle);
1382        if (cousins.length <= ordinal) {
1383            return null;
1384        }
1385        return cousins[ordinal];
1386    }
1387
1388    /**
1389     * Returns the ancestor of <code>member</code> at the given level
1390     * or distance. It is assumed that any error checking required
1391     * has been done prior to calling this function.
1392     * <p>This method takes into consideration the fact that there
1393     * may be intervening hidden members between <code>member</code>
1394     * and the ancestor. If <code>targetLevel</code> is not null, then
1395     * the method will only return a member if the level at
1396     * <code>distance</code> from the member is actually the
1397     * <code>targetLevel</code> specified.
1398     * @param evaluator The evaluation context
1399     * @param member The member for which the ancestor is to be found
1400     * @param distance The distance up the chain the ancestor is to
1401     * be found.
1402     * @param targetLevel The desired targetLevel of the ancestor. If <code>null</code>,
1403     * then the distance completely determines the desired ancestor.
1404     * @return The ancestor member, or <code>null</code> if no such
1405     * ancestor exists.
1406     */

1407    static Member ancestor(Evaluator evaluator,
1408                           Member member,
1409                           int distance,
1410                           Level targetLevel) {
1411        if ((targetLevel != null) &&
1412            (member.getHierarchy() != targetLevel.getHierarchy())) {
1413            throw MondrianResource.instance().MemberNotInLevelHierarchy.ex(
1414                member.getUniqueName(), targetLevel.getUniqueName());
1415        }
1416
1417        if (distance == 0) {
1418            /*
1419            * Shortcut if there's nowhere to go.
1420            */

1421            return member;
1422        } else if (distance < 0) {
1423            /*
1424            * Can't go backwards.
1425            */

1426            return member.getHierarchy().getNullMember();
1427        }
1428
1429        Member[] ancestors = member.getAncestorMembers();
1430        final SchemaReader schemaReader = evaluator.getSchemaReader();
1431
1432        Member result = member.getHierarchy().getNullMember();
1433
1434        searchLoop:
1435        for (int i = 0; i < ancestors.length; i++) {
1436            final Member ancestorMember = ancestors[i];
1437
1438            if (targetLevel != null) {
1439                if (ancestorMember.getLevel() == targetLevel) {
1440                    if (schemaReader.isVisible(ancestorMember)) {
1441                        result = ancestorMember;
1442                        break searchLoop;
1443                    } else {
1444                        result = member.getHierarchy().getNullMember();
1445                        break searchLoop;
1446                    }
1447                }
1448            } else {
1449                if (schemaReader.isVisible(ancestorMember)) {
1450                    distance--;
1451
1452                    //
1453
// Make sure that this ancestor is really on the right
1454
// targetLevel. If a targetLevel was specified and at least
1455
// one of the ancestors was hidden, this this algorithm goes
1456
// too far up the ancestor list. It's not a problem, except
1457
// that we need to check if it's happened and return the
1458
// hierarchy's null member instead.
1459
//
1460
// For example, consider what happens with
1461
// Ancestor([Store].[Israel].[Haifa], [Store].[Store State]).
1462
// The distance from [Haifa] to [Store State] is 1, but that
1463
// lands us at the country targetLevel, which is clearly
1464
// wrong.
1465
//
1466
if (distance == 0) {
1467                        if (targetLevel == null || ancestorMember.getLevel() == targetLevel) {
1468                            result = ancestorMember;
1469                            break searchLoop;
1470                        } else {
1471                            result = member.getHierarchy().getNullMember();
1472                            break searchLoop;
1473                        }
1474                    }
1475                }
1476            }
1477        }
1478
1479        return result;
1480    }
1481
1482    /**
1483     * Compares a pair of members according to their positions in a
1484     * prefix-order (or postfix-order, if <code>post</code> is true) walk
1485     * over a hierarchy.
1486     *
1487     * @param m1 First member
1488     * @param m2 Second member
1489     * @param post Whether to sortMembers in postfix order. If true, a parent will
1490     * sortMembers immediately after its last child. If false, a parent will sortMembers
1491     * immediately before its first child.
1492     * @return -1 if m1 collates before m2,
1493     * 0 if m1 equals m2,
1494     * 1 if m1 collates after m2
1495     */

1496    public static int compareHierarchically(
1497            Member m1,
1498            Member m2,
1499            boolean post) {
1500        if (equals(m1, m2)) {
1501            return 0;
1502        }
1503        while (true) {
1504            int depth1 = m1.getDepth();
1505            int depth2 = m2.getDepth();
1506            if (depth1 < depth2) {
1507                m2 = m2.getParentMember();
1508                if (equals(m1, m2)) {
1509                    return post ? 1 : -1;
1510                }
1511            } else if (depth1 > depth2) {
1512                m1 = m1.getParentMember();
1513                if (equals(m1, m2)) {
1514                    return post ? -1 : 1;
1515                }
1516            } else {
1517                Member prev1 = m1;
1518                Member prev2 = m2;
1519                m1 = m1.getParentMember();
1520                m2 = m2.getParentMember();
1521                if (equals(m1, m2)) {
1522                    return compareSiblingMembers(prev1, prev2);
1523                }
1524            }
1525        }
1526    }
1527
1528    /**
1529     * Compares two members which are known to have the same parent.
1530     *
1531     * First, compare by ordinal.
1532     * This is only valid now we know they're siblings, because
1533     * ordinals are only unique within a parent.
1534     * If the dimension does not use ordinals, both ordinals
1535     * will be -1.
1536     *
1537     * <p>If the ordinals do not differ, compare using regular member
1538     * comparison.
1539     *
1540     * @param m1 First member
1541     * @param m2 Second member
1542     * @return -1 if m1 collates less than m2,
1543     * 1 if m1 collates after m2,
1544     * 0 if m1 == m2.
1545     */

1546    public static int compareSiblingMembers(Member m1, Member m2) {
1547        // calculated members collate after non-calculated
1548
final boolean calculated1 = m1.isCalculatedInQuery();
1549        final boolean calculated2 = m2.isCalculatedInQuery();
1550        if (calculated1) {
1551            if (!calculated2) {
1552                return 1;
1553            }
1554        } else {
1555            if (calculated2) {
1556                return -1;
1557            }
1558        }
1559        final Comparable JavaDoc k1 = m1.getOrderKey();
1560        final Comparable JavaDoc k2 = m2.getOrderKey();
1561        if ((k1 != null) && (k2 != null)) {
1562            return k1.compareTo(k2);
1563        } else {
1564            final int ordinal1 = m1.getOrdinal();
1565            final int ordinal2 = m2.getOrdinal();
1566            return (ordinal1 == ordinal2) ?
1567                m1.compareTo(m2) :
1568                (ordinal1 < ordinal2) ?
1569                -1 :
1570                1;
1571        }
1572    }
1573
1574    /**
1575     * Returns whether we can convert an argument to a parameter tyoe.
1576     * @param fromExp argument type
1577     * @param to parameter type
1578     * @param conversionCount in/out count of number of conversions performed;
1579     * is incremented if the conversion is non-trivial (for
1580     * example, converting a member to a level).
1581     */

1582    public static boolean canConvert(
1583            Exp fromExp,
1584            int to,
1585            int[] conversionCount) {
1586        int from = fromExp.getCategory();
1587        if (from == to) {
1588            return true;
1589        }
1590        switch (from) {
1591        case Category.Array:
1592            return false;
1593        case Category.Dimension:
1594            // Seems funny that you can 'downcast' from a dimension, doesn't
1595
// it? But we add an implicit 'CurrentMember', for example,
1596
// '[Time].PrevMember' actually means
1597
// '[Time].CurrentMember.PrevMember'.
1598
switch (to) {
1599            case Category.Member:
1600            case Category.Tuple:
1601                // It's easier to convert dimension to member than dimension
1602
// to hierarchy or level.
1603
conversionCount[0]++;
1604                return true;
1605            case Category.Hierarchy:
1606            case Category.Level:
1607                conversionCount[0] += 2;
1608                return true;
1609            default:
1610                return false;
1611            }
1612        case Category.Hierarchy:
1613            switch (to) {
1614            case Category.Dimension:
1615            case Category.Member:
1616            case Category.Tuple:
1617                conversionCount[0]++;
1618                return true;
1619            default:
1620                return false;
1621            }
1622        case Category.Level:
1623            switch (to) {
1624            case Category.Dimension:
1625                // It's more difficult to convert to a dimension than a
1626
// hierarchy. For example, we want '[Store City].CurrentMember'
1627
// to resolve to <Hierarchy>.CurrentMember rather than
1628
// <Dimension>.CurrentMember.
1629
conversionCount[0] += 2;
1630                return true;
1631            case Category.Hierarchy:
1632                conversionCount[0]++;
1633                return true;
1634            default:
1635                return false;
1636            }
1637        case Category.Logical:
1638            return false;
1639        case Category.Member:
1640            switch (to) {
1641            case Category.Dimension:
1642            case Category.Hierarchy:
1643            case Category.Level:
1644            case Category.Tuple:
1645                conversionCount[0]++;
1646                return true;
1647            case (Category.Numeric | Category.Expression):
1648                // We assume that members are numeric, so a cast to a numeric
1649
// expression is less expensive than a conversion to a string
1650
// expression.
1651
conversionCount[0]++;
1652                return true;
1653            case Category.Value:
1654            case (Category.String | Category.Expression):
1655                conversionCount[0] += 2;
1656                return true;
1657            default:
1658                return false;
1659            }
1660        case Category.Numeric | Category.Constant:
1661            return to == Category.Value ||
1662                to == Category.Numeric;
1663        case Category.Numeric:
1664            return to == Category.Value ||
1665                to == Category.Integer ||
1666                to == (Category.Integer | Category.Constant) ||
1667                to == (Category.Numeric | Category.Constant);
1668        case Category.Integer:
1669            return to == Category.Value ||
1670                to == (Category.Integer | Category.Constant) ||
1671                to == Category.Numeric ||
1672                to == (Category.Numeric | Category.Constant);
1673        case Category.Set:
1674            return false;
1675        case Category.String | Category.Constant:
1676            return to == Category.Value ||
1677                to == Category.String;
1678        case Category.String:
1679            return to == Category.Value ||
1680                to == (Category.String | Category.Constant);
1681        case Category.Tuple:
1682            return to == Category.Value ||
1683                to == Category.Numeric;
1684        case Category.Value:
1685            return false;
1686        case Category.Symbol:
1687            return false;
1688        case Category.Null:
1689            // now null supports members as well as numerics
1690
return to == Category.Numeric || to == Category.Member;
1691        default:
1692            throw newInternal("unknown category " + from);
1693        }
1694    }
1695
1696    /**
1697     * Returns whether one of the members in a tuple is null.
1698     */

1699    static boolean tupleContainsNullMember(Member[] tuple) {
1700        for (int i = 0; i < tuple.length; i++) {
1701            Member member = tuple[i];
1702            if (member.isNull()) {
1703                return true;
1704            }
1705        }
1706        return false;
1707    }
1708
1709    public static Member[] makeNullTuple(final TupleType tupleType) {
1710        Member[] members = new Member[tupleType.elementTypes.length];
1711        for (int i = 0; i < tupleType.elementTypes.length; i++) {
1712            MemberType type = (MemberType) tupleType.elementTypes[i];
1713            members[i] = makeNullMember(type);
1714        }
1715        return members;
1716    }
1717
1718    static Member makeNullMember(
1719            MemberType memberType) {
1720        Hierarchy hierarchy = memberType.getHierarchy();
1721        if (hierarchy == null) {
1722            return NullMember;
1723        }
1724        return hierarchy.getNullMember();
1725    }
1726
1727    /**
1728     * Validates the arguments to a function and resolves the function.
1729     *
1730     * @param validator validator used to validate function arguments and
1731     * resolve the function
1732     * @param args arguments to the function
1733     * @param newArgs returns the resolved arguments to the function
1734     * @param name function name
1735     * @param syntax syntax style used to invoke function
1736     *
1737     * @return resolved function definition
1738     */

1739    public static FunDef resolveFunArgs(
1740        Validator validator, Exp[] args, Exp[] newArgs, String JavaDoc name,
1741        Syntax syntax) {
1742
1743        Query query = validator.getQuery();
1744        Cube cube = null;
1745        if (query != null) {
1746            cube = query.getCube();
1747        }
1748        for (int i = 0; i < args.length; i++) {
1749            newArgs[i] = validator.validate(args[i], false);
1750        }
1751        final FunTable funTable = validator.getFunTable();
1752        FunDef funDef = funTable.getDef(newArgs, validator, name, syntax);
1753
1754        // If the first argument to a function is either:
1755
// 1) the measures dimension or
1756
// 2) a measures member where the function returns another member or
1757
// a set,
1758
// then these are functions that dynamically return one or more
1759
// members ofthe measures dimension. In that case, we cannot use
1760
// native cross joins because the functions need to be executed to
1761
// determine the resultant measures.
1762
//
1763
// As a result, we disallow functions like AllMembers applied on the
1764
// Measures dimension as well as functions like the range operator,
1765
// siblings, and lag, when the argument is a measure member.
1766
// However, we do allow functions like isEmpty, rank, and topPercent.
1767
// Also, the set function is ok since it just enumerates its
1768
// arguments.
1769
if (!(funDef instanceof SetFunDef) && query != null &&
1770            query.nativeCrossJoinVirtualCube())
1771        {
1772            int[] paramCategories = funDef.getParameterCategories();
1773            if (paramCategories.length > 0 &&
1774                ((paramCategories[0] == Category.Dimension &&
1775                    newArgs[0] instanceof DimensionExpr &&
1776                    ((DimensionExpr) newArgs[0]).getDimension().
1777                        getOrdinal(cube) == 0) ||
1778                (paramCategories[0] == Category.Member &&
1779                    newArgs[0] instanceof MemberExpr &&
1780                    ((MemberExpr) newArgs[0]).getMember().getDimension().
1781                        getOrdinal(cube) == 0 &&
1782                    (funDef.getReturnCategory() == Category.Member ||
1783                        funDef.getReturnCategory() == Category.Set))))
1784            {
1785                query.setVirtualCubeNonNativeCrossJoin();
1786            }
1787        }
1788
1789        return funDef;
1790    }
1791
1792    static void appendTuple(StringBuilder JavaDoc buf, Member[] members) {
1793        buf.append("(");
1794        for (int j = 0; j < members.length; j++) {
1795            if (j > 0) {
1796                buf.append(", ");
1797            }
1798            Member member = members[j];
1799            buf.append(member.getUniqueName());
1800        }
1801        buf.append(")");
1802    }
1803
1804    /**
1805     * Returns whether two tuples are equal.
1806     *
1807     * <p>The members are allowed to be in different positions. For example,
1808     * <blockquote>
1809     * <code>([Gender].[M], [Store].[USA]) IS ([Store].[USA], [Gender].[M])</code>
1810     * </blockquote>
1811     * returns <code>true</code>.
1812     */

1813    static boolean equalTuple(Member[] members0, Member[] members1) {
1814        final int count = members0.length;
1815        if (count != members1.length) {
1816            return false;
1817        }
1818        outer:
1819        for (int i = 0; i < count; i++) {
1820            // First check the member at the corresponding ordinal. It is more
1821
// likely to be there.
1822
final Member member0 = members0[i];
1823            if (member0.equals(members1[i])) {
1824                continue;
1825            }
1826            // Look for this member in other positions.
1827
// We can assume that the members in members0 are distinct (because
1828
// they belong to different dimensions), so this test is valid.
1829
for (int j = 0; j < count; j++) {
1830                if (i != j && member0.equals(members1[j])) {
1831                    continue outer;
1832                }
1833            }
1834            // This member of members0 does not occur in any position of
1835
// members1. The tuples are not equal.
1836
return false;
1837        }
1838        return true;
1839    }
1840
1841    static FunDef createDummyFunDef(
1842        Resolver resolver,
1843        int returnCategory,
1844        Exp[] args)
1845    {
1846        final int[] argCategories = ExpBase.getTypes(args);
1847        return new FunDefBase(resolver, returnCategory, argCategories) {};
1848    }
1849
1850    // Inner classes
1851

1852
1853    private static abstract class MemberComparator implements Comparator {
1854        private static final Logger LOGGER =
1855                Logger.getLogger(MemberComparator.class);
1856        Map<Member, Object JavaDoc> mapMemberToValue;
1857        private boolean desc;
1858
1859        MemberComparator(Map<Member, Object JavaDoc> mapMemberToValue, boolean desc) {
1860            this.mapMemberToValue = mapMemberToValue;
1861            this.desc = desc;
1862        }
1863
1864        // implement Comparator
1865
public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1866            Member m1 = (Member) o1,
1867                    m2 = (Member) o2;
1868            int c = compareInternal(m1, m2);
1869            if (LOGGER.isDebugEnabled()) {
1870                LOGGER.debug(
1871                        "compare " +
1872                        m1.getUniqueName() +
1873                        "(" + mapMemberToValue.get(m1) + "), " +
1874                        m2.getUniqueName() +
1875                        "(" + mapMemberToValue.get(m2) + ")" +
1876                        " yields " + c);
1877            }
1878            return c;
1879        }
1880
1881        protected abstract int compareInternal(Member m1, Member m2);
1882
1883        protected int compareByValue(Member m1, Member m2) {
1884            Object JavaDoc value1 = mapMemberToValue.get(m1),
1885                    value2 = mapMemberToValue.get(m2);
1886            final int c = FunUtil.compareValues(value1, value2);
1887            return desc ? -c : c;
1888        }
1889
1890        protected int compareHierarchicallyButSiblingsByValue(Member m1, Member m2) {
1891            if (FunUtil.equals(m1, m2)) {
1892                return 0;
1893            }
1894            while (true) {
1895                int depth1 = m1.getDepth(),
1896                        depth2 = m2.getDepth();
1897                if (depth1 < depth2) {
1898                    m2 = m2.getParentMember();
1899                    if (Util.equals(m1, m2)) {
1900                        return -1;
1901                    }
1902                } else if (depth1 > depth2) {
1903                    m1 = m1.getParentMember();
1904                    if (Util.equals(m1, m2)) {
1905                        return 1;
1906                    }
1907                } else {
1908                    Member prev1 = m1, prev2 = m2;
1909                    m1 = m1.getParentMember();
1910                    m2 = m2.getParentMember();
1911                    if (Util.equals(m1, m2)) {
1912                        // including case where both parents are null
1913
int c = compareByValue(prev1, prev2);
1914                        if (c != 0) {
1915                            return c;
1916                        }
1917                        // prev1 and prev2 are siblings.
1918
// Order according to hierarchy, if the values do not differ.
1919
// Needed to have a consistent sortMembers if members with equal (null!)
1920
// values are compared.
1921
c = FunUtil.compareSiblingMembers(prev1, prev2);
1922                        return c;
1923                    }
1924                }
1925            }
1926        }
1927    }
1928
1929    private static class HierarchicalMemberComparator
1930            extends MemberComparator {
1931        HierarchicalMemberComparator(Map<Member, Object JavaDoc> mapMemberToValue, boolean desc) {
1932            super(mapMemberToValue, desc);
1933        }
1934
1935        protected int compareInternal(Member m1, Member m2) {
1936            return compareHierarchicallyButSiblingsByValue(m1, m2);
1937        }
1938    }
1939
1940    private static class BreakMemberComparator extends MemberComparator {
1941        BreakMemberComparator(Map<Member, Object JavaDoc> mapMemberToValue, boolean desc) {
1942            super(mapMemberToValue, desc);
1943        }
1944
1945        protected int compareInternal(Member m1, Member m2) {
1946            return compareByValue(m1, m2);
1947        }
1948    }
1949
1950    /**
1951     * Compares tuples, which are represented as arrays of {@link Member}s.
1952     */

1953    private static abstract class ArrayComparator implements Comparator {
1954        private static final Logger LOGGER =
1955                Logger.getLogger(ArrayComparator.class);
1956        int arity;
1957
1958        ArrayComparator(int arity) {
1959            this.arity = arity;
1960        }
1961
1962        private static String JavaDoc toString(Member[] a) {
1963            StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
1964            for (int i = 0; i < a.length; i++) {
1965                Member member = a[i];
1966                if (i > 0) {
1967                    sb.append(",");
1968                }
1969                sb.append(member.getUniqueName());
1970            }
1971            return sb.toString();
1972        }
1973
1974        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1975            final Member[] a1 = (Member[]) o1;
1976            final Member[] a2 = (Member[]) o2;
1977            final int c = compare(a1, a2);
1978            if (LOGGER.isDebugEnabled()) {
1979                LOGGER.debug(
1980                        "compare {" + toString(a1)+ "}, {" + toString(a2) +
1981                        "} yields " + c);
1982            }
1983            return c;
1984        }
1985
1986        protected abstract int compare(Member[] a1, Member[] a2);
1987    }
1988
1989    /**
1990     * Extension to {@link ArrayComparator} which compares tuples by evaluating
1991     * an expression.
1992     */

1993    private static abstract class ArrayExpComparator
1994            extends ArrayComparator {
1995        Evaluator evaluator;
1996        final Calc calc;
1997
1998        ArrayExpComparator(Evaluator evaluator, Calc calc, int arity) {
1999            super(arity);
2000            this.evaluator = evaluator;
2001            this.calc = calc;
2002        }
2003
2004    }
2005
2006    private static class HierarchicalArrayComparator
2007            extends ArrayExpComparator {
2008        private final boolean desc;
2009
2010        HierarchicalArrayComparator(
2011                Evaluator evaluator, Calc calc, int arity, boolean desc) {
2012            super(evaluator, calc, arity);
2013            this.desc = desc;
2014        }
2015
2016        protected int compare(Member[] a1, Member[] a2) {
2017            int c = 0;
2018            evaluator = evaluator.push();
2019            for (int i = 0; i < arity; i++) {
2020                Member m1 = a1[i],
2021                        m2 = a2[i];
2022                c = compareHierarchicallyButSiblingsByValue(m1, m2);
2023                if (c != 0) {
2024                    break;
2025                }
2026                // compareHierarchicallyButSiblingsByValue imposes a total order
2027
//Util.assertTrue(m1 == m2);
2028
Util.assertTrue(m1.equals(m2));
2029                evaluator.setContext(m1);
2030            }
2031            evaluator = evaluator.pop();
2032            return c;
2033        }
2034
2035        protected int compareHierarchicallyButSiblingsByValue(
2036                Member m1, Member m2) {
2037            if (FunUtil.equals(m1, m2)) {
2038                return 0;
2039            }
2040            while (true) {
2041                int depth1 = m1.getDepth(),
2042                        depth2 = m2.getDepth();
2043                if (depth1 < depth2) {
2044                    m2 = m2.getParentMember();
2045                    if (FunUtil.equals(m1, m2)) {
2046                        return -1;
2047                    }
2048                } else if (depth1 > depth2) {
2049                    m1 = m1.getParentMember();
2050                    if (FunUtil.equals(m1, m2)) {
2051                        return 1;
2052                    }
2053                } else {
2054                    Member prev1 = m1, prev2 = m2;
2055                    m1 = m1.getParentMember();
2056                    m2 = m2.getParentMember();
2057                    if (FunUtil.equals(m1, m2)) {
2058                        // including case where both parents are null
2059
int c = compareByValue(prev1, prev2);
2060                        if (c == 0) {
2061                            c = FunUtil.compareSiblingMembers(prev1, prev2);
2062                        }
2063                        return desc ? -c : c;
2064                    }
2065                }
2066            }
2067        }
2068        private int compareByValue(Member m1, Member m2) {
2069            int c;
2070            Member old = evaluator.setContext(m1);
2071            Object JavaDoc v1 = calc.evaluate(evaluator);
2072            evaluator.setContext(m2);
2073            Object JavaDoc v2 = calc.evaluate(evaluator);
2074            // important to restore the evaluator state -- and this is faster
2075
// than calling evaluator.push()
2076
evaluator.setContext(old);
2077            c = FunUtil.compareValues(v1, v2);
2078            return c;
2079        }
2080    }
2081
2082    private static class BreakArrayComparator extends ArrayExpComparator {
2083        BreakArrayComparator(Evaluator evaluator, Calc calc, int arity) {
2084            super(evaluator, calc, arity);
2085        }
2086
2087        protected int compare(Member[] a1, Member[] a2) {
2088            evaluator.setContext(a1);
2089            Object JavaDoc v1 = calc.evaluate(evaluator);
2090            evaluator.setContext(a2);
2091            Object JavaDoc v2 = calc.evaluate(evaluator);
2092            return FunUtil.compareValues(v1, v2);
2093        }
2094    }
2095
2096    /**
2097     * Compares arrays of {@link Member}s so as to convert them into hierarchical
2098     * order. Applies lexicographic order to the array.
2099     */

2100    private static class HierarchizeArrayComparator extends ArrayComparator {
2101        private final boolean post;
2102
2103        HierarchizeArrayComparator(int arity, boolean post) {
2104            super(arity);
2105            this.post = post;
2106        }
2107
2108        protected int compare(Member[] a1, Member[] a2) {
2109            for (int i = 0; i < arity; i++) {
2110                Member m1 = a1[i],
2111                        m2 = a2[i];
2112                int c = FunUtil.compareHierarchically(m1, m2, post);
2113                if (c != 0) {
2114                    return c;
2115                }
2116                // compareHierarchically imposes a total order
2117
//Util.assertTrue(m1 == m2);
2118
Util.assertTrue(m1.equals(m2));
2119            }
2120            return 0;
2121        }
2122    }
2123
2124    /**
2125     * Compares {@link Member}s so as to arrage them in prefix or postfix
2126     * hierarchical order.
2127     */

2128    private static class HierarchizeComparator implements Comparator {
2129        private final boolean post;
2130
2131        HierarchizeComparator(boolean post) {
2132            this.post = post;
2133        }
2134        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2135            return FunUtil.compareHierarchically((Member) o1, (Member) o2, post);
2136        }
2137    }
2138
2139    /**
2140     * Reverses the order of a {@link Comparator}.
2141     */

2142    private static class ReverseComparator implements Comparator {
2143        Comparator comparator;
2144        ReverseComparator(Comparator comparator) {
2145            this.comparator = comparator;
2146        }
2147
2148        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2149            int c = comparator.compare(o1, o2);
2150            return -c;
2151        }
2152    }
2153
2154    static class SetWrapper {
2155        List v = new ArrayList();
2156        public int errorCount = 0, nullCount = 0;
2157
2158        //private double avg = Double.NaN;
2159
//todo: parameterize inclusion of nulls
2160
//by making this a method of the SetWrapper, we can cache the result
2161
//this allows its reuse in Correlation
2162
// public double getAverage() {
2163
// if (avg == Double.NaN) {
2164
// double sum = 0.0;
2165
// for (int i = 0; i < resolvers.size(); i++) {
2166
// sum += ((Double) resolvers.elementAt(i)).doubleValue();
2167
// }
2168
// //todo: should look at context and optionally include nulls
2169
// avg = sum / (double) resolvers.size();
2170
// }
2171
// return avg;
2172
// }
2173
}
2174
2175    /**
2176     * Compares cell values, so that larger values compare first.
2177     *
2178     * <p>Nulls compare last, exceptions (including the
2179     * object which indicates the the cell is not in the cache yet) next,
2180     * then numbers and strings are compared by value.
2181     */

2182    private static class DescendingValueComparator implements Comparator {
2183        /**
2184         * The singleton.
2185         */

2186        static final DescendingValueComparator instance =
2187                new DescendingValueComparator();
2188
2189        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
2190            return - compareValues(o1, o2);
2191        }
2192    }
2193
2194    /**
2195     * Null member of unknown hierarchy.
2196     */

2197    private static class NullMember implements Member {
2198        public Member getParentMember() {
2199            throw new UnsupportedOperationException JavaDoc();
2200        }
2201
2202        public Level getLevel() {
2203            throw new UnsupportedOperationException JavaDoc();
2204        }
2205
2206        public Hierarchy getHierarchy() {
2207            throw new UnsupportedOperationException JavaDoc();
2208        }
2209
2210        public String JavaDoc getParentUniqueName() {
2211            throw new UnsupportedOperationException JavaDoc();
2212        }
2213
2214        public MemberType getMemberType() {
2215            throw new UnsupportedOperationException JavaDoc();
2216        }
2217
2218        public void setName(String JavaDoc name) {
2219            throw new UnsupportedOperationException JavaDoc();
2220        }
2221
2222        public boolean isAll() {
2223            return false;
2224        }
2225
2226        public boolean isMeasure() {
2227            throw new UnsupportedOperationException JavaDoc();
2228        }
2229
2230        public boolean isNull() {
2231            return true;
2232        }
2233
2234        public boolean isChildOrEqualTo(Member member) {
2235            throw new UnsupportedOperationException JavaDoc();
2236        }
2237
2238        public boolean isCalculated() {
2239            throw new UnsupportedOperationException JavaDoc();
2240        }
2241
2242        public int getSolveOrder() {
2243            throw new UnsupportedOperationException JavaDoc();
2244        }
2245
2246        public Exp getExpression() {
2247            throw new UnsupportedOperationException JavaDoc();
2248        }
2249
2250        public Member[] getAncestorMembers() {
2251            throw new UnsupportedOperationException JavaDoc();
2252        }
2253
2254        public boolean isCalculatedInQuery() {
2255            throw new UnsupportedOperationException JavaDoc();
2256        }
2257
2258        public Object JavaDoc getPropertyValue(String JavaDoc propertyName) {
2259            throw new UnsupportedOperationException JavaDoc();
2260        }
2261
2262        public Object JavaDoc getPropertyValue(String JavaDoc propertyName, boolean matchCase) {
2263            throw new UnsupportedOperationException JavaDoc();
2264        }
2265
2266        public String JavaDoc getPropertyFormattedValue(String JavaDoc propertyName) {
2267            throw new UnsupportedOperationException JavaDoc();
2268        }
2269
2270        public void setProperty(String JavaDoc name, Object JavaDoc value) {
2271            throw new UnsupportedOperationException JavaDoc();
2272        }
2273
2274        public Property[] getProperties() {
2275            throw new UnsupportedOperationException JavaDoc();
2276        }
2277
2278        public int getOrdinal() {
2279            throw new UnsupportedOperationException JavaDoc();
2280        }
2281
2282        public Comparable JavaDoc getOrderKey() {
2283            throw new UnsupportedOperationException JavaDoc();
2284        }
2285
2286        public boolean isHidden() {
2287            throw new UnsupportedOperationException JavaDoc();
2288        }
2289
2290        public int getDepth() {
2291            throw new UnsupportedOperationException JavaDoc();
2292        }
2293
2294        public Member getDataMember() {
2295            throw new UnsupportedOperationException JavaDoc();
2296        }
2297
2298        public String JavaDoc getUniqueName() {
2299            throw new UnsupportedOperationException JavaDoc();
2300        }
2301
2302        public String JavaDoc getName() {
2303            throw new UnsupportedOperationException JavaDoc();
2304        }
2305
2306        public String JavaDoc getDescription() {
2307            throw new UnsupportedOperationException JavaDoc();
2308        }
2309
2310        public OlapElement lookupChild(SchemaReader schemaReader, String JavaDoc s) {
2311            throw new UnsupportedOperationException JavaDoc();
2312        }
2313
2314        public OlapElement lookupChild(
2315            SchemaReader schemaReader, String JavaDoc s, MatchType matchType) {
2316            throw new UnsupportedOperationException JavaDoc();
2317        }
2318
2319        public String JavaDoc getQualifiedName() {
2320            throw new UnsupportedOperationException JavaDoc();
2321        }
2322
2323        public String JavaDoc getCaption() {
2324            throw new UnsupportedOperationException JavaDoc();
2325        }
2326
2327        public Dimension getDimension() {
2328            throw new UnsupportedOperationException JavaDoc();
2329        }
2330
2331        public int compareTo(Object JavaDoc o) {
2332            throw new UnsupportedOperationException JavaDoc();
2333        }
2334    }
2335
2336    public static void main(String JavaDoc[] args) {
2337        System.out.println("done");
2338    }
2339}
2340
2341// End FunUtil.java
2342
Popular Tags