001    /*
002     // $Id: //open/mondrian/src/main/mondrian/rolap/RolapDimension.java#32 $
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, 10 August, 2001
012     */
013    
014    package mondrian.rolap;
015    
016    import org.apache.log4j.Logger;
017    import mondrian.olap.*;
018    import mondrian.resource.MondrianResource;
019    
020    /**
021     * <code>RolapDimension</code> implements {@link Dimension}for a ROLAP
022     * database.
023     *
024     * <h2><a name="topic_ordinals">Topic: Dimension ordinals </a></h2>
025     *
026     * {@link RolapEvaluator} needs each dimension to have an ordinal, so that it
027     * can store the evaluation context as an array of members.
028     *
029     * <p>
030     * The ordinal of a dimension <em>within a particular cube</em> is found by
031     * calling {@link #getOrdinal(Cube)}. Ordinals
032     * are contiguous and zero-based. Zero is always the <code>[Measures]</code>
033     * dimension.
034     *
035     * <p>
036     * A dimension may be either shared or private to a particular cube. The
037     * dimension object doesn't actually know which; {@link Schema} has a list of
038     * shared hierarchies ({@link Schema#getSharedHierarchies}), and {@link Cube}
039     * has a list of dimensions ({@link Cube#getDimensions}).
040     *
041     * <p>
042     * If a dimension is shared between several cubes, the {@link Dimension}objects
043     * which represent them may (or may not be) the same. (That's why there's no
044     * <code>getCube()</code> method.)
045     *
046     * <p>
047     * Furthermore, since members are created by a {@link MemberReader}which
048     * belongs to the {@link RolapHierarchy}, you will the members will be the same
049     * too. For example, if you query <code>[Product].[Beer]</code> from the
050     * <code>Sales</code> and <code>Warehouse</code> cubes, you will get the
051     * same {@link RolapMember}object.
052     * ({@link RolapSchema#mapSharedHierarchyToReader} holds the mapping. I don't
053     * know whether it's still necessary.)
054     *
055     * @author jhyde
056     * @since 10 August, 2001
057     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapDimension.java#32 $
058     */
059    class RolapDimension extends DimensionBase {
060    
061        private static final Logger LOGGER = Logger.getLogger(RolapDimension.class);
062    
063        private final Schema schema;
064    
065        RolapDimension(
066            Schema schema,
067            String name,
068            DimensionType dimensionType,
069            final boolean highCardinality)
070        {
071            // todo: recognition of a time dimension should be improved
072            // allow multiple time dimensions
073            super(name, dimensionType, highCardinality);
074            this.schema = schema;
075            this.hierarchies = new RolapHierarchy[0];
076        }
077    
078        /**
079         * Creates a dimension from an XML definition.
080         *
081         * @pre schema != null
082         */
083        RolapDimension(
084            RolapSchema schema,
085            RolapCube cube,
086            MondrianDef.Dimension xmlDimension,
087            MondrianDef.CubeDimension xmlCubeDimension)
088        {
089            this(
090                schema,
091                xmlDimension.name,
092                xmlDimension.getDimensionType(),
093                xmlDimension.highCardinality);
094    
095            Util.assertPrecondition(schema != null);
096    
097            if (cube != null) {
098                Util.assertTrue(cube.getSchema() == schema);
099            }
100    
101            if (!Util.isEmpty(xmlDimension.caption)) {
102                setCaption(xmlDimension.caption);
103            }
104            this.hierarchies = new RolapHierarchy[xmlDimension.hierarchies.length];
105            for (int i = 0; i < xmlDimension.hierarchies.length; i++) {
106    
107                // remaps the xml hierarchy relation to the fact table.
108                // moved out of RolapHierarchy constructor
109                // this should eventually be phased out completely
110                if (xmlDimension.hierarchies[i].relation == null &&
111                        xmlDimension.hierarchies[i].memberReaderClass == null &&
112                        cube != null) {
113                    xmlDimension.hierarchies[i].relation = cube.fact;
114                }
115    
116                RolapHierarchy hierarchy = new RolapHierarchy(
117                    this, xmlDimension.hierarchies[i], xmlCubeDimension);
118                hierarchies[i] = hierarchy;
119            }
120    
121            // if there was no dimension type assigned, determine now.
122            if (dimensionType == null) {
123                for (int i = 0; i < hierarchies.length; i++) {
124                    Level[] levels = hierarchies[i].getLevels();
125                    LevLoop:
126                    for (int j = 0; j < levels.length; j++) {
127                        Level lev = levels[j];
128                        if (lev.isAll()) {
129                            continue LevLoop;
130                        }
131                        if (dimensionType == null) {
132                            // not set yet - set it according to current level
133                            dimensionType = (lev.getLevelType().isTime())
134                                ? DimensionType.TimeDimension
135                                : isMeasures()
136                                ? DimensionType.MeasuresDimension
137                                : DimensionType.StandardDimension;
138    
139                        } else {
140                            // Dimension type was set according to first level.
141                            // Make sure that other levels fit to definition.
142                            if (dimensionType == DimensionType.TimeDimension &&
143                                !lev.getLevelType().isTime() &&
144                                !lev.isAll()) {
145                                throw MondrianResource.instance().NonTimeLevelInTimeHierarchy.ex(
146                                        getUniqueName());
147                            }
148                            if (dimensionType != DimensionType.TimeDimension &&
149                                lev.getLevelType().isTime()) {
150                                throw MondrianResource.instance().TimeLevelInNonTimeHierarchy.ex(
151                                        getUniqueName());
152                            }
153                        }
154                    }
155                }
156            }
157        }
158    
159        protected Logger getLogger() {
160            return LOGGER;
161        }
162    
163        /**
164         * Initializes a dimension within the context of a cube.
165         */
166        void init(MondrianDef.CubeDimension xmlDimension) {
167            for (int i = 0; i < hierarchies.length; i++) {
168                if (hierarchies[i] != null) {
169                    ((RolapHierarchy) hierarchies[i]).init(xmlDimension);
170                }
171            }
172        }
173    
174        RolapHierarchy newHierarchy(String subName, boolean hasAll) {
175            RolapHierarchy hierarchy = new RolapHierarchy(this, subName, hasAll);
176            this.hierarchies = (RolapHierarchy[])
177                RolapUtil.addElement(this.hierarchies, hierarchy);
178            return hierarchy;
179        }
180    
181        /**
182         * Returns the hierarchy of an expression.
183         *
184         * <p>In this case, the expression is a dimension, so the hierarchy is the
185         * dimension's default hierarchy (its first).
186         */
187        public Hierarchy getHierarchy() {
188            return hierarchies[0];
189        }
190    
191        public int getOrdinal(Cube cube) {
192            // this is temporary to verify that all calls to this method are for
193            // the measures dimension
194            assert isMeasures();
195            return 0;
196        }
197    
198        public Schema getSchema() {
199            return schema;
200        }
201    }
202    
203    // End RolapDimension.java