/*
* 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-2013 Pentaho Corporation.. All rights reserved.
*/
package mondrian.olap.fun;
import mondrian.calc.*;
import mondrian.calc.impl.AbstractMemberCalc;
import mondrian.calc.impl.AbstractTupleCalc;
import mondrian.mdx.ResolvedFunCall;
import mondrian.olap.*;
import mondrian.olap.type.*;
import java.util.ArrayList;
import java.util.List;
/**
* Definition of the <code><Set>.Item</code> MDX function.
*
* <p>Syntax:
* <blockquote><code>
* <Set>.Item(<Index>)<br/>
* <Set>.Item(<String Expression> [, ...])
* </code></blockquote>
*
* @author jhyde
* @since Mar 23, 2006
*/
class SetItemFunDef extends FunDefBase {
static final Resolver intResolver =
new ReflectiveMultiResolver(
"Item",
"<Set>.Item(<Index>)",
"Returns a tuple from the set specified in <Set>. The tuple to be returned is specified by the zero-based position of the tuple in the set in <Index>.",
new String[] {"mmxn"},
SetItemFunDef.class);
static final Resolver stringResolver =
new ResolverBase(
"Item",
"<Set>.Item(<String> [, ...])",
"Returns a tuple from the set specified in <Set>. The tuple to be returned is specified by the member name (or names) in <String>.",
Syntax.Method)
{
public FunDef resolve(
Exp[] args,
Validator validator,
List<Conversion> conversions)
{
if (args.length < 1) {
return null;
}
final Exp setExp = args[0];
if (!(setExp.getType() instanceof SetType)) {
return null;
}
final SetType setType = (SetType) setExp.getType();
final int arity = setType.getArity();
// All args must be strings.
for (int i = 1; i < args.length; i++) {
if (!validator.canConvert(
i, args[i], Category.String, conversions))
{
return null;
}
}
if (args.length - 1 != arity) {
throw Util.newError(
"Argument count does not match set's cardinality " + arity);
}
final int category = arity == 1 ? Category.Member : Category.Tuple;
FunDef dummy = createDummyFunDef(this, category, args);
return new SetItemFunDef(dummy);
}
};
public SetItemFunDef(FunDef dummyFunDef) {
super(dummyFunDef);
}
public Type getResultType(Validator validator, Exp[] args) {
SetType setType = (SetType) args[0].getType();
return setType.getElementType();
}
public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
final ListCalc listCalc =
compiler.compileList(call.getArg(0));
final Type elementType =
((SetType) listCalc.getType()).getElementType();
final boolean isString =
call.getArgCount() < 2
|| call.getArg(1).getType() instanceof StringType;
final IntegerCalc indexCalc;
final StringCalc[] stringCalcs;
List<Calc> calcList = new ArrayList<Calc>();
calcList.add(listCalc);
if (isString) {
indexCalc = null;
stringCalcs = new StringCalc[call.getArgCount() - 1];
for (int i = 0; i < stringCalcs.length; i++) {
stringCalcs[i] = compiler.compileString(call.getArg(i + 1));
calcList.add(stringCalcs[i]);
}
} else {
stringCalcs = null;
indexCalc = compiler.compileInteger(call.getArg(1));
calcList.add(indexCalc);
}
Calc[] calcs = calcList.toArray(new Calc[calcList.size()]);
if (elementType instanceof TupleType) {
final TupleType tupleType = (TupleType) elementType;
final Member[] nullTuple = makeNullTuple(tupleType);
if (isString) {
return new AbstractTupleCalc(call, calcs) {
public Member[] evaluateTuple(Evaluator evaluator) {
final int savepoint = evaluator.savepoint();
final TupleList list;
try {
evaluator.setNonEmpty(false);
list = listCalc.evaluateList(evaluator);
assert list != null;
} finally {
evaluator.restore(savepoint);
}
try {
String[] results = new String[stringCalcs.length];
for (int i = 0; i < stringCalcs.length; i++) {
results[i] =
stringCalcs[i].evaluateString(evaluator);
}
listLoop:
for (List<Member> members : list) {
for (int j = 0; j < results.length; j++) {
String result = results[j];
final Member member = members.get(j);
if (!matchMember(member, result)) {
continue listLoop;
}
}
// All members match. Return the current one.
return members.toArray(
new Member[members.size()]);
}
} finally {
evaluator.restore(savepoint);
}
// We use 'null' to represent the null tuple. Don't
// know why.
return null;
}
};
} else {
return new AbstractTupleCalc(call, calcs) {
public Member[] evaluateTuple(Evaluator evaluator) {
final int savepoint = evaluator.savepoint();
final TupleList list;
try {
evaluator.setNonEmpty(false);
list =
listCalc.evaluateList(evaluator);
} finally {
evaluator.restore(savepoint);
}
assert list != null;
try {
final int index =
indexCalc.evaluateInteger(evaluator);
int listSize = list.size();
if (index >= listSize || index < 0) {
return nullTuple;
} else {
final List<Member> members =
list.get(index);
return members.toArray(
new Member[members.size()]);
}
} finally {
evaluator.restore(savepoint);
}
}
};
}
} else {
final MemberType memberType = (MemberType) elementType;
final Member nullMember = makeNullMember(memberType);
if (isString) {
return new AbstractMemberCalc(call, calcs) {
public Member evaluateMember(Evaluator evaluator) {
final int savepoint = evaluator.savepoint();
final List<Member> list;
try {
evaluator.setNonEmpty(false);
list =
listCalc.evaluateList(evaluator).slice(0);
assert list != null;
} finally {
evaluator.restore(savepoint);
}
try {
final String result =
stringCalcs[0].evaluateString(evaluator);
for (Member member : list) {
if (matchMember(member, result)) {
return member;
}
}
return nullMember;
} finally {
evaluator.restore(savepoint);
}
}
};
} else {
return new AbstractMemberCalc(call, calcs) {
public Member evaluateMember(Evaluator evaluator) {
final int savepoint = evaluator.savepoint();
final List<Member> list;
try {
evaluator.setNonEmpty(false);
list =
listCalc.evaluateList(evaluator).slice(0);
assert list != null;
} finally {
evaluator.restore(savepoint);
}
try {
final int index =
indexCalc.evaluateInteger(evaluator);
int listSize = list.size();
if (index >= listSize || index < 0) {
return nullMember;
} else {
return list.get(index);
}
} finally {
evaluator.restore(savepoint);
}
}
};
}
}
}
private static boolean matchMember(final Member member, String name) {
return member.getName().equals(name);
}
}
// End SetItemFunDef.java