001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/type/TypeUtil.java#10 $ 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) 2005-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.olap.type; 011 012 import mondrian.olap.Hierarchy; 013 import mondrian.olap.Util; 014 import mondrian.olap.Category; 015 016 /** 017 * Utility methods relating to types. 018 * 019 * @author jhyde 020 * @since Feb 17, 2005 021 * @version $Id: //open/mondrian/src/main/mondrian/olap/type/TypeUtil.java#10 $ 022 */ 023 public class TypeUtil { 024 public static Hierarchy typeToHierarchy(Type type) { 025 if (type instanceof MemberType 026 || type instanceof LevelType 027 || type instanceof HierarchyType 028 || type instanceof DimensionType) { 029 return type.getHierarchy(); 030 } else { 031 throw Util.newInternal("not an mdx object"); 032 } 033 } 034 035 /** 036 * Given a set type, returns the element type. Or its element type, if it 037 * is a set type. And so on. 038 * 039 * @param type Type 040 * @return underlying element type which is not a set type 041 */ 042 public static Type stripSetType(Type type) { 043 while (type instanceof SetType) { 044 type = ((SetType) type).getElementType(); 045 } 046 return type; 047 } 048 049 /** 050 * Converts a type to a member or tuple type. 051 * If it cannot, returns null. 052 * 053 * @param type Type 054 * @return member or tuple type 055 */ 056 public static Type toMemberOrTupleType(Type type) { 057 type = stripSetType(type); 058 if (type instanceof TupleType) { 059 return type; 060 } else { 061 return toMemberType(type); 062 } 063 } 064 065 /** 066 * Converts a type to a member type. 067 * If it is a set, strips the set. 068 * If it is a member type, returns the type unchanged. 069 * If it is a dimension, hierarchy or level type, converts it to 070 * a member type. 071 * If it is a tuple, number, string, or boolean, returns null. 072 * 073 * @param type Type 074 * @return type as a member type 075 */ 076 public static MemberType toMemberType(Type type) { 077 type = stripSetType(type); 078 if (type instanceof MemberType) { 079 return (MemberType) type; 080 } else if (type instanceof DimensionType || 081 type instanceof HierarchyType || 082 type instanceof LevelType) { 083 return MemberType.forType(type); 084 } else { 085 return null; 086 } 087 } 088 089 /** 090 * Returns whether this type is union-compatible with another. 091 * In general, to be union-compatible, types must have the same 092 * dimensionality. 093 * 094 * @param type1 First type 095 * @param type2 Second type 096 * @return whether types are union-compatible 097 */ 098 public static boolean isUnionCompatible(Type type1, Type type2) { 099 if (type1 instanceof TupleType) { 100 TupleType tupleType1 = (TupleType) type1; 101 if (type2 instanceof TupleType) { 102 TupleType tupleType2 = (TupleType) type2; 103 if (tupleType1.elementTypes.length == 104 tupleType2.elementTypes.length) { 105 for (int i = 0; i < tupleType1.elementTypes.length; i++) { 106 if (!isUnionCompatible( 107 tupleType1.elementTypes[i], 108 tupleType2.elementTypes[i])) { 109 return false; 110 } 111 } 112 return true; 113 } 114 } 115 return false; 116 } else { 117 final MemberType memberType1 = toMemberType(type1); 118 if (memberType1 == null) { 119 return false; 120 } 121 final MemberType memberType2 = toMemberType(type2); 122 if (memberType2 == null) { 123 return false; 124 } 125 final Hierarchy hierarchy1 = memberType1.getHierarchy(); 126 final Hierarchy hierarchy2 = memberType2.getHierarchy(); 127 return equal(hierarchy1, hierarchy2); 128 } 129 } 130 131 private static boolean equal( 132 final Hierarchy hierarchy1, final Hierarchy hierarchy2) { 133 //noinspection RedundantIfStatement 134 if (hierarchy1 == null || 135 hierarchy2 == null || 136 hierarchy2.getUniqueName().equals( 137 hierarchy1.getUniqueName())) { 138 // They are compatible. 139 return true; 140 } else { 141 return false; 142 } 143 } 144 145 /** 146 * Returns whether a value of a given type can be evaluated to a scalar 147 * value. 148 * 149 * <p>The rules are as follows:<ul> 150 * <li>Clearly boolean, numeric and string expressions can be evaluated. 151 * <li>Member and tuple expressions can be interpreted as a scalar value. 152 * The expression is evaluated to establish the context where a measure 153 * can be evaluated. 154 * <li>Hierarchy and dimension expressions are implicitly 155 * converted into the current member, and evaluated as above. 156 * <li>Level expressions cannot be evaluated 157 * <li>Cube and Set (even sets with a single member) cannot be evaluated. 158 * </ul> 159 * 160 * @param type Type 161 * @return Whether an expression of this type can be evaluated to yield a 162 * scalar value. 163 */ 164 public static boolean canEvaluate(Type type) { 165 return ! (type instanceof SetType || 166 type instanceof CubeType || 167 type instanceof LevelType); 168 } 169 170 /** 171 * Returns whether a type is a set type. 172 * 173 * @param type Type 174 * @return Whether a value of this type can be evaluated to yield a set. 175 */ 176 public static boolean isSet(Type type) { 177 return type instanceof SetType; 178 } 179 180 public static boolean couldBeMember(Type type) { 181 return type instanceof MemberType || 182 type instanceof HierarchyType || 183 type instanceof DimensionType; 184 } 185 186 /** 187 * Converts a {@link Type} value to a {@link Category} ordinal. 188 * 189 * @param type Type 190 * @return category ordinal 191 */ 192 public static int typeToCategory(Type type) { 193 if (type instanceof NullType) { 194 return Category.Null; 195 } else if (type instanceof DateTimeType) { 196 return Category.DateTime; 197 } else if (type instanceof NumericType) { 198 return Category.Numeric; 199 } else if (type instanceof BooleanType) { 200 return Category.Logical; 201 } else if (type instanceof DimensionType) { 202 return Category.Dimension; 203 } else if (type instanceof HierarchyType) { 204 return Category.Hierarchy; 205 } else if (type instanceof MemberType) { 206 return Category.Member; 207 } else if (type instanceof LevelType) { 208 return Category.Level; 209 } else if (type instanceof SymbolType) { 210 return Category.Symbol; 211 } else if (type instanceof StringType) { 212 return Category.String; 213 } else if (type instanceof ScalarType) { 214 return Category.Value; 215 } else if (type instanceof SetType) { 216 return Category.Set; 217 } else if (type instanceof TupleType) { 218 return Category.Tuple; 219 } else { 220 throw Util.newInternal("Unknown type " + type); 221 } 222 } 223 224 /** 225 * Returns a type sufficiently broad to hold any value of several types, 226 * but as narrow as possible. If there is no such type, returns null. 227 * 228 * <p>The result is equivalent to calling 229 * {@link Type#computeCommonType(Type, int[])} pairwise. 230 * 231 * @param allowConversions Whether to allow implicit conversions 232 * @param types Array of types 233 * @return Most general type which encompases all types 234 */ 235 public static Type computeCommonType( 236 boolean allowConversions, 237 Type... types) 238 { 239 if (types.length == 0) { 240 return null; 241 } 242 Type type = types[0]; 243 int[] conversionCount = allowConversions ? new int[] {0} : null; 244 for (int i = 1; i < types.length; ++i) { 245 if (type == null) { 246 return null; 247 } 248 type = type.computeCommonType(types[i], conversionCount); 249 } 250 return type; 251 } 252 253 /** 254 * Returns whether we can convert an argument of a given category to a 255 * given parameter category. 256 * 257 * @param from actual argument category 258 * @param to formal parameter category 259 * @param conversionCount in/out count of number of conversions performed; 260 * is incremented if the conversion is non-trivial (for 261 * example, converting a member to a level). 262 * @return whether can convert from 'from' to 'to' 263 */ 264 public static boolean canConvert( 265 int from, 266 int to, 267 int[] conversionCount) 268 { 269 if (from == to) { 270 return true; 271 } 272 switch (from) { 273 case Category.Array: 274 return false; 275 case Category.Dimension: 276 // Seems funny that you can 'downcast' from a dimension, doesn't 277 // it? But we add an implicit 'CurrentMember', for example, 278 // '[Time].PrevMember' actually means 279 // '[Time].CurrentMember.PrevMember'. 280 switch (to) { 281 case Category.Member: 282 case Category.Tuple: 283 // It's easier to convert dimension to member than dimension 284 // to hierarchy or level. 285 conversionCount[0]++; 286 return true; 287 case Category.Hierarchy: 288 case Category.Level: 289 conversionCount[0] += 2; 290 return true; 291 default: 292 return false; 293 } 294 case Category.Hierarchy: 295 switch (to) { 296 case Category.Dimension: 297 case Category.Member: 298 case Category.Tuple: 299 conversionCount[0]++; 300 return true; 301 default: 302 return false; 303 } 304 case Category.Level: 305 switch (to) { 306 case Category.Dimension: 307 // It's more difficult to convert to a dimension than a 308 // hierarchy. For example, we want '[Store City].CurrentMember' 309 // to resolve to <Hierarchy>.CurrentMember rather than 310 // <Dimension>.CurrentMember. 311 conversionCount[0] += 2; 312 return true; 313 case Category.Hierarchy: 314 conversionCount[0]++; 315 return true; 316 default: 317 return false; 318 } 319 case Category.Logical: 320 switch (to) { 321 case Category.Value: 322 return true; 323 default: 324 return false; 325 } 326 case Category.Member: 327 switch (to) { 328 case Category.Dimension: 329 case Category.Hierarchy: 330 case Category.Level: 331 case Category.Tuple: 332 conversionCount[0]++; 333 return true; 334 case Category.Numeric: 335 // We assume that members are numeric, so a cast to a numeric 336 // expression is less expensive than a conversion to a string 337 // expression. 338 conversionCount[0]++; 339 return true; 340 case Category.Value: 341 case Category.String: 342 conversionCount[0] += 2; 343 return true; 344 default: 345 return false; 346 } 347 case Category.Numeric | Category.Constant: 348 return to == Category.Value || 349 to == Category.Numeric; 350 case Category.Numeric: 351 switch (to) { 352 case Category.Logical: 353 conversionCount[0]++; 354 return true; 355 default: 356 return to == Category.Value || 357 to == Category.Integer || 358 to == (Category.Integer | Category.Constant) || 359 to == (Category.Numeric | Category.Constant); 360 } 361 case Category.Integer: 362 return to == Category.Value || 363 to == (Category.Integer | Category.Constant) || 364 to == Category.Numeric || 365 to == (Category.Numeric | Category.Constant); 366 case Category.Set: 367 return false; 368 case Category.String | Category.Constant: 369 return to == Category.Value || 370 to == Category.String; 371 case Category.String: 372 return to == Category.Value || 373 to == (Category.String | Category.Constant); 374 case Category.DateTime | Category.Constant: 375 return to == Category.Value || 376 to == Category.DateTime; 377 case Category.DateTime: 378 return to == Category.Value || 379 to == (Category.DateTime | Category.Constant); 380 case Category.Tuple: 381 switch (to) { 382 case Category.Value: 383 case Category.Numeric: 384 conversionCount[0]++; 385 return true; 386 default: 387 return false; 388 } 389 case Category.Value: 390 return false; 391 case Category.Symbol: 392 return false; 393 case Category.Null: 394 // now null supports members as well as scalars; but scalar is 395 // preferred 396 if (Category.isScalar(to)) { 397 return true; 398 } else if (to == Category.Member) { 399 conversionCount[0] += 2; 400 return true; 401 } else { 402 return false; 403 } 404 case Category.Empty: 405 return false; 406 default: 407 throw Util.newInternal("unknown category " + from); 408 } 409 } 410 411 static <T> T neq(T t1, T t2) { 412 return t1 == null ? t2 413 : t2 == null ? t1 414 : t1.equals(t2) ? t1 415 : null; 416 } 417 } 418 419 // End TypeUtil.java