001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2001-2005 Julian Hyde 008// Copyright (C) 2005-2009 Pentaho and others 009// All Rights Reserved. 010// 011// jhyde, 10 August, 2001 012*/ 013package mondrian.rolap; 014 015import mondrian.olap.*; 016import mondrian.resource.MondrianResource; 017 018import org.apache.log4j.Logger; 019 020import java.util.Collections; 021import java.util.Map; 022 023/** 024 * <code>RolapDimension</code> implements {@link Dimension}for a ROLAP 025 * database. 026 * 027 * <h2><a name="topic_ordinals">Topic: Dimension ordinals </a></h2> 028 * 029 * {@link RolapEvaluator} needs each dimension to have an ordinal, so that it 030 * can store the evaluation context as an array of members. 031 * 032 * <p> 033 * A dimension may be either shared or private to a particular cube. The 034 * dimension object doesn't actually know which; {@link Schema} has a list of 035 * shared hierarchies ({@link Schema#getSharedHierarchies}), and {@link Cube} 036 * has a list of dimensions ({@link Cube#getDimensions}). 037 * 038 * <p> 039 * If a dimension is shared between several cubes, the {@link Dimension}objects 040 * which represent them may (or may not be) the same. (That's why there's no 041 * <code>getCube()</code> method.) 042 * 043 * <p> 044 * Furthermore, since members are created by a {@link MemberReader}which 045 * belongs to the {@link RolapHierarchy}, you will the members will be the same 046 * too. For example, if you query <code>[Product].[Beer]</code> from the 047 * <code>Sales</code> and <code>Warehouse</code> cubes, you will get the 048 * same {@link RolapMember}object. 049 * ({@link RolapSchema#mapSharedHierarchyToReader} holds the mapping. I don't 050 * know whether it's still necessary.) 051 * 052 * @author jhyde 053 * @since 10 August, 2001 054 */ 055class RolapDimension extends DimensionBase { 056 057 private static final Logger LOGGER = Logger.getLogger(RolapDimension.class); 058 059 private final Schema schema; 060 private final Map<String, Annotation> annotationMap; 061 062 RolapDimension( 063 Schema schema, 064 String name, 065 String caption, 066 boolean visible, 067 String description, 068 DimensionType dimensionType, 069 final boolean highCardinality, 070 Map<String, Annotation> annotationMap) 071 { 072 // todo: recognition of a time dimension should be improved 073 // allow multiple time dimensions 074 super( 075 name, 076 caption, 077 visible, 078 description, 079 dimensionType, 080 highCardinality); 081 assert annotationMap != null; 082 this.schema = schema; 083 this.annotationMap = annotationMap; 084 this.hierarchies = new RolapHierarchy[0]; 085 } 086 087 /** 088 * Creates a dimension from an XML definition. 089 * 090 * @pre schema != null 091 */ 092 RolapDimension( 093 RolapSchema schema, 094 RolapCube cube, 095 MondrianDef.Dimension xmlDimension, 096 MondrianDef.CubeDimension xmlCubeDimension) 097 { 098 this( 099 schema, 100 xmlDimension.name, 101 xmlDimension.caption, 102 xmlDimension.visible, 103 xmlDimension.description, 104 xmlDimension.getDimensionType(), 105 xmlDimension.highCardinality, 106 RolapHierarchy.createAnnotationMap(xmlCubeDimension.annotations)); 107 108 Util.assertPrecondition(schema != null); 109 110 if (cube != null) { 111 Util.assertTrue(cube.getSchema() == schema); 112 } 113 114 if (!Util.isEmpty(xmlDimension.caption)) { 115 setCaption(xmlDimension.caption); 116 } 117 this.hierarchies = new RolapHierarchy[xmlDimension.hierarchies.length]; 118 for (int i = 0; i < xmlDimension.hierarchies.length; i++) { 119 // remaps the xml hierarchy relation to the fact table. 120 // moved out of RolapHierarchy constructor 121 // this should eventually be phased out completely 122 if (xmlDimension.hierarchies[i].relation == null 123 && xmlDimension.hierarchies[i].memberReaderClass == null 124 && cube != null) 125 { 126 xmlDimension.hierarchies[i].relation = cube.fact; 127 } 128 129 RolapHierarchy hierarchy = new RolapHierarchy( 130 this, xmlDimension.hierarchies[i], xmlCubeDimension); 131 hierarchies[i] = hierarchy; 132 } 133 134 // if there was no dimension type assigned, determine now. 135 if (dimensionType == null) { 136 for (int i = 0; i < hierarchies.length; i++) { 137 Level[] levels = hierarchies[i].getLevels(); 138 LevLoop: 139 for (int j = 0; j < levels.length; j++) { 140 Level lev = levels[j]; 141 if (lev.isAll()) { 142 continue LevLoop; 143 } 144 if (dimensionType == null) { 145 // not set yet - set it according to current level 146 dimensionType = (lev.getLevelType().isTime()) 147 ? DimensionType.TimeDimension 148 : isMeasures() 149 ? DimensionType.MeasuresDimension 150 : DimensionType.StandardDimension; 151 152 } else { 153 // Dimension type was set according to first level. 154 // Make sure that other levels fit to definition. 155 if (dimensionType == DimensionType.TimeDimension 156 && !lev.getLevelType().isTime() 157 && !lev.isAll()) 158 { 159 throw MondrianResource.instance() 160 .NonTimeLevelInTimeHierarchy.ex( 161 getUniqueName()); 162 } 163 if (dimensionType != DimensionType.TimeDimension 164 && lev.getLevelType().isTime()) 165 { 166 throw MondrianResource.instance() 167 .TimeLevelInNonTimeHierarchy.ex( 168 getUniqueName()); 169 } 170 } 171 } 172 } 173 } 174 } 175 176 protected Logger getLogger() { 177 return LOGGER; 178 } 179 180 /** 181 * Initializes a dimension within the context of a cube. 182 */ 183 void init(MondrianDef.CubeDimension xmlDimension) { 184 for (int i = 0; i < hierarchies.length; i++) { 185 if (hierarchies[i] != null) { 186 ((RolapHierarchy) hierarchies[i]).init(xmlDimension); 187 } 188 } 189 } 190 191 /** 192 * Creates a hierarchy. 193 * 194 * @param subName Name of this hierarchy. 195 * @param hasAll Whether hierarchy has an 'all' member 196 * @param closureFor Hierarchy for which the new hierarchy is a closure; 197 * null for regular hierarchies 198 * @return Hierarchy 199 */ 200 RolapHierarchy newHierarchy( 201 String subName, 202 boolean hasAll, 203 RolapHierarchy closureFor) 204 { 205 RolapHierarchy hierarchy = 206 new RolapHierarchy( 207 this, subName, 208 caption, visible, description, hasAll, closureFor, 209 Collections.<String, Annotation>emptyMap()); 210 this.hierarchies = Util.append(this.hierarchies, hierarchy); 211 return hierarchy; 212 } 213 214 /** 215 * Returns the hierarchy of an expression. 216 * 217 * <p>In this case, the expression is a dimension, so the hierarchy is the 218 * dimension's default hierarchy (its first). 219 */ 220 public Hierarchy getHierarchy() { 221 return hierarchies[0]; 222 } 223 224 public Schema getSchema() { 225 return schema; 226 } 227 228 public Map<String, Annotation> getAnnotationMap() { 229 return annotationMap; 230 } 231} 232 233// End RolapDimension.java