Skip to content

Commit

Permalink
MONDRIAN: Implement Generate(<Set>, <String>[, <String>]), StrToSet.
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian/": change = 8287]
  • Loading branch information
julianhyde committed Dec 9, 2006
1 parent bfc4979 commit 59c4a40
Show file tree
Hide file tree
Showing 12 changed files with 560 additions and 355 deletions.
7 changes: 7 additions & 0 deletions src/main/mondrian/calc/impl/ConstantCalc.java
Expand Up @@ -68,6 +68,13 @@ public static ConstantCalc constantInteger(int i) {
return new ConstantCalc(new NumericType(), i);
}

/**
* Creates an expression which evaluates to a string.
*/
public static StringCalc constantString(String s) {
return new ConstantCalc(new StringType(), s);
}

/**
* Creates an expression which evaluates to null.
*/
Expand Down
9 changes: 2 additions & 7 deletions src/main/mondrian/mdx/NamedSetExpr.java
Expand Up @@ -80,14 +80,9 @@ public List evaluateList(Evaluator evaluator) {
}

public boolean dependsOn(Dimension dimension) {
/*
BCHOW, JVS: Given that named sets are never re-evaluated within the scope of a query,
dependency inference seems meaningless for them, so returning false is
probably the right thing to do always (unless the dependencies are supposed to
have some use outside of caching).
*/
// Given that a named set is never re-evaluated within the scope
// of a query, effectively it's independent of all dimensions.
return false;
// TODO: namedSet.getExp().dependsOn(dimension);
}
};
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/mondrian/olap/fun/BuiltinFunTable.java
Expand Up @@ -993,7 +993,8 @@ public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {

define(FilterFunDef.instance);

define(GenerateFunDef.Resolver);
define(GenerateFunDef.ListResolver);
define(GenerateFunDef.StringResolver);
define(HeadTailFunDef.HeadResolver);

define(HierarchizeFunDef.Resolver);
Expand Down
32 changes: 25 additions & 7 deletions src/main/mondrian/olap/fun/FunInfo.java
Expand Up @@ -14,6 +14,7 @@
import mondrian.olap.Syntax;

import java.util.*;
import java.lang.reflect.Array;

/**
* Support class for the {@link mondrian.tui.CmdRunner} allowing one to view
Expand Down Expand Up @@ -137,14 +138,31 @@ public int[][] getParameterCategories() {

public int compareTo(FunInfo fi) {
int c = this.name.compareTo(fi.name);
if (c == 0) {
final String pc = this.parameterTypes == null ? "" :
Arrays.asList(this.parameterTypes).toString();
final String otherPc = fi.parameterTypes == null ? "" :
Arrays.asList(fi.parameterTypes).toString();
c = pc.compareTo(otherPc);
if (c != 0) {
return c;
}
return c;
final List pcList = toList(this.getParameterCategories());
final String pc = pcList.toString();
final List otherPcList = toList(fi.getParameterCategories());
final String otherPc = otherPcList.toString();
return pc.compareTo(otherPc);
}

private static List toList(Object a) {
final List<Object> list = new ArrayList<Object>();
if (a == null) {
return list;
}
final int length = Array.getLength(a);
for (int i = 0; i < length; i++) {
final Object o = Array.get(a, i);
if (o.getClass().isArray()) {
list.add(toList(o));
} else {
list.add(o);
}
}
return list;
}
}

Expand Down
223 changes: 162 additions & 61 deletions src/main/mondrian/olap/fun/GenerateFunDef.java
Expand Up @@ -10,14 +10,14 @@
package mondrian.olap.fun;

import mondrian.olap.*;
import mondrian.olap.type.Type;
import mondrian.olap.type.TypeUtil;
import mondrian.olap.type.SetType;
import mondrian.olap.type.TupleType;
import mondrian.olap.type.*;
import mondrian.calc.Calc;
import mondrian.calc.ExpCompiler;
import mondrian.calc.ListCalc;
import mondrian.calc.StringCalc;
import mondrian.calc.impl.AbstractListCalc;
import mondrian.calc.impl.ConstantCalc;
import mondrian.calc.impl.AbstractStringCalc;
import mondrian.mdx.ResolvedFunCall;

import java.util.List;
Expand All @@ -33,12 +33,22 @@
* @since Mar 23, 2006
*/
class GenerateFunDef extends FunDefBase {
static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver(
static final ReflectiveMultiResolver ListResolver =
new ReflectiveMultiResolver(
"Generate",
"Generate(<Set1>, <Set2>[, ALL])",
"Applies a set to each member of another set and joins the resulting sets by union.",
new String[] {"fxxx", "fxxxy"},
GenerateFunDef.class);

static final ReflectiveMultiResolver StringResolver =
new ReflectiveMultiResolver(
"Generate",
"Generate(<Set>, <String>[, <String>])",
"Applies a set to a string expression and joins resulting sets by string concatenation.",
new String[] {"fSxS", "fSxSS"},
GenerateFunDef.class);

private static final String[] ReservedWords = new String[] {"ALL"};

public GenerateFunDef(FunDef dummyFunDef) {
Expand All @@ -47,77 +57,168 @@ public GenerateFunDef(FunDef dummyFunDef) {

public Type getResultType(Validator validator, Exp[] args) {
final Type type = args[1].getType();
final Type memberType = TypeUtil.toMemberOrTupleType(type);
return new SetType(memberType);
if (type instanceof StringType) {
// Generate(<Set>, <String>[, <String>])
return type;
} else {
final Type memberType = TypeUtil.toMemberOrTupleType(type);
return new SetType(memberType);
}
}

public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
final String literalArg = getLiteralArg(call, 2, "", ReservedWords);
final boolean all = literalArg.equalsIgnoreCase("ALL");
final boolean tuple = ((SetType) listCalc1.getType()).getElementType()
instanceof TupleType;

return new AbstractListCalc(call, new Calc[] {listCalc1, listCalc2}) {
public List evaluateList(Evaluator evaluator) {
final List list1 = listCalc1.evaluateList(evaluator);
final ListCalc listCalc = compiler.compileList(call.getArg(0));
final boolean tuple = ((SetType) listCalc.getType()).getElementType()
instanceof TupleType;
if (call.getArg(1).getType() instanceof StringType) {
final StringCalc stringCalc = compiler.compileString(call.getArg(1));
final StringCalc delimCalc;
if (call.getArgCount() == 3) {
delimCalc = compiler.compileString(call.getArg(2));
} else {
delimCalc = ConstantCalc.constantString("");
}

return new GenerateStringCalcImpl(
call, listCalc, stringCalc, tuple, delimCalc);
} else {
final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
final String literalArg = getLiteralArg(call, 2, "", ReservedWords);
final boolean all = literalArg.equalsIgnoreCase("ALL");

return new GenerateListCalcImpl(
call, listCalc, listCalc2, tuple, all);
}
}

private static class GenerateListCalcImpl extends AbstractListCalc {
private final ListCalc listCalc1;
private final ListCalc listCalc2;
private final boolean tuple;
private final boolean all;

public GenerateListCalcImpl(
ResolvedFunCall call,
ListCalc listCalc1,
ListCalc listCalc2,
boolean tuple, boolean all) {
super(call, new Calc[]{listCalc1, listCalc2});
this.listCalc1 = listCalc1;
this.listCalc2 = listCalc2;
this.tuple = tuple;
this.all = all;
}

public List evaluateList(Evaluator evaluator) {
List<Object> result = new ArrayList<Object>();
if (tuple) {
final List<Member[]> list1 = listCalc1.evaluateList(evaluator);
final Evaluator evaluator2 = evaluator.push();
List result = new ArrayList();
if (tuple) {
if (all) {
for (Object aList1 : list1) {
final Member[] members = (Member[]) aList1;
evaluator2.setContext(members);
final List result2 = listCalc2.evaluateList(
evaluator2);
result.addAll(result2);
}
} else {
final Set<Object> emitted = new HashSet<Object>();
for (Object aList1 : list1) {
final Member[] members = (Member[]) aList1;
evaluator2.setContext(members);
final List result2 = listCalc2.evaluateList(
evaluator2);
for (Object row : result2) {
if (emitted.add(row)) {
result.add(row);
}
if (all) {
for (Member[] members : list1) {
evaluator2.setContext(members);
final List<Object> result2 =
listCalc2.evaluateList(evaluator2);
result.addAll(result2);
}
} else {
final Set<Object> emitted = new HashSet<Object>();
for (Member[] members : list1) {
evaluator2.setContext(members);
final List<Object> result2 =
listCalc2.evaluateList(evaluator2);
for (Object row : result2) {
if (emitted.add(row)) {
result.add(row);
}
}
}
}
} else {
final List<Member> list1 = listCalc1.evaluateList(evaluator);
final Evaluator evaluator2 = evaluator.push();
if (all) {
for (Member member : list1) {
evaluator2.setContext(member);
final List<Object> result2 =
listCalc2.evaluateList(evaluator2);
result.addAll(result2);
}
} else {
if (all) {
for (Object aList1 : list1) {
final Member member = (Member) aList1;
evaluator2.setContext(member);
final List result2 = listCalc2.evaluateList(
evaluator2);
result.addAll(result2);
}
} else {
Set<Object> emitted = new HashSet<Object>();
for (Object aList1 : list1) {
final Member member = (Member) aList1;
evaluator2.setContext(member);
final List result2 = listCalc2.evaluateList(
evaluator2);
for (Object row : result2) {
if (emitted.add(row)) {
result.add(row);
}
Set<Object> emitted = new HashSet<Object>();
for (Member member : list1) {
evaluator2.setContext(member);
final List<Object> result2 = listCalc2.evaluateList(evaluator2);
for (Object row : result2) {
if (emitted.add(row)) {
result.add(row);
}
}
}
}
return result;
}
return result;
}

public boolean dependsOn(Dimension dimension) {
return anyDependsButFirst(getCalcs(), dimension);
}
}

private static class GenerateStringCalcImpl extends AbstractStringCalc {
private final ListCalc listCalc;
private final StringCalc stringCalc;
private final boolean tuple;
private final StringCalc sepCalc;

public GenerateStringCalcImpl(
ResolvedFunCall call,
ListCalc listCalc,
StringCalc stringCalc,
boolean tuple, StringCalc sepCalc) {
super(call, new Calc[]{listCalc, stringCalc});
this.listCalc = listCalc;
this.stringCalc = stringCalc;
this.tuple = tuple;
this.sepCalc = sepCalc;
}

public boolean dependsOn(Dimension dimension) {
return anyDependsButFirst(getCalcs(), dimension);
public String evaluateString(Evaluator evaluator) {
StringBuilder buf = new StringBuilder();
int k = 0;
if (tuple) {
final List<Member[]> list1 = listCalc.evaluateList(evaluator);
final Evaluator evaluator2 = evaluator.push();
for (Member[] members : list1) {
evaluator2.setContext(members);
if (k++ > 0) {
String sep = sepCalc.evaluateString(evaluator2);
buf.append(sep);
}
final String result2 =
stringCalc.evaluateString(evaluator2);
buf.append(result2);
}
} else {
final List<Member> list1 = listCalc.evaluateList(evaluator);
final Evaluator evaluator2 = evaluator.push();
for (Member member : list1) {
evaluator2.setContext(member);
if (k++ > 0) {
String sep = sepCalc.evaluateString(evaluator2);
buf.append(sep);
}
final String result2 =
stringCalc.evaluateString(evaluator2);
buf.append(result2);
}
}
};
return buf.toString();
}

public boolean dependsOn(Dimension dimension) {
return anyDependsButFirst(getCalcs(), dimension);
}
}
}

Expand Down
15 changes: 10 additions & 5 deletions src/main/mondrian/olap/fun/StrToSetFunDef.java
Expand Up @@ -178,10 +178,11 @@ private static List<Member> parseMemberList(
}
}

public static final int BEFORE_SEG = 0;
public static final int IN_BRACKET_SEG = 1;
public static final int AFTER_SEG = 2;
public static final int IN_SEG = 3;
// State values
private static final int BEFORE_SEG = 0;
private static final int IN_BRACKET_SEG = 1;
private static final int AFTER_SEG = 2;
private static final int IN_SEG = 3;

static int parseMember(
Evaluator evaluator,
Expand Down Expand Up @@ -262,7 +263,7 @@ static int parseMember(
switch (c) {
case ' ':
// Skip over any spaces
// TODO: testcase '[foo] . [bar]'
// TODO: test this case: '[foo] . [bar]'
++i;
break;
case '.':
Expand Down Expand Up @@ -398,6 +399,10 @@ public FunDef resolve(
}
return new StrToSetFunDef(argTypes);
}

public FunDef getFunDef() {
return new StrToSetFunDef(new int[] {Category.String});
}
}
}

Expand Down

0 comments on commit 59c4a40

Please sign in to comment.