001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/ParameterFunDef.java#14 $ 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) 2003-2006 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 // 010 // jhyde, Feb 14, 2003 011 */ 012 package mondrian.olap.fun; 013 014 import mondrian.olap.*; 015 import mondrian.olap.type.*; 016 import mondrian.mdx.*; 017 018 /** 019 * A <code>ParameterFunDef</code> is a pseudo-function describing calls to 020 * <code>Parameter</code> and <code>ParamRef</code> functions. It exists only 021 * fleetingly, and is then converted into a {@link mondrian.olap.Parameter}. 022 * For internal use only. 023 * 024 * @author jhyde 025 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/ParameterFunDef.java#14 $ 026 * @since Feb 14, 2003 027 */ 028 public class ParameterFunDef extends FunDefBase { 029 public final String parameterName; 030 private final Type type; 031 public final Exp exp; 032 public final String parameterDescription; 033 034 ParameterFunDef( 035 FunDef funDef, 036 String parameterName, 037 Type type, 038 int returnCategory, 039 Exp exp, 040 String description) { 041 super(funDef.getName(), 042 funDef.getSignature(), 043 funDef.getDescription(), 044 funDef.getSyntax(), 045 returnCategory, 046 funDef.getParameterCategories()); 047 assertPrecondition(getName().equals("Parameter") || 048 getName().equals("ParamRef")); 049 this.parameterName = parameterName; 050 this.type = type; 051 this.exp = exp; 052 this.parameterDescription = description; 053 } 054 055 public Exp createCall(Validator validator, Exp[] args) { 056 Parameter parameter = validator.createOrLookupParam( 057 this.getName().equals("Parameter"), 058 parameterName, type, exp, parameterDescription); 059 return new ParameterExpr(parameter); 060 } 061 062 public Type getResultType(Validator validator, Exp[] args) { 063 return type; 064 } 065 066 private static boolean isConstant(Exp typeArg) { 067 if (typeArg instanceof LevelExpr) { 068 // e.g. "[Time].[Quarter]" 069 return true; 070 } 071 if (typeArg instanceof HierarchyExpr) { 072 // e.g. "[Time].[By Week]" 073 return true; 074 } 075 if (typeArg instanceof DimensionExpr) { 076 // e.g. "[Time]" 077 return true; 078 } 079 if (typeArg instanceof FunCall) { 080 // e.g. "[Time].CurrentMember.Hierarchy". They probably wrote 081 // "[Time]", and the automatic type conversion did the rest. 082 FunCall hierarchyCall = (FunCall) typeArg; 083 if (hierarchyCall.getFunName().equals("Hierarchy") && 084 hierarchyCall.getArgCount() > 0 && 085 hierarchyCall.getArg(0) instanceof FunCall) { 086 FunCall currentMemberCall = (FunCall) hierarchyCall.getArg(0); 087 if (currentMemberCall.getFunName().equals("CurrentMember") && 088 currentMemberCall.getArgCount() > 0 && 089 currentMemberCall.getArg(0) instanceof DimensionExpr) { 090 return true; 091 } 092 } 093 } 094 return false; 095 } 096 097 public static String getParameterName(Exp[] args) { 098 if (args[0] instanceof Literal && 099 args[0].getCategory() == Category.String) { 100 return (String) ((Literal) args[0]).getValue(); 101 } else { 102 throw Util.newInternal("Parameter name must be a string constant"); 103 } 104 } 105 106 /** 107 * Returns an approximate type for a parameter, based upon the 1'th 108 * argument. Does not use the default value expression, so this method 109 * can safely be used before the expression has been validated. 110 */ 111 public static Type getParameterType(Exp[] args) { 112 if (args[1] instanceof Id) { 113 Id id = (Id) args[1]; 114 String[] names = id.toStringArray(); 115 if (names.length == 1) { 116 final String name = names[0]; 117 if (name.equals("NUMERIC")) { 118 return new NumericType(); 119 } 120 if (name.equals("STRING")) { 121 return new StringType(); 122 } 123 } 124 } else if (args[1] instanceof Literal) { 125 final Literal literal = (Literal) args[1]; 126 if (literal.getValue().equals("NUMERIC")) { 127 return new NumericType(); 128 } else if (literal.getValue().equals("STRING")) { 129 return new StringType(); 130 } 131 } else if (args[1] instanceof MemberExpr) { 132 return new MemberType(null,null,null,null); 133 } 134 return new StringType(); 135 } 136 137 /** 138 * Resolves calls to the <code>Parameter</code> MDX function. 139 */ 140 public static class ParameterResolver extends MultiResolver { 141 private static final String[] SIGNATURES = 142 new String[]{ 143 // Parameter(string const, symbol, string[, string const]): string 144 "fS#yS#", "fS#yS", 145 // Parameter(string const, symbol, numeric[, string const]): numeric 146 "fn#yn#", "fn#yn", 147 // Parameter(string const, hierarchy constant, member[, string const]): member 148 "fm#hm#", "fm#hm", 149 }; 150 151 public ParameterResolver() { 152 super("Parameter", 153 "Parameter(<Name>, <Type>, <DefaultValue>, <Description>)", 154 "Returns default value of parameter.", SIGNATURES); 155 } 156 157 public String[] getReservedWords() { 158 return new String[]{"NUMERIC", "STRING"}; 159 } 160 161 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 162 String parameterName = getParameterName(args); 163 Exp typeArg = args[1]; 164 int category; 165 Type type = typeArg.getType(); 166 switch (typeArg.getCategory()) { 167 case Category.Dimension: 168 case Category.Hierarchy: 169 case Category.Level: 170 Dimension dimension = type.getDimension(); 171 if (!isConstant(typeArg)) { 172 throw newEvalException( 173 dummyFunDef, 174 "Invalid parameter '" + parameterName + 175 "'. Type must be a NUMERIC, STRING, or a dimension, hierarchy or level"); 176 } 177 if (dimension == null) { 178 throw newEvalException( 179 dummyFunDef, 180 "Invalid dimension for parameter '" + 181 parameterName + "'"); 182 } 183 type = new MemberType( 184 type.getDimension(), 185 type.getHierarchy(), 186 type.getLevel(), 187 null); 188 category = Category.Member; 189 break; 190 191 case Category.Symbol: 192 String s = (String) ((Literal) typeArg).getValue(); 193 if (s.equalsIgnoreCase("NUMERIC")) { 194 category = Category.Numeric; 195 type = new NumericType(); 196 break; 197 } else if (s.equalsIgnoreCase("STRING")) { 198 category = Category.String; 199 type = new StringType(); 200 break; 201 } 202 // fall through and throw error 203 default: 204 // Error is internal because the function call has already been 205 // type-checked. 206 throw newEvalException( 207 dummyFunDef, 208 "Invalid type for parameter '" + parameterName + 209 "'; expecting NUMERIC, STRING or a hierarchy"); 210 } 211 Exp exp = args[2]; 212 if (exp.getCategory() != category) { 213 String typeName = 214 Category.instance.getName(category).toUpperCase(); 215 throw newEvalException( 216 dummyFunDef, 217 "Default value of parameter '" + parameterName + 218 "' is inconsistent with its type, " + typeName); 219 } 220 if (category == Category.Member) { 221 final Type expType = exp.getType(); 222 if (type.getDimension() != null && 223 expType.getDimension() != type.getDimension() || 224 type.getHierarchy() != null && 225 expType.getHierarchy() != type.getHierarchy() || 226 type.getLevel() != null && 227 expType.getLevel() != type.getLevel()) { 228 throw newEvalException( 229 dummyFunDef, 230 "Default value of parameter '" + parameterName + 231 "' is not consistent with the parameter type '" + 232 type); 233 } 234 } 235 String parameterDescription = null; 236 if (args.length > 3) { 237 if (args[3] instanceof Literal && 238 args[3].getCategory() == Category.String) { 239 parameterDescription = 240 (String) ((Literal) args[3]).getValue(); 241 } else { 242 throw newEvalException( 243 dummyFunDef, 244 "Description of parameter '" + parameterName + 245 "' must be a string constant"); 246 } 247 } 248 249 return new ParameterFunDef( 250 dummyFunDef, parameterName, type, category, 251 exp, parameterDescription); 252 } 253 } 254 255 /** 256 * Resolves calls to the <code>ParamRef</code> MDX function. 257 */ 258 public static class ParamRefResolver extends MultiResolver { 259 public ParamRefResolver() { 260 super( 261 "ParamRef", 262 "ParamRef(<Name>)", 263 "Returns the current value of this parameter. If it is null, returns the default value.", 264 new String[]{"fv#"}); 265 } 266 267 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 268 String parameterName = getParameterName(args); 269 return new ParameterFunDef( 270 dummyFunDef, parameterName, null, Category.Unknown, null, 271 null); 272 } 273 } 274 } 275 276 // End ParameterFunDef.java