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