/*
// 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) 2001-2005 Julian Hyde
// Copyright (C) 2005-2016 Pentaho and others
// All Rights Reserved.
*/
package mondrian.rolap;
import mondrian.mdx.HierarchyExpr;
import mondrian.mdx.ResolvedFunCall;
import mondrian.olap.*;
import mondrian.olap.fun.VisualTotalsFunDef.VisualTotalMember;
import mondrian.util.Bug;
/**
* RolapCubeMember wraps RolapMembers and binds them to a specific cube.
* RolapCubeMember wraps or overrides RolapMember methods that directly
* reference the wrapped Member. Methods that only contain calls to other
* methods do not need wrapped.
*
* @author Will Gorman, 19 October 2007
*/
public class RolapCubeMember
extends DelegatingRolapMember
implements RolapMemberInCube
{
protected final RolapCubeLevel cubeLevel;
protected final RolapCubeMember parentCubeMember;
/**
* Creates a RolapCubeMember.
*
* @param parent Parent member
* @param member Member of underlying (non-cube) hierarchy
* @param cubeLevel Level
*/
public RolapCubeMember(
RolapCubeMember parent, RolapMember member, RolapCubeLevel cubeLevel)
{
super(member);
this.parentCubeMember = parent;
this.cubeLevel = cubeLevel;
assert !member.isAll() || getClass() != RolapCubeMember.class;
}
@Override
public String getUniqueName() {
// We are making a hard design decision to compute uniqueName every
// time it is requested rather than storing it. RolapCubeMember is thin
// wrapper, so cheap to construct that we don't need to cache instances.
//
// Storing uniqueName would make creation of RolapCubeMember more
// expensive and use significantly more memory, so we don't do that.
// That meakes each call to getUniqueName more expensive, so we try to
// minimize the number of calls to this method.
return cubeLevel.getHierarchy().convertMemberName(
member.getUniqueName());
}
/**
* Returns the underlying member. This is a member of a shared dimension and
* does not belong to a cube.
*
* @return Underlying member
*/
public final RolapMember getRolapMember() {
return member;
}
// final is important for performance
public final RolapCube getCube() {
return cubeLevel.getCube();
}
public final RolapCubeMember getDataMember() {
RolapMember member = (RolapMember) super.getDataMember();
if (member == null) {
return null;
}
return new RolapCubeMember(parentCubeMember, member, cubeLevel);
}
public int compareTo(Object o) {
// light wrapper around rolap member compareTo
RolapCubeMember other = null;
if (o instanceof VisualTotalMember) {
// REVIEW: Maybe VisualTotalMember should extend/implement
// RolapCubeMember. Then we can remove special-cases such as this.
other = (RolapCubeMember) ((VisualTotalMember) o).getMember();
} else {
other = (RolapCubeMember) o;
}
return member.compareTo(other.member);
}
public String toString() {
return getUniqueName();
}
public int hashCode() {
return member.hashCode();
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof RolapCubeMember) {
return equals((RolapCubeMember) o);
}
if (o instanceof Member) {
assert !Bug.BugSegregateRolapCubeMemberFixed;
return getUniqueName().equals(((Member) o).getUniqueName());
}
return false;
}
public boolean equals(OlapElement o) {
return o.getClass() == RolapCubeMember.class
&& equals((RolapCubeMember) o);
}
private boolean equals(RolapCubeMember that) {
assert that != null; // public method should have checked
// Assume that RolapCubeLevel is canonical. (Besides, its equals method
// is very slow.)
return this.cubeLevel == that.cubeLevel
&& this.member.equals(that.member);
}
// override with stricter return type; final important for performance
public final RolapCubeHierarchy getHierarchy() {
return cubeLevel.getHierarchy();
}
// override with stricter return type; final important for performance
public final RolapCubeDimension getDimension() {
return cubeLevel.getDimension();
}
/**
* {@inheritDoc}
*
* <p>This method is central to how RolapCubeMember works. It allows
* a member from the cache to be used within different usages of the same
* shared dimension. The cache member is the same, but the RolapCubeMembers
* wrapping the cache member report that they belong to different levels,
* and hence different hierarchies, dimensions, and cubes.
*/
// override with stricter return type; final important for performance
public final RolapCubeLevel getLevel() {
return cubeLevel;
}
public void setProperty(String name, Object value) {
synchronized (this) {
super.setProperty(name, value);
}
}
public Object getPropertyValue(String propertyName, boolean matchCase) {
// we need to wrap these children as rolap cube members
Property property = Property.lookup(propertyName, matchCase);
if (property != null) {
switch (property.ordinal) {
case Property.DIMENSION_UNIQUE_NAME_ORDINAL:
return getDimension().getUniqueName();
case Property.HIERARCHY_UNIQUE_NAME_ORDINAL:
return getHierarchy().getUniqueName();
case Property.LEVEL_UNIQUE_NAME_ORDINAL:
return getLevel().getUniqueName();
case Property.MEMBER_UNIQUE_NAME_ORDINAL:
return getUniqueName();
case Property.MEMBER_NAME_ORDINAL:
return getName();
case Property.MEMBER_CAPTION_ORDINAL:
return getCaption();
case Property.PARENT_UNIQUE_NAME_ORDINAL:
return parentCubeMember == null
? null
: parentCubeMember.getUniqueName();
case Property.MEMBER_KEY_ORDINAL:
case Property.KEY_ORDINAL:
return this == this.getHierarchy().getAllMember() ? 0
: getKey();
}
}
// fall through to rolap member
return member.getPropertyValue(propertyName, matchCase);
}
public Object getPropertyValue(String propertyName) {
return this.getPropertyValue(propertyName, true);
}
public final RolapCubeMember getParentMember() {
return parentCubeMember;
}
// this method is overridden to make sure that any HierarchyExpr returns
// the cube hierarchy vs. shared hierarchy. this is the case for
// SqlMemberSource.RolapParentChildMemberNoClosure
public Exp getExpression() {
Exp exp = member.getExpression();
if (exp instanceof ResolvedFunCall) {
// convert any args to RolapCubeHierarchies
ResolvedFunCall fcall = (ResolvedFunCall)exp;
for (int i = 0; i < fcall.getArgCount(); i++) {
if (fcall.getArg(i) instanceof HierarchyExpr) {
HierarchyExpr expr = (HierarchyExpr)fcall.getArg(i);
if (expr.getHierarchy().equals(
member.getHierarchy()))
{
fcall.getArgs()[i] =
new HierarchyExpr(this.getHierarchy());
}
}
}
}
return exp;
}
public OlapElement lookupChild(
SchemaReader schemaReader,
Id.Segment childName,
MatchType matchType)
{
return
schemaReader.lookupMemberChildByName(this, childName, matchType);
}
}
// End RolapCubeMember.java