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