001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2001-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho and others
009// All Rights Reserved.
010*/
011package mondrian.rolap;
012
013import mondrian.mdx.HierarchyExpr;
014import mondrian.mdx.ResolvedFunCall;
015import mondrian.olap.*;
016import mondrian.olap.fun.VisualTotalsFunDef.VisualTotalMember;
017import mondrian.util.Bug;
018
019/**
020 * RolapCubeMember wraps RolapMembers and binds them to a specific cube.
021 * RolapCubeMember wraps or overrides RolapMember methods that directly
022 * reference the wrapped Member.  Methods that only contain calls to other
023 * methods do not need wrapped.
024 *
025 * @author Will Gorman, 19 October 2007
026 */
027public class RolapCubeMember
028    extends DelegatingRolapMember
029    implements RolapMemberInCube
030{
031    protected final RolapCubeLevel cubeLevel;
032    protected final RolapCubeMember parentCubeMember;
033
034    /**
035     * Creates a RolapCubeMember.
036     *
037     * @param parent Parent member
038     * @param member Member of underlying (non-cube) hierarchy
039     * @param cubeLevel Level
040     */
041    public RolapCubeMember(
042        RolapCubeMember parent, RolapMember member, RolapCubeLevel cubeLevel)
043    {
044        super(member);
045        this.parentCubeMember = parent;
046        this.cubeLevel = cubeLevel;
047        assert !member.isAll() || getClass() != RolapCubeMember.class;
048    }
049
050    @Override
051    public String getUniqueName() {
052        // We are making a hard design decision to compute uniqueName every
053        // time it is requested rather than storing it. RolapCubeMember is thin
054        // wrapper, so cheap to construct that we don't need to cache instances.
055        //
056        // Storing uniqueName would make creation of RolapCubeMember more
057        // expensive and use significantly more memory, so we don't do that.
058        // That meakes each call to getUniqueName more expensive, so we try to
059        // minimize the number of calls to this method.
060        return cubeLevel.getHierarchy().convertMemberName(
061            member.getUniqueName());
062    }
063
064    /**
065     * Returns the underlying member. This is a member of a shared dimension and
066     * does not belong to a cube.
067     *
068     * @return Underlying member
069     */
070    public final RolapMember getRolapMember() {
071        return member;
072    }
073
074    // final is important for performance
075    public final RolapCube getCube() {
076        return cubeLevel.getCube();
077    }
078
079    public final RolapCubeMember getDataMember() {
080        RolapMember member = (RolapMember) super.getDataMember();
081        if (member == null) {
082            return null;
083        }
084        return new RolapCubeMember(parentCubeMember, member, cubeLevel);
085    }
086
087    public int compareTo(Object o) {
088        // light wrapper around rolap member compareTo
089        RolapCubeMember other = null;
090        if (o instanceof VisualTotalMember) {
091            // REVIEW: Maybe VisualTotalMember should extend/implement
092            // RolapCubeMember. Then we can remove special-cases such as this.
093            other = (RolapCubeMember) ((VisualTotalMember) o).getMember();
094        } else {
095            other = (RolapCubeMember) o;
096        }
097        return member.compareTo(other.member);
098    }
099
100    public String toString() {
101        return getUniqueName();
102    }
103
104    public int hashCode() {
105        return member.hashCode();
106    }
107
108    public boolean equals(Object o) {
109        if (o == this) {
110            return true;
111        }
112        if (o instanceof RolapCubeMember) {
113            return equals((RolapCubeMember) o);
114        }
115        if (o instanceof Member) {
116            assert !Bug.BugSegregateRolapCubeMemberFixed;
117            return getUniqueName().equals(((Member) o).getUniqueName());
118        }
119        return false;
120    }
121
122    public boolean equals(OlapElement o) {
123        return o.getClass() == RolapCubeMember.class
124            && equals((RolapCubeMember) o);
125    }
126
127    private boolean equals(RolapCubeMember that) {
128        assert that != null; // public method should have checked
129        // Assume that RolapCubeLevel is canonical. (Besides, its equals method
130        // is very slow.)
131        return this.cubeLevel == that.cubeLevel
132               && this.member.equals(that.member);
133    }
134
135    // override with stricter return type; final important for performance
136    public final RolapCubeHierarchy getHierarchy() {
137        return cubeLevel.getHierarchy();
138    }
139
140    // override with stricter return type; final important for performance
141    public final RolapCubeDimension getDimension() {
142        return cubeLevel.getDimension();
143    }
144
145    /**
146     * {@inheritDoc}
147     *
148     * <p>This method is central to how RolapCubeMember works. It allows
149     * a member from the cache to be used within different usages of the same
150     * shared dimension. The cache member is the same, but the RolapCubeMembers
151     * wrapping the cache member report that they belong to different levels,
152     * and hence different hierarchies, dimensions, and cubes.
153     */
154    // override with stricter return type; final important for performance
155    public final RolapCubeLevel getLevel() {
156        return cubeLevel;
157    }
158
159    public void setProperty(String name, Object value) {
160        synchronized (this) {
161            super.setProperty(name, value);
162        }
163    }
164
165    public Object getPropertyValue(String propertyName, boolean matchCase) {
166        // we need to wrap these children as rolap cube members
167        Property property = Property.lookup(propertyName, matchCase);
168        if (property != null) {
169            switch (property.ordinal) {
170            case Property.DIMENSION_UNIQUE_NAME_ORDINAL:
171                return getDimension().getUniqueName();
172
173            case Property.HIERARCHY_UNIQUE_NAME_ORDINAL:
174                return getHierarchy().getUniqueName();
175
176            case Property.LEVEL_UNIQUE_NAME_ORDINAL:
177                return getLevel().getUniqueName();
178
179            case Property.MEMBER_UNIQUE_NAME_ORDINAL:
180                return getUniqueName();
181
182            case Property.MEMBER_NAME_ORDINAL:
183                return getName();
184
185            case Property.MEMBER_CAPTION_ORDINAL:
186                return getCaption();
187
188            case Property.PARENT_UNIQUE_NAME_ORDINAL:
189                return parentCubeMember == null
190                    ? null
191                    : parentCubeMember.getUniqueName();
192
193            case Property.MEMBER_KEY_ORDINAL:
194            case Property.KEY_ORDINAL:
195                return this == this.getHierarchy().getAllMember() ? 0
196                    : getKey();
197            }
198        }
199        // fall through to rolap member
200        return member.getPropertyValue(propertyName, matchCase);
201    }
202
203    public final RolapCubeMember getParentMember() {
204        return parentCubeMember;
205    }
206
207    // this method is overridden to make sure that any HierarchyExpr returns
208    // the cube hierarchy vs. shared hierarchy.  this is the case for
209    // SqlMemberSource.RolapParentChildMemberNoClosure
210    public Exp getExpression() {
211        Exp exp = member.getExpression();
212        if (exp instanceof ResolvedFunCall) {
213            // convert any args to RolapCubeHierarchies
214            ResolvedFunCall fcall = (ResolvedFunCall)exp;
215            for (int i = 0; i < fcall.getArgCount(); i++) {
216                if (fcall.getArg(i) instanceof HierarchyExpr) {
217                    HierarchyExpr expr = (HierarchyExpr)fcall.getArg(i);
218                    if (expr.getHierarchy().equals(
219                            member.getHierarchy()))
220                    {
221                        fcall.getArgs()[i] =
222                            new HierarchyExpr(this.getHierarchy());
223                    }
224                }
225            }
226        }
227        return exp;
228    }
229
230    public OlapElement lookupChild(
231        SchemaReader schemaReader,
232        Id.Segment childName,
233        MatchType matchType)
234    {
235        return
236            schemaReader.lookupMemberChildByName(this, childName, matchType);
237    }
238
239}
240
241// End RolapCubeMember.java