KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > RolapHierarchy


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/RolapHierarchy.java#57 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2001-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2007 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 //
11 // jhyde, 10 August, 2001
12 */

13
14 package mondrian.rolap;
15
16 import mondrian.olap.*;
17 import mondrian.olap.fun.BuiltinFunTable;
18 import mondrian.olap.type.Type;
19 import mondrian.olap.type.MemberType;
20 import mondrian.rolap.sql.SqlQuery;
21 import mondrian.resource.MondrianResource;
22 import mondrian.mdx.HierarchyExpr;
23 import mondrian.mdx.UnresolvedFunCall;
24
25 import org.apache.log4j.Logger;
26
27 import java.util.List JavaDoc;
28 import java.util.ArrayList JavaDoc;
29
30 /**
31  * <code>RolapHierarchy</code> implements {@link Hierarchy} for a ROLAP database.
32  *
33  * @author jhyde
34  * @since 10 August, 2001
35  * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapHierarchy.java#57 $
36  */

37 public class RolapHierarchy extends HierarchyBase {
38
39     private static final Logger LOGGER = Logger.getLogger(RolapHierarchy.class);
40
41     /**
42      * The raw member reader. For a member reader which incorporates access
43      * control and deals with hidden members (if the hierarchy is ragged), use
44      * {@link #getMemberReader(Role)}.
45      */

46     private MemberReader memberReader;
47     MondrianDef.Hierarchy xmlHierarchy;
48     private String JavaDoc memberReaderClass;
49     private MondrianDef.Relation relation;
50     private Member defaultMember;
51     private String JavaDoc defaultMemberName;
52     private RolapNullMember nullMember;
53
54     /**
55      * If this hierarchy is a public -- that is, it belongs to a dimension
56      * which is a usage of a shared dimension -- then
57      * <code>sharedHierarchyName</code> holds the unique name of the shared
58      * hierarchy; otherwise it is null.
59      *
60      * <p> Suppose this hierarchy is "Weekly" in the dimension "Order Date" of
61      * cube "Sales", and that "Order Date" is a usage of the "Time"
62      * dimension. Then <code>sharedHierarchyName</code> will be
63      * "[Time].[Weekly]".
64      */

65     private String JavaDoc sharedHierarchyName;
66
67     private Exp aggregateChildrenExpression;
68
69     // for newClosedPeerHierarchy() to copy; but never used??
70
private String JavaDoc primaryKey;
71
72     /**
73      * Type for members of this hierarchy. Set once to avoid excessive newing.
74      */

75     final Type memberType = MemberType.forHierarchy(this);
76
77     /**
78      * The level that the null member belongs too.
79      */

80     private final RolapLevel nullLevel;
81
82     /**
83      * The 'all' member of this hierarchy. This exists even if the hierarchy
84      * does not officially have an 'all' member.
85      */

86     private RolapMember allMember;
87     private static final String JavaDoc ALL_LEVEL_CARDINALITY = "1";
88
89     RolapHierarchy(RolapDimension dimension, String JavaDoc subName, boolean hasAll) {
90         super(dimension, subName, hasAll);
91
92         this.levels = new RolapLevel[0];
93         setCaption(dimension.getCaption());
94
95         this.allLevelName = "(All)";
96         this.allMemberName = "All " + name + "s";
97         if (hasAll) {
98             Util.discard(newLevel(this.allLevelName,
99                 RolapLevel.ALL | RolapLevel.UNIQUE));
100         }
101
102         // The null member belongs to a level with very similar properties to
103
// the 'all' level.
104
this.nullLevel = new RolapLevel(
105                 this, 0, this.allLevelName, null, null, null, null, null, null,
106                 null, RolapProperty.emptyArray,
107                 RolapLevel.ALL | RolapLevel.UNIQUE,
108                 null,
109                 RolapLevel.HideMemberCondition.Never,
110                 LevelType.Null, "");
111     }
112
113     /**
114      * Creates a <code>RolapHierarchy</code>.
115      *
116      * @param cube Cube this hierarchy belongs to, or null if this is a shared
117      * hierarchy
118      */

119     RolapHierarchy(RolapCube cube, RolapDimension dimension,
120             MondrianDef.Hierarchy xmlHierarchy,
121             MondrianDef.CubeDimension xmlCubeDimension) {
122         this(dimension, xmlHierarchy.name, xmlHierarchy.hasAll);
123
124         if (xmlHierarchy.relation == null &&
125                 xmlHierarchy.memberReaderClass == null &&
126                 cube != null) {
127             xmlHierarchy.relation = cube.fact;
128         }
129         this.xmlHierarchy = xmlHierarchy;
130         this.relation = xmlHierarchy.relation;
131         if (xmlHierarchy.relation instanceof MondrianDef.InlineTable) {
132             this.relation = convertInlineTableToRelation(
133                     (MondrianDef.InlineTable) xmlHierarchy.relation);
134         }
135         this.memberReaderClass = xmlHierarchy.memberReaderClass;
136
137         // Create an 'all' level even if the hierarchy does not officially
138
// have one.
139
if (xmlHierarchy.allMemberName != null) {
140             this.allMemberName = xmlHierarchy.allMemberName;
141         }
142         if (xmlHierarchy.allLevelName != null) {
143             this.allLevelName = xmlHierarchy.allLevelName;
144         }
145         RolapLevel allLevel = new RolapLevel(
146             this, 0, this.allLevelName, null, null, null, null, null, null,
147             null, RolapProperty.emptyArray,
148             RolapLevel.ALL | RolapLevel.UNIQUE,
149             null,
150             RolapLevel.HideMemberCondition.Never,
151             LevelType.Regular, ALL_LEVEL_CARDINALITY);
152         this.allMember = new RolapMember(
153             null, allLevel, null, allMemberName, Member.MemberType.ALL);
154         // assign "all member" caption
155
if (xmlHierarchy.allMemberCaption != null &&
156             xmlHierarchy.allMemberCaption.length() > 0) {
157             this.allMember.setCaption(xmlHierarchy.allMemberCaption);
158         }
159         this.allMember.setOrdinal(0);
160
161         // If the hierarchy has an 'all' member, the 'all' level is level 0.
162
if (hasAll) {
163             this.levels = new RolapLevel[xmlHierarchy.levels.length + 1];
164             this.levels[0] = allLevel;
165             for (int i = 0; i < xmlHierarchy.levels.length; i++) {
166                 final MondrianDef.Level xmlLevel = xmlHierarchy.levels[i];
167                 if (xmlLevel.getKeyExp() == null &&
168                         xmlHierarchy.memberReaderClass == null) {
169                     throw MondrianResource.instance().LevelMustHaveNameExpression.ex(xmlLevel.name);
170                 }
171                 levels[i + 1] = new RolapLevel(this, i + 1, xmlLevel);
172             }
173         } else {
174             this.levels = new RolapLevel[xmlHierarchy.levels.length];
175             for (int i = 0; i < xmlHierarchy.levels.length; i++) {
176                 levels[i] = new RolapLevel(this, i, xmlHierarchy.levels[i]);
177             }
178         }
179
180         if (xmlCubeDimension instanceof MondrianDef.DimensionUsage) {
181             String JavaDoc sharedDimensionName =
182                 ((MondrianDef.DimensionUsage) xmlCubeDimension).source;
183             this.sharedHierarchyName = sharedDimensionName;
184             if (subName != null) {
185                 this.sharedHierarchyName += "." + subName; // e.g. "Time.Weekly"
186
}
187         } else {
188             this.sharedHierarchyName = null;
189         }
190         if (xmlHierarchy.relation != null &&
191                 xmlHierarchy.memberReaderClass != null) {
192             throw MondrianResource.instance().
193                 HierarchyMustNotHaveMoreThanOneSource.ex(getUniqueName());
194         }
195         this.primaryKey = xmlHierarchy.primaryKey;
196         if (!Util.isEmpty(xmlHierarchy.caption)) {
197             setCaption(xmlHierarchy.caption);
198         }
199         defaultMemberName = xmlHierarchy.defaultMember;
200     }
201
202     private MondrianDef.Relation convertInlineTableToRelation(
203             MondrianDef.InlineTable inlineTable) {
204         MondrianDef.View view = new MondrianDef.View();
205         view.alias = inlineTable.alias;
206         view.selects = new MondrianDef.SQL[1];
207         final MondrianDef.SQL select = view.selects[0] = new MondrianDef.SQL();
208         select.dialect = "generic";
209         final SqlQuery.Dialect dialect;
210         dialect = getRolapSchema().getDialect();
211
212         final int columnCount = inlineTable.columnDefs.array.length;
213         List JavaDoc<String JavaDoc> columnNames = new ArrayList JavaDoc<String JavaDoc>();
214         List JavaDoc<String JavaDoc> columnTypes = new ArrayList JavaDoc<String JavaDoc>();
215         for (int i = 0; i < columnCount; i++) {
216             columnNames.add(inlineTable.columnDefs.array[i].name);
217             columnTypes.add(inlineTable.columnDefs.array[i].type);
218         }
219         List JavaDoc<String JavaDoc[]> valueList = new ArrayList JavaDoc<String JavaDoc[]>();
220         for (MondrianDef.Row row : inlineTable.rows.array) {
221             String JavaDoc[] values = new String JavaDoc[columnCount];
222             for (MondrianDef.Value value : row.values) {
223                 final int columnOrdinal = columnNames.indexOf(value.column);
224                 if (columnOrdinal < 0) {
225                     throw Util.newError(
226                         "Unknown column '" + value.column + "'");
227                 }
228                 values[columnOrdinal] = value.cdata;
229             }
230             valueList.add(values);
231         }
232         select.cdata = dialect.generateInline(
233                 columnNames,
234                 columnTypes,
235                 valueList);
236         return view;
237     }
238
239     protected Logger getLogger() {
240         return LOGGER;
241     }
242
243     public boolean equals(Object JavaDoc o) {
244         if (this == o) {
245             return true;
246         }
247         if (!(o instanceof RolapHierarchy)) {
248             return false;
249         }
250
251         RolapHierarchy that = (RolapHierarchy)o;
252         if (sharedHierarchyName == null || that.sharedHierarchyName == null) {
253             return false;
254         } else {
255             return sharedHierarchyName.equals(that.sharedHierarchyName) &&
256                 getUniqueName().equals(that.getUniqueName());
257         }
258     }
259
260     public int hashCode() {
261         return super.hashCode() ^ (sharedHierarchyName == null
262             ? 0
263             : sharedHierarchyName.hashCode());
264     }
265
266     /**
267      * Initializes a hierarchy within the context of a cube.
268      */

269     void init(RolapCube cube, MondrianDef.CubeDimension xmlDimension) {
270         // first create memberReader
271
if (this.memberReader == null) {
272             this.memberReader = getRolapSchema().createMemberReader(
273                 sharedHierarchyName, this, memberReaderClass);
274         }
275         for (int i = 0; i < levels.length; i++) {
276             ((RolapLevel) levels[i]).init(cube, xmlDimension);
277         }
278         if (defaultMemberName != null) {
279             String JavaDoc[] uniqueNameParts = Util.explode(defaultMemberName);
280
281             // We strip off the parent dimension name if the defaultMemberName
282
// is the full unique name, [Time].[2004] rather than simply
283
// [2004].
284
//Dimension dim = getDimension();
285
// What we should strip off is hierarchy name
286
if (this.name.equals(uniqueNameParts[0])) {
287                 String JavaDoc[] tmp = new String JavaDoc[uniqueNameParts.length-1];
288                 System.arraycopy(uniqueNameParts, 1, tmp, 0,
289                                 uniqueNameParts.length-1);
290                 uniqueNameParts = tmp;
291             }
292
293             // Now lookup the name from the hierarchy's members.
294
defaultMember = memberReader.lookupMember(uniqueNameParts, false);
295             if (defaultMember == null) {
296                 throw Util.newInternal(
297                     "Can not find Default Member with name \""
298                         + defaultMemberName + "\" in Hierarchy \"" +
299                         getName() + "\"");
300             }
301         }
302     }
303     void setMemberReader(MemberReader memberReader) {
304         this.memberReader = memberReader;
305     }
306     MemberReader getMemberReader() {
307         return this.memberReader;
308     }
309
310     RolapLevel newLevel(String JavaDoc name, int flags) {
311         RolapLevel level = new RolapLevel(
312                 this, this.levels.length, name, null, null, null, null,
313                 null, null, null, RolapProperty.emptyArray, flags, null,
314                 RolapLevel.HideMemberCondition.Never, LevelType.Regular, "");
315         this.levels = (RolapLevel[]) RolapUtil.addElement(this.levels, level);
316         return level;
317     }
318
319     /**
320      * If this hierarchy has precisely one table, returns that table;
321      * if this hierarchy has no table, return the cube's fact-table;
322      * otherwise, returns null.
323      */

324     MondrianDef.Relation getUniqueTable() {
325         if (relation instanceof MondrianDef.Table ||
326                 relation instanceof MondrianDef.View) {
327             return relation;
328         } else if (relation instanceof MondrianDef.Join) {
329             return null;
330         } else {
331             throw Util.newInternal(
332                     "hierarchy's relation is a " + relation.getClass());
333         }
334     }
335
336     boolean tableExists(String JavaDoc tableName) {
337         return (relation != null) && tableExists(tableName, relation);
338     }
339
340     private static boolean tableExists(String JavaDoc tableName,
341                                        MondrianDef.Relation relation) {
342         if (relation instanceof MondrianDef.Table) {
343             MondrianDef.Table table = (MondrianDef.Table) relation;
344             // Check by table name and alias
345
return table.name.equals(tableName) ||
346                 ((table.alias != null) && table.alias.equals(tableName));
347         }
348         if (relation instanceof MondrianDef.Join) {
349             MondrianDef.Join join = (MondrianDef.Join) relation;
350             return tableExists(tableName, join.left) ||
351                 tableExists(tableName, join.right);
352         }
353         return false;
354     }
355
356     public RolapSchema getRolapSchema() {
357         return (RolapSchema) dimension.getSchema();
358     }
359
360     public MondrianDef.Relation getRelation() {
361         return relation;
362     }
363
364     public Member getDefaultMember() {
365         // use lazy initialization to get around bootstrap issues
366
if (defaultMember == null) {
367             List JavaDoc rootMembers = memberReader.getRootMembers();
368             if (rootMembers.size() == 0) {
369                 throw MondrianResource.instance().InvalidHierarchyCondition.ex(this.getUniqueName());
370 /*
371                 throw Util.newError(
372                     "cannot get default member: hierarchy " + getUniqueName() +
373                     " has no root members");
374 */

375             }
376             defaultMember = (RolapMember) rootMembers.get(0);
377         }
378         return defaultMember;
379     }
380
381     public Member getNullMember() {
382         // use lazy initialization to get around bootstrap issues
383
if (nullMember == null) {
384             nullMember = new RolapNullMember(nullLevel);
385         }
386         return nullMember;
387     }
388
389     /**
390      * Returns the 'all' member.
391      */

392     public RolapMember getAllMember() {
393         return allMember;
394     }
395
396     public Member createMember(
397             Member parent,
398             Level level,
399             String JavaDoc name,
400             Formula formula) {
401         if (formula == null) {
402             return new RolapMember(
403                 (RolapMember) parent, (RolapLevel) level, name);
404         } else if (level.getDimension().isMeasures()) {
405             return new RolapCalculatedMeasure(
406                 (RolapMember) parent, (RolapLevel) level, name, formula);
407         } else {
408             return new RolapCalculatedMember(
409                 (RolapMember) parent, (RolapLevel) level, name, formula);
410         }
411     }
412
413     String JavaDoc getAlias() {
414         return getName();
415     }
416
417     /**
418      * Adds to the FROM clause of the query the tables necessary to access the
419      * members of this hierarchy. If <code>expression</code> is not null, adds
420      * the tables necessary to compute that expression.
421      *
422      * <p> This method is idempotent: if you call it more than once, it only
423      * adds the table(s) to the FROM clause once.
424      *
425      * @param query Query to add the hierarchy to
426      * @param expression Level to qualify up to; if null, qualifies up to the
427      * topmost ('all') expression, which may require more columns and more joins
428      */

429     void addToFrom(SqlQuery query, MondrianDef.Expression expression) {
430         if (relation == null) {
431             throw Util.newError(
432                     "cannot add hierarchy " + getUniqueName() +
433                     " to query: it does not have a <Table>, <View> or <Join>");
434         }
435         final boolean failIfExists = false;
436         MondrianDef.Relation subRelation = relation;
437         if (relation instanceof MondrianDef.Join) {
438             if (expression != null) {
439                 // Suppose relation is
440
// (((A join B) join C) join D)
441
// and the fact table is
442
// F
443
// and our expression uses C. We want to make the expression
444
// F left join ((A join B) join C).
445
// Search for the smallest subset of the relation which
446
// uses C.
447
subRelation = relationSubset(relation, expression.getTableAlias());
448
449             }
450         }
451         query.addFrom(subRelation, null, failIfExists);
452     }
453
454     /**
455      * Returns the smallest subset of <code>relation</code> which contains
456      * the relation <code>alias</code>, or null if these is no relation with
457      * such an alias.
458      */

459     private static MondrianDef.Relation relationSubset(
460         MondrianDef.Relation relation,
461         String JavaDoc alias) {
462
463         if (relation instanceof MondrianDef.Table) {
464             MondrianDef.Table table = (MondrianDef.Table) relation;
465             // lookup by both alias and table name
466
return (table.getAlias().equals(alias))
467                 ? relation
468                 : (table.name.equals(alias) ? relation : null);
469
470         } else if (relation instanceof MondrianDef.Join) {
471             MondrianDef.Join join = (MondrianDef.Join) relation;
472             MondrianDef.Relation rightRelation = relationSubset(join.right, alias);
473             return (rightRelation == null)
474                 ? relationSubset(join.left, alias)
475                 : join;
476
477         } else {
478             throw Util.newInternal("bad relation type " + relation);
479         }
480     }
481
482     /**
483      * Returns a member reader which enforces the access-control profile of
484      * <code>role</code>.
485      *
486      * @pre role != null
487      * @post return != null
488      */

489     MemberReader getMemberReader(Role role) {
490         final Access access = role.getAccess(this);
491         switch (access) {
492         case NONE:
493             throw Util.newInternal("Illegal access to members of hierarchy "
494                     + this);
495         case ALL:
496             return (isRagged())
497                 ? new RestrictedMemberReader(memberReader, role)
498                 : memberReader;
499
500         case CUSTOM:
501             return new RestrictedMemberReader(memberReader, role);
502         default:
503             throw Util.badValue(access);
504         }
505     }
506
507     /**
508      * A hierarchy is ragged if it contains one or more levels with hidden
509      * members.
510      */

511     public boolean isRagged() {
512         for (int i = 0; i < levels.length; i++) {
513             RolapLevel level = (RolapLevel) levels[i];
514             if (level.getHideMemberCondition() !=
515                     RolapLevel.HideMemberCondition.Never) {
516                 return true;
517             }
518         }
519         return false;
520     }
521
522     /**
523      * Returns an expression which will compute a member's value by aggregating
524      * its children.
525      *
526      * <p>It is efficient to share one expression between all calculated members in
527      * a parent-child hierarchy, so we only need need to validate the expression
528      * once.
529      */

530     synchronized Exp getAggregateChildrenExpression() {
531         if (aggregateChildrenExpression == null) {
532             UnresolvedFunCall fc = new UnresolvedFunCall(
533                 "$AggregateChildren",
534                 Syntax.Internal,
535                 new Exp[] {new HierarchyExpr(this)});
536             Validator validator =
537                     Util.createSimpleValidator(BuiltinFunTable.instance());
538             aggregateChildrenExpression = fc.accept(validator);
539         }
540         return aggregateChildrenExpression;
541     }
542
543     /**
544      * Builds a dimension which maps onto a table holding the transitive
545      * closure of the relationship for this parent-child level.
546      *
547      * <p>This method is triggered by the
548      * {@link mondrian.olap.MondrianDef.Closure} element
549      * in a schema, and is only meaningful for a parent-child hierarchy.
550      *
551      * <p>When a Schema contains a parent-child Hierarchy that has an
552      * associated closure table, Mondrian creates a parallel internal
553      * Hierarchy, called a "closed peer", that refers to the closure table.
554      * This is indicated in the schema at the level of a Level, by including a
555      * Closure element. The closure table represents
556      * the transitive closure of the parent-child relationship.
557      *
558      * <p>The peer dimension, with its single hierarchy, and 3 levels (all,
559      * closure, item) really 'belong to' the parent-child level. If a single
560      * hierarchy had two parent-child levels (however unlikely this might be)
561      * then each level would have its own auxiliary dimension.
562      *
563      * <p>For example, in the demo schema the [HR].[Employee] dimension
564      * contains a parent-child hierarchy:
565      *
566      * <pre>
567      * &lt;Dimension name="Employees" foreignKey="employee_id"&gt;
568      * &lt;Hierarchy hasAll="true" allMemberName="All Employees"
569      * primaryKey="employee_id"&gt;
570      * &lt;Table name="employee"/&gt;
571      * &lt;Level name="Employee Id" type="Numeric" uniqueMembers="true"
572      * column="employee_id" parentColumn="supervisor_id"
573      * nameColumn="full_name" nullParentValue="0"&gt;
574      * &lt;Closure parentColumn="supervisor_id" childColumn="employee_id"&gt;
575      * &lt;Table name="employee_closure"/&gt;
576      * &lt;/Closure&gt;
577      * ...
578      * </pre>
579      * The internal closed peer Hierarchy has this structure:
580      * <pre>
581      * &lt;Dimension name="Employees" foreignKey="employee_id"&gt;
582      * ...
583      * &lt;Hierarchy name="Employees$Closure"
584      * hasAll="true" allMemberName="All Employees"
585      * primaryKey="employee_id" primaryKeyTable="employee_closure"&gt;
586      * &lt;Join leftKey="supervisor_id" rightKey="employee_id"&gt;
587      * &lt;Table name="employee_closure"/&gt;
588      * &lt;Table name="employee"/&gt;
589      * &lt;/Join&gt;
590      * &lt;Level name="Closure" type="Numeric" uniqueMembers="false"
591      * table="employee_closure" column="supervisor_id"/&gt;
592      * &lt;Level name="Employee" type="Numeric" uniqueMembers="true"
593      * table="employee_closure" column="employee_id"/&gt;
594      * &lt;/Hierarchy&gt;
595      * </pre>
596      *
597      * <p>Note that the original Level with the Closure produces two Levels in
598      * the closed peer Hierarchy: a simple peer (Employee) and a closed peer
599      * (Closure).
600      *
601      * @param src a parent-child Level that has a Closure clause
602      * @param clos a Closure clause
603      * @return the closed peer Level in the closed peer Hierarchy
604      */

605     RolapDimension createClosedPeerDimension(
606         RolapLevel src,
607         MondrianDef.Closure clos,
608         RolapCube cube,
609         MondrianDef.CubeDimension xmlDimension) {
610
611         // REVIEW (mb): What about attribute primaryKeyTable?
612

613         // Create a peer dimension.
614
RolapDimension peerDimension = new RolapDimension(
615             dimension.getSchema(),
616             dimension.getName() + "$Closure",
617             ((RolapDimension)dimension).getNextOrdinal(),
618             DimensionType.StandardDimension);
619
620         // Create a peer hierarchy.
621
RolapHierarchy peerHier = peerDimension.newHierarchy(subName, true);
622         peerHier.allMemberName = allMemberName;
623         peerHier.allMember = allMember;
624         peerHier.allLevelName = allLevelName;
625         peerHier.sharedHierarchyName = sharedHierarchyName;
626         peerHier.primaryKey = primaryKey;
627         MondrianDef.Join join = new MondrianDef.Join();
628         peerHier.relation = join;
629         join.left = clos.table; // the closure table
630
join.leftKey = clos.parentColumn;
631         join.right = relation; // the unclosed base table
632
join.rightKey = clos.childColumn;
633
634         // Create the upper level.
635
// This represents all groups of descendants. For example, in the
636
// Employee closure hierarchy, this level has a row for every employee.
637
int index = peerHier.levels.length;
638         int flags = src.getFlags() &~ RolapLevel.UNIQUE;
639         MondrianDef.Expression keyExp =
640             new MondrianDef.Column(clos.table.name, clos.parentColumn);
641
642         RolapLevel level = new RolapLevel(peerHier, index++,
643             "Closure",
644             keyExp, null, null, null,
645             null, null, // no longer a parent-child hierarchy
646
null,
647             RolapProperty.emptyArray,
648             flags,
649             src.getDatatype(),
650             src.getHideMemberCondition(),
651             src.getLevelType(),
652             "");
653         peerHier.levels =
654             (RolapLevel[]) RolapUtil.addElement(peerHier.levels, level);
655
656         // Create lower level.
657
// This represents individual items. For example, in the Employee
658
// closure hierarchy, this level has a row for every direct and
659
// indirect report of every employee (which is more than the number
660
// of employees).
661
flags = src.getFlags() | RolapLevel.UNIQUE;
662         keyExp = new MondrianDef.Column(clos.table.name, clos.childColumn);
663         RolapLevel sublevel = new RolapLevel(
664             peerHier,
665             index++,
666             "Item",
667             keyExp,
668             null,
669             null,
670             null,
671             null,
672             null, // no longer a parent-child hierarchy
673
null,
674             RolapProperty.emptyArray,
675             flags,
676             src.getDatatype(),
677             src.getHideMemberCondition(),
678             src.getLevelType(), "");
679         peerHier.levels =
680             (RolapLevel[]) RolapUtil.addElement(peerHier.levels, sublevel);
681
682 /*
683 RME HACK
684 */

685         cube.createUsage(peerHier, xmlDimension);
686
687         return peerDimension;
688     }
689
690
691
692     /**
693      * A <code>RolapNullMember</code> is the null member of its hierarchy.
694      * Every hierarchy has precisely one. They are yielded by operations such as
695      * <code>[Gender].[All].ParentMember</code>. Null members are usually
696      * omitted from sets (in particular, in the set constructor operator "{ ...
697      * }".
698      */

699     class RolapNullMember extends RolapMember {
700         RolapNullMember(final RolapLevel level) {
701             super(null, level, null, "#Null", MemberType.NULL);
702             assert level != null;
703         }
704
705         public boolean isNull() {
706             return true;
707         }
708     }
709
710     private static class RolapCalculatedMeasure
711         extends RolapCalculatedMember
712         implements RolapMeasure
713     {
714         public RolapCalculatedMeasure(
715             RolapMember parent, RolapLevel level, String JavaDoc name, Formula formula) {
716             super(parent, level, name, formula);
717         }
718
719         public CellFormatter getFormatter() {
720             return null;
721         }
722     }
723 }
724 // End RolapHierarchy.java
725
Popular Tags