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