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