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