001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeMember.java#8 $
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    // wgorman, 19 October 2007
012    */
013    
014    package mondrian.rolap;
015    
016    import java.util.ArrayList;
017    import java.util.List;
018    
019    import mondrian.mdx.HierarchyExpr;
020    import mondrian.mdx.ResolvedFunCall;
021    import mondrian.olap.Exp;
022    import mondrian.olap.Id;
023    import mondrian.olap.MatchType;
024    import mondrian.olap.Member;
025    import mondrian.olap.OlapElement;
026    import mondrian.olap.Property;
027    import mondrian.olap.SchemaReader;
028    
029    /**
030     * RolapCubeMember wraps RolapMembers and binds them to a specific cube.
031     * RolapCubeMember wraps or overrides RolapMember methods that directly
032     * reference the wrapped Member.  Methods that only contain calls to other
033     * methods do not need wrapped.
034     *
035     * @author Will Gorman (wgorman@pentaho.org)
036     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeMember.java#8 $
037     */
038    public class RolapCubeMember extends RolapMember {
039    
040        protected final String rolapAllMemberCubeName;
041    
042        protected final RolapMember rolapMember;
043    
044        protected final RolapCubeLevel rolapLevel;
045    
046        protected final RolapCube rolapCube;
047    
048        public RolapCubeMember(RolapCubeMember parent, RolapMember member,
049                RolapCubeLevel level, RolapCube cube) {
050            super();
051    
052            this.parentMember = parent;
053            this.rolapMember = member;
054            this.rolapLevel = level;
055            this.rolapCube = cube;
056            if (parent != null) {
057                this.parentUniqueName = parent.getUniqueName();
058            }
059            if (member.isAll()) {
060                // this is a special case ...
061                // replace hierarchy name portion of all member with new name
062                if (member.getLevel().getHierarchy().getName().equals(
063                        level.getHierarchy().getName())) {
064                    rolapAllMemberCubeName = member.getName();
065                } else {
066                    // special case if we're dealing with a closure
067                    String replacement =
068                        level.getHierarchy().getName().replaceAll("\\$", "\\\\\\$");
069    
070                    // convert string to regular expression
071                    String memberLevelName = member.getLevel().getHierarchy().getName().replaceAll("\\.", "\\\\.");
072    
073                    rolapAllMemberCubeName = member.getName().replaceAll(
074                            memberLevelName,
075                           replacement);
076                }
077                setUniqueName(rolapAllMemberCubeName);
078            } else {
079                rolapAllMemberCubeName = null;
080                Object name = rolapMember.getPropertyValue(Property.NAME.name);
081                if (name != null
082                        && !(rolapMember.getKey() != null && name.equals(rolapMember
083                                .getKey()))) {
084                    // Save memory by only saving the name as a property if it's
085                    // different from the key.
086                    setUniqueName(name);
087                } else if (rolapMember.getKey() != null) {
088                    setUniqueName(rolapMember.getKey());
089                }
090            }
091        }
092    
093        public int getDepth() {
094            return rolapMember.getDepth();
095        }
096    
097        public boolean isNull() {
098            return rolapMember.isNull();
099        }
100    
101        public boolean isMeasure() {
102            return rolapMember.isMeasure();
103        }
104    
105        public boolean isAll() {
106            return rolapMember.isAll();
107        }
108    
109        public RolapMember getRolapMember() {
110            return rolapMember;
111        }
112    
113        /**
114         * Returns the cube this cube member belongs to.
115         *
116         * <p>This method is not in the {@link Member} interface, because regular
117         * members may be shared, and therefore do not belong to a specific cube.
118         *
119         * @return Cube this cube member belongs to
120         */
121        public RolapCube getCube() {
122            return rolapCube;
123        }
124    
125        public Member getDataMember() {
126    
127            RolapMember member = (RolapMember) rolapMember.getDataMember();
128            if (member != null) {
129                RolapCubeMember cubeDataMember =
130                    new RolapCubeMember(
131                        getParentMember(), member,
132                        getLevel(), this.rolapCube);
133                return cubeDataMember;
134            } else {
135                return null;
136            }
137        }
138    
139        public int compareTo(Object o) {
140            // light wrapper around rolap member compareTo
141            RolapCubeMember other = (RolapCubeMember) o;
142            return rolapMember.compareTo(other.rolapMember);
143        }
144    
145        public boolean equals(Object o) {
146            return (o == this)
147                   || ((o instanceof RolapCubeMember)
148                       && equals((RolapCubeMember) o));
149        }
150    
151        public boolean equals(OlapElement o) {
152            return o.getClass() == RolapCubeMember.class
153                && equals((RolapCubeMember) o);
154        }
155    
156        private boolean equals(RolapCubeMember that) {
157            assert that != null; // public method should have checked
158            // Do not use equalsIgnoreCase; unique names should be identical, and
159            // hashCode assumes this.
160            return this.rolapLevel.equals(that.rolapLevel)
161                    && this.rolapMember.equals(that.rolapMember);
162        }
163    
164        public Object getKey() {
165            return rolapMember.getKey();
166        }
167    
168        // override with stricter return type
169        public RolapCubeHierarchy getHierarchy() {
170            return (RolapCubeHierarchy) super.getHierarchy();
171        }
172    
173        /**
174         * {@inheritDoc}
175         *
176         * <p>This method is central to how RolapCubeMember works. It allows
177         * a member from the cache to be used within different usages of the same
178         * shared dimension. The cache member is the same, but the RolapCubeMembers
179         * wrapping the cache member report that they belong to different levels,
180         * and hence different hierarchies, dimensions, and cubes.
181         */
182        // this is cube dependent
183        public RolapCubeLevel getLevel() {
184            return rolapLevel;
185        }
186    
187        public String getName() {
188            if (rolapMember.isAll()) {
189                return rolapAllMemberCubeName;
190            }
191            return rolapMember.getName();
192        }
193    
194        public Comparable getOrderKey() {
195            return rolapMember.getOrderKey();
196        }
197    
198        void setOrderKey(Comparable orderKey) {
199            // this should never be called
200            throw new UnsupportedOperationException();
201        }
202    
203        public int getOrdinal() {
204            return rolapMember.getOrdinal();
205        }
206    
207        void setOrdinal(int ordinal) {
208            rolapMember.setOrdinal(ordinal);
209        }
210    
211        public synchronized void setProperty(String name, Object value) {
212            rolapMember.setProperty(name, value);
213        }
214    
215        public Object getPropertyValue(String propertyName, boolean matchCase) {
216    
217            // we need to wrap these children as rolap cube members
218            Property property = Property.lookup(propertyName, matchCase);
219            if (property != null) {
220                Member parentMember;
221                switch (property.ordinal) {
222                case Property.CONTRIBUTING_CHILDREN_ORDINAL:
223                    List<RolapMember> list = new ArrayList<RolapMember>();
224                    List<RolapMember> origList =
225                        (List) rolapMember.getPropertyValue(
226                            propertyName, matchCase);
227                    for (RolapMember member : origList) {
228                        list.add(
229                            new RolapCubeMember(
230                                this, member, this.getLevel(), this.rolapCube));
231                    }
232                    return list;
233    
234                case Property.DIMENSION_UNIQUE_NAME_ORDINAL:
235                    return getHierarchy().getDimension().getUniqueName();
236    
237                case Property.HIERARCHY_UNIQUE_NAME_ORDINAL:
238                    return getHierarchy().getUniqueName();
239    
240                case Property.LEVEL_UNIQUE_NAME_ORDINAL:
241                    return getLevel().getUniqueName();
242    
243                case Property.MEMBER_UNIQUE_NAME_ORDINAL:
244                    return getUniqueName();
245    
246                case Property.MEMBER_NAME_ORDINAL:
247                    return getName();
248    
249                case Property.MEMBER_CAPTION_ORDINAL:
250                    return getCaption();
251    
252                case Property.PARENT_UNIQUE_NAME_ORDINAL:
253                    parentMember = getParentMember();
254                    return parentMember == null ? null : parentMember
255                            .getUniqueName();
256                case Property.CHILDREN_CARDINALITY_ORDINAL:
257                    // because rolapcalculated member overrides this property,
258                    // we need to make sure it gets called
259                    if (rolapMember instanceof RolapCalculatedMember) {
260                        return
261                            rolapMember.getPropertyValue(propertyName, matchCase);
262                    } else {
263                        return super.getPropertyValue(propertyName, matchCase);
264                    }
265    
266                case Property.MEMBER_KEY_ORDINAL:
267                case Property.KEY_ORDINAL:
268                    return this == this.getHierarchy().getAllMember() ? 0
269                            : getKey();
270    
271                }
272            }
273            // fall through to rolap member
274            return rolapMember.getPropertyValue(propertyName, matchCase);
275        }
276    
277        public int getSolveOrder() {
278            return rolapMember.getSolveOrder();
279        }
280    
281        protected Object getPropertyFromMap(String propertyName,
282                boolean matchCase) {
283            return rolapMember.getPropertyFromMap(propertyName, matchCase);
284        }
285    
286        public final MemberType getMemberType() {
287            return rolapMember.getMemberType();
288        }
289    
290        public RolapCubeMember getParentMember() {
291            return (RolapCubeMember) super.getParentMember();
292        }
293    
294        public String getCaption() {
295            return rolapMember.getCaption();
296        }
297    
298        public boolean isCalculated() {
299            return rolapMember.isCalculated();
300        }
301    
302        public boolean isCalculatedInQuery() {
303            return rolapMember.isCalculatedInQuery();
304        }
305    
306        // this method is overridden to make sure that any HierarchyExpr returns
307        // the cube hierarchy vs. shared hierarchy.  this is the case for
308        // SqlMemberSource.RolapParentChildMemberNoClosure
309        public Exp getExpression() {
310            Exp exp = rolapMember.getExpression();
311            if (exp instanceof ResolvedFunCall) {
312                // convert any args to RolapCubeHierarchies
313                ResolvedFunCall fcall = (ResolvedFunCall)exp;
314                for (int i = 0; i < fcall.getArgCount(); i++) {
315                    if (fcall.getArg(i) instanceof HierarchyExpr) {
316                        HierarchyExpr expr = (HierarchyExpr)fcall.getArg(i);
317                        if (expr.getHierarchy().equals(rolapMember.getHierarchy())) {
318                            fcall.getArgs()[i] = new HierarchyExpr(this.getHierarchy());
319                        }
320                    }
321                }
322    
323            }
324            return exp;
325        }
326    
327        public OlapElement lookupChild(SchemaReader schemaReader,
328                Id.Segment childName, MatchType matchType) {
329            return
330                schemaReader.lookupMemberChildByName(this, childName, matchType);
331        }
332    
333    }
334    
335    // End RolapCubeMember.java