001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#111 $ 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, 3 March, 2002 012 */ 013 package mondrian.olap.fun; 014 015 import mondrian.olap.*; 016 import mondrian.olap.type.*; 017 import mondrian.resource.MondrianResource; 018 import mondrian.calc.Calc; 019 import mondrian.calc.ResultStyle; 020 import mondrian.calc.DoubleCalc; 021 import mondrian.mdx.*; 022 import mondrian.rolap.RolapHierarchy; 023 import mondrian.util.FilteredIterableList; 024 import mondrian.util.ConcatenableList; 025 026 import org.apache.log4j.Logger; 027 028 import java.util.*; 029 import java.io.PrintWriter; 030 031 /** 032 * <code>FunUtil</code> contains a set of methods useful within the 033 * <code>mondrian.olap.fun</code> package. 034 * 035 * @author jhyde 036 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#111 $ 037 * @since 1.0 038 */ 039 public class FunUtil extends Util { 040 041 static final String[] emptyStringArray = new String[0]; 042 private static final boolean debug = false; 043 public static final NullMember NullMember = new NullMember(); 044 045 /** 046 * Special value which indicates that a <code>double</code> computation 047 * has returned the MDX null value. See {@link DoubleCalc}. 048 */ 049 public static final double DoubleNull = 0.000000012345; 050 051 /** 052 * Special value which indicates that a <code>double</code> computation 053 * has returned the MDX EMPTY value. See {@link DoubleCalc}. 054 */ 055 public static final double DoubleEmpty = -0.000000012345; 056 057 /** 058 * Special value which indicates that an <code>int</code> computation 059 * has returned the MDX null value. See {@link mondrian.calc.IntegerCalc}. 060 */ 061 public static final int IntegerNull = Integer.MIN_VALUE + 1; 062 063 /** 064 * Null value in three-valued boolean logic. 065 * Actually, a placeholder until we actually implement 3VL. 066 */ 067 public static final boolean BooleanNull = false; 068 069 /** 070 * Creates an exception which indicates that an error has occurred while 071 * executing a given function. 072 */ 073 public static RuntimeException newEvalException( 074 FunDef funDef, 075 String message) { 076 Util.discard(funDef); // TODO: use this 077 return new MondrianEvaluationException(message); 078 } 079 080 /** 081 * Creates an exception which indicates that an error has occurred while 082 * executing a given function. 083 */ 084 public static RuntimeException newEvalException(Throwable throwable) { 085 return new MondrianEvaluationException( 086 throwable.getClass().getName() + ": " + throwable.getMessage()); 087 } 088 089 public static boolean isMemberType(Calc calc) { 090 Type type = calc.getType(); 091 return (type instanceof SetType) && 092 (((SetType) type).getElementType() instanceof MemberType); 093 } 094 095 public static void checkIterListResultStyles(Calc calc) { 096 switch (calc.getResultStyle()) { 097 case ITERABLE: 098 case LIST: 099 case MUTABLE_LIST: 100 break; 101 default: 102 throw ResultStyleException.generateBadType( 103 ResultStyle.ITERABLE_LIST_MUTABLELIST, 104 calc.getResultStyle()); 105 } 106 } 107 108 public static void checkListResultStyles(Calc calc) { 109 switch (calc.getResultStyle()) { 110 case LIST: 111 case MUTABLE_LIST: 112 break; 113 default: 114 throw ResultStyleException.generateBadType( 115 ResultStyle.LIST_MUTABLELIST, 116 calc.getResultStyle()); 117 } 118 } 119 120 /** 121 * Returns an argument whose value is a literal. 122 */ 123 static String getLiteralArg( 124 ResolvedFunCall call, 125 int i, 126 String defaultValue, 127 String[] allowedValues) { 128 if (i >= call.getArgCount()) { 129 if (defaultValue == null) { 130 throw newEvalException(call.getFunDef(), 131 "Required argument is missing"); 132 } else { 133 return defaultValue; 134 } 135 } 136 Exp arg = call.getArg(i); 137 if (!(arg instanceof Literal) || 138 arg.getCategory() != Category.Symbol) { 139 throw newEvalException(call.getFunDef(), 140 "Expected a symbol, found '" + arg + "'"); 141 } 142 String s = (String) ((Literal) arg).getValue(); 143 StringBuilder sb = new StringBuilder(64); 144 for (int j = 0; j < allowedValues.length; j++) { 145 String allowedValue = allowedValues[j]; 146 if (allowedValue.equalsIgnoreCase(s)) { 147 return allowedValue; 148 } 149 if (j > 0) { 150 sb.append(", "); 151 } 152 sb.append(allowedValue); 153 } 154 throw newEvalException(call.getFunDef(), 155 "Allowed values are: {" + sb + "}"); 156 } 157 158 /** 159 * Returns the ordinal of a literal argument. If the argument does not 160 * belong to the supplied enumeration, returns -1. 161 */ 162 static <E extends Enum<E>> E getLiteralArg( 163 ResolvedFunCall call, 164 int i, 165 E defaultValue, 166 Class<E> allowedValues) { 167 if (i >= call.getArgCount()) { 168 if (defaultValue == null) { 169 throw newEvalException(call.getFunDef(), 170 "Required argument is missing"); 171 } else { 172 return defaultValue; 173 } 174 } 175 Exp arg = call.getArg(i); 176 if (!(arg instanceof Literal) || 177 arg.getCategory() != Category.Symbol) { 178 throw newEvalException(call.getFunDef(), 179 "Expected a symbol, found '" + arg + "'"); 180 } 181 String s = (String) ((Literal) arg).getValue(); 182 for (E e : allowedValues.getEnumConstants()) { 183 if (e.name().equalsIgnoreCase(s)) { 184 return e; 185 } 186 } 187 StringBuilder buf = new StringBuilder(64); 188 int k = 0; 189 for (E e : allowedValues.getEnumConstants()) { 190 if (k++ > 0) { 191 buf.append(", "); 192 } 193 buf.append(e.name()); 194 } 195 throw newEvalException(call.getFunDef(), 196 "Allowed values are: {" + buf + "}"); 197 } 198 199 /** 200 * Throws an error if the expressions don't have the same hierarchy. 201 * @param left 202 * @param right 203 * @throws MondrianEvaluationException if expressions don't have the same 204 * hierarchy 205 */ 206 static void checkCompatible(Exp left, Exp right, FunDef funDef) { 207 final Type leftType = TypeUtil.stripSetType(left.getType()); 208 final Type rightType = TypeUtil.stripSetType(right.getType()); 209 if (!TypeUtil.isUnionCompatible(leftType, rightType)) { 210 throw newEvalException(funDef, "Expressions must have the same hierarchy"); 211 } 212 } 213 214 /** 215 * Returns <code>true</code> if the mask in <code>flag</code> is set. 216 * @param value The value to check. 217 * @param mask The mask within value to look for. 218 * @param strict If <code>true</code> all the bits in mask must be set. If 219 * <code>false</code> the method will return <code>true</code> if any of the 220 * bits in <code>mask</code> are set. 221 * @return <code>true</code> if the correct bits in <code>mask</code> are set. 222 */ 223 static boolean checkFlag(int value, int mask, boolean strict) { 224 return (strict) 225 ? ((value & mask) == mask) 226 : ((value & mask) != 0); 227 } 228 229 /** 230 * Adds every element of <code>right</code> which is not in <code>set</code> 231 * to both <code>set</code> and <code>left</code>. 232 */ 233 static <T> void addUnique(List<T> left, List<T> right, Set<Object> set) { 234 assert left != null; 235 assert right != null; 236 if (right.isEmpty()) { 237 return; 238 } 239 for (int i = 0, n = right.size(); i < n; i++) { 240 T o = right.get(i); 241 Object p = o; 242 if (o instanceof Object[]) { 243 p = new ArrayHolder<Object>((Object[]) o); 244 } 245 if (set.add(p)) { 246 left.add(o); 247 } 248 } 249 } 250 251 static List<Member> addMembers( 252 final SchemaReader schemaReader, 253 final List<Member> members, 254 final Hierarchy hierarchy) 255 { 256 // only add accessible levels 257 for (Level level : schemaReader.getHierarchyLevels(hierarchy)) { 258 addMembers(schemaReader, members, level); 259 } 260 return members; 261 } 262 263 static List<Member> addMembers( 264 SchemaReader schemaReader, 265 List<Member> members, 266 Level level) { 267 List<Member> levelMembers = schemaReader.getLevelMembers(level, true); 268 members.addAll(levelMembers); 269 return members; 270 } 271 272 /** 273 * Removes every member from a list which is calculated. 274 * The list must not be null, and must consist only of members. 275 * 276 * @param memberList Member list 277 * @return List of non-calculated members 278 */ 279 static List<Member> removeCalculatedMembers(List<Member> memberList) 280 { 281 return new FilteredIterableList<Member>( 282 memberList, 283 new FilteredIterableList.Filter<Member>() { 284 public boolean accept(final Member m) { 285 return ! m.isCalculated(); 286 } 287 } 288 ); 289 } 290 291 /** 292 * Returns whether <code>m0</code> is an ancestor of <code>m1</code>. 293 * 294 * @param strict if true, a member is not an ancestor of itself 295 */ 296 static boolean isAncestorOf(Member m0, Member m1, boolean strict) { 297 if (strict) { 298 if (m1 == null) { 299 return false; 300 } 301 m1 = m1.getParentMember(); 302 } 303 while (m1 != null) { 304 if (m1.equals(m0)) { 305 return true; 306 } 307 m1 = m1.getParentMember(); 308 } 309 return false; 310 } 311 312 /** 313 * For each member in a list, evaluate an expression and create a map 314 * from members to values. 315 * 316 * <p>If the list contains tuples, use 317 * {@link #evaluateTuples(mondrian.olap.Evaluator, mondrian.calc.Calc, java.util.List)}. 318 * 319 * @param evaluator Evaluation context 320 * @param exp Expression to evaluate 321 * @param members List of members 322 * @param parentsToo If true, evaluate the expression for all ancestors 323 * of the members as well 324 * 325 * @pre exp != null 326 * @pre exp.getType() instanceof ScalarType 327 */ 328 static Map<Member, Object> evaluateMembers( 329 Evaluator evaluator, 330 Calc exp, 331 List<Member> members, 332 boolean parentsToo) 333 { 334 // REVIEW: is this necessary? 335 evaluator = evaluator.push(); 336 337 assert exp.getType() instanceof ScalarType; 338 Map<Member, Object> mapMemberToValue = new HashMap<Member, Object>(); 339 for (Member member : members) { 340 while (true) { 341 evaluator.setContext(member); 342 Object result = exp.evaluate(evaluator); 343 if (result == null) { 344 result = Util.nullValue; 345 } 346 mapMemberToValue.put(member, result); 347 if (!parentsToo) { 348 break; 349 } 350 member = member.getParentMember(); 351 if (member == null) { 352 break; 353 } 354 if (mapMemberToValue.containsKey(member)) { 355 break; 356 } 357 } 358 } 359 return mapMemberToValue; 360 } 361 362 /** 363 * For each tuple in a list, evaluates an expression and creates a map 364 * from tuples to values. 365 * 366 * @param evaluator Evaluation context 367 * @param exp Expression to evaluate 368 * @param members List of members (or List of Member[] tuples) 369 * 370 * @pre exp != null 371 * @pre exp.getType() instanceof ScalarType 372 */ 373 static Map<Object, Object> evaluateTuples( 374 Evaluator evaluator, 375 Calc exp, 376 List<Member[]> members) { 377 // RME 378 evaluator = evaluator.push(); 379 380 assert exp.getType() instanceof ScalarType; 381 Map<Object, Object> mapMemberToValue = new HashMap<Object, Object>(); 382 for (int i = 0, count = members.size(); i < count; i++) { 383 Member[] tuples = members.get(i); 384 evaluator.setContext(tuples); 385 Object result = exp.evaluate(evaluator); 386 if (result == null) { 387 result = Util.nullValue; 388 } 389 mapMemberToValue.put(new ArrayHolder<Member>(tuples), result); 390 } 391 return mapMemberToValue; 392 } 393 394 static Map<Member, Object> evaluateMembers( 395 Evaluator evaluator, 396 List<Member> members, 397 boolean parentsToo) { 398 Map<Member, Object> mapMemberToValue = new HashMap<Member, Object>(); 399 for (int i = 0, count = members.size(); i < count; i++) { 400 Member member = members.get(i); 401 while (true) { 402 evaluator.setContext(member); 403 Object result = evaluator.evaluateCurrent(); 404 mapMemberToValue.put(member, result); 405 if (!parentsToo) { 406 break; 407 } 408 member = member.getParentMember(); 409 if (member == null) { 410 break; 411 } 412 if (mapMemberToValue.containsKey(member)) { 413 break; 414 } 415 } 416 } 417 return mapMemberToValue; 418 } 419 420 /** 421 * Helper function to sortMembers a list of members according to an expression. 422 * 423 * <p>NOTE: This function does not preserve the contents of the validator. 424 */ 425 static void sortMembers( 426 Evaluator evaluator, 427 List<Member> members, 428 Calc exp, 429 boolean desc, 430 boolean brk) 431 { 432 if (members.isEmpty()) { 433 return; 434 } 435 Object first = members.get(0); 436 Map<Member, Object> mapMemberToValue; 437 if (first instanceof Member) { 438 final boolean parentsToo = !brk; 439 mapMemberToValue = evaluateMembers(evaluator, exp, members, parentsToo); 440 Comparator<Member> comparator; 441 if (brk) { 442 comparator = 443 new BreakMemberComparator(mapMemberToValue, desc).wrap(); 444 } else { 445 comparator = 446 new HierarchicalMemberComparator(mapMemberToValue, desc) 447 .wrap(); 448 } 449 Collections.sort(members, comparator); 450 } else { 451 Util.assertTrue(first instanceof Member[]); 452 final int arity = ((Member[]) first).length; 453 Comparator<Member[]> comparator; 454 if (brk) { 455 comparator = new BreakArrayComparator(evaluator, exp, arity) 456 .wrap(); 457 if (desc) { 458 comparator = new ReverseComparator<Member[]>(comparator); 459 } 460 } else { 461 comparator = 462 new HierarchicalArrayComparator( 463 evaluator, exp, arity, desc).wrap(); 464 } 465 Collections.sort((List) members, comparator); 466 } 467 if (debug) { 468 final PrintWriter pw = new PrintWriter(System.out); 469 for (int i = 0; i < members.size(); i++) { 470 Member o = members.get(i); 471 pw.print(i); 472 pw.print(": "); 473 if (mapMemberToValue != null) { 474 pw.print(mapMemberToValue.get(o)); 475 pw.print(": "); 476 } 477 pw.println(o); 478 } 479 pw.flush(); 480 } 481 } 482 483 /** 484 * Helper function to sortMembers a list of members according to an expression. 485 * 486 * <p>NOTE: This function does not preserve the contents of the validator. 487 */ 488 public static void sortTuples( 489 Evaluator evaluator, 490 List<Member[]> tuples, 491 Calc exp, 492 boolean desc, 493 boolean brk, 494 int arity) 495 { 496 if (tuples.isEmpty()) { 497 return; 498 } 499 Comparator<Member[]> comparator; 500 if (brk) { 501 comparator = 502 new BreakArrayComparator(evaluator, exp, arity).wrap(); 503 if (desc) { 504 comparator = new ReverseComparator<Member[]>(comparator); 505 } 506 } else { 507 comparator = 508 new HierarchicalArrayComparator( 509 evaluator, exp, arity, desc).wrap(); 510 } 511 Collections.sort(tuples, comparator); 512 if (debug) { 513 final PrintWriter pw = new PrintWriter(System.out); 514 for (int i = 0; i < tuples.size(); i++) { 515 Member[] o = tuples.get(i); 516 pw.print(i); 517 pw.print(": "); 518 pw.println(o); 519 } 520 pw.flush(); 521 } 522 } 523 524 public static void hierarchize(List members, boolean post) { 525 if (members.isEmpty()) { 526 return; 527 } 528 final Object first = members.get(0); 529 if (first instanceof Member) { 530 if (((Member) first).getDimension().isHighCardinality()) { 531 return; 532 } 533 List<Member> memberList = Util.cast(members); 534 Comparator<Member> comparator = new HierarchizeComparator(post); 535 members.toArray(); 536 Collections.sort(memberList, comparator); 537 } else { 538 assert first instanceof Member[]; 539 final int arity = ((Member[]) first).length; 540 List<Member[]> tupleList = Util.cast(members); 541 Comparator<Member[]> comparator = 542 new HierarchizeArrayComparator(arity, post).wrap(); 543 Collections.sort(tupleList, comparator); 544 } 545 } 546 547 static int sign(double d) { 548 return (d == 0) 549 ? 0 550 : (d < 0) 551 ? -1 552 : 1; 553 } 554 555 /** 556 * Compares double-precision values according to MDX semantics. 557 * 558 * <p>MDX requires a total order: 559 * <pre> 560 * -inf < NULL < ... < -1 < ... < 0 < ... < NaN < +inf 561 * </pre> 562 * but this is different than Java semantics, specifically with regard 563 * to {@link Double#NaN}. 564 */ 565 public static int compareValues(double d1, double d2) { 566 if (Double.isNaN(d1)) { 567 if (d2 == Double.POSITIVE_INFINITY) { 568 return -1; 569 } else if (Double.isNaN(d2)) { 570 return 0; 571 } else { 572 return 1; 573 } 574 } else if (Double.isNaN(d2)) { 575 if (d1 == Double.POSITIVE_INFINITY) { 576 return 1; 577 } else { 578 return -1; 579 } 580 } else if (d1 == d2) { 581 return 0; 582 } else if (d1 == FunUtil.DoubleNull) { 583 if (d2 == Double.NEGATIVE_INFINITY) { 584 return 1; 585 } else { 586 return -1; 587 } 588 } else if (d2 == FunUtil.DoubleNull) { 589 if (d1 == Double.NEGATIVE_INFINITY) { 590 return -1; 591 } else { 592 return 1; 593 } 594 } else if (d1 < d2) { 595 return -1; 596 } else { 597 return 1; 598 } 599 } 600 601 public static int compareValues(int i, int j) { 602 return (i == j) 603 ? 0 604 : (i < j) 605 ? -1 606 : 1; 607 } 608 609 /** 610 * Compares two cell values. 611 * 612 * <p>Nulls compare last, exceptions (including the 613 * object which indicates the the cell is not in the cache yet) next, 614 * then numbers and strings are compared by value. 615 * 616 * @param value0 First cell value 617 * @param value1 Second cell value 618 * @return -1, 0, or 1, depending upon whether first cell value is less 619 * than, equal to, or greater than the second 620 */ 621 public static int compareValues(Object value0, Object value1) { 622 if (value0 == value1) { 623 return 0; 624 } 625 // null is less than anything else 626 if (value0 == null) { 627 return -1; 628 } 629 if (value1 == null) { 630 return 1; 631 } 632 if (value0 instanceof RuntimeException || 633 value1 instanceof RuntimeException) { 634 // one of the values is not in cache; continue as best as we can 635 return 0; 636 } else if (value0 == Util.nullValue) { 637 return -1; // null == -infinity 638 } else if (value1 == Util.nullValue) { 639 return 1; // null == -infinity 640 } else if (value0 instanceof String) { 641 return ((String) value0).compareTo((String) value1); 642 } else if (value0 instanceof Number) { 643 return FunUtil.compareValues( 644 ((Number) value0).doubleValue(), 645 ((Number) value1).doubleValue()); 646 } else { 647 throw Util.newInternal("cannot compare " + value0); 648 } 649 } 650 651 /** 652 * Turns the mapped values into relative values (percentages) for easy 653 * use by the general topOrBottom function. This might also be a useful 654 * function in itself. 655 */ 656 static void toPercent(List members, Map mapMemberToValue, boolean isMember) { 657 double total = 0; 658 int memberCount = members.size(); 659 for (int i = 0; i < memberCount; i++) { 660 Object o = (isMember) 661 ? mapMemberToValue.get(members.get(i)) 662 : mapMemberToValue.get( 663 new ArrayHolder<Member>((Member []) members.get(i))); 664 if (o instanceof Number) { 665 total += ((Number) o).doubleValue(); 666 } 667 } 668 for (int i = 0; i < memberCount; i++) { 669 Object mo = members.get(i); 670 Object o = (isMember) ? 671 mapMemberToValue.get(mo) : 672 mapMemberToValue.get(new ArrayHolder<Member>((Member []) mo)); 673 if (o instanceof Number) { 674 double d = ((Number) o).doubleValue(); 675 if (isMember) { 676 mapMemberToValue.put( 677 mo, 678 d / total * (double) 100); 679 } else { 680 mapMemberToValue.put( 681 new ArrayHolder<Member>((Member []) mo), 682 d / total * (double) 100); 683 } 684 } 685 } 686 } 687 688 689 /** 690 * Decodes the syntactic type of an operator. 691 * 692 * @param flags A encoded string which represents an operator signature, 693 * as used by the <code>flags</code> parameter used to construct a 694 * {@link FunDefBase}. 695 * 696 * @return A {@link Syntax} 697 */ 698 public static Syntax decodeSyntacticType(String flags) { 699 char c = flags.charAt(0); 700 switch (c) { 701 case 'p': 702 return Syntax.Property; 703 case 'f': 704 return Syntax.Function; 705 case 'm': 706 return Syntax.Method; 707 case 'i': 708 return Syntax.Infix; 709 case 'P': 710 return Syntax.Prefix; 711 case 'Q': 712 return Syntax.Postfix; 713 case 'I': 714 return Syntax.Internal; 715 default: 716 throw newInternal( 717 "unknown syntax code '" + c + "' in string '" + flags + "'"); 718 } 719 } 720 721 /** 722 * Decodes the signature of a function into a category code which describes 723 * the return type of the operator. 724 * 725 * <p>For example, <code>decodeReturnType("fnx")</code> returns 726 * <code>{@link Category#Numeric}</code>, indicating this function has a 727 * numeric return value. 728 * 729 * @param flags The signature of an operator, 730 * as used by the <code>flags</code> parameter used to construct a 731 * {@link FunDefBase}. 732 * 733 * @return An array {@link Category} codes. 734 */ 735 public static int decodeReturnCategory(String flags) { 736 final int returnCategory = decodeCategory(flags, 1); 737 if ((returnCategory & Category.Mask) != returnCategory) { 738 throw newInternal("bad return code flag in flags '" + flags + "'"); 739 } 740 return returnCategory; 741 } 742 743 /** 744 * Decodes the <code>offset</code>th character of an encoded method 745 * signature into a type category. 746 * 747 * <p>The codes are: 748 * <table border="1"> 749 * 750 * <tr><td>a</td><td>{@link Category#Array}</td></tr> 751 * 752 * <tr><td>d</td><td>{@link Category#Dimension}</td></tr> 753 * 754 * <tr><td>h</td><td>{@link Category#Hierarchy}</td></tr> 755 * 756 * <tr><td>l</td><td>{@link Category#Level}</td></tr> 757 * 758 * <tr><td>b</td><td>{@link Category#Logical}</td></tr> 759 * 760 * <tr><td>m</td><td>{@link Category#Member}</td></tr> 761 * 762 * <tr><td>N</td><td>Constant {@link Category#Numeric}</td></tr> 763 * 764 * <tr><td>n</td><td>{@link Category#Numeric}</td></tr> 765 * 766 * <tr><td>x</td><td>{@link Category#Set}</td></tr> 767 * 768 * <tr><td>#</td><td>Constant {@link Category#String}</td></tr> 769 * 770 * <tr><td>S</td><td>{@link Category#String}</td></tr> 771 * 772 * <tr><td>t</td><td>{@link Category#Tuple}</td></tr> 773 * 774 * <tr><td>v</td><td>{@link Category#Value}</td></tr> 775 * 776 * <tr><td>y</td><td>{@link Category#Symbol}</td></tr> 777 * 778 * </table> 779 * 780 * @param flags Encoded signature string 781 * @param offset 0-based offset of character within string 782 * @return A {@link Category} 783 */ 784 public static int decodeCategory(String flags, int offset) { 785 char c = flags.charAt(offset); 786 switch (c) { 787 case 'a': 788 return Category.Array; 789 case 'd': 790 return Category.Dimension; 791 case 'h': 792 return Category.Hierarchy; 793 case 'l': 794 return Category.Level; 795 case 'b': 796 return Category.Logical; 797 case 'm': 798 return Category.Member; 799 case 'N': 800 return Category.Numeric | Category.Constant; 801 case 'n': 802 return Category.Numeric; 803 case 'I': 804 return Category.Numeric | Category.Integer | Category.Constant; 805 case 'i': 806 return Category.Numeric | Category.Integer; 807 case 'x': 808 return Category.Set; 809 case '#': 810 return Category.String | Category.Constant; 811 case 'S': 812 return Category.String; 813 case 't': 814 return Category.Tuple; 815 case 'v': 816 return Category.Value; 817 case 'y': 818 return Category.Symbol; 819 case 'U': 820 return Category.Null; 821 case 'e': 822 return Category.Empty; 823 case 'D': 824 return Category.DateTime; 825 default: 826 throw newInternal( 827 "unknown type code '" + c + "' in string '" + flags + "'"); 828 } 829 } 830 831 /** 832 * Decodes a string of parameter types into an array of type codes. 833 * 834 * <p>Each character is decoded using {@link #decodeCategory(String, int)}. 835 * For example, <code>decodeParameterTypes("nx")</code> returns 836 * <code>{{@link Category#Numeric}, {@link Category#Set}}</code>. 837 * 838 * @param flags The signature of an operator, 839 * as used by the <code>flags</code> parameter used to construct a 840 * {@link FunDefBase}. 841 * 842 * @return An array {@link Category} codes. 843 */ 844 public static int[] decodeParameterCategories(String flags) { 845 int[] parameterCategories = new int[flags.length() - 2]; 846 for (int i = 0; i < parameterCategories.length; i++) { 847 parameterCategories[i] = decodeCategory(flags, i + 2); 848 } 849 return parameterCategories; 850 } 851 852 /** 853 * Sorts an array of values. 854 */ 855 public static void sortValuesDesc(Object[] values) { 856 Arrays.sort(values, DescendingValueComparator.instance); 857 } 858 859 /** 860 * Binary searches an array of values. 861 */ 862 public static int searchValuesDesc(Object[] values, Object value) { 863 return Arrays.binarySearch( 864 values, value, DescendingValueComparator.instance); 865 } 866 867 static double percentile( 868 Evaluator evaluator, 869 List members, 870 Calc exp, 871 double p) 872 { 873 SetWrapper sw = evaluateSet(evaluator, members, exp); 874 if (sw.errorCount > 0) { 875 return Double.NaN; 876 } else if (sw.v.size() == 0) { 877 return FunUtil.DoubleNull; 878 } 879 double[] asArray = new double[sw.v.size()]; 880 for (int i = 0; i < asArray.length; i++) { 881 asArray[i] = (Double) sw.v.get(i); 882 } 883 Arrays.sort(asArray); 884 885 /* 886 * The median is defined as the value that has exactly the same 887 * number of entries before it in the sorted list as after. 888 * So, if the number of entries in the list is odd, the 889 * median is the entry at (length-1)/2 (using zero-based indexes). 890 * If the number of entries is even, the median is defined as the 891 * arithmetic mean of the two numbers in the middle of the list, or 892 * (entries[length/2 - 1] + entries[length/2]) / 2. 893 */ 894 int length = asArray.length; 895 896 if (p == 0.5) { 897 // Special case for median. 898 if ((length & 1) == 1) { 899 // The length is odd. Note that length/2 is an integer 900 // expression, and it's positive so we save ourselves a divide. 901 return asArray[length >> 1]; 902 } else { 903 return (asArray[(length >> 1) - 1] + asArray[length >> 1]) 904 / 2.0; 905 } 906 } else if (p <= 0.0) { 907 return asArray[0]; 908 } else if (p >= 1.0) { 909 return asArray[length - 1]; 910 } else { 911 final double jD = Math.floor(length * p); 912 int j = (int) jD; 913 double alpha = (p - jD) * length; 914 assert alpha >= 0; 915 assert alpha <= 1; 916 return asArray[j] * (1.0 - alpha) 917 + asArray[j + 1] * alpha; 918 } 919 } 920 921 /** 922 * Returns the member which lies upon a particular quartile according to a 923 * given expression. 924 * 925 * @param evaluator Evaluator 926 * @param members List of members 927 * @param exp Expression to rank members 928 * @param range Quartile (1, 2 or 3) 929 * 930 * @pre range >= 1 && range <= 3 931 */ 932 protected static double quartile( 933 Evaluator evaluator, 934 List members, 935 Calc exp, 936 int range) { 937 Util.assertPrecondition(range >= 1 && range <= 3, "range >= 1 && range <= 3"); 938 939 SetWrapper sw = evaluateSet(evaluator, members, exp); 940 if (sw.errorCount > 0) { 941 return Double.NaN; 942 } else if (sw.v.size() == 0) { 943 return DoubleNull; 944 } 945 946 double[] asArray = new double[sw.v.size()]; 947 for (int i = 0; i < asArray.length; i++) { 948 asArray[i] = ((Double) sw.v.get(i)).doubleValue(); 949 } 950 951 Arrays.sort(asArray); 952 // get a quartile, median is a second q 953 double dm = 0.25 * asArray.length * range; 954 int median = (int) Math.floor(dm); 955 return dm == median && median < asArray.length - 1 ? 956 (asArray[median] + asArray[median + 1]) / 2 : 957 asArray[median]; 958 } 959 960 public static Object min(Evaluator evaluator, List members, Calc calc) { 961 SetWrapper sw = evaluateSet(evaluator, members, calc); 962 if (sw.errorCount > 0) { 963 return Double.NaN; 964 } else { 965 final int size = sw.v.size(); 966 if (size == 0) { 967 return Util.nullValue; 968 } else { 969 Double min = (Double) sw.v.get(0); 970 for (int i = 1; i < size; i++) { 971 Double iValue = (Double) sw.v.get(i); 972 if (iValue < min) { 973 min = iValue; 974 } 975 } 976 return min; 977 } 978 } 979 } 980 981 public static Object max(Evaluator evaluator, List members, Calc exp) { 982 SetWrapper sw = evaluateSet(evaluator, members, exp); 983 if (sw.errorCount > 0) { 984 return Double.NaN; 985 } else { 986 final int size = sw.v.size(); 987 if (size == 0) { 988 return Util.nullValue; 989 } else { 990 Double max = (Double) sw.v.get(0); 991 for (int i = 1; i < size; i++) { 992 Double iValue = (Double) sw.v.get(i); 993 if (iValue > max) { 994 max = iValue; 995 } 996 } 997 return max; 998 } 999 } 1000 } 1001 1002 static Object var( 1003 Evaluator evaluator, 1004 List members, 1005 Calc exp, 1006 boolean biased) { 1007 SetWrapper sw = evaluateSet(evaluator, members, exp); 1008 return _var(sw, biased); 1009 } 1010 1011 private static Object _var(SetWrapper sw, boolean biased) { 1012 if (sw.errorCount > 0) { 1013 return new Double(Double.NaN); 1014 } else if (sw.v.size() == 0) { 1015 return Util.nullValue; 1016 } else { 1017 double stdev = 0.0; 1018 double avg = _avg(sw); 1019 for (int i = 0; i < sw.v.size(); i++) { 1020 stdev += Math.pow((((Double) sw.v.get(i)).doubleValue() - avg),2); 1021 } 1022 int n = sw.v.size(); 1023 if (!biased) { 1024 n--; 1025 } 1026 return new Double(stdev / (double) n); 1027 } 1028 } 1029 1030 static double correlation( 1031 Evaluator evaluator, 1032 List memberList, 1033 Calc exp1, 1034 Calc exp2) { 1035 SetWrapper sw1 = evaluateSet(evaluator, memberList, exp1); 1036 SetWrapper sw2 = evaluateSet(evaluator, memberList, exp2); 1037 Object covar = _covariance(sw1, sw2, false); 1038 Object var1 = _var(sw1, false); //this should be false, yes? 1039 Object var2 = _var(sw2, false); 1040 if ((covar instanceof Double) && 1041 (var1 instanceof Double) && 1042 (var2 instanceof Double)) { 1043 return ((Double) covar).doubleValue() / 1044 Math.sqrt(((Double) var1).doubleValue() * 1045 ((Double) var2).doubleValue()); 1046 } else { 1047 return DoubleNull; 1048 } 1049 } 1050 1051 static Object covariance(Evaluator evaluator, List members, 1052 Calc exp1, Calc exp2, boolean biased) { 1053 SetWrapper sw1 = evaluateSet(evaluator.push(), members, exp1); 1054 SetWrapper sw2 = evaluateSet(evaluator.push(), members, exp2); 1055 // todo: because evaluateSet does not add nulls to the SetWrapper, this 1056 // solution may lead to mismatched lists and is therefore not robust 1057 return _covariance(sw1, sw2, biased); 1058 } 1059 1060 1061 private static Object _covariance(SetWrapper sw1, 1062 SetWrapper sw2, 1063 boolean biased) { 1064 if (sw1.v.size() != sw2.v.size()) { 1065 return Util.nullValue; 1066 } 1067 double avg1 = _avg(sw1); 1068 double avg2 = _avg(sw2); 1069 double covar = 0.0; 1070 for (int i = 0; i < sw1.v.size(); i++) { 1071 //all of this casting seems inefficient - can we make SetWrapper 1072 //contain an array of double instead? 1073 double diff1 = (((Double) sw1.v.get(i)).doubleValue() - avg1); 1074 double diff2 = (((Double) sw2.v.get(i)).doubleValue() - avg2); 1075 covar += (diff1 * diff2); 1076 } 1077 int n = sw1.v.size(); 1078 if (!biased) { 1079 n--; 1080 } 1081 return new Double(covar / (double) n); 1082 } 1083 1084 static Object stdev( 1085 Evaluator evaluator, 1086 List members, 1087 Calc exp, 1088 boolean biased) { 1089 Object o = var(evaluator, members, exp, biased); 1090 return (o instanceof Double) 1091 ? new Double(Math.sqrt(((Double) o).doubleValue())) 1092 : o; 1093 } 1094 1095 public static Object avg(Evaluator evaluator, List members, Calc calc) { 1096 SetWrapper sw = evaluateSet(evaluator, members, calc); 1097 return (sw.errorCount > 0) ? 1098 new Double(Double.NaN) : 1099 (sw.v.size() == 0) ? 1100 Util.nullValue : 1101 new Double(_avg(sw)); 1102 } 1103 1104 //todo: parameterize inclusion of nulls 1105 //also, maybe make _avg a method of setwrapper, so we can cache the result (i.e. for correl) 1106 private static double _avg(SetWrapper sw) { 1107 double sum = 0.0; 1108 for (int i = 0; i < sw.v.size(); i++) { 1109 sum += ((Double) sw.v.get(i)).doubleValue(); 1110 } 1111 //todo: should look at context and optionally include nulls 1112 return sum / (double) sw.v.size(); 1113 } 1114 1115 public static Object sum(Evaluator evaluator, List members, Calc exp) { 1116 double d = sumDouble(evaluator, members, exp); 1117 return d == DoubleNull ? Util.nullValue : new Double(d); 1118 } 1119 1120 public static double sumDouble(Evaluator evaluator, List members, Calc exp) { 1121 SetWrapper sw = evaluateSet(evaluator, members, exp); 1122 if (sw.errorCount > 0) { 1123 return Double.NaN; 1124 } else if (sw.v.size() == 0) { 1125 return DoubleNull; 1126 } else { 1127 double sum = 0.0; 1128 for (int i = 0; i < sw.v.size(); i++) { 1129 sum += ((Double) sw.v.get(i)).doubleValue(); 1130 } 1131 return sum; 1132 } 1133 } 1134 public static double sumDouble(Evaluator evaluator, Iterable iterable, Calc exp) { 1135 SetWrapper sw = evaluateSet(evaluator, iterable, exp); 1136 if (sw.errorCount > 0) { 1137 return Double.NaN; 1138 } else if (sw.v.size() == 0) { 1139 return DoubleNull; 1140 } else { 1141 double sum = 0.0; 1142 for (int i = 0; i < sw.v.size(); i++) { 1143 sum += ((Double) sw.v.get(i)).doubleValue(); 1144 } 1145 return sum; 1146 } 1147 } 1148 public static int count( 1149 Evaluator evaluator, 1150 Iterable iterable, 1151 boolean includeEmpty) { 1152 if (iterable == null) { 1153 return 0; 1154 } 1155 if (includeEmpty) { 1156 if (iterable instanceof Collection) { 1157 return ((Collection) iterable).size(); 1158 } else { 1159 int retval = 0; 1160 Iterator it = iterable.iterator(); 1161 while (it.hasNext()) { 1162 // must get the next one 1163 it.next(); 1164 retval++; 1165 } 1166 return retval; 1167 } 1168 } else { 1169 int retval = 0; 1170 for (Object object : iterable) { 1171 if (object instanceof Member) { 1172 evaluator.setContext((Member) object); 1173 } else { 1174 evaluator.setContext((Member[]) object); 1175 } 1176 Object o = evaluator.evaluateCurrent(); 1177 if (o != Util.nullValue && o != null) { 1178 retval++; 1179 } 1180 } 1181 return retval; 1182 } 1183 } 1184 1185 /* 1186 public static int countOld( 1187 Evaluator evaluator, 1188 List members, 1189 boolean includeEmpty) { 1190 if (members == null) { 1191 System.out.println("FunUtil.count List: null 0"); 1192 return 0; 1193 } 1194 if (includeEmpty) { 1195 System.out.println("FunUtil.count List: "+members.size()); 1196 return members.size(); 1197 } else { 1198 int retval = 0; 1199 for (int i = 0; i < members.size(); i++) { 1200 final Object member = members.get(i); 1201 if (member instanceof Member) { 1202 evaluator.setContext((Member) member); 1203 } else { 1204 evaluator.setContext((Member[]) member); 1205 } 1206 Object o = evaluator.evaluateCurrent(); 1207 if (o != Util.nullValue && o != null) { 1208 retval++; 1209 } 1210 } 1211 System.out.println("FunUtil.count List: "+retval); 1212 return retval; 1213 } 1214 } 1215 public static int countIterable( 1216 Evaluator evaluator, 1217 Iterable iterable, 1218 boolean includeEmpty) { 1219 if (iterable == null) { 1220 System.out.println("FunUtil.countIterable Iterable: null 0"); 1221 return 0; 1222 } 1223 int retval = 0; 1224 Iterator it = iterable.iterator(); 1225 while (it.hasNext()) { 1226 final Object member = it.next(); 1227 if (member instanceof Member) { 1228 evaluator.setContext((Member) member); 1229 } else if (member instanceof Member[]) { 1230 evaluator.setContext((Member[]) member); 1231 } 1232 if (includeEmpty) { 1233 retval++; 1234 } else { 1235 Object o = evaluator.evaluateCurrent(); 1236 if (o != Util.nullValue && o != null) { 1237 retval++; 1238 } 1239 } 1240 } 1241 System.out.println("FunUtil.countIterable Iterable: "+retval); 1242 return retval; 1243 } 1244 */ 1245 1246 /** 1247 * Evaluates <code>exp</code> (if defined) over <code>members</code> to 1248 * generate a {@link List} of {@link SetWrapper} objects, which contains 1249 * a {@link Double} value and meta information, unlike 1250 * {@link #evaluateMembers}, which only produces values. 1251 * 1252 * @pre exp != null 1253 */ 1254 static SetWrapper evaluateSet( 1255 Evaluator evaluator, 1256 Iterable members, 1257 Calc calc) { 1258 Util.assertPrecondition(members != null, "members != null"); 1259 Util.assertPrecondition(calc != null, "calc != null"); 1260 Util.assertPrecondition(calc.getType() instanceof ScalarType); 1261 1262 // todo: treat constant exps as evaluateMembers() does 1263 SetWrapper retval = new SetWrapper(); 1264 for (Object obj : members) { 1265 if (obj instanceof Member[]) { 1266 evaluator.setContext((Member[])obj); 1267 } else { 1268 evaluator.setContext((Member)obj); 1269 } 1270 Object o = calc.evaluate(evaluator); 1271 if (o == null || o == Util.nullValue) { 1272 retval.nullCount++; 1273 } else if (o instanceof Throwable) { 1274 // Carry on summing, so that if we are running in a 1275 // BatchingCellReader, we find out all the dependent cells we 1276 // need 1277 retval.errorCount++; 1278 } else if (o instanceof Double) { 1279 retval.v.add(o); 1280 } else if (o instanceof Number) { 1281 retval.v.add(((Number) o).doubleValue()); 1282 } else { 1283 retval.v.add(o); 1284 } 1285 } 1286 return retval; 1287 } 1288 1289 /** 1290 * Evaluates one or more expressions against the member list returning 1291 * a SetWrapper array. Where this differs very significantly from the 1292 * above evaluateSet methods is how it count null values and Throwables; 1293 * this method adds nulls to the SetWrapper Vector rather than not adding 1294 * anything - as the above method does. The impact of this is that if, for 1295 * example, one was creating a list of x,y values then each list will have 1296 * the same number of values (though some might be null) - this allows 1297 * higher level code to determine how to handle the lack of data rather than 1298 * having a non-equal number (if one is plotting x,y values it helps to 1299 * have the same number and know where a potential gap is the data is. 1300 */ 1301 static SetWrapper[] evaluateSet( 1302 Evaluator evaluator, 1303 List members, 1304 DoubleCalc[] calcs, 1305 boolean isTuples) { 1306 Util.assertPrecondition(calcs != null, "calcs != null"); 1307 1308 // todo: treat constant exps as evaluateMembers() does 1309 SetWrapper[] retvals = new SetWrapper[calcs.length]; 1310 for (int i = 0; i < calcs.length; i++) { 1311 retvals[i] = new SetWrapper(); 1312 } 1313 for (final Object member : members) { 1314 if (isTuples) { 1315 evaluator.setContext((List<Member>) member); 1316 } else { 1317 evaluator.setContext((Member) member); 1318 } 1319 for (int i = 0; i < calcs.length; i++) { 1320 DoubleCalc calc = calcs[i]; 1321 SetWrapper retval = retvals[i]; 1322 double o = calc.evaluateDouble(evaluator); 1323 if (o == FunUtil.DoubleNull) { 1324 retval.nullCount++; 1325 retval.v.add(null); 1326 } else { 1327 retval.v.add(o); 1328 } 1329 // TODO: If the expression yielded an error, carry on 1330 // summing, so that if we are running in a 1331 // BatchingCellReader, we find out all the dependent cells 1332 // we need 1333 } 1334 } 1335 return retvals; 1336 } 1337 1338 static List<Member> periodsToDate( 1339 Evaluator evaluator, 1340 Level level, 1341 Member member) { 1342 if (member == null) { 1343 member = evaluator.getContext(level.getHierarchy().getDimension()); 1344 } 1345 Member m = member; 1346 while (m != null) { 1347 if (m.getLevel() == level) { 1348 break; 1349 } 1350 m = m.getParentMember(); 1351 } 1352 // If m == null, then "level" was lower than member's level. 1353 // periodsToDate([Time].[Quarter], [Time].[1997] is valid, 1354 // but will return an empty List 1355 List<Member> members = new ArrayList<Member>(); 1356 if (m != null) { 1357 // e.g. m is [Time].[1997] and member is [Time].[1997].[Q1].[3] 1358 // we now have to make m to be the first member of the range, 1359 // so m becomes [Time].[1997].[Q1].[1] 1360 SchemaReader reader = evaluator.getSchemaReader(); 1361 m = Util.getFirstDescendantOnLevel(reader, m, member.getLevel()); 1362 reader.getMemberRange(level, m, member, members); 1363 } 1364 return members; 1365 } 1366 1367 static List<Member> memberRange( 1368 Evaluator evaluator, 1369 Member startMember, 1370 Member endMember) 1371 { 1372 final Level level = startMember.getLevel(); 1373 assertTrue(level == endMember.getLevel()); 1374 List<Member> members = new ArrayList<Member>(); 1375 evaluator.getSchemaReader().getMemberRange( 1376 level, startMember, endMember, members); 1377 1378 if (members.isEmpty()) { 1379 // The result is empty, so maybe the members are reversed. This is 1380 // cheaper than comparing the members before we call getMemberRange. 1381 evaluator.getSchemaReader().getMemberRange( 1382 level, endMember, startMember, members); 1383 } 1384 return members; 1385 } 1386 1387 /** 1388 * Returns the member under ancestorMember having the same relative position 1389 * under member's parent. 1390 * <p>For exmaple, cousin([Feb 2001], [Q3 2001]) is [August 2001]. 1391 * @param schemaReader The reader to use 1392 * @param member The member for which we'll find the cousin. 1393 * @param ancestorMember The cousin's ancestor. 1394 * @return The child of <code>ancestorMember</code> in the same position under 1395 * <code>ancestorMember</code> as <code>member</code> is under its parent. 1396 */ 1397 static Member cousin(SchemaReader schemaReader, 1398 Member member, 1399 Member ancestorMember) { 1400 if (ancestorMember.isNull()) { 1401 return ancestorMember; 1402 } 1403 if (member.getHierarchy() != ancestorMember.getHierarchy()) { 1404 throw MondrianResource.instance().CousinHierarchyMismatch.ex( 1405 member.getUniqueName(), ancestorMember.getUniqueName()); 1406 } 1407 if (member.getLevel().getDepth() < ancestorMember.getLevel().getDepth()) { 1408 return member.getHierarchy().getNullMember(); 1409 } 1410 1411 Member cousin = cousin2(schemaReader, member, ancestorMember); 1412 if (cousin == null) { 1413 cousin = member.getHierarchy().getNullMember(); 1414 } 1415 1416 return cousin; 1417 } 1418 1419 static private Member cousin2(SchemaReader schemaReader, 1420 Member member1, 1421 Member member2) { 1422 if (member1.getLevel() == member2.getLevel()) { 1423 return member2; 1424 } 1425 Member uncle = cousin2(schemaReader, member1.getParentMember(), member2); 1426 if (uncle == null) { 1427 return null; 1428 } 1429 int ordinal = Util.getMemberOrdinalInParent(schemaReader, member1); 1430 List<Member> cousins = schemaReader.getMemberChildren(uncle); 1431 if (cousins.size() <= ordinal) { 1432 return null; 1433 } 1434 return cousins.get(ordinal); 1435 } 1436 1437 /** 1438 * Returns the ancestor of <code>member</code> at the given level 1439 * or distance. It is assumed that any error checking required 1440 * has been done prior to calling this function. 1441 * <p>This method takes into consideration the fact that there 1442 * may be intervening hidden members between <code>member</code> 1443 * and the ancestor. If <code>targetLevel</code> is not null, then 1444 * the method will only return a member if the level at 1445 * <code>distance</code> from the member is actually the 1446 * <code>targetLevel</code> specified. 1447 * @param evaluator The evaluation context 1448 * @param member The member for which the ancestor is to be found 1449 * @param distance The distance up the chain the ancestor is to 1450 * be found. 1451 * @param targetLevel The desired targetLevel of the ancestor. If <code>null</code>, 1452 * then the distance completely determines the desired ancestor. 1453 * @return The ancestor member, or <code>null</code> if no such 1454 * ancestor exists. 1455 */ 1456 static Member ancestor(Evaluator evaluator, 1457 Member member, 1458 int distance, 1459 Level targetLevel) { 1460 if ((targetLevel != null) && 1461 (member.getHierarchy() != targetLevel.getHierarchy())) { 1462 throw MondrianResource.instance().MemberNotInLevelHierarchy.ex( 1463 member.getUniqueName(), targetLevel.getUniqueName()); 1464 } 1465 1466 if (distance == 0) { 1467 /* 1468 * Shortcut if there's nowhere to go. 1469 */ 1470 return member; 1471 } else if (distance < 0) { 1472 /* 1473 * Can't go backwards. 1474 */ 1475 return member.getHierarchy().getNullMember(); 1476 } 1477 1478 List<Member> ancestors = member.getAncestorMembers(); 1479 final SchemaReader schemaReader = evaluator.getSchemaReader(); 1480 1481 Member result = member.getHierarchy().getNullMember(); 1482 1483 searchLoop: 1484 for (int i = 0; i < ancestors.size(); i++) { 1485 final Member ancestorMember = ancestors.get(i); 1486 1487 if (targetLevel != null) { 1488 if (ancestorMember.getLevel() == targetLevel) { 1489 if (schemaReader.isVisible(ancestorMember)) { 1490 result = ancestorMember; 1491 break; 1492 } else { 1493 result = member.getHierarchy().getNullMember(); 1494 break; 1495 } 1496 } 1497 } else { 1498 if (schemaReader.isVisible(ancestorMember)) { 1499 distance--; 1500 1501 // 1502 // Make sure that this ancestor is really on the right 1503 // targetLevel. If a targetLevel was specified and at least 1504 // one of the ancestors was hidden, this this algorithm goes 1505 // too far up the ancestor list. It's not a problem, except 1506 // that we need to check if it's happened and return the 1507 // hierarchy's null member instead. 1508 // 1509 // For example, consider what happens with 1510 // Ancestor([Store].[Israel].[Haifa], [Store].[Store State]). 1511 // The distance from [Haifa] to [Store State] is 1, but that 1512 // lands us at the country targetLevel, which is clearly 1513 // wrong. 1514 // 1515 if (distance == 0) { 1516 result = ancestorMember; 1517 break; 1518 } 1519 } 1520 } 1521 } 1522 1523 return result; 1524 } 1525 1526 /** 1527 * Compares a pair of members according to their positions in a 1528 * prefix-order (or postfix-order, if <code>post</code> is true) walk 1529 * over a hierarchy. 1530 * 1531 * @param m1 First member 1532 * @param m2 Second member 1533 * @param post Whether to sortMembers in postfix order. If true, a parent will 1534 * sortMembers immediately after its last child. If false, a parent will sortMembers 1535 * immediately before its first child. 1536 * @return -1 if m1 collates before m2, 1537 * 0 if m1 equals m2, 1538 * 1 if m1 collates after m2 1539 */ 1540 public static int compareHierarchically( 1541 Member m1, 1542 Member m2, 1543 boolean post) 1544 { 1545 // Strip away the LimitedRollupMember wrapper, if it exists. The 1546 // wrapper does not implement equals and comparisons correctly. This 1547 // is safe this method has no side-effects: it just returns an int. 1548 if (m1 instanceof RolapHierarchy.LimitedRollupMember) { 1549 m1 = ((RolapHierarchy.LimitedRollupMember) m1).member; 1550 } 1551 if (m2 instanceof RolapHierarchy.LimitedRollupMember) { 1552 m2 = ((RolapHierarchy.LimitedRollupMember) m2).member; 1553 } 1554 if (equals(m1, m2)) { 1555 return 0; 1556 } 1557 while (true) { 1558 int depth1 = m1.getDepth(); 1559 int depth2 = m2.getDepth(); 1560 if (depth1 < depth2) { 1561 m2 = m2.getParentMember(); 1562 if (equals(m1, m2)) { 1563 return post ? 1 : -1; 1564 } 1565 } else if (depth1 > depth2) { 1566 m1 = m1.getParentMember(); 1567 if (equals(m1, m2)) { 1568 return post ? -1 : 1; 1569 } 1570 } else { 1571 Member prev1 = m1; 1572 Member prev2 = m2; 1573 m1 = m1.getParentMember(); 1574 m2 = m2.getParentMember(); 1575 if (equals(m1, m2)) { 1576 final int c = compareSiblingMembers(prev1, prev2); 1577 // compareHierarchically needs to impose a total order; 1578 // cannot return 0 for non-equal members 1579 assert c != 0 : 1580 "Members " + prev1 + ", " + prev2 + 1581 " are not equal, but compare returned 0."; 1582 return c; 1583 } 1584 } 1585 } 1586 } 1587 1588 /** 1589 * Compares two members which are known to have the same parent. 1590 * 1591 * First, compare by ordinal. 1592 * This is only valid now we know they're siblings, because 1593 * ordinals are only unique within a parent. 1594 * If the dimension does not use ordinals, both ordinals 1595 * will be -1. 1596 * 1597 * <p>If the ordinals do not differ, compare using regular member 1598 * comparison. 1599 * 1600 * @param m1 First member 1601 * @param m2 Second member 1602 * @return -1 if m1 collates less than m2, 1603 * 1 if m1 collates after m2, 1604 * 0 if m1 == m2. 1605 */ 1606 public static int compareSiblingMembers(Member m1, Member m2) { 1607 // calculated members collate after non-calculated 1608 final boolean calculated1 = m1.isCalculatedInQuery(); 1609 final boolean calculated2 = m2.isCalculatedInQuery(); 1610 if (calculated1) { 1611 if (!calculated2) { 1612 return 1; 1613 } 1614 } else { 1615 if (calculated2) { 1616 return -1; 1617 } 1618 } 1619 final Comparable k1 = m1.getOrderKey(); 1620 final Comparable k2 = m2.getOrderKey(); 1621 if ((k1 != null) && (k2 != null)) { 1622 return k1.compareTo(k2); 1623 } else { 1624 final int ordinal1 = m1.getOrdinal(); 1625 final int ordinal2 = m2.getOrdinal(); 1626 return (ordinal1 == ordinal2) ? 1627 m1.compareTo(m2) : 1628 (ordinal1 < ordinal2) ? 1629 -1 : 1630 1; 1631 } 1632 } 1633 1634 /** 1635 * Returns whether one of the members in a tuple is null. 1636 */ 1637 static boolean tupleContainsNullMember(Member[] tuple) { 1638 for (Member member : tuple) { 1639 if (member.isNull()) { 1640 return true; 1641 } 1642 } 1643 return false; 1644 } 1645 1646 public static Member[] makeNullTuple(final TupleType tupleType) { 1647 Member[] members = new Member[tupleType.elementTypes.length]; 1648 for (int i = 0; i < tupleType.elementTypes.length; i++) { 1649 MemberType type = (MemberType) tupleType.elementTypes[i]; 1650 members[i] = makeNullMember(type); 1651 } 1652 return members; 1653 } 1654 1655 static Member makeNullMember( 1656 MemberType memberType) { 1657 Hierarchy hierarchy = memberType.getHierarchy(); 1658 if (hierarchy == null) { 1659 return NullMember; 1660 } 1661 return hierarchy.getNullMember(); 1662 } 1663 1664 /** 1665 * Validates the arguments to a function and resolves the function. 1666 * 1667 * @param validator validator used to validate function arguments and 1668 * resolve the function 1669 * @param args arguments to the function 1670 * @param newArgs returns the resolved arguments to the function 1671 * @param name function name 1672 * @param syntax syntax style used to invoke function 1673 * 1674 * @return resolved function definition 1675 */ 1676 public static FunDef resolveFunArgs( 1677 Validator validator, Exp[] args, Exp[] newArgs, String name, 1678 Syntax syntax) { 1679 1680 Query query = validator.getQuery(); 1681 Cube cube = null; 1682 if (query != null) { 1683 cube = query.getCube(); 1684 } 1685 for (int i = 0; i < args.length; i++) { 1686 newArgs[i] = validator.validate(args[i], false); 1687 } 1688 final FunTable funTable = validator.getFunTable(); 1689 FunDef funDef = funTable.getDef(newArgs, validator, name, syntax); 1690 1691 // If the first argument to a function is either: 1692 // 1) the measures dimension or 1693 // 2) a measures member where the function returns another member or 1694 // a set, 1695 // then these are functions that dynamically return one or more 1696 // members ofthe measures dimension. In that case, we cannot use 1697 // native cross joins because the functions need to be executed to 1698 // determine the resultant measures. 1699 // 1700 // As a result, we disallow functions like AllMembers applied on the 1701 // Measures dimension as well as functions like the range operator, 1702 // siblings, and lag, when the argument is a measure member. 1703 // However, we do allow functions like isEmpty, rank, and topPercent. 1704 // Also, the set function is ok since it just enumerates its 1705 // arguments. 1706 if (!(funDef instanceof SetFunDef) && query != null && 1707 query.nativeCrossJoinVirtualCube()) 1708 { 1709 int[] paramCategories = funDef.getParameterCategories(); 1710 if (paramCategories.length > 0 && 1711 ((paramCategories[0] == Category.Dimension && 1712 newArgs[0] instanceof DimensionExpr && 1713 ((DimensionExpr) newArgs[0]).getDimension(). 1714 getOrdinal(cube) == 0) || 1715 (paramCategories[0] == Category.Member && 1716 newArgs[0] instanceof MemberExpr && 1717 ((MemberExpr) newArgs[0]).getMember().getDimension(). 1718 getOrdinal(cube) == 0 && 1719 (funDef.getReturnCategory() == Category.Member || 1720 funDef.getReturnCategory() == Category.Set)))) 1721 { 1722 query.setVirtualCubeNonNativeCrossJoin(); 1723 } 1724 } 1725 1726 return funDef; 1727 } 1728 1729 static void appendTuple(StringBuilder buf, Member[] members) { 1730 buf.append("("); 1731 for (int j = 0; j < members.length; j++) { 1732 if (j > 0) { 1733 buf.append(", "); 1734 } 1735 Member member = members[j]; 1736 buf.append(member.getUniqueName()); 1737 } 1738 buf.append(")"); 1739 } 1740 1741 /** 1742 * Returns whether two tuples are equal. 1743 * 1744 * <p>The members are allowed to be in different positions. For example, 1745 * <blockquote> 1746 * <code>([Gender].[M], [Store].[USA]) IS ([Store].[USA], [Gender].[M])</code> 1747 * </blockquote> 1748 * returns <code>true</code>. 1749 */ 1750 static boolean equalTuple(Member[] members0, Member[] members1) { 1751 final int count = members0.length; 1752 if (count != members1.length) { 1753 return false; 1754 } 1755 outer: 1756 for (int i = 0; i < count; i++) { 1757 // First check the member at the corresponding ordinal. It is more 1758 // likely to be there. 1759 final Member member0 = members0[i]; 1760 if (member0.equals(members1[i])) { 1761 continue; 1762 } 1763 // Look for this member in other positions. 1764 // We can assume that the members in members0 are distinct (because 1765 // they belong to different dimensions), so this test is valid. 1766 for (int j = 0; j < count; j++) { 1767 if (i != j && member0.equals(members1[j])) { 1768 continue outer; 1769 } 1770 } 1771 // This member of members0 does not occur in any position of 1772 // members1. The tuples are not equal. 1773 return false; 1774 } 1775 return true; 1776 } 1777 1778 static FunDef createDummyFunDef( 1779 Resolver resolver, 1780 int returnCategory, 1781 Exp[] args) 1782 { 1783 final int[] argCategories = ExpBase.getTypes(args); 1784 return new FunDefBase(resolver, returnCategory, argCategories) { 1785 }; 1786 } 1787 1788 public static List<Member> getNonEmptyMemberChildren( 1789 Evaluator evaluator, 1790 Member member) 1791 { 1792 SchemaReader sr = evaluator.getSchemaReader(); 1793 if (evaluator.isNonEmpty()) { 1794 return sr.getMemberChildren(member, evaluator); 1795 } else { 1796 return sr.getMemberChildren(member); 1797 } 1798 } 1799 1800 /** 1801 * Returns members of a level which are not empty (according to the 1802 * criteria expressed by the evaluator). 1803 * 1804 * @param evaluator Evaluator, determines non-empty criteria 1805 * @param level Level 1806 * @param includeCalcMembers Whether to include calculated members 1807 */ 1808 static List<Member> getNonEmptyLevelMembers( 1809 final Evaluator evaluator, 1810 final Level level, 1811 final boolean includeCalcMembers) 1812 { 1813 SchemaReader sr = evaluator.getSchemaReader(); 1814 if (evaluator.isNonEmpty()) { 1815 List<Member> members = sr.getLevelMembers(level, evaluator); 1816 if (includeCalcMembers) { 1817 return addLevelCalculatedMembers(sr, level, members); 1818 } 1819 return members; 1820 } 1821 return sr.getLevelMembers(level, includeCalcMembers); 1822 } 1823 1824 static List<Member> levelMembers( 1825 final Level level, 1826 final Evaluator evaluator, 1827 final boolean includeCalcMembers) 1828 { 1829 List <Member> memberList = 1830 getNonEmptyLevelMembers(evaluator, level, includeCalcMembers); 1831 if (!includeCalcMembers) { 1832 memberList = removeCalculatedMembers(memberList); 1833 } 1834 hierarchize(memberList, false); 1835 return memberList; 1836 } 1837 1838 static List<Member> hierarchyMembers( 1839 Hierarchy hierarchy, 1840 Evaluator evaluator, 1841 final boolean includeCalcMembers) 1842 { 1843 List<Member> memberList; 1844 if (evaluator.isNonEmpty()) { 1845 // Allow the SQL generator to generate optimized SQL since we know 1846 // we're only interested in non-empty members of this level. 1847 memberList = new ArrayList<Member>(); 1848 for (Level level : hierarchy.getLevels()) { 1849 List<Member> members = 1850 getNonEmptyLevelMembers( 1851 evaluator, level, includeCalcMembers); 1852 memberList.addAll(members); 1853 } 1854 } else { 1855 memberList = addMembers( 1856 evaluator.getSchemaReader(), 1857 new ConcatenableList<Member>(), hierarchy); 1858 if (!includeCalcMembers && memberList != null) { 1859 memberList = removeCalculatedMembers(memberList); 1860 } 1861 } 1862 hierarchize(memberList, false); 1863 return memberList; 1864 } 1865 1866 static List<Member> dimensionMembers( 1867 Dimension dimension, 1868 Evaluator evaluator, 1869 final boolean includeCalcMembers) 1870 { 1871 Hierarchy hierarchy = dimension.getHierarchy(); 1872 return hierarchyMembers(hierarchy, evaluator, includeCalcMembers); 1873 } 1874 1875 // ~ Inner classes --------------------------------------------------------- 1876 1877 private static abstract class MemberComparator implements Comparator<Member> { 1878 private static final Logger LOGGER = 1879 Logger.getLogger(MemberComparator.class); 1880 Map<Member, Object> mapMemberToValue; 1881 private boolean desc; 1882 1883 MemberComparator(Map<Member, Object> mapMemberToValue, boolean desc) { 1884 this.mapMemberToValue = mapMemberToValue; 1885 this.desc = desc; 1886 } 1887 1888 Comparator<Member> wrap() { 1889 final MemberComparator comparator = this; 1890 if (LOGGER.isDebugEnabled()) { 1891 return new Comparator<Member>() { 1892 public int compare(Member m1, Member m2) { 1893 final int c = comparator.compare(m1, m2); 1894 LOGGER.debug( 1895 "compare " + 1896 m1.getUniqueName() + 1897 "(" + mapMemberToValue.get(m1) + "), " + 1898 m2.getUniqueName() + 1899 "(" + mapMemberToValue.get(m2) + ")" + 1900 " yields " + c); 1901 return c; 1902 } 1903 }; 1904 } else { 1905 return this; 1906 } 1907 } 1908 1909 protected final int compareByValue(Member m1, Member m2) { 1910 Object value1 = mapMemberToValue.get(m1), 1911 value2 = mapMemberToValue.get(m2); 1912 final int c = FunUtil.compareValues(value1, value2); 1913 return desc ? -c : c; 1914 } 1915 1916 protected final int compareHierarchicallyButSiblingsByValue( 1917 Member m1, Member m2) 1918 { 1919 if (FunUtil.equals(m1, m2)) { 1920 return 0; 1921 } 1922 while (true) { 1923 int depth1 = m1.getDepth(), 1924 depth2 = m2.getDepth(); 1925 if (depth1 < depth2) { 1926 m2 = m2.getParentMember(); 1927 if (Util.equals(m1, m2)) { 1928 return -1; 1929 } 1930 } else if (depth1 > depth2) { 1931 m1 = m1.getParentMember(); 1932 if (Util.equals(m1, m2)) { 1933 return 1; 1934 } 1935 } else { 1936 Member prev1 = m1, prev2 = m2; 1937 m1 = m1.getParentMember(); 1938 m2 = m2.getParentMember(); 1939 if (Util.equals(m1, m2)) { 1940 // including case where both parents are null 1941 int c = compareByValue(prev1, prev2); 1942 if (c != 0) { 1943 return c; 1944 } 1945 // prev1 and prev2 are siblings. 1946 // Order according to hierarchy, if the values do not differ. 1947 // Needed to have a consistent sortMembers if members with equal (null!) 1948 // values are compared. 1949 c = FunUtil.compareSiblingMembers(prev1, prev2); 1950 return c; 1951 } 1952 } 1953 } 1954 } 1955 } 1956 1957 private static class HierarchicalMemberComparator 1958 extends MemberComparator 1959 { 1960 HierarchicalMemberComparator( 1961 Map<Member, Object> mapMemberToValue, boolean desc) 1962 { 1963 super(mapMemberToValue, desc); 1964 } 1965 1966 public int compare(Member m1, Member m2) { 1967 return compareHierarchicallyButSiblingsByValue(m1, m2); 1968 } 1969 } 1970 1971 private static class BreakMemberComparator extends MemberComparator { 1972 BreakMemberComparator(Map<Member, Object> mapMemberToValue, boolean desc) { 1973 super(mapMemberToValue, desc); 1974 } 1975 1976 public final int compare(Member m1, Member m2) { 1977 return compareByValue(m1, m2); 1978 } 1979 } 1980 1981 /** 1982 * Compares tuples, which are represented as arrays of {@link Member}s. 1983 */ 1984 private static abstract class ArrayComparator 1985 implements Comparator<Member[]> 1986 { 1987 private static final Logger LOGGER = 1988 Logger.getLogger(ArrayComparator.class); 1989 1990 final int arity; 1991 1992 ArrayComparator(int arity) { 1993 this.arity = arity; 1994 } 1995 1996 Comparator<Member[]> wrap() { 1997 if (LOGGER.isDebugEnabled()) { 1998 return new LoggingTupleComparator(this, LOGGER); 1999 } else { 2000 return this; 2001 } 2002 } 2003 } 2004 2005 private static class LoggingTupleComparator 2006 implements Comparator<Member[]> 2007 { 2008 private final Comparator<Member[]> comparator; 2009 private final Logger logger; 2010 2011 LoggingTupleComparator(Comparator<Member[]> comparator, Logger logger) { 2012 this.comparator = comparator; 2013 this.logger = logger; 2014 } 2015 2016 private static String toString(Member[] a) { 2017 StringBuilder sb = new StringBuilder(); 2018 for (int i = 0; i < a.length; i++) { 2019 Member member = a[i]; 2020 if (i > 0) { 2021 sb.append(","); 2022 } 2023 sb.append(member.getUniqueName()); 2024 } 2025 return sb.toString(); 2026 } 2027 2028 public int compare(Member[] a1, Member[] a2) { 2029 int c = comparator.compare(a1, a2); 2030 logger.debug( 2031 "compare {" + toString(a1) + "}, {" + toString(a2) + 2032 "} yields " + c); 2033 return c; 2034 } 2035 } 2036 2037 /** 2038 * Extension to {@link ArrayComparator} which compares tuples by evaluating 2039 * an expression. 2040 */ 2041 private static abstract class ArrayExpComparator 2042 extends ArrayComparator { 2043 Evaluator evaluator; 2044 final Calc calc; 2045 2046 ArrayExpComparator(Evaluator evaluator, Calc calc, int arity) { 2047 super(arity); 2048 this.evaluator = evaluator; 2049 this.calc = calc; 2050 } 2051 } 2052 2053 private static class HierarchicalArrayComparator 2054 extends ArrayExpComparator { 2055 private final boolean desc; 2056 2057 HierarchicalArrayComparator( 2058 Evaluator evaluator, Calc calc, int arity, boolean desc) { 2059 super(evaluator, calc, arity); 2060 this.desc = desc; 2061 } 2062 2063 public int compare(Member[] a1, Member[] a2) { 2064 int c = 0; 2065 evaluator = evaluator.push(); 2066 for (int i = 0; i < arity; i++) { 2067 Member m1 = a1[i], 2068 m2 = a2[i]; 2069 c = compareHierarchicallyButSiblingsByValue(m1, m2); 2070 if (c != 0) { 2071 break; 2072 } 2073 // compareHierarchicallyButSiblingsByValue imposes a total order 2074 Util.assertTrue(m1.equals(m2)); 2075 evaluator.setContext(m1); 2076 } 2077 evaluator = evaluator.pop(); 2078 return c; 2079 } 2080 2081 protected int compareHierarchicallyButSiblingsByValue( 2082 Member m1, Member m2) { 2083 if (FunUtil.equals(m1, m2)) { 2084 return 0; 2085 } 2086 while (true) { 2087 int depth1 = m1.getDepth(), 2088 depth2 = m2.getDepth(); 2089 if (depth1 < depth2) { 2090 m2 = m2.getParentMember(); 2091 if (FunUtil.equals(m1, m2)) { 2092 return -1; 2093 } 2094 } else if (depth1 > depth2) { 2095 m1 = m1.getParentMember(); 2096 if (FunUtil.equals(m1, m2)) { 2097 return 1; 2098 } 2099 } else { 2100 Member prev1 = m1, prev2 = m2; 2101 m1 = m1.getParentMember(); 2102 m2 = m2.getParentMember(); 2103 if (FunUtil.equals(m1, m2)) { 2104 // including case where both parents are null 2105 int c = compareByValue(prev1, prev2); 2106 if (c == 0) { 2107 c = FunUtil.compareSiblingMembers(prev1, prev2); 2108 } 2109 return desc ? -c : c; 2110 } 2111 } 2112 } 2113 } 2114 2115 private int compareByValue(Member m1, Member m2) { 2116 int c; 2117 Member old = evaluator.setContext(m1); 2118 Object v1 = calc.evaluate(evaluator); 2119 evaluator.setContext(m2); 2120 Object v2 = calc.evaluate(evaluator); 2121 // important to restore the evaluator state -- and this is faster 2122 // than calling evaluator.push() 2123 evaluator.setContext(old); 2124 c = FunUtil.compareValues(v1, v2); 2125 return c; 2126 } 2127 } 2128 2129 private static class BreakArrayComparator extends ArrayExpComparator { 2130 BreakArrayComparator(Evaluator evaluator, Calc calc, int arity) { 2131 super(evaluator, calc, arity); 2132 } 2133 2134 public int compare(Member[] a1, Member[] a2) { 2135 evaluator.setContext(a1); 2136 Object v1 = calc.evaluate(evaluator); 2137 evaluator.setContext(a2); 2138 Object v2 = calc.evaluate(evaluator); 2139 return FunUtil.compareValues(v1, v2); 2140 } 2141 } 2142 2143 /** 2144 * Compares arrays of {@link Member}s so as to convert them into hierarchical 2145 * order. Applies lexicographic order to the array. 2146 */ 2147 private static class HierarchizeArrayComparator extends ArrayComparator { 2148 private final boolean post; 2149 2150 HierarchizeArrayComparator(int arity, boolean post) { 2151 super(arity); 2152 this.post = post; 2153 } 2154 2155 public int compare(Member[] a1, Member[] a2) { 2156 for (int i = 0; i < arity; i++) { 2157 Member m1 = a1[i], 2158 m2 = a2[i]; 2159 int c = FunUtil.compareHierarchically(m1, m2, post); 2160 if (c != 0) { 2161 return c; 2162 } 2163 } 2164 return 0; 2165 } 2166 } 2167 2168 /** 2169 * Compares {@link Member}s so as to arrage them in prefix or postfix 2170 * hierarchical order. 2171 */ 2172 private static class HierarchizeComparator implements Comparator<Member> { 2173 private final boolean post; 2174 2175 HierarchizeComparator(boolean post) { 2176 this.post = post; 2177 } 2178 public int compare(Member m1, Member m2) { 2179 return FunUtil.compareHierarchically(m1, m2, post); 2180 } 2181 } 2182 2183 /** 2184 * Reverses the order of a {@link Comparator}. 2185 */ 2186 private static class ReverseComparator<T> implements Comparator<T> { 2187 Comparator<T> comparator; 2188 ReverseComparator(Comparator<T> comparator) { 2189 this.comparator = comparator; 2190 } 2191 2192 public int compare(T o1, T o2) { 2193 int c = comparator.compare(o1, o2); 2194 return - c; 2195 } 2196 } 2197 2198 static class SetWrapper { 2199 List v = new ArrayList(); 2200 public int errorCount = 0, nullCount = 0; 2201 2202 //private double avg = Double.NaN; 2203 //todo: parameterize inclusion of nulls 2204 //by making this a method of the SetWrapper, we can cache the result 2205 //this allows its reuse in Correlation 2206 // public double getAverage() { 2207 // if (avg == Double.NaN) { 2208 // double sum = 0.0; 2209 // for (int i = 0; i < resolvers.size(); i++) { 2210 // sum += ((Double) resolvers.elementAt(i)).doubleValue(); 2211 // } 2212 // //todo: should look at context and optionally include nulls 2213 // avg = sum / (double) resolvers.size(); 2214 // } 2215 // return avg; 2216 // } 2217 } 2218 2219 /** 2220 * Compares cell values, so that larger values compare first. 2221 * 2222 * <p>Nulls compare last, exceptions (including the 2223 * object which indicates the the cell is not in the cache yet) next, 2224 * then numbers and strings are compared by value. 2225 */ 2226 private static class DescendingValueComparator implements Comparator { 2227 /** 2228 * The singleton. 2229 */ 2230 static final DescendingValueComparator instance = 2231 new DescendingValueComparator(); 2232 2233 public int compare(Object o1, Object o2) { 2234 return - compareValues(o1, o2); 2235 } 2236 } 2237 2238 /** 2239 * Null member of unknown hierarchy. 2240 */ 2241 private static class NullMember implements Member { 2242 public Member getParentMember() { 2243 throw new UnsupportedOperationException(); 2244 } 2245 2246 public Level getLevel() { 2247 throw new UnsupportedOperationException(); 2248 } 2249 2250 public Hierarchy getHierarchy() { 2251 throw new UnsupportedOperationException(); 2252 } 2253 2254 public String getParentUniqueName() { 2255 throw new UnsupportedOperationException(); 2256 } 2257 2258 public MemberType getMemberType() { 2259 throw new UnsupportedOperationException(); 2260 } 2261 2262 public void setName(String name) { 2263 throw new UnsupportedOperationException(); 2264 } 2265 2266 public boolean isAll() { 2267 return false; 2268 } 2269 2270 public boolean isMeasure() { 2271 throw new UnsupportedOperationException(); 2272 } 2273 2274 public boolean isNull() { 2275 return true; 2276 } 2277 2278 public boolean isChildOrEqualTo(Member member) { 2279 throw new UnsupportedOperationException(); 2280 } 2281 2282 public boolean isCalculated() { 2283 throw new UnsupportedOperationException(); 2284 } 2285 2286 public int getSolveOrder() { 2287 throw new UnsupportedOperationException(); 2288 } 2289 2290 public Exp getExpression() { 2291 throw new UnsupportedOperationException(); 2292 } 2293 2294 public List<Member> getAncestorMembers() { 2295 throw new UnsupportedOperationException(); 2296 } 2297 2298 public boolean isCalculatedInQuery() { 2299 throw new UnsupportedOperationException(); 2300 } 2301 2302 public Object getPropertyValue(String propertyName) { 2303 throw new UnsupportedOperationException(); 2304 } 2305 2306 public Object getPropertyValue(String propertyName, boolean matchCase) { 2307 throw new UnsupportedOperationException(); 2308 } 2309 2310 public String getPropertyFormattedValue(String propertyName) { 2311 throw new UnsupportedOperationException(); 2312 } 2313 2314 public void setProperty(String name, Object value) { 2315 throw new UnsupportedOperationException(); 2316 } 2317 2318 public Property[] getProperties() { 2319 throw new UnsupportedOperationException(); 2320 } 2321 2322 public int getOrdinal() { 2323 throw new UnsupportedOperationException(); 2324 } 2325 2326 public Comparable getOrderKey() { 2327 throw new UnsupportedOperationException(); 2328 } 2329 2330 public boolean isHidden() { 2331 throw new UnsupportedOperationException(); 2332 } 2333 2334 public int getDepth() { 2335 throw new UnsupportedOperationException(); 2336 } 2337 2338 public Member getDataMember() { 2339 throw new UnsupportedOperationException(); 2340 } 2341 2342 public String getUniqueName() { 2343 throw new UnsupportedOperationException(); 2344 } 2345 2346 public String getName() { 2347 throw new UnsupportedOperationException(); 2348 } 2349 2350 public String getDescription() { 2351 throw new UnsupportedOperationException(); 2352 } 2353 2354 public OlapElement lookupChild(SchemaReader schemaReader,Id.Segment s) { 2355 throw new UnsupportedOperationException(); 2356 } 2357 2358 public OlapElement lookupChild( 2359 SchemaReader schemaReader, Id.Segment s, MatchType matchType) { 2360 throw new UnsupportedOperationException(); 2361 } 2362 2363 public String getQualifiedName() { 2364 throw new UnsupportedOperationException(); 2365 } 2366 2367 public String getCaption() { 2368 throw new UnsupportedOperationException(); 2369 } 2370 2371 public Dimension getDimension() { 2372 throw new UnsupportedOperationException(); 2373 } 2374 2375 public int compareTo(Object o) { 2376 throw new UnsupportedOperationException(); 2377 } 2378 2379 public boolean equals(Object obj) { 2380 throw new UnsupportedOperationException(); 2381 } 2382 2383 public int hashCode() { 2384 throw new UnsupportedOperationException(); 2385 } 2386 } 2387 } 2388 2389 // End FunUtil.java