001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunDefBase.java#32 $
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) 2002-2002 Kana Software, Inc.
007    // Copyright (C) 2002-2007 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/src/main/mondrian/olap/fun/FunDefBase.java#32 $
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 example, function,
098         *                       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, and parameter
129         *                    types of this operator. The "Members" operator has a syntactic
130         *                    type "pxd" which means "an operator with
131         *                    {@link Syntax#Property property} syntax (p) which returns a set
132         *                    (x) and takes a dimension (d) as its argument".
133         *                    See {@link FunUtil#decodeSyntacticType(String)},
134         *                    {@link FunUtil#decodeReturnCategory(String)},
135         *                    {@link FunUtil#decodeParameterCategories(String)}.
136         */
137        protected FunDefBase(
138            String name,
139            String description,
140            String flags)
141        {
142            this(
143                name,
144                null,
145                description,
146                decodeSyntacticType(flags),
147                decodeReturnCategory(flags),
148                decodeParameterCategories(flags));
149        }
150    
151        /**
152         * Creates an operator with an explicit signature.
153         *
154         * <p>In most cases, the signature can be generated automatically, and
155         * you should use the constructor which creates an implicit signature,
156         * {@link #FunDefBase(String, String, String, String)}
157         * instead.
158         *
159         * @param name        Name of the function, for example "Members".
160         * @param signature   Signature of the function, for example
161         *                    "&lt;Dimension&gt;.Members".
162         * @param description Description of the function, for example
163         *                    "Returns the set of all members in a dimension."
164         * @param flags       Encoding of the syntactic type, return type, and parameter
165         *                    types of this operator. The "Members" operator has a syntactic
166         *                    type "pxd" which means "an operator with
167         *                    {@link Syntax#Property property} syntax (p) which returns a set
168         *                    (x) and takes a dimension (d) as its argument".
169         *                    See {@link FunUtil#decodeSyntacticType(String)},
170         *                    {@link FunUtil#decodeReturnCategory(String)},
171         *                    {@link FunUtil#decodeParameterCategories(String)}.
172         */
173        protected FunDefBase(
174            String name,
175            String signature,
176            String description,
177            String flags)
178        {
179            this(
180                name,
181                signature,
182                description,
183                decodeSyntacticType(flags),
184                decodeReturnCategory(flags),
185                decodeParameterCategories(flags));
186        }
187    
188        /**
189         * Convenience constructor when we are created by a {@link Resolver}.
190         *
191         * @param resolver Resolver
192         * @param returnType Return type
193         * @param parameterTypes Parameter types
194         */
195        FunDefBase(Resolver resolver, int returnType, int[] parameterTypes) {
196            this(
197                resolver.getName(),
198                null,
199                null,
200                resolver.getSyntax(),
201                returnType,
202                parameterTypes);
203        }
204    
205        /**
206         * Copy constructor.
207         *
208         * @param funDef Function definition to copy
209         */
210        FunDefBase(FunDef funDef) {
211            this(
212                funDef.getName(), funDef.getSignature(),
213                funDef.getDescription(), funDef.getSyntax(),
214                funDef.getReturnCategory(), funDef.getParameterCategories());
215        }
216    
217        public String getName() {
218            return name;
219        }
220    
221        public String getDescription() {
222            return description;
223        }
224    
225        public Syntax getSyntax() {
226            return Syntax.class.getEnumConstants()[flags];
227        }
228    
229        public int getReturnCategory() {
230            return returnCategory;
231        }
232    
233        public int[] getParameterCategories() {
234            return parameterCategories;
235        }
236    
237        public Exp createCall(Validator validator, Exp[] args) {
238            int[] categories = getParameterCategories();
239            Util.assertTrue(categories.length == args.length);
240            for (int i = 0; i < args.length; i++) {
241                args[i] = validateArg(validator, args, i, categories[i]);
242            }
243            final Type type = getResultType(validator, args);
244            if (type == null) {
245                throw Util.newInternal("could not derive type");
246            }
247            return new ResolvedFunCall(this, args, type);
248        }
249    
250        /**
251         * Validates an argument to a call to this function.
252         *
253         * <p>The default implementation of this method adds an implicit
254         * conversion to the correct type. Derived classes may override.
255         *
256         * @param validator Validator
257         * @param args Arguments to this function
258         * @param i Ordinal of argument
259         * @param category Expected {@link Category category} of argument
260         * @return Validated argument
261         */
262        protected Exp validateArg(
263                Validator validator,
264                Exp[] args,
265                int i,
266                int category) {
267            return args[i];
268        }
269    
270        /**
271         * Converts a type to a different category, maintaining as much type
272         * information as possible.
273         *
274         * For example, given <code>LevelType(dimension=Time, hierarchy=unknown,
275         * level=unkown)</code> and category=Hierarchy, returns
276         * <code>HierarchyType(dimension=Time)</code>.
277         *
278         * @param type Type
279         * @param category Desired category
280         * @return Type after conversion to desired category
281         */
282        static Type castType(Type type, int category) {
283            switch (category) {
284            case Category.Logical:
285                return new BooleanType();
286            case Category.Numeric:
287                return new NumericType();
288            case Category.Numeric | Category.Integer:
289                return new DecimalType(Integer.MAX_VALUE, 0);
290            case Category.String:
291                return new StringType();
292            case Category.DateTime:
293                return new DateTimeType();
294            case Category.Symbol:
295                return new SymbolType();
296            case Category.Value:
297                return new ScalarType();
298            case Category.Cube:
299                if (type instanceof Cube) {
300                    return new CubeType((Cube) type);
301                }
302                return null;
303            case Category.Dimension:
304                if (type != null) {
305                    return DimensionType.forType(type);
306                }
307                return null;
308            case Category.Hierarchy:
309                if (type != null) {
310                    return HierarchyType.forType(type);
311                }
312                return null;
313            case Category.Level:
314                if (type != null) {
315                    return LevelType.forType(type);
316                }
317                return null;
318            case Category.Member:
319                if (type != null) {
320                    final MemberType memberType = TypeUtil.toMemberType(type);
321                    if (memberType != null) {
322                        return memberType;
323                    }
324                }
325                // Take a wild guess.
326                return MemberType.Unknown;
327            case Category.Tuple:
328                if (type != null) {
329                    final Type memberType = TypeUtil.toMemberOrTupleType(type);
330                    if (memberType != null) {
331                        return memberType;
332                    }
333                }
334                return null;
335            case Category.Set:
336                if (type != null) {
337                    final Type memberType = TypeUtil.toMemberOrTupleType(type);
338                    if (memberType != null) {
339                        return new SetType(memberType);
340                    }
341                }
342                return null;
343            case Category.Empty:
344                return new NullType();
345            default:
346                throw Category.instance.badValue(category);
347            }
348        }
349    
350        /**
351         * Returns the type of a call to this function with a given set of
352         * arguments.<p/>
353         *
354         * The default implementation makes the coarse assumption that the return
355         * type is in some way related to the type of the first argument.
356         * Operators whose arguments don't follow the requirements of this
357         * implementation should override this method.<p/>
358         *
359         * If the function definition says it returns a literal type (numeric,
360         * string, symbol) then it's a fair guess that the function call
361         * returns the same kind of value.<p/>
362         *
363         * If the function definition says it returns an object type (cube,
364         * dimension, hierarchy, level, member) then we check the first
365         * argument of the function. Suppose that the function definition says
366         * that it returns a hierarchy, and the first argument of the function
367         * happens to be a member. Then it's reasonable to assume that this
368         * function returns a member.
369         *
370         * @param validator Validator
371         * @param args Arguments to the call to this operator
372         * @return result type of a call this function
373         */
374        public Type getResultType(Validator validator, Exp[] args) {
375            Type firstArgType =
376                args.length > 0 ?
377                    args[0].getType() :
378                    null;
379            Type type = castType(firstArgType, getReturnCategory());
380            if (type != null) {
381                return type;
382            }
383            throw Util.newInternal(
384                "Cannot deduce type of call to function '" + this.name + "'");
385        }
386    
387        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
388            throw Util.newInternal("function '" + getSignature() +
389                    "' has not been implemented");
390        }
391    
392        public String getSignature() {
393            return getSyntax().getSignature(getName(), getReturnCategory(),
394                    getParameterCategories());
395        }
396    
397        public void unparse(Exp[] args, PrintWriter pw) {
398            getSyntax().unparse(getName(), args, pw);
399        }
400    }
401    
402    // End FunDefBase.java