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