/* // $Id: //guest/julian_hyde/mondrian/src/main/mondrian/rolap/RolapHierarchy.java#1 $ // This software is subject to the terms of the Common Public License // Agreement, available at the following URL: // http://www.opensource.org/licenses/cpl.html. // (C) Copyright 2001-2002 Kana Software, Inc. and others. // All Rights Reserved. // You must accept the terms of that agreement to use this software. // // jhyde, 10 August, 2001 */ package mondrian.rolap; import mondrian.olap.*; import java.util.Properties; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * <code>RolapHierarchy</code> implements {@link Hierarchy} for a ROLAP database. * * @author jhyde * @since 10 August, 2001 * @version $Id: //guest/julian_hyde/mondrian/src/main/mondrian/rolap/RolapHierarchy.java#1 $ */ class RolapHierarchy extends HierarchyBase { MemberReader memberReader; private MondrianDef.Hierarchy xmlHierarchy; private RolapMember defaultMember; private RolapMember nullMember; /** * If this hierarchy is a public -- that is, it belongs to a dimension * which is a usage of a shared dimension -- then * <code>sharedHierarchy</code> holds the unique name of the shared * hierarchy; otherwise it is null. * * <p> Suppose this hierarchy is "Weekly" in the dimension "Order Date" of * cube "Sales", and that "Order Date" is a usage of the "Time" * dimension. Then <code>sharedHierarchy</code> will be "[Time].[Weekly]". **/ private String sharedHierarchy; /** * Source query, for example "select * from product join * * product_category". Level- and measure-column names refer to * expressions in this select list. **/ String sql; String primaryKey; // name of pk column in dimension table // String foreignKey; // name of fk column in fact table /** * If a dimension has more than this number of members, use a {@link * SmartMemberReader}. **/ static final int LARGE_DIMENSION_THRESHOLD = 100; // /** @deprecated **/ // RolapHierarchy(String subName, boolean hasAll, RolapLevel[] levels) // { // this.subName = subName; // this.hasAll = hasAll; // if (hasAll) { // this.levels = new RolapLevel[levels.length + 1]; // this.levels[0] = new RolapLevel(this, "(All)", null, null); // System.arraycopy(levels, 0, this.levels, 1, levels.length); // } else { // this.levels = levels; // } // } private RolapHierarchy(RolapDimension dimension, String subName, boolean hasAll) { this.dimension = dimension; this.subName = subName; this.hasAll = hasAll; this.levels = new RolapLevel[0]; if (hasAll) { Util.discard(newLevel("(All)", null, null, RolapLevel.ALL)); } this.name = dimension.getName(); this.uniqueName = dimension.getUniqueName(); if (this.subName != null) { this.name += "." + subName; // e.g. "Time.Weekly" this.uniqueName = Util.makeFqName(name); // e.g. "[Time.Weekly]" } } RolapHierarchy( RolapDimension dimension, String subName, boolean hasAll, String sql, String primaryKey, String foreignKey) { this(dimension, subName, hasAll); this.sql = sql; this.primaryKey = primaryKey; setForeignKey(foreignKey); } RolapHierarchy( RolapDimension dimension, MondrianDef.Hierarchy xmlHierarchy, MondrianDef.CubeDimension xmlCubeDimension) { this(dimension, xmlHierarchy.name, xmlHierarchy.hasAll.booleanValue()); this.xmlHierarchy = xmlHierarchy; if (hasAll) { this.levels = new RolapLevel[xmlHierarchy.levels.length + 1]; this.levels[0] = new RolapLevel( this, 0, "(All)", null, null, RolapLevel.ALL); for (int i = 0; i < xmlHierarchy.levels.length; i++) { levels[i + 1] = new RolapLevel( this, i + 1, xmlHierarchy.levels[i]); } } else { this.levels = new RolapLevel[xmlHierarchy.levels.length]; for (int i = 0; i < xmlHierarchy.levels.length; i++) { levels[i] = new RolapLevel(this, i, xmlHierarchy.levels[i]); } } this.sharedHierarchy = null; if (xmlCubeDimension instanceof MondrianDef.DimensionUsage) { String sharedDimensionName = ((MondrianDef.DimensionUsage) xmlCubeDimension).source; this.sharedHierarchy = sharedDimensionName; if (subName != null) { this.sharedHierarchy += "." + subName; // e.g. "Time.Weekly" } } this.sql = xmlHierarchy.sql; this.primaryKey = xmlHierarchy.primaryKey; setForeignKey(xmlCubeDimension.foreignKey); } private void setForeignKey(String foreignKey) { RolapCube cube = (RolapCube) getCube(); if (cube.factTable != null) { // virtual cubes (which don't have fact tables) don't create usages HierarchyUsage usage = getUsage(cube.factTable); usage.foreignKey = foreignKey; } } void init() { for (int i = 0; i < levels.length; i++) { ((RolapLevel) levels[i]).init(); } if (this.memberReader != null) { } else if (this.sharedHierarchy != null) { RolapConnection connection = (RolapConnection) getCube().getConnection(); this.memberReader = (MemberReader) connection.mapSharedHierarchyToReader.get( this.sharedHierarchy); if (this.memberReader == null) { this.memberReader = createMemberReader(); // share, for other uses of the same shared hierarchy connection.mapSharedHierarchyToReader.put( this.sharedHierarchy, this.memberReader); } } else { this.memberReader = createMemberReader(); } } RolapLevel newLevel(String name, String column, String ordinalColumn, int flags) { RolapLevel level = new RolapLevel( this, this.levels.length, name, column, ordinalColumn, flags); this.levels = (RolapLevel[]) RolapUtil.addElement( this.levels, level); return level; } RolapLevel newLevel(String name, String column, String ordinalColumn) { return newLevel(name, column, ordinalColumn, 0); } RolapLevel newLevel(String name, String column) { return newLevel(name, column, null, 0); } private MemberReader createMemberReader() { if (this.memberReader != null) { return this.memberReader; } if (sql != null) { SqlMemberSource source = new SqlMemberSource(this); int memberCount = source.getMemberCount(); if (memberCount > LARGE_DIMENSION_THRESHOLD && source.canDoRolap()) { return new SmartMemberReader(source); } else { return new CacheMemberReader(source); } } if (xmlHierarchy != null && xmlHierarchy.memberReaderClass != null) { Exception e2 = null; try { Properties properties = null; Class clazz = Class.forName( xmlHierarchy.memberReaderClass); Constructor constructor = clazz.getConstructor(new Class[] { RolapHierarchy.class, Properties.class}); Object o = constructor.newInstance( new Object[] {this, properties}); if (o instanceof MemberReader) { return (MemberReader) o; } else if (o instanceof MemberSource) { return new CacheMemberReader((MemberSource) o); } else { throw Util.getRes().newInternal( "member reader class " + clazz + " does not implement " + MemberSource.class); } } catch (ClassNotFoundException e) { e2 = e; } catch (NoSuchMethodException e) { e2 = e; } catch (InstantiationException e) { e2 = e; } catch (IllegalAccessException e) { e2 = e; } catch (InvocationTargetException e) { e2 = e; } if (e2 != null) { throw Util.getRes().newInternal( e2, "while instantiating member reader '" + xmlHierarchy.memberReaderClass); } } throw Util.newInternal( "cannot create member reader for hierarchy '" + name + "'"); } public Member getDefaultMember() { // use lazy initialization to get around bootstrap issues if (defaultMember == null) { defaultMember = memberReader.getRootMembers()[0]; } return defaultMember; } public Member getNullMember() { // use lazy initialization to get around bootstrap issues if (nullMember == null) { nullMember = new RolapMember( null, (RolapLevel) levels[0], null, "#Null"); } return nullMember; } public Member[] getRootMembers() { return memberReader.getRootMembers(); } public Member createMember( Member parent, Level level, String name, Formula formula) { if (formula != null) { return new RolapCalculatedMember( (RolapMember) parent, (RolapLevel) level, name, formula); } else { return new RolapMember( (RolapMember) parent, (RolapLevel) level, name); } } public int getMembersCount(int nMaxMembers) { return memberReader.getMemberCount(); } String getAlias() { return getName(); } HierarchyUsage createUsage(String factTable) { if (sharedHierarchy == null) { return new PrivateHierarchyUsage(factTable, this); } else { return new SharedHierarchyUsage(factTable, sharedHierarchy); } } HierarchyUsage getUsage(String factTable) { RolapConnection connection = (RolapConnection) getCube().getConnection(); HierarchyUsage usageKey = createUsage(factTable), usage = (HierarchyUsage) connection.hierarchyUsages.get(usageKey); if (usage == null) { connection.hierarchyUsages.put(usageKey, usageKey); usage = usageKey; } return usage; } }; /** * A <code>HierarchyUsage</code> is the usage of a hierarchy in the context * of a cube. Private hierarchies can only be used in their own * cube. Public hierarchies can be used in several cubes. The problem comes * when several cubes which the same public hierarchy are brought together * in one virtual cube. There are now several usages of the same public * hierarchy. Which one to use? It depends upon what measure we are * currently using. We should use the hierarchy usage for the fact table * which underlies the measure. That is what determines the foreign key to * join on. * * A <code>HierarchyUsage</code> is identified by * <code>(hierarchy.sharedHierarchy, factTable)</code> if the hierarchy is * shared, or <code>(hierarchy, factTable)</code> if it is private. **/ abstract class HierarchyUsage { String factTable; /** The foreign key by which {@link #hierarchy} should be joined to * {@link #factTable}. **/ String foreignKey; }; class SharedHierarchyUsage extends HierarchyUsage { String sharedHierarchy; SharedHierarchyUsage(String factTable, String sharedHierarchy) { this.factTable = factTable; this.sharedHierarchy = sharedHierarchy; } public boolean equals(Object o) { if (o instanceof SharedHierarchyUsage) { SharedHierarchyUsage that = (SharedHierarchyUsage) o; return this.factTable.equals(that.factTable) && this.sharedHierarchy.equals(that.sharedHierarchy); } return false; } public int hashCode() { int h = factTable.hashCode(); int i = sharedHierarchy.hashCode(); return (h << 4) | i; } }; class PrivateHierarchyUsage extends HierarchyUsage { RolapHierarchy hierarchy; PrivateHierarchyUsage(String factTable, RolapHierarchy hierarchy) { this.factTable = factTable; this.hierarchy = hierarchy; } public boolean equals(Object o) { if (o instanceof PrivateHierarchyUsage) { PrivateHierarchyUsage that = (PrivateHierarchyUsage) o; return this.factTable.equals(that.factTable) && this.hierarchy.equals(that.hierarchy); } return false; } public int hashCode() { int h = factTable.hashCode(); int i = hierarchy.hashCode(); return (h << 4) | i; } }; // End RolapHierarchy.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 1609 | Julian Hyde |
mondrian: Forgot a couple of files last change. Unit tests for builtin functions. Don't return null members (e.g. [Gender].[F].PrevMember). |
||
#4 | 1603 | Julian Hyde |
mondrian: Add Andreas' taglib; Rename 'mondrian.rolap.Util.assert' to 'assertTrue', because 'assert' is a keyword in jdk 1.4; Fix 'NON EMPTY'; JUnit framework for tests; Add Oracle dataset. |
||
#3 | 1576 | Julian Hyde |
mondrian: fix dataset (add column customer.ordinal); create dataset for oracle; get queries working on oracle; get format strings working; refactor out new packages mondrian.rolap.agg and mondrian.rolap.sql. |
||
#2 | 1496 | Julian Hyde |
Mondrian: Qualify table names with schemas. Add 'Hierarchy.schema', 'Cube.factSchema','Hierarchy.table' attributes. MondrianDef is now generated (so we need boot.jar). |
||
#1 | 1453 | Julian Hyde | mondrian: first source check-in |