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