Skip to content

Commit

Permalink
MONDRIAN: Member properties.
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian/": change = 143]
  • Loading branch information
julianhyde committed Sep 13, 2002
1 parent 43ab956 commit f9812e1
Show file tree
Hide file tree
Showing 20 changed files with 411 additions and 73 deletions.
15 changes: 14 additions & 1 deletion demo/FoodMart.xml
Expand Up @@ -17,7 +17,16 @@
<Level name="Store Country" column="store_country" uniqueMembers="true"/>
<Level name="Store State" column="store_state" uniqueMembers="true"/>
<Level name="Store City" column="store_city" uniqueMembers="false"/>
<Level name="Store Name" column="store_name" uniqueMembers="true"/>
<Level name="Store Name" column="store_name" uniqueMembers="true">
<Property name="Store Type" column="store_type"/>
<Property name="Store Manager" column="store_manager"/>
<Property name="Store Sqft" column="store_sqft" type="Numeric"/>
<Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/>
<Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/>
<Property name="Meat Sqft" column="meat_sqft" type="Numeric"/>
<Property name="Has coffee bar" column="coffee_bar" type="Boolean"/>
<Property name="Street address" column="store_street_address" type="String"/>
</Level>
</Hierarchy>
</Dimension>

Expand Down Expand Up @@ -127,6 +136,10 @@ CONCAT(`customer`.`fname`, " ", `customer`.`lname`)
"lname"
</SQL>
</ExpressionView>
<Property name="Gender" column="gender"/>
<Property name="Marital Status" column="marital_status"/>
<Property name="Education" column="education"/>
<Property name="Yearly Income" column="yearly_income"/>
</Level>
<!--
<Query>
Expand Down
7 changes: 7 additions & 0 deletions src/main/mondrian/olap/Evaluator.java
Expand Up @@ -25,6 +25,13 @@ public interface Evaluator {
Cube getCube();
/** Creates a new evaluator with the same state. */
Evaluator push(Member[] members);
/** Creates a new evaluator with the same state.
* Equivalent to {@link #push(Member[]) push(new Member[0])}. **/
Evaluator push();
/** Creates a new evaluator with the same state except for one member.
* Equivalent to {@link #push(Member[]) push(new Member[]
* &#124;member&#125;)}. **/
Evaluator push(Member member);
/** Restores previous evaluator. */
Evaluator pop();
/** Makes <code>member</code> the current member of its dimension. Returns
Expand Down
4 changes: 3 additions & 1 deletion src/main/mondrian/olap/ExpBase.java
Expand Up @@ -246,8 +246,10 @@ private static String getTypeDescription(int type) {
return "<Parameter>";
case CatCube:
return "<Cube>";
case CatValue:
return "<Value>";
default:
throw new Error("unkown type " + type);
throw Util.newInternal("unknown expression type " + type);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/mondrian/olap/Level.java
Expand Up @@ -34,7 +34,7 @@ public interface Level extends OlapElement {
static final int MONTHS = 3;
static final int WEEKS = 4;
static final int DAYS = 5;

Property[] getProperties();
}

// End Level.java
5 changes: 5 additions & 0 deletions src/main/mondrian/olap/Member.java
Expand Up @@ -118,6 +118,11 @@ public interface Member extends OlapElement {
* {@link Literal}.
**/
Exp getFormatStringExp();

/**
* Returns the value of the property named <code>propertyName</code>.
*/
Object getProperty(String propertyName);
}

// End Member.java
3 changes: 3 additions & 0 deletions src/main/mondrian/olap/MemberBase.java
Expand Up @@ -12,6 +12,9 @@

package mondrian.olap;
import java.util.Vector;
import java.util.HashMap;
import java.util.Map;
import java.util.Collections;

/**
* <code>MemberBase</code> is a partial implementation of {@link Member}.
Expand Down
18 changes: 18 additions & 0 deletions src/main/mondrian/olap/Mondrian.xml
Expand Up @@ -290,6 +290,7 @@ todo:
The SQL expression used to populate this level's ordinal.
</Doc>
</Object>
<Array name="properties" type="Property" min="0"/>
<Code>
public Expression getNameExp() {
if (nameExp != null) {
Expand All @@ -309,9 +310,26 @@ todo:
return null;
}
}
public Expression getPropertyExp(int i) {
return new Column(table, properties[i].column);
}
</Code>
</Element>

<Element type="Property">
<Doc>
Member property.
</Doc>
<Attribute name="name"><Doc></Doc></Attribute>
<Attribute name="column"><Doc></Doc></Attribute>
<Attribute name="type" default="String">
<Doc>todo:</Doc>
<Value>String</Value>
<Value>Numeric</Value>
<Value>Boolean</Value>
</Attribute>
</Element>

<Element type="Measure">
<Attribute name="name"><Doc></Doc></Attribute>
<Attribute name="column"><Doc>todo:</Doc></Attribute>
Expand Down
39 changes: 39 additions & 0 deletions src/main/mondrian/olap/Property.java
@@ -0,0 +1,39 @@
/*
// $Id$
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// (C) Copyright 2001-2002 Kana Software, Inc. and others.
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
// jhyde, 12 September, 2002
*/

package mondrian.olap;

/**
* <code>Property</code> is the definition of a member property.
*/
public abstract class Property {
private String name;
/** The datatype; one of {@link #TYPE_STRING}, {@link #TYPE_NUMERIC},
* {@link #TYPE_BOOLEAN}. */
private int type;
public static final int TYPE_STRING = 0;
public static final int TYPE_NUMERIC = 1;
public static final int TYPE_BOOLEAN = 2;

protected Property(String name, int type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public int getType() {
return type;
}
}

// End Property.java
147 changes: 138 additions & 9 deletions src/main/mondrian/olap/fun/BuiltinFunTable.java
Expand Up @@ -903,7 +903,12 @@ public void testCousinWrongHierarchy(FoodMartTestCase test) {
"Members '[Time].[1997]' and '[Gender].[All Genders].[M]' are not compatible as cousins");
}
});
define(new FunDefBase("CurrentMember", "<Dimension>.CurrentMember", "Returns the current member along a dimension during an iteration.", "pmd"));
define(new FunDefBase("CurrentMember", "<Dimension>.CurrentMember", "Returns the current member along a dimension during an iteration.", "pmd") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Dimension dimension = getDimensionArg(evaluator, args, 0, true);
return evaluator.getContext(dimension);
}
});
define(new FunDefBase("DefaultMember", "<Dimension>.DefaultMember", "Returns the default member of a dimension.", "pmd") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Dimension dimension = getDimensionArg(evaluator, args, 0, true);
Expand Down Expand Up @@ -1275,7 +1280,7 @@ public void testAll(TestCase test) {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Vector members = (Vector) getArg(evaluator, args, 0);
ExpBase exp = (ExpBase) getArg(evaluator, args, 1);
return sum(evaluator.push(new Member[0]), members, exp);
return sum(evaluator.push(), members, exp);
}
}));
define(new FunDefBase("Value", "<Measure>.Value", "Returns the value of a measure.", "pnm") {
Expand Down Expand Up @@ -1430,13 +1435,12 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
new FunkBase() {
public Object evaluate(Evaluator evaluator, Exp[] args) {
// todo: implement ALL
Hashtable set2 = toHashtable(
(Vector) getArg(evaluator, args, 1));
HashSet set2 = toHashSet((Vector) getArg(evaluator, args, 1));
Vector set1 = (Vector) getArg(evaluator, args, 0);
Vector result = new Vector();
for (int i = 0, count = set1.size(); i < count; i++) {
Object o = set1.elementAt(i);
if (set2.get(o) == null) {
if (!set2.contains(o)) {
result.addElement(o);
}
}
Expand All @@ -1450,7 +1454,7 @@ public Object evaluate(Evaluator evaluator, Exp[] args) {
Vector members = (Vector) getArg(evaluator, args, 0);
Exp exp = args[1];
Vector result = new Vector();
Evaluator evaluator2 = evaluator.push(new Member[0]);
Evaluator evaluator2 = evaluator.push();
for (int i = 0, count = members.size(); i < count; i++) {
Object o = members.elementAt(i);
if (o instanceof Member) {
Expand Down Expand Up @@ -1958,6 +1962,72 @@ protected FunDef resolve(Exp[] args, int[] conversionCount) {
}
});

define(new ResolverBase(
"Properties",
"<Member>.Properties(<String Expression>)",
"Returns the value of a member property.",
FunDef.TypeMethod) {
public FunDef resolve(Exp[] args, int[] conversionCount) {
final int[] argTypes = new int[]{Exp.CatMember, Exp.CatString | Exp.CatExpression};
if (args.length != 2 ||
args[0].getType() != Exp.CatMember ||
args[1].getType() != Exp.CatString) {
return null;
}
int returnType;
if (args[1] instanceof Literal) {
String propertyName = (String) ((Literal) args[1]).getValue();
Hierarchy hierarchy = args[0].getHierarchy();
Level[] levels = hierarchy.getLevels();
Property property = lookupProperty(
levels[levels.length - 1], propertyName);
if (property == null) {
// we'll likely get a runtime error
returnType = Exp.CatValue;
} else {
switch (property.getType()) {
case Property.TYPE_BOOLEAN:
returnType = Exp.CatLogical;
break;
case Property.TYPE_NUMERIC:
returnType = Exp.CatNumeric;
break;
case Property.TYPE_STRING:
returnType = Exp.CatString;
break;
default:
throw Util.newInternal("Unknown property type " + property.getType());
}
}
} else {
returnType = Exp.CatValue;
}
return new PropertiesFunDef(name, signature, description, syntacticType, returnType, argTypes);
}
public void testPropertiesExpr(FoodMartTestCase test) {
String s = test.executeExpr(
"[Store].[USA].[CA].[Beverly Hills].[Store 6].Properties(\"Store Type\")");
test.assertEquals("Gourmet Supermarket", s);
}

/** Tests that non-existent property throws an error. **/
public void testPropertiesNonExistent(FoodMartTestCase test) {
test.assertExprThrows(
"[Store].[USA].[CA].[Beverly Hills].[Store 6].Properties(\"Foo\")",
"Property 'Foo' is not valid for");
}

public void testPropertiesFilter(FoodMartTestCase test) {
Result result = test.execute(
"SELECT { [Store Sales] } ON COLUMNS," + nl +
" TOPCOUNT( Filter( [Store].[Store Name].Members," + nl +
" [Store].CurrentMember.Properties(\"Store Type\") = \"Supermarket\" )," + nl +
" 10, [Store Sales]) ON ROWS" + nl +
"FROM [Sales]");
test.assertEquals(8, result.getAxes()[1].positions.length);
}
});

//
// PARAMETER FUNCTIONS
if (false) define(new FunDefBase("Parameter", "Parameter(<Name>, <Type>, <DefaultValue>, <Description>)", "Returns default value of parameter.", "f*"));
Expand Down Expand Up @@ -2005,22 +2075,32 @@ public double evaluate(double d1, double d2) {
define(new FunDefBase("OR", "<Logical Expression> OR <Logical Expression>", "Returns the disjunction of two conditions.", "ibbb"));
define(new FunDefBase("XOR", "<Logical Expression> XOR <Logical Expression>", "Returns whether two conditions are mutually exclusive.", "ibbb"));
define(new FunDefBase("NOT", "NOT <Logical Expression>", "Returns the negation of a condition.", "Pbb"));
define(new FunDefBase("=", "<String Expression> = <String Expression>", "Returns whether two expressions are equal.", "ibSS") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
String o0 = getStringArg(evaluator, args, 0, null),
o1 = getStringArg(evaluator, args, 1, null);
return toBoolean(o0.equals(o1));
}
});
define(new FunDefBase("=", "<Numeric Expression> = <Numeric Expression>", "Returns whether two expressions are equal.", "ibnn"));
define(new FunDefBase("<>", "<String Expression> <> <String Expression>", "Returns whether two expressions are not equal.", "ibSS"));
define(new FunDefBase("<>", "<Numeric Expression> <> <Numeric Expression>", "Returns whether two expressions are not equal.", "ibnn"));
define(new FunDefBase("<", "<Numeric Expression> < <Numeric Expression>", "Returns whether an expression is less than another.", "ibnn"));
define(new FunDefBase("<=", "<Numeric Expression> <= <Numeric Expression>", "Returns whether an expression is less than or equal to another.", "ibnn"));
define(new FunDefBase(">", "<Numeric Expression> > <Numeric Expression>", "Returns whether an expression is greater than another.", "ibnn") {
public Object evaluate(Evaluator evaluator, Exp[] args) {
Double o0 = getDoubleArg(evaluator, args, 0),
o1 = getDoubleArg(evaluator, args, 1);
return o0.doubleValue() > o1.doubleValue() ?
Boolean.TRUE :
Boolean.FALSE;
return toBoolean(o0.doubleValue() > o1.doubleValue());
}
});
define(new FunDefBase(">=", "<Numeric Expression> >= <Numeric Expression>", "Returns whether an expression is greater than or equal to another.", "ibnn"));
}

private Boolean toBoolean(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}

public TestSuite suite() {
TestSuite suite = new TestSuite("builtin functions");
for (Iterator resolverses = upperName2Resolvers.values().iterator();
Expand Down Expand Up @@ -2066,6 +2146,55 @@ void test() {
"select <a member>.UniqueName from Sales",
};
}

private boolean isValidProperty(
Member member, String propertyName) {
return lookupProperty(member.getLevel(), propertyName) != null;
}

/**
* Finds a member property called <code>propertyName</code> at, or above,
* <code>level</code>.
*/
private Property lookupProperty(
Level level, String propertyName) {
do {
Property[] properties = level.getProperties();
for (int i = 0; i < properties.length; i++) {
Property property = properties[i];
if (property.getName().equals(propertyName)) {
return property;
}
}
level = level.getParentLevel();
} while (level != null);
return null;
}

private class PropertiesFunDef extends FunDefBase {
public PropertiesFunDef(
String name, String signature, String description,
int syntacticType, int returnType, int[] parameterTypes) {
super(name, signature, description, syntacticType, returnType, parameterTypes);
}

public Object evaluate(Evaluator evaluator, Exp[] args) {
Member member = getMemberArg(evaluator, args, 0, true);
String s = getStringArg(evaluator, args, 1, null);
Object o = member.getProperty(s);
if (o == null) {
if (isValidProperty(member, s)) {
o = member.getHierarchy().getNullMember();
} else {
throw new MondrianEvaluationException(
"Property '" + s +
"' is not valid for member '" + member + "'");
}
}
return o;
}
}

}

// End BuiltinFunTable.java
1 change: 1 addition & 0 deletions src/main/mondrian/olap/fun/FunDefBase.java
Expand Up @@ -28,6 +28,7 @@ class FunDefBase extends FunUtil implements FunDef {
private String description;
protected int returnType;
protected int[] parameterTypes;
boolean isAbstract = false;

FunDefBase(
String name, String signature, String description,
Expand Down

0 comments on commit f9812e1

Please sign in to comment.