/*
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// You must accept the terms of that agreement to use this software.
//
// Copyright (C) 2002-2005 Julian Hyde
// Copyright (C) 2005-2009 Pentaho and others
// All Rights Reserved.
*/
package mondrian.olap.fun;
import mondrian.calc.Calc;
import mondrian.calc.ExpCompiler;
import mondrian.mdx.ResolvedFunCall;
import mondrian.olap.*;
import mondrian.olap.type.*;
import mondrian.olap.type.DimensionType;
import mondrian.olap.type.LevelType;
import java.io.PrintWriter;
/**
* <code>FunDefBase</code> is the default implementation of {@link FunDef}.
*
* <h3>Signatures</h3>
*
* <p>A function is defined by the following:</p>
*
* <table border="1">
* <tr><th>Parameter</th><th>Meaning</th><th>Example</th></tr>
* <tr>
* <td>name</td><td>Name of the function</td><td>"Members"</td>
* </tr>
* <tr>
* <td>signature</td>
* <td>Signature of the function</td>
* <td>"<Dimension>.Members"</td>
* </tr>
* <tr>
* <td>description</td>
* <td>Description of the function</td>
* <td>"Returns the set of all members in a dimension."</td>
* </tr>
* <tr>
* <td>flags</td>
* <td>Encoding of the syntactic type, return type, and parameter
* types of this operator. The encoding is described below.</td>
* <td>"pxd"</tr>
* </table>
*
* The <code>flags</code> field is an string which encodes
* the syntactic type, return type, and parameter types of this operator.
* <ul>
* <li>The first character determines the syntactic type, as described by
* {@link FunUtil#decodeSyntacticType(String)}.
* <li>The second character determines the return type, as described by
* {@link FunUtil#decodeReturnCategory(String)}.
* <li>The third and subsequence characters determine the types of the
* arguments arguments, as described by
* {@link FunUtil#decodeParameterCategories(String)}.
* </ul><p/>
*
* For example, <code>"pxd"</code> means "an operator with
* {@link Syntax#Property property} syntax (p) which returns a set
* (x) and takes a dimension (d) as its argument".<p/>
*
* The arguments are always read from left to right, regardless of the
* syntactic type of the operator. For example, the
* <code>"<Set>.Item(<Index>)"</code> operator
* (signature <code>"mmxn"</code>) has the
* syntax of a method-call, and takes two parameters:
* a set (x) and a numeric (n).<p/>
*
* @author jhyde
* @since 26 February, 2002
*/
public abstract class FunDefBase extends FunUtil implements FunDef {
protected final int flags;
private final String name;
final String signature;
private final String description;
protected final int returnCategory;
protected final int[] parameterCategories;
/**
* Creates an operator.
*
* @param name Name of the function, for example "Members".
* @param signature Signature of the function, for example
* "<Dimension>.Members".
* @param description Description of the function, for example
* "Returns the set of all members in a dimension."
* @param syntax Syntactic type of the operator (for
* example, function, method, infix operator)
* @param returnCategory The {@link Category} of the value returned by this
* operator.
* @param parameterCategories An array of {@link Category} codes, one for
* each parameter.
*/
FunDefBase(
String name,
String signature,
String description,
Syntax syntax,
int returnCategory,
int[] parameterCategories)
{
assert name != null;
assert syntax != null;
this.name = name;
this.signature = signature;
this.description = description;
this.flags = syntax.ordinal();
this.returnCategory = returnCategory;
this.parameterCategories = parameterCategories;
}
/**
* Creates an operator.
*
* @param name Name of the function, for example "Members".
* @param description Description of the function, for example
* "Returns the set of all members in a dimension."
* @param flags Encoding of the syntactic type, return type,
* and parameter types of this operator. The
* "Members" operator has a syntactic type
* "pxd" which means "an operator with
* {@link Syntax#Property property} syntax (p) which
* returns a set (x) and takes a dimension (d) as its
* argument".
* See {@link FunUtil#decodeSyntacticType(String)},
* {@link FunUtil#decodeReturnCategory(String)},
* {@link FunUtil#decodeParameterCategories(String)}.
*/
protected FunDefBase(
String name,
String description,
String flags)
{
this(
name,
null,
description,
decodeSyntacticType(flags),
decodeReturnCategory(flags),
decodeParameterCategories(flags));
}
/**
* Creates an operator with an explicit signature.
*
* <p>In most cases, the signature can be generated automatically, and
* you should use the constructor which creates an implicit signature,
* {@link #FunDefBase(String, String, String, String)}
* instead.
*
* @param name Name of the function, for example "Members".
* @param signature Signature of the function, for example
* "<Dimension>.Members".
* @param description Description of the function, for example
* "Returns the set of all members in a dimension."
* @param flags Encoding of the syntactic type, return type, and
* parameter types of this operator. The "Members"
* operator has a syntactic type "pxd" which means "an
* operator with {@link Syntax#Property property} syntax
* (p) which returns a set (x) and takes a dimension (d)
* as its argument". See
* {@link FunUtil#decodeSyntacticType(String)},
* {@link FunUtil#decodeReturnCategory(String)},
* {@link FunUtil#decodeParameterCategories(String)}.
*/
protected FunDefBase(
String name,
String signature,
String description,
String flags)
{
this(
name,
signature,
description,
decodeSyntacticType(flags),
decodeReturnCategory(flags),
decodeParameterCategories(flags));
}
/**
* Convenience constructor when we are created by a {@link Resolver}.
*
* @param resolver Resolver
* @param returnType Return type
* @param parameterTypes Parameter types
*/
FunDefBase(Resolver resolver, int returnType, int[] parameterTypes) {
this(
resolver.getName(),
null,
null,
resolver.getSyntax(),
returnType,
parameterTypes);
}
/**
* Copy constructor.
*
* @param funDef Function definition to copy
*/
FunDefBase(FunDef funDef) {
this(
funDef.getName(), funDef.getSignature(),
funDef.getDescription(), funDef.getSyntax(),
funDef.getReturnCategory(), funDef.getParameterCategories());
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Syntax getSyntax() {
return Syntax.class.getEnumConstants()[flags];
}
public int getReturnCategory() {
return returnCategory;
}
public int[] getParameterCategories() {
return parameterCategories;
}
public Exp createCall(Validator validator, Exp[] args) {
int[] categories = getParameterCategories();
Util.assertTrue(categories.length == args.length);
for (int i = 0; i < args.length; i++) {
args[i] = validateArg(validator, args, i, categories[i]);
}
final Type type = getResultType(validator, args);
if (type == null) {
throw Util.newInternal("could not derive type");
}
return new ResolvedFunCall(this, args, type);
}
/**
* Validates an argument to a call to this function.
*
* <p>The default implementation of this method adds an implicit
* conversion to the correct type. Derived classes may override.
*
* @param validator Validator
* @param args Arguments to this function
* @param i Ordinal of argument
* @param category Expected {@link Category category} of argument
* @return Validated argument
*/
protected Exp validateArg(
Validator validator,
Exp[] args,
int i,
int category)
{
return args[i];
}
/**
* Converts a type to a different category, maintaining as much type
* information as possible.
*
* For example, given <code>LevelType(dimension=Time, hierarchy=unknown,
* level=unkown)</code> and category=Hierarchy, returns
* <code>HierarchyType(dimension=Time)</code>.
*
* @param type Type
* @param category Desired category
* @return Type after conversion to desired category
*/
static Type castType(Type type, int category) {
switch (category) {
case Category.Logical:
return new BooleanType();
case Category.Numeric:
return new NumericType();
case Category.Numeric | Category.Integer:
return new DecimalType(Integer.MAX_VALUE, 0);
case Category.String:
return new StringType();
case Category.DateTime:
return new DateTimeType();
case Category.Symbol:
return new SymbolType();
case Category.Value:
return new ScalarType();
case Category.Cube:
if (type instanceof Cube) {
return new CubeType((Cube) type);
}
return null;
case Category.Dimension:
if (type != null) {
return DimensionType.forType(type);
}
return null;
case Category.Hierarchy:
if (type != null) {
return HierarchyType.forType(type);
}
return null;
case Category.Level:
if (type != null) {
return LevelType.forType(type);
}
return null;
case Category.Member:
if (type != null) {
final MemberType memberType = TypeUtil.toMemberType(type);
if (memberType != null) {
return memberType;
}
}
// Take a wild guess.
return MemberType.Unknown;
case Category.Tuple:
if (type != null) {
final Type memberType = TypeUtil.toMemberOrTupleType(type);
if (memberType != null) {
return memberType;
}
}
return null;
case Category.Set:
if (type != null) {
final Type memberType = TypeUtil.toMemberOrTupleType(type);
if (memberType != null) {
return new SetType(memberType);
}
}
return null;
case Category.Empty:
return new EmptyType();
default:
throw Category.instance.badValue(category);
}
}
/**
* Returns the type of a call to this function with a given set of
* arguments.<p/>
*
* The default implementation makes the coarse assumption that the return
* type is in some way related to the type of the first argument.
* Operators whose arguments don't follow the requirements of this
* implementation should override this method.<p/>
*
* If the function definition says it returns a literal type (numeric,
* string, symbol) then it's a fair guess that the function call
* returns the same kind of value.<p/>
*
* If the function definition says it returns an object type (cube,
* dimension, hierarchy, level, member) then we check the first
* argument of the function. Suppose that the function definition says
* that it returns a hierarchy, and the first argument of the function
* happens to be a member. Then it's reasonable to assume that this
* function returns a member.
*
* @param validator Validator
* @param args Arguments to the call to this operator
* @return result type of a call this function
*/
public Type getResultType(Validator validator, Exp[] args) {
Type firstArgType =
args.length > 0
? args[0].getType()
: null;
Type type = castType(firstArgType, getReturnCategory());
if (type != null) {
return type;
}
throw Util.newInternal(
"Cannot deduce type of call to function '" + this.name + "'");
}
public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
throw Util.newInternal(
"function '" + getSignature()
+ "' has not been implemented");
}
public String getSignature() {
return getSyntax().getSignature(
getName(),
getReturnCategory(),
getParameterCategories());
}
public void unparse(Exp[] args, PrintWriter pw) {
getSyntax().unparse(getName(), args, pw);
}
}
// End FunDefBase.java