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>"<Dimension>.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>"<Set>.Item(<Index>)"</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 * "<Dimension>.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 * "<Dimension>.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