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