001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/MemberBase.java#42 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2001-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2008 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 017 import java.util.List; 018 import java.util.ArrayList; 019 020 /** 021 * <code>MemberBase</code> is a partial implementation of {@link Member}. 022 * 023 * @author jhyde 024 * @since 6 August, 2001 025 * @version $Id: //open/mondrian/src/main/mondrian/olap/MemberBase.java#42 $ 026 */ 027 public abstract class MemberBase 028 extends OlapElementBase 029 implements Member { 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 protected String parentUniqueName; 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.parentUniqueName = (parentMember == null) 083 ? null 084 : parentMember.getUniqueName(); 085 this.flags = memberType.ordinal() 086 | (memberType == MemberType.ALL ? FLAG_ALL : 0) 087 | (memberType == MemberType.NULL ? FLAG_NULL : 0) 088 | (computeCalculated(memberType) ? FLAG_CALCULATED : 0) 089 | (level.getHierarchy().getDimension().isMeasures() ? FLAG_MEASURE : 0); 090 } 091 092 protected MemberBase() { 093 this.level = null; 094 this.flags = 0; 095 this.parentUniqueName = null; 096 } 097 098 public String getQualifiedName() { 099 return MondrianResource.instance().MdxMemberName.str(getUniqueName()); 100 } 101 102 public abstract String getName(); 103 104 public String getUniqueName() { 105 return uniqueName; 106 } 107 108 public String getCaption() { 109 // if there is a member formatter for the members level, 110 // we will call this interface to provide the display string 111 MemberFormatter mf = getLevel().getMemberFormatter(); 112 if (mf != null) { 113 return mf.formatMember(this); 114 } 115 final String caption = super.getCaption(); 116 return (caption != null) 117 ? caption 118 : getName(); 119 } 120 121 public String getParentUniqueName() { 122 return parentUniqueName; 123 } 124 125 public Dimension getDimension() { 126 return getLevel().getDimension(); 127 } 128 129 public Hierarchy getHierarchy() { 130 return getLevel().getHierarchy(); 131 } 132 133 public Level getLevel() { 134 return level; 135 } 136 137 public MemberType getMemberType() { 138 return MEMBER_TYPE_VALUES[flags & FLAG_TYPE_MASK]; 139 } 140 141 public String getDescription() { 142 return null; 143 } 144 145 public boolean isMeasure() { 146 return (flags & FLAG_MEASURE) != 0; 147 } 148 149 public boolean isAll() { 150 return (flags & FLAG_ALL) != 0; 151 } 152 153 public boolean isNull() { 154 return (flags & FLAG_NULL) != 0; 155 } 156 157 public boolean isCalculated() { 158 return (flags & FLAG_CALCULATED) != 0; 159 } 160 161 public OlapElement lookupChild( 162 SchemaReader schemaReader, 163 Id.Segment childName) 164 { 165 return lookupChild(schemaReader, childName, MatchType.EXACT); 166 } 167 168 public OlapElement lookupChild( 169 SchemaReader schemaReader, 170 Id.Segment childName, 171 MatchType matchType) 172 { 173 return schemaReader.lookupMemberChildByName( 174 this, childName, matchType); 175 } 176 177 // implement Member 178 public Member getParentMember() { 179 // use the cache if possible (getAdoMember can be very expensive) 180 if (parentUniqueName == null) { 181 return null; // we are root member, which has no parent 182 } else if (parentMember != null) { 183 return parentMember; 184 } else { 185 boolean failIfNotFound = true; 186 final Hierarchy hierarchy = getHierarchy(); 187 final SchemaReader schemaReader = 188 hierarchy.getDimension().getSchema().getSchemaReader(); 189 List<Id.Segment> parentUniqueNameParts = 190 Util.parseIdentifier(parentUniqueName); 191 parentMember = 192 schemaReader.getMemberByUniqueName( 193 parentUniqueNameParts, failIfNotFound); 194 return parentMember; 195 } 196 } 197 198 // implement Member 199 public boolean isChildOrEqualTo(Member member) { 200 return (member != null) && isChildOrEqualTo(member.getUniqueName()); 201 } 202 203 /** 204 * Returns whether this <code>Member</code>'s unique name is equal to, a 205 * child of, or a descendent of a member whose unique name is 206 * <code>uniqueName</code>. 207 */ 208 public boolean isChildOrEqualTo(String uniqueName) { 209 if (uniqueName == null) { 210 return false; 211 } 212 213 // The mapping member uniqueName --> parent uniqueName is more 214 // efficient than using getAdoMember(). 215 String thisUniqueName = getUniqueName(); 216 if (thisUniqueName.equals(uniqueName)) { 217 //found a match 218 return true; 219 } 220 String parentUniqueName = getParentUniqueName(); 221 return (parentUniqueName == null) 222 // have reached root 223 ? false 224 // try candidate's parentMember 225 : ((MemberBase) getParentMember()).isChildOrEqualTo(uniqueName); 226 } 227 228 /** 229 * Computes the value to be returned by {@link #isCalculated()}, so it can 230 * be cached in a variable. 231 * 232 * @param memberType Member type 233 * @return Whether this member is calculated 234 */ 235 protected boolean computeCalculated(final MemberType memberType) { 236 // If the member is not created from the "with member ..." MDX, the 237 // calculated will be null. But it may be still a calculated measure 238 // stored in the cube. 239 return isCalculatedInQuery() || memberType == MemberType.FORMULA; 240 } 241 242 public int getSolveOrder() { 243 return -1; 244 } 245 246 /** 247 * Returns the expression by which this member is calculated. The expression 248 * is not null if and only if the member is not calculated. 249 * 250 * @post (return != null) == (isCalculated()) 251 */ 252 public Exp getExpression() { 253 return null; 254 } 255 256 // implement Member 257 public List<Member> getAncestorMembers() { 258 List<Member> list = new ArrayList<Member>(); 259 Member parentMember = getParentMember(); 260 while (parentMember != null) { 261 list.add(parentMember); 262 parentMember = parentMember.getParentMember(); 263 } 264 return list; 265 } 266 267 /** 268 * Returns the ordinal of this member within its hierarchy. 269 * The default implementation returns -1. 270 */ 271 public int getOrdinal() { 272 return -1; 273 } 274 275 /** 276 * Returns the order key of this member among its siblings. 277 * The default implementation returns null. 278 */ 279 public Comparable getOrderKey() { 280 return null; 281 } 282 283 public boolean isHidden() { 284 return false; 285 } 286 287 public Member getDataMember() { 288 return null; 289 } 290 291 public String getPropertyFormattedValue(String propertyName) { 292 return getPropertyValue(propertyName).toString(); 293 } 294 } 295 296 // End MemberBase.java