001 /*
002 // $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/MemberBase.java#3 $
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 // jhyde, 6 August, 2001
012 */
013
014 package mondrian.olap;
015 import mondrian.resource.MondrianResource;
016 import mondrian.util.Bug;
017
018 import java.util.List;
019 import java.util.ArrayList;
020
021 /**
022 * <code>MemberBase</code> is a partial implementation of {@link Member}.
023 *
024 * @author jhyde
025 * @since 6 August, 2001
026 * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/MemberBase.java#3 $
027 */
028 public abstract class MemberBase
029 extends OlapElementBase
030 implements Member
031 {
032
033 protected Member parentMember;
034 protected final Level level;
035 protected String uniqueName;
036
037 /**
038 * Combines member type and other properties, such as whether the member
039 * is the 'all' or 'null' member of its hierarchy and whether it is a
040 * measure or is calculated, into an integer field.
041 *
042 * <p>The fields are:<ul>
043 * <li>bits 0, 1, 2 ({@link #FLAG_TYPE_MASK}) are member type;
044 * <li>bit 3 ({@link #FLAG_HIDDEN}) is set if the member is hidden;
045 * <li>bit 4 ({@link #FLAG_ALL}) is set if this is the all member of its
046 * hierarchy;
047 * <li>bit 5 ({@link #FLAG_NULL}) is set if this is the null member of its
048 * hierarchy;
049 * <li>bit 6 ({@link #FLAG_CALCULATED}) is set if this is a calculated
050 * member.
051 * <li>bit 7 ({@link #FLAG_MEASURE}) is set if this is a measure.
052 * </ul>
053 *
054 * NOTE: jhyde, 2007/8/10. It is necessary to cache whether the member is
055 * 'all', 'calculated' or 'null' in the member's state, because these
056 * properties are used so often. If we used a virtual method call - say we
057 * made each subclass implement 'boolean isNull()' - it would be slower.
058 * We use one flags field rather than 4 boolean fields to save space.
059 */
060 protected final int flags;
061
062 private static final int FLAG_TYPE_MASK = 0x07;
063 private static final int FLAG_HIDDEN = 0x08;
064 private static final int FLAG_ALL = 0x10;
065 private static final int FLAG_NULL = 0x20;
066 private static final int FLAG_CALCULATED = 0x40;
067 private static final int FLAG_MEASURE = 0x80;
068
069 /**
070 * Cached values of {@link mondrian.olap.Member.MemberType} enumeration.
071 * Without caching, get excessive calls to {@link Object#clone}.
072 */
073 private static final MemberType[] MEMBER_TYPE_VALUES = MemberType.values();
074
075 protected MemberBase(
076 Member parentMember,
077 Level level,
078 MemberType memberType)
079 {
080 this.parentMember = parentMember;
081 this.level = level;
082 this.flags = memberType.ordinal()
083 | (memberType == MemberType.ALL ? FLAG_ALL : 0)
084 | (memberType == MemberType.NULL ? FLAG_NULL : 0)
085 | (computeCalculated(memberType) ? FLAG_CALCULATED : 0)
086 | (level.getHierarchy().getDimension().isMeasures()
087 ? FLAG_MEASURE
088 : 0);
089 }
090
091 public String getQualifiedName() {
092 return MondrianResource.instance().MdxMemberName.str(getUniqueName());
093 }
094
095 public abstract String getName();
096
097 public final String getUniqueName() {
098 return uniqueName;
099 }
100
101 public String getCaption() {
102 // if there is a member formatter for the members level,
103 // we will call this interface to provide the display string
104 MemberFormatter mf = getLevel().getMemberFormatter();
105 if (mf != null) {
106 return mf.formatMember(this);
107 }
108 final String caption = super.getCaption();
109 return (caption != null)
110 ? caption
111 : getName();
112 }
113
114 public final String getParentUniqueName() {
115 return parentMember == null
116 ? null
117 : parentMember.getUniqueName();
118 }
119
120 public Dimension getDimension() {
121 return level.getDimension();
122 }
123
124 public Hierarchy getHierarchy() {
125 return level.getHierarchy();
126 }
127
128 public Level getLevel() {
129 return level;
130 }
131
132 public MemberType getMemberType() {
133 return MEMBER_TYPE_VALUES[flags & FLAG_TYPE_MASK];
134 }
135
136 public String getDescription() {
137 return (String) getPropertyValue(Property.DESCRIPTION.name);
138 }
139
140 public boolean isMeasure() {
141 return (flags & FLAG_MEASURE) != 0;
142 }
143
144 public boolean isAll() {
145 return (flags & FLAG_ALL) != 0;
146 }
147
148 public boolean isNull() {
149 return (flags & FLAG_NULL) != 0;
150 }
151
152 public boolean isCalculated() {
153 return (flags & FLAG_CALCULATED) != 0;
154 }
155
156 public boolean isEvaluated() {
157 return isCalculated();
158 }
159
160 public OlapElement lookupChild(
161 SchemaReader schemaReader,
162 Id.Segment childName,
163 MatchType matchType)
164 {
165 return schemaReader.lookupMemberChildByName(
166 this, childName, matchType);
167 }
168
169 // implement Member
170 public Member getParentMember() {
171 return parentMember;
172 }
173
174 // implement Member
175 public boolean isChildOrEqualTo(Member member) {
176 // REVIEW: Using uniqueName to calculate ancestry seems inefficient,
177 // because we can't afford to store every member's unique name, so
178 // we want to compute it on the fly
179 assert !Bug.BugSegregateRolapCubeMemberFixed;
180 return (member != null) && isChildOrEqualTo(member.getUniqueName());
181 }
182
183 /**
184 * Returns whether this <code>Member</code>'s unique name is equal to, a
185 * child of, or a descendent of a member whose unique name is
186 * <code>uniqueName</code>.
187 */
188 public boolean isChildOrEqualTo(String uniqueName) {
189 if (uniqueName == null) {
190 return false;
191 }
192
193 return isChildOrEqualTo(this, uniqueName);
194 }
195
196 private static boolean isChildOrEqualTo(Member member, String uniqueName) {
197 while (true) {
198 String thisUniqueName = member.getUniqueName();
199 if (thisUniqueName.equals(uniqueName)) {
200 // found a match
201 return true;
202 }
203 // try candidate's parentMember
204 member = member.getParentMember();
205 if (member == null) {
206 // have reached root
207 return false;
208 }
209 }
210 }
211
212 /**
213 * Computes the value to be returned by {@link #isCalculated()}, so it can
214 * be cached in a variable.
215 *
216 * @param memberType Member type
217 * @return Whether this member is calculated
218 */
219 protected boolean computeCalculated(final MemberType memberType) {
220 // If the member is not created from the "with member ..." MDX, the
221 // calculated will be null. But it may be still a calculated measure
222 // stored in the cube.
223 return isCalculatedInQuery() || memberType == MemberType.FORMULA;
224 }
225
226 public int getSolveOrder() {
227 return -1;
228 }
229
230 /**
231 * Returns the expression by which this member is calculated. The expression
232 * is not null if and only if the member is not calculated.
233 *
234 * @post (return != null) == (isCalculated())
235 */
236 public Exp getExpression() {
237 return null;
238 }
239
240 // implement Member
241 public List<Member> getAncestorMembers() {
242 final SchemaReader schemaReader =
243 getDimension().getSchema().getSchemaReader();
244 final ArrayList<Member> ancestorList = new ArrayList<Member>();
245 schemaReader.getMemberAncestors(this, ancestorList);
246 return ancestorList;
247 }
248
249 /**
250 * Returns the ordinal of this member within its hierarchy.
251 * The default implementation returns -1.
252 */
253 public int getOrdinal() {
254 return -1;
255 }
256
257 /**
258 * Returns the order key of this member among its siblings.
259 * The default implementation returns null.
260 */
261 public Comparable getOrderKey() {
262 return null;
263 }
264
265 public boolean isHidden() {
266 return false;
267 }
268
269 public Member getDataMember() {
270 return null;
271 }
272
273 public String getPropertyFormattedValue(String propertyName) {
274 return getPropertyValue(propertyName).toString();
275 }
276
277 public boolean isParentChildLeaf() {
278 return false;
279 }
280 }
281
282 // End MemberBase.java