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