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