001    /*
002    // $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/fun/FunDefBase.java#1 $
003    // This software is subject to the terms of the Eclipse Public License v1.0
004    // Agreement, available at the following URL:
005    // http://www.eclipse.org/legal/epl-v10.html.
006    // Copyright (C) 2002-2002 Kana Software, Inc.
007    // Copyright (C) 2002-2009 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    //
011    // jhyde, 26 February, 2002
012    */
013    package mondrian.olap.fun;
014    
015    import mondrian.olap.*;
016    import mondrian.olap.type.*;
017    import mondrian.olap.type.LevelType;
018    import mondrian.olap.type.DimensionType;
019    import mondrian.calc.Calc;
020    import mondrian.calc.ExpCompiler;
021    import mondrian.mdx.ResolvedFunCall;
022    
023    import java.io.PrintWriter;
024    
025    /**
026     * <code>FunDefBase</code> is the default implementation of {@link FunDef}.
027     *
028     * <h3>Signatures</h3>
029     *
030     * <p>A function is defined by the following:</p>
031     *
032     * <table border="1">
033     * <tr><th>Parameter</th><th>Meaning</th><th>Example</th></tr>
034     * <tr>
035     * <td>name</td><td>Name of the function</td><td>"Members"</td>
036     * </tr>
037     * <tr>
038     * <td>signature</td>
039     * <td>Signature of the function</td>
040     * <td>"&lt;Dimension&gt;.Members"</td>
041     * </tr>
042     * <tr>
043     * <td>description</td>
044     * <td>Description of the function</td>
045     * <td>"Returns the set of all members in a dimension."</td>
046     * </tr>
047     * <tr>
048     * <td>flags</td>
049     * <td>Encoding of the syntactic type, return type, and parameter
050     * types of this operator. The encoding is described below.</td>
051     * <td>"pxd"</tr>
052     * </table>
053     *
054     * The <code>flags</code> field is an string which encodes
055     * the syntactic type, return type, and parameter types of this operator.
056     * <ul>
057     * <li>The first character determines the syntactic type, as described by
058     * {@link FunUtil#decodeSyntacticType(String)}.
059     * <li>The second character determines the return type, as described by
060     * {@link FunUtil#decodeReturnCategory(String)}.
061     * <li>The third and subsequence characters determine the types of the
062     * arguments arguments, as described by
063     * {@link FunUtil#decodeParameterCategories(String)}.
064     * </ul><p/>
065     *
066     * For example,  <code>"pxd"</code> means "an operator with
067     * {@link Syntax#Property property} syntax (p) which returns a set
068     * (x) and takes a dimension (d) as its argument".<p/>
069     *
070     * The arguments are always read from left to right, regardless of the
071     * syntactic type of the operator. For example, the
072     * <code>"&lt;Set&gt;.Item(&lt;Index&gt;)"</code> operator
073     * (signature <code>"mmxn"</code>) has the
074     * syntax of a method-call, and takes two parameters:
075     * a set (x) and a numeric (n).<p/>
076     *
077     * @author jhyde
078     * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/fun/FunDefBase.java#1 $
079     * @since 26 February, 2002
080     */
081    public abstract class FunDefBase extends FunUtil implements FunDef {
082        protected final int flags;
083        private final String name;
084        final String signature;
085        private final String description;
086        protected final int returnCategory;
087        protected final int[] parameterCategories;
088    
089        /**
090         * Creates an operator.
091         *
092         * @param name           Name of the function, for example "Members".
093         * @param signature      Signature of the function, for example
094         *                       "&lt;Dimension&gt;.Members".
095         * @param description    Description of the function, for example
096         *                       "Returns the set of all members in a dimension."
097         * @param syntax         Syntactic type of the operator (for
098         *                       example, function, method, infix operator)
099         * @param returnCategory The {@link Category} of the value returned by this
100         *                       operator.
101         * @param parameterCategories An array of {@link Category} codes, one for
102         *                       each parameter.
103         */
104        FunDefBase(
105            String name,
106            String signature,
107            String description,
108            Syntax syntax,
109            int returnCategory,
110            int[] parameterCategories)
111        {
112            assert name != null;
113            assert syntax != null;
114            this.name = name;
115            this.signature = signature;
116            this.description = description;
117            this.flags = syntax.ordinal();
118            this.returnCategory = returnCategory;
119            this.parameterCategories = parameterCategories;
120        }
121    
122        /**
123         * Creates an operator.
124         *
125         * @param name        Name of the function, for example "Members".
126         * @param description Description of the function, for example
127         *                    "Returns the set of all members in a dimension."
128         * @param flags       Encoding of the syntactic type, return type,
129         *                    and parameter types of this operator. The
130         *                    "Members" operator has a syntactic type
131         *                    "pxd" which means "an operator with
132         *                    {@link Syntax#Property property} syntax (p) which
133         *                    returns a set (x) and takes a dimension (d) as its
134         *                    argument".
135         *                    See {@link FunUtil#decodeSyntacticType(String)},
136         *                    {@link FunUtil#decodeReturnCategory(String)},
137         *                    {@link FunUtil#decodeParameterCategories(String)}.
138         */
139        protected FunDefBase(
140            String name,
141            String description,
142            String flags)
143        {
144            this(
145                name,
146                null,
147                description,
148                decodeSyntacticType(flags),
149                decodeReturnCategory(flags),
150                decodeParameterCategories(flags));
151        }
152    
153        /**
154         * Creates an operator with an explicit signature.
155         *
156         * <p>In most cases, the signature can be generated automatically, and
157         * you should use the constructor which creates an implicit signature,
158         * {@link #FunDefBase(String, String, String, String)}
159         * instead.
160         *
161         * @param name        Name of the function, for example "Members".
162         * @param signature   Signature of the function, for example
163         *                    "&lt;Dimension&gt;.Members".
164         * @param description Description of the function, for example
165         *                    "Returns the set of all members in a dimension."
166         * @param flags       Encoding of the syntactic type, return type, and
167         *                    parameter types of this operator. The "Members"
168         *                    operator has a syntactic type "pxd" which means "an
169         *                    operator with {@link Syntax#Property property} syntax
170         *                    (p) which returns a set (x) and takes a dimension (d)
171         *                    as its argument".  See
172         *                    {@link FunUtil#decodeSyntacticType(String)},
173         *                    {@link FunUtil#decodeReturnCategory(String)},
174         *                    {@link FunUtil#decodeParameterCategories(String)}.
175         */
176        protected FunDefBase(
177            String name,
178            String signature,
179            String description,
180            String flags)
181        {
182            this(
183                name,
184                signature,
185                description,
186                decodeSyntacticType(flags),
187                decodeReturnCategory(flags),
188                decodeParameterCategories(flags));
189        }
190    
191        /**
192         * Convenience constructor when we are created by a {@link Resolver}.
193         *
194         * @param resolver Resolver
195         * @param returnType Return type
196         * @param parameterTypes Parameter types
197         */
198        FunDefBase(Resolver resolver, int returnType, int[] parameterTypes) {
199            this(
200                resolver.getName(),
201                null,
202                null,
203                resolver.getSyntax(),
204                returnType,
205                parameterTypes);
206        }
207    
208        /**
209         * Copy constructor.
210         *
211         * @param funDef Function definition to copy
212         */
213        FunDefBase(FunDef funDef) {
214            this(
215                funDef.getName(), funDef.getSignature(),
216                funDef.getDescription(), funDef.getSyntax(),
217                funDef.getReturnCategory(), funDef.getParameterCategories());
218        }
219    
220        public String getName() {
221            return name;
222        }
223    
224        public String getDescription() {
225            return description;
226        }
227    
228        public Syntax getSyntax() {
229            return Syntax.class.getEnumConstants()[flags];
230        }
231    
232        public int getReturnCategory() {
233            return returnCategory;
234        }
235    
236        public int[] getParameterCategories() {
237            return parameterCategories;
238        }
239    
240        public Exp createCall(Validator validator, Exp[] args) {
241            int[] categories = getParameterCategories();
242            Util.assertTrue(categories.length == args.length);
243            for (int i = 0; i < args.length; i++) {
244                args[i] = validateArg(validator, args, i, categories[i]);
245            }
246            final Type type = getResultType(validator, args);
247            if (type == null) {
248                throw Util.newInternal("could not derive type");
249            }
250            return new ResolvedFunCall(this, args, type);
251        }
252    
253        /**
254         * Validates an argument to a call to this function.
255         *
256         * <p>The default implementation of this method adds an implicit
257         * conversion to the correct type. Derived classes may override.
258         *
259         * @param validator Validator
260         * @param args Arguments to this function
261         * @param i Ordinal of argument
262         * @param category Expected {@link Category category} of argument
263         * @return Validated argument
264         */
265        protected Exp validateArg(
266            Validator validator,
267            Exp[] args,
268            int i,
269            int category)
270        {
271            return args[i];
272        }
273    
274        /**
275         * Converts a type to a different category, maintaining as much type
276         * information as possible.
277         *
278         * For example, given <code>LevelType(dimension=Time, hierarchy=unknown,
279         * level=unkown)</code> and category=Hierarchy, returns
280         * <code>HierarchyType(dimension=Time)</code>.
281         *
282         * @param type Type
283         * @param category Desired category
284         * @return Type after conversion to desired category
285         */
286        static Type castType(Type type, int category) {
287            switch (category) {
288            case Category.Logical:
289                return new BooleanType();
290            case Category.Numeric:
291                return new NumericType();
292            case Category.Numeric | Category.Integer:
293                return new DecimalType(Integer.MAX_VALUE, 0);
294            case Category.String:
295                return new StringType();
296            case Category.DateTime:
297                return new DateTimeType();
298            case Category.Symbol:
299                return new SymbolType();
300            case Category.Value:
301                return new ScalarType();
302            case Category.Cube:
303                if (type instanceof Cube) {
304                    return new CubeType((Cube) type);
305                }
306                return null;
307            case Category.Dimension:
308                if (type != null) {
309                    return DimensionType.forType(type);
310                }
311                return null;
312            case Category.Hierarchy:
313                if (type != null) {
314                    return HierarchyType.forType(type);
315                }
316                return null;
317            case Category.Level:
318                if (type != null) {
319                    return LevelType.forType(type);
320                }
321                return null;
322            case Category.Member:
323                if (type != null) {
324                    final MemberType memberType = TypeUtil.toMemberType(type);
325                    if (memberType != null) {
326                        return memberType;
327                    }
328                }
329                // Take a wild guess.
330                return MemberType.Unknown;
331            case Category.Tuple:
332                if (type != null) {
333                    final Type memberType = TypeUtil.toMemberOrTupleType(type);
334                    if (memberType != null) {
335                        return memberType;
336                    }
337                }
338                return null;
339            case Category.Set:
340                if (type != null) {
341                    final Type memberType = TypeUtil.toMemberOrTupleType(type);
342                    if (memberType != null) {
343                        return new SetType(memberType);
344                    }
345                }
346                return null;
347            case Category.Empty:
348                return new EmptyType();
349            default:
350                throw Category.instance.badValue(category);
351            }
352        }
353    
354        /**
355         * Returns the type of a call to this function with a given set of
356         * arguments.<p/>
357         *
358         * The default implementation makes the coarse assumption that the return
359         * type is in some way related to the type of the first argument.
360         * Operators whose arguments don't follow the requirements of this
361         * implementation should override this method.<p/>
362         *
363         * If the function definition says it returns a literal type (numeric,
364         * string, symbol) then it's a fair guess that the function call
365         * returns the same kind of value.<p/>
366         *
367         * If the function definition says it returns an object type (cube,
368         * dimension, hierarchy, level, member) then we check the first
369         * argument of the function. Suppose that the function definition says
370         * that it returns a hierarchy, and the first argument of the function
371         * happens to be a member. Then it's reasonable to assume that this
372         * function returns a member.
373         *
374         * @param validator Validator
375         * @param args Arguments to the call to this operator
376         * @return result type of a call this function
377         */
378        public Type getResultType(Validator validator, Exp[] args) {
379            Type firstArgType =
380                args.length > 0
381                ? args[0].getType()
382                : null;
383            Type type = castType(firstArgType, getReturnCategory());
384            if (type != null) {
385                return type;
386            }
387            throw Util.newInternal(
388                "Cannot deduce type of call to function '" + this.name + "'");
389        }
390    
391        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
392            throw Util.newInternal(
393                "function '" + getSignature()
394                + "' has not been implemented");
395        }
396    
397        public String getSignature() {
398            return getSyntax().getSignature(
399                getName(),
400                getReturnCategory(),
401                getParameterCategories());
402        }
403    
404        public void unparse(Exp[] args, PrintWriter pw) {
405            getSyntax().unparse(getName(), args, pw);
406        }
407    }
408    
409    // End FunDefBase.java