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