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