001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/UdfResolver.java#17 $
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.fun;
011    
012    import mondrian.olap.*;
013    import mondrian.olap.type.*;
014    import mondrian.spi.UserDefinedFunction;
015    import mondrian.calc.*;
016    import mondrian.calc.impl.GenericCalc;
017    import mondrian.mdx.ResolvedFunCall;
018    
019    /**
020     * Resolver for user-defined functions.
021     *
022     * @author jhyde
023     * @since 2.0
024     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/UdfResolver.java#17 $
025     */
026    public class UdfResolver implements Resolver {
027        private final UserDefinedFunction udf;
028        private static final String[] emptyStringArray = new String[0];
029    
030        public UdfResolver(UserDefinedFunction udf) {
031            this.udf = udf;
032        }
033        public String getName() {
034            return udf.getName();
035        }
036    
037        public String getDescription() {
038            return udf.getDescription();
039        }
040    
041        public String getSignature() {
042            Type[] parameterTypes = udf.getParameterTypes();
043            int[] parameterCategories = new int[parameterTypes.length];
044            for (int i = 0; i < parameterCategories.length; i++) {
045                parameterCategories[i] = TypeUtil.typeToCategory(parameterTypes[i]);
046            }
047            Type returnType = udf.getReturnType(parameterTypes);
048            int returnCategory = TypeUtil.typeToCategory(returnType);
049            return getSyntax().getSignature(
050                getName(),
051                returnCategory,
052                parameterCategories);
053        }
054    
055        public Syntax getSyntax() {
056            return udf.getSyntax();
057        }
058    
059        public FunDef getFunDef() {
060            Type[] parameterTypes = udf.getParameterTypes();
061            int[] parameterCategories = new int[parameterTypes.length];
062            for (int i = 0; i < parameterCategories.length; i++) {
063                parameterCategories[i] = TypeUtil.typeToCategory(parameterTypes[i]);
064            }
065            Type returnType = udf.getReturnType(parameterTypes);
066            return new UdfFunDef(parameterCategories, returnType);
067        }
068    
069        public FunDef resolve(
070                Exp[] args, Validator validator, int[] conversionCount) {
071            final Type[] parameterTypes = udf.getParameterTypes();
072            if (args.length != parameterTypes.length) {
073                return null;
074            }
075            int[] parameterCategories = new int[parameterTypes.length];
076            Type[] castArgTypes = new Type[parameterTypes.length];
077            for (int i = 0; i < parameterTypes.length; i++) {
078                Type parameterType = parameterTypes[i];
079                final Exp arg = args[i];
080                final Type argType = arg.getType();
081                final int parameterCategory = TypeUtil.typeToCategory(parameterType);
082                if (!validator.canConvert(
083                        arg, parameterCategory, conversionCount)) {
084                    return null;
085                }
086                parameterCategories[i] = parameterCategory;
087                if (!parameterType.equals(argType)) {
088                    castArgTypes[i] =
089                        FunDefBase.castType(argType, parameterCategory);
090                }
091            }
092            final Type returnType = udf.getReturnType(castArgTypes);
093            return new UdfFunDef(parameterCategories, returnType);
094        }
095    
096        public boolean requiresExpression(int k) {
097            return false;
098        }
099    
100        public String[] getReservedWords() {
101            final String[] reservedWords = udf.getReservedWords();
102            return reservedWords == null ? emptyStringArray : reservedWords;
103        }
104    
105        /**
106         * Adapter which converts a {@link UserDefinedFunction} into a
107         * {@link FunDef}.
108         */
109        private class UdfFunDef extends FunDefBase {
110            private Type returnType;
111    
112            public UdfFunDef(int[] parameterCategories, Type returnType) {
113                super(
114                    UdfResolver.this,
115                    TypeUtil.typeToCategory(returnType),
116                    parameterCategories);
117                this.returnType = returnType;
118            }
119    
120            public Type getResultType(Validator validator, Exp[] args) {
121                return returnType;
122            }
123    
124            public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
125                final Exp[] args = call.getArgs();
126                Calc[] calcs = new Calc[args.length];
127                UserDefinedFunction.Argument[] expCalcs =
128                    new UserDefinedFunction.Argument[args.length];
129                for (int i = 0; i < args.length; i++) {
130                    Exp arg = args[i];
131                    final Calc calc = compiler.compileAs(
132                        arg,
133                        castType(arg.getType(), parameterCategories[i]),
134                        ResultStyle.ANY_LIST);
135                    final Calc scalarCalc = compiler.compileScalar(arg, true);
136                    expCalcs[i] = new CalcExp(calc, scalarCalc);
137                }
138    
139                return new CalcImpl(call, calcs, udf, expCalcs);
140            }
141        }
142    
143        /**
144         * Expression which evaluates a user-defined function.
145         */
146        private static class CalcImpl extends GenericCalc {
147            private final Calc[] calcs;
148            private final UserDefinedFunction udf;
149            private final UserDefinedFunction.Argument[] args;
150    
151            public CalcImpl(
152                    ResolvedFunCall call,
153                    Calc[] calcs,
154                    UserDefinedFunction udf,
155                    UserDefinedFunction.Argument[] args) {
156                super(call);
157                this.calcs = calcs;
158                this.udf = Util.createUdf(udf.getClass());
159                this.args = args;
160            }
161    
162            public Calc[] getCalcs() {
163                return calcs;
164            }
165    
166            public Object evaluate(Evaluator evaluator) {
167                return udf.execute(evaluator, args);
168            }
169    
170            public boolean dependsOn(Dimension dimension) {
171                // Be pessimistic. This effectively disables expression caching.
172                return true;
173            }
174        }
175    
176        /**
177         * Wrapper around a {@link Calc} to make it appear as an {@link Exp}.
178         * Only the {@link #evaluate(mondrian.olap.Evaluator)}
179         * and {@link #evaluateScalar(mondrian.olap.Evaluator)} methods are
180         * supported.
181         */
182        private static class CalcExp implements UserDefinedFunction.Argument {
183            private final Calc calc;
184            private final Calc scalarCalc;
185    
186            public CalcExp(Calc calc, Calc scalarCalc) {
187                this.calc = calc;
188                this.scalarCalc = scalarCalc;
189            }
190    
191            public Type getType() {
192                return calc.getType();
193            }
194    
195            public Object evaluate(Evaluator evaluator) {
196                return calc.evaluate(evaluator);
197            }
198    
199            public Object evaluateScalar(Evaluator evaluator) {
200                return scalarCalc.evaluate(evaluator);
201            }
202        }
203    
204    }
205    
206    // End UdfResolver.java