001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2005-2005 Julian Hyde 008// Copyright (C) 2005-2013 Pentaho 009// All Rights Reserved. 010*/ 011package mondrian.olap.type; 012 013import mondrian.mdx.UnresolvedFunCall; 014import mondrian.olap.*; 015import mondrian.olap.fun.Resolver; 016 017import java.util.*; 018 019/** 020 * Utility methods relating to types. 021 * 022 * @author jhyde 023 * @since Feb 17, 2005 024 */ 025public class TypeUtil { 026 /** 027 * Given a set type, returns the element type. Or its element type, if it 028 * is a set type. And so on. 029 * 030 * @param type Type 031 * @return underlying element type which is not a set type 032 */ 033 public static Type stripSetType(Type type) { 034 while (type instanceof SetType) { 035 type = ((SetType) type).getElementType(); 036 } 037 return type; 038 } 039 040 /** 041 * Converts a type to a member or tuple type. 042 * If it cannot, returns null. 043 * 044 * @param type Type 045 * @return member or tuple type 046 */ 047 public static Type toMemberOrTupleType(Type type) { 048 type = stripSetType(type); 049 if (type instanceof TupleType) { 050 return type; 051 } else { 052 return toMemberType(type); 053 } 054 } 055 056 /** 057 * Converts a type to a member type. 058 * If it is a set, strips the set. 059 * If it is a member type, returns the type unchanged. 060 * If it is a dimension, hierarchy or level type, converts it to 061 * a member type. 062 * If it is a tuple, number, string, or boolean, returns null. 063 * 064 * @param type Type 065 * @return type as a member type 066 */ 067 public static MemberType toMemberType(Type type) { 068 type = stripSetType(type); 069 if (type instanceof MemberType) { 070 return (MemberType) type; 071 } else if (type instanceof DimensionType 072 || type instanceof HierarchyType 073 || type instanceof LevelType) 074 { 075 return MemberType.forType(type); 076 } else { 077 return null; 078 } 079 } 080 081 /** 082 * Returns whether this type is union-compatible with another. 083 * In general, to be union-compatible, types must have the same 084 * dimensionality. 085 * 086 * @param type1 First type 087 * @param type2 Second type 088 * @return whether types are union-compatible 089 */ 090 public static boolean isUnionCompatible(Type type1, Type type2) { 091 if (type1 instanceof TupleType) { 092 TupleType tupleType1 = (TupleType) type1; 093 if (type2 instanceof TupleType) { 094 TupleType tupleType2 = (TupleType) type2; 095 if (tupleType1.elementTypes.length 096 == tupleType2.elementTypes.length) 097 { 098 for (int i = 0; i < tupleType1.elementTypes.length; i++) { 099 if (!isUnionCompatible( 100 tupleType1.elementTypes[i], 101 tupleType2.elementTypes[i])) 102 { 103 return false; 104 } 105 } 106 return true; 107 } 108 } 109 return false; 110 } else { 111 final MemberType memberType1 = toMemberType(type1); 112 if (memberType1 == null) { 113 return false; 114 } 115 final MemberType memberType2 = toMemberType(type2); 116 if (memberType2 == null) { 117 return false; 118 } 119 final Hierarchy hierarchy1 = memberType1.getHierarchy(); 120 final Hierarchy hierarchy2 = memberType2.getHierarchy(); 121 return equal(hierarchy1, hierarchy2); 122 } 123 } 124 125 /** 126 * Returns whether two hierarchies are equal. 127 * 128 * @param hierarchy1 First hierarchy 129 * @param hierarchy2 Second hierarchy 130 * @return Whether hierarchies are equal 131 */ 132 private static boolean equal( 133 final Hierarchy hierarchy1, 134 final Hierarchy hierarchy2) 135 { 136 return hierarchy1 == null 137 || hierarchy2 == null 138 || hierarchy2.getUniqueName().equals( 139 hierarchy1.getUniqueName()); 140 } 141 142 /** 143 * Returns whether a value of a given type can be evaluated to a scalar 144 * value. 145 * 146 * <p>The rules are as follows:<ul> 147 * <li>Clearly boolean, numeric and string expressions can be evaluated. 148 * <li>Member and tuple expressions can be interpreted as a scalar value. 149 * The expression is evaluated to establish the context where a measure 150 * can be evaluated. 151 * <li>Hierarchy and dimension expressions are implicitly 152 * converted into the current member, and evaluated as above. 153 * <li>Level expressions cannot be evaluated 154 * <li>Cube and Set (even sets with a single member) cannot be evaluated. 155 * </ul> 156 * 157 * @param type Type 158 * @return Whether an expression of this type can be evaluated to yield a 159 * scalar value. 160 */ 161 public static boolean canEvaluate(Type type) { 162 return ! (type instanceof SetType 163 || type instanceof CubeType 164 || type instanceof LevelType); 165 } 166 167 /** 168 * Returns whether a type is a set type. 169 * 170 * @param type Type 171 * @return Whether a value of this type can be evaluated to yield a set. 172 */ 173 public static boolean isSet(Type type) { 174 return type instanceof SetType; 175 } 176 177 public static boolean couldBeMember(Type type) { 178 return type instanceof MemberType 179 || type instanceof HierarchyType 180 || type instanceof DimensionType; 181 } 182 183 /** 184 * Converts a {@link Type} value to a {@link Category} ordinal. 185 * 186 * @param type Type 187 * @return category ordinal 188 */ 189 public static int typeToCategory(Type type) { 190 if (type instanceof NullType) { 191 return Category.Null; 192 } else if (type instanceof EmptyType) { 193 return Category.Empty; 194 } else if (type instanceof DateTimeType) { 195 return Category.DateTime; 196 } else if (type instanceof DecimalType 197 && ((DecimalType)type).getScale() == 0) 198 { 199 return Category.Integer; 200 } else if (type instanceof NumericType) { 201 return Category.Numeric; 202 } else if (type instanceof BooleanType) { 203 return Category.Logical; 204 } else if (type instanceof DimensionType) { 205 return Category.Dimension; 206 } else if (type instanceof HierarchyType) { 207 return Category.Hierarchy; 208 } else if (type instanceof MemberType) { 209 return Category.Member; 210 } else if (type instanceof LevelType) { 211 return Category.Level; 212 } else if (type instanceof SymbolType) { 213 return Category.Symbol; 214 } else if (type instanceof StringType) { 215 return Category.String; 216 } else if (type instanceof ScalarType) { 217 return Category.Value; 218 } else if (type instanceof SetType) { 219 return Category.Set; 220 } else if (type instanceof TupleType) { 221 return Category.Tuple; 222 } else { 223 throw Util.newInternal("Unknown type " + type); 224 } 225 } 226 227 /** 228 * Returns a type sufficiently broad to hold any value of several types, 229 * but as narrow as possible. If there is no such type, returns null. 230 * 231 * <p>The result is equivalent to calling 232 * {@link Type#computeCommonType(Type, int[])} pairwise. 233 * 234 * @param allowConversions Whether to allow implicit conversions 235 * @param types Array of types 236 * @return Most general type which encompases all types 237 */ 238 public static Type computeCommonType( 239 boolean allowConversions, 240 Type... types) 241 { 242 if (types.length == 0) { 243 return null; 244 } 245 Type type = types[0]; 246 int[] conversionCount = allowConversions ? new int[] {0} : null; 247 for (int i = 1; i < types.length; ++i) { 248 if (type == null) { 249 return null; 250 } 251 type = type.computeCommonType(types[i], conversionCount); 252 } 253 return type; 254 } 255 256 /** 257 * Returns whether we can convert an argument of a given category to a 258 * given parameter category. 259 * 260 * @param ordinal argument ordinal 261 * @param fromType actual argument type 262 * @param to formal parameter category 263 * @param conversions list of implicit conversions required (out) 264 * @return whether can convert from 'from' to 'to' 265 */ 266 public static boolean canConvert( 267 int ordinal, 268 Type fromType, 269 int to, 270 List<Resolver.Conversion> conversions) 271 { 272 final int from = typeToCategory(fromType); 273 if (from == to) { 274 return true; 275 } 276 RuntimeException e = null; 277 switch (from) { 278 case Category.Array: 279 return false; 280 case Category.Dimension: 281 // We can go from Dimension to Hierarchy if the dimension has a 282 // default hierarchy. From there, we can go to Member or Tuple. 283 // Even if the dimension does not have a default hierarchy, we claim 284 // now that we can do the conversion, to prevent other overloads 285 // from being chosen; we will hit an error either at compile time or 286 // at run time. 287 switch (to) { 288 case Category.Member: 289 case Category.Tuple: 290 case Category.Hierarchy: 291 // It is more difficult to convert dimension->hierarchy than 292 // hierarchy->dimension 293 conversions.add(new ConversionImpl(from, to, ordinal, 2, e)); 294 return true; 295 case Category.Level: 296 // It is more difficult to convert dimension->level than 297 // dimension->member or dimension->hierarchy->member. 298 conversions.add(new ConversionImpl(from, to, ordinal, 3, null)); 299 return true; 300 default: 301 return false; 302 } 303 case Category.Hierarchy: 304 // Seems funny that you can 'downcast' from a hierarchy, doesn't 305 // it? But we add an implicit 'CurrentMember', for example, 306 // '[Product].PrevMember' actually means 307 // '[Product].CurrentMember.PrevMember'. 308 switch (to) { 309 case Category.Dimension: 310 case Category.Member: 311 case Category.Tuple: 312 conversions.add(new ConversionImpl(from, to, ordinal, 1, null)); 313 return true; 314 default: 315 return false; 316 } 317 case Category.Level: 318 switch (to) { 319 case Category.Dimension: 320 // It's more difficult to convert to a dimension than a 321 // hierarchy. For example, we want '[Store City].CurrentMember' 322 // to resolve to <Hierarchy>.CurrentMember rather than 323 // <Dimension>.CurrentMember. 324 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 325 return true; 326 case Category.Hierarchy: 327 conversions.add(new ConversionImpl(from, to, ordinal, 1, null)); 328 return true; 329 default: 330 return false; 331 } 332 case Category.Logical: 333 switch (to) { 334 case Category.Value: 335 return true; 336 default: 337 return false; 338 } 339 case Category.Member: 340 switch (to) { 341 case Category.Dimension: 342 case Category.Hierarchy: 343 case Category.Level: 344 case Category.Tuple: 345 conversions.add(new ConversionImpl(from, to, ordinal, 1, null)); 346 return true; 347 case Category.Set: 348 // It is more expensive to convert from Member->Set (cost=2) 349 // than Member->Tuple (cost=1). In particular, m.Tuple(n) should 350 // resolve to <Tuple>.Item(<Numeric>) rather than 351 // <Set>.Item(<Numeric>). 352 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 353 return true; 354 case Category.Numeric: 355 // It is more expensive to convert from Member->Scalar (cost=3) 356 // than Member->Set (cost=2). In particular, we want 'member * 357 // set' to resolve to the crossjoin operator, '{m} * set'. 358 conversions.add(new ConversionImpl(from, to, ordinal, 3, null)); 359 return true; 360 case Category.Value: 361 case Category.String: 362 // We assume that measures are numeric, so a cast to a string or 363 // general value expression is more expensive (cost=4) than a 364 // conversion to a numeric expression (cost=3). 365 conversions.add(new ConversionImpl(from, to, ordinal, 4, null)); 366 return true; 367 default: 368 return false; 369 } 370 case Category.Numeric | Category.Constant: 371 switch (to) { 372 case Category.Value: 373 case Category.Numeric: 374 return true; 375 default: 376 return false; 377 } 378 case Category.Numeric: 379 switch (to) { 380 case Category.Logical: 381 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 382 return true; 383 case Category.Value: 384 case Category.Integer: 385 case (Category.Integer | Category.Constant): 386 case (Category.Numeric | Category.Constant): 387 return true; 388 default: 389 return false; 390 } 391 case Category.Integer: 392 switch (to) { 393 case Category.Value: 394 case (Category.Integer | Category.Constant): 395 case Category.Numeric: 396 case (Category.Numeric | Category.Constant): 397 return true; 398 default: 399 return false; 400 } 401 case Category.Set: 402 return false; 403 case Category.String | Category.Constant: 404 switch (to) { 405 case Category.Value: 406 case Category.String: 407 return true; 408 default: 409 return false; 410 } 411 case Category.String: 412 switch (to) { 413 case Category.Value: 414 case (Category.String | Category.Constant): 415 return true; 416 default: 417 return false; 418 } 419 case Category.DateTime | Category.Constant: 420 switch (to) { 421 case Category.Value: 422 case Category.DateTime: 423 return true; 424 default: 425 return false; 426 } 427 case Category.DateTime: 428 switch (to) { 429 case Category.Value: 430 case (Category.DateTime | Category.Constant): 431 return true; 432 default: 433 return false; 434 } 435 case Category.Tuple: 436 switch (to) { 437 case Category.Set: 438 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 439 return true; 440 case Category.Numeric: 441 // It is more expensive to convert from Tuple->Scalar (cost=4) 442 // than Tuple->Set (cost=3). In particular, we want 'tuple * 443 // set' to resolve to the crossjoin operator, '{tuple} * set'. 444 // This is analogous to Member->Numeric conversion. 445 conversions.add(new ConversionImpl(from, to, ordinal, 3, null)); 446 return true; 447 case Category.String: 448 case Category.Value: 449 // We assume that measures are numeric, so a cast to a string or 450 // general value expression is more expensive (cost=4) than a 451 // conversion to a numeric expression (cost=3). 452 conversions.add(new ConversionImpl(from, to, ordinal, 4, null)); 453 return true; 454 default: 455 return false; 456 } 457 case Category.Value: 458 // We can implicitly cast from value to a more specific scalar type, 459 // but the cost is significant. 460 switch (to) { 461 case Category.String: 462 case Category.Numeric: 463 case Category.Logical: 464 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 465 return true; 466 default: 467 return false; 468 } 469 case Category.Symbol: 470 return false; 471 case Category.Null: 472 // now null supports members as well as scalars; but scalar is 473 // preferred 474 if (Category.isScalar(to)) { 475 return true; 476 } else if (to == Category.Member) { 477 conversions.add(new ConversionImpl(from, to, ordinal, 2, null)); 478 return true; 479 } else { 480 return false; 481 } 482 case Category.Empty: 483 return false; 484 default: 485 throw Util.newInternal( 486 "unknown category " + from + " for type " + fromType); 487 } 488 } 489 490 static <T> T neq(T t1, T t2) { 491 return t1 == null ? t2 492 : t2 == null ? t1 493 : t1.equals(t2) ? t1 494 : null; 495 } 496 497 /** 498 * Returns the hierarchies in a set, member, or tuple type. 499 * 500 * @param type Type 501 * @return List of hierarchies 502 */ 503 public static List<Hierarchy> getHierarchies(Type type) { 504 if (type instanceof SetType) { 505 type = ((SetType) type).getElementType(); 506 } 507 if (type instanceof TupleType) { 508 final TupleType tupleType = (TupleType) type; 509 List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>(); 510 for (Type elementType : tupleType.elementTypes) { 511 hierarchyList.add(elementType.getHierarchy()); 512 } 513 return hierarchyList; 514 } else { 515 return Collections.singletonList(type.getHierarchy()); 516 } 517 } 518 519 /** 520 * Implementation of {@link mondrian.olap.fun.Resolver.Conversion}. 521 */ 522 private static class ConversionImpl implements Resolver.Conversion { 523 final int from; 524 final int to; 525 /** 526 * Which argument. Arguments are 0-based, and in particular the 'this' 527 * of a call of member or method call syntax is argument 0. Argument -1 528 * is the return. 529 */ 530 final int ordinal; 531 532 /** 533 * Score of the conversion. A higher value is more onerous and therefore 534 * a call using such a conversion is less likly to be chosen. 535 */ 536 final int cost; 537 538 final RuntimeException e; 539 540 /** 541 * Creates a conversion. 542 * 543 * @param from From type 544 * @param to To type 545 * @param ordinal Ordinal of argument 546 * @param cost Cost of conversion 547 * @param e Exception 548 */ 549 public ConversionImpl( 550 int from, 551 int to, 552 int ordinal, 553 int cost, 554 RuntimeException e) 555 { 556 this.from = from; 557 this.to = to; 558 this.ordinal = ordinal; 559 this.cost = cost; 560 this.e = e; 561 } 562 563 public int getCost() { 564 return cost; 565 } 566 567 public void checkValid() { 568 if (e != null) { 569 throw e; 570 } 571 } 572 573 public void apply(Validator validator, List<Exp> args) { 574 final Exp arg = args.get(ordinal); 575 switch (from) { 576 case Category.Member: 577 case Category.Tuple: 578 switch (to) { 579 case Category.Set: 580 final Exp newArg = 581 validator.validate( 582 new UnresolvedFunCall( 583 "{}", Syntax.Braces, new Exp[]{arg}), false); 584 args.set(ordinal, newArg); 585 break; 586 default: 587 // do nothing 588 } 589 default: 590 // do nothing 591 } 592 } 593 594 // for debug 595 public String toString() { 596 return "Conversion(from=" + Category.instance().getName(from) 597 + ", to=" + Category.instance().getName(to) 598 + ", ordinal=" 599 + ordinal + ", cost=" 600 + cost + ", e=" + e + ")"; 601 } 602 } 603} 604 605// End TypeUtil.java