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) 2007-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap4j;
011
012import mondrian.olap.*;
013
014import org.olap4j.OlapException;
015import org.olap4j.impl.*;
016import org.olap4j.mdx.IdentifierSegment;
017import org.olap4j.metadata.Cube;
018import org.olap4j.metadata.Dimension;
019import org.olap4j.metadata.Hierarchy;
020import org.olap4j.metadata.*;
021import org.olap4j.metadata.Member;
022import org.olap4j.metadata.NamedSet;
023import org.olap4j.metadata.Schema;
024
025import java.util.*;
026
027/**
028 * Implementation of {@link Cube}
029 * for the Mondrian OLAP engine.
030 *
031 * @author jhyde
032 * @since May 24, 2007
033 */
034class MondrianOlap4jCube
035    extends MondrianOlap4jMetadataElement
036    implements Cube, Named
037{
038    final mondrian.olap.Cube cube;
039    final MondrianOlap4jSchema olap4jSchema;
040
041    MondrianOlap4jCube(
042        mondrian.olap.Cube cube,
043        MondrianOlap4jSchema olap4jSchema)
044    {
045        this.cube = cube;
046        this.olap4jSchema = olap4jSchema;
047    }
048
049    public Schema getSchema() {
050        return olap4jSchema;
051    }
052
053    public int hashCode() {
054        return olap4jSchema.hashCode()
055            ^ cube.hashCode();
056    }
057
058    public boolean equals(Object obj) {
059        if (obj instanceof MondrianOlap4jCube) {
060            MondrianOlap4jCube that = (MondrianOlap4jCube) obj;
061            return this.olap4jSchema == that.olap4jSchema
062                && this.cube.equals(that.cube);
063        }
064        return false;
065    }
066
067    public NamedList<Dimension> getDimensions() {
068        NamedList<MondrianOlap4jDimension> list =
069            new NamedListImpl<MondrianOlap4jDimension>();
070        final MondrianOlap4jConnection olap4jConnection =
071            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
072        final mondrian.olap.SchemaReader schemaReader =
073            olap4jConnection.getMondrianConnection2().getSchemaReader()
074            .withLocus();
075        for (mondrian.olap.Dimension dimension
076            : schemaReader.getCubeDimensions(cube))
077        {
078            list.add(
079                new MondrianOlap4jDimension(
080                    olap4jSchema, dimension));
081        }
082        return Olap4jUtil.cast(list);
083    }
084
085    public NamedList<Hierarchy> getHierarchies() {
086        NamedList<MondrianOlap4jHierarchy> list =
087            new NamedListImpl<MondrianOlap4jHierarchy>();
088        final MondrianOlap4jConnection olap4jConnection =
089            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
090        final mondrian.olap.SchemaReader schemaReader =
091            olap4jConnection.getMondrianConnection2().getSchemaReader()
092            .withLocus();
093        for (mondrian.olap.Dimension dimension
094            : schemaReader.getCubeDimensions(cube))
095        {
096            for (mondrian.olap.Hierarchy hierarchy
097                : schemaReader.getDimensionHierarchies(dimension))
098            {
099                list.add(
100                    new MondrianOlap4jHierarchy(
101                        olap4jSchema, hierarchy));
102            }
103        }
104        return Olap4jUtil.cast(list);
105    }
106
107    public List<Measure> getMeasures() {
108        final Dimension dimension = getDimensions().get("Measures");
109        if (dimension == null) {
110            return Collections.emptyList();
111        }
112        final MondrianOlap4jConnection olap4jConnection =
113            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
114        try {
115            final mondrian.olap.SchemaReader schemaReader =
116                olap4jConnection.getMondrianConnection().getSchemaReader()
117                .withLocus();
118            final MondrianOlap4jLevel measuresLevel =
119                (MondrianOlap4jLevel)
120                    dimension.getDefaultHierarchy()
121                        .getLevels().get(0);
122            final List<Measure> measures =
123                new ArrayList<Measure>();
124            List<mondrian.olap.Member> levelMembers =
125                schemaReader.getLevelMembers(
126                    measuresLevel.level,
127                    true);
128            for (mondrian.olap.Member member : levelMembers) {
129                // This corrects MONDRIAN-1123, a ClassCastException (see below)
130                // that occurs when you create a calculated member on a
131                // dimension other than Measures:
132                // java.lang.ClassCastException:
133                // mondrian.olap4j.MondrianOlap4jMember cannot be cast to
134                // org.olap4j.metadata.Measure
135                MondrianOlap4jMember olap4jMember = olap4jConnection.toOlap4j(
136                    member);
137                if (olap4jMember instanceof Measure) {
138                    measures.add((Measure) olap4jMember);
139                }
140            }
141            return measures;
142        } catch (OlapException e) {
143            // OlapException not possible, since measures are stored in memory.
144            // Demote from checked to unchecked exception.
145            throw new RuntimeException(e);
146        }
147    }
148
149    public NamedList<NamedSet> getSets() {
150        final NamedListImpl<MondrianOlap4jNamedSet> list =
151            new NamedListImpl<MondrianOlap4jNamedSet>();
152        final MondrianOlap4jConnection olap4jConnection =
153            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
154        for (mondrian.olap.NamedSet namedSet : cube.getNamedSets()) {
155            list.add(olap4jConnection.toOlap4j(cube, namedSet));
156        }
157        return Olap4jUtil.cast(list);
158    }
159
160    public Collection<Locale> getSupportedLocales() {
161        throw new UnsupportedOperationException();
162    }
163
164    public String getName() {
165        return cube.getName();
166    }
167
168    public String getUniqueName() {
169        return cube.getUniqueName();
170    }
171
172    public String getCaption() {
173        return cube.getLocalized(
174            OlapElement.LocalizedProperty.CAPTION, olap4jSchema.getLocale());
175    }
176
177    public String getDescription() {
178        return cube.getLocalized(
179            OlapElement.LocalizedProperty.DESCRIPTION,
180            olap4jSchema.getLocale());
181    }
182
183    public boolean isVisible() {
184        return cube.isVisible();
185    }
186
187    public MondrianOlap4jMember lookupMember(
188        List<IdentifierSegment> nameParts)
189        throws OlapException
190    {
191        final MondrianOlap4jConnection olap4jConnection =
192            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
193        final Role role = olap4jConnection.getMondrianConnection().getRole();
194        final SchemaReader schemaReader =
195            cube.getSchemaReader(role).withLocus();
196        return lookupMember(schemaReader, nameParts);
197    }
198
199    private MondrianOlap4jMember lookupMember(
200        SchemaReader schemaReader,
201        List<IdentifierSegment> nameParts)
202    {
203        final List<mondrian.olap.Id.Segment> segmentList =
204            new ArrayList<mondrian.olap.Id.Segment>();
205        for (IdentifierSegment namePart : nameParts) {
206            segmentList.add(Util.convert(namePart));
207        }
208        final mondrian.olap.Member member =
209            schemaReader.getMemberByUniqueName(segmentList, false);
210        if (member == null) {
211            return null;
212        }
213
214        return olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
215            .olap4jConnection.toOlap4j(member);
216    }
217
218    public List<Member> lookupMembers(
219        Set<Member.TreeOp> treeOps,
220        List<IdentifierSegment> nameParts) throws OlapException
221    {
222        final MondrianOlap4jConnection olap4jConnection =
223            olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData.olap4jConnection;
224        final Role role = olap4jConnection.getMondrianConnection().getRole();
225        final SchemaReader schemaReader =
226            cube.getSchemaReader(role).withLocus();
227        final MondrianOlap4jMember member =
228            lookupMember(schemaReader, nameParts);
229        if (member == null) {
230            return Collections.emptyList();
231        }
232
233        // Add ancestors and/or the parent. Ancestors are prepended, to ensure
234        // hierarchical order.
235        final List<MondrianOlap4jMember> list =
236            new ArrayList<MondrianOlap4jMember>();
237        if (treeOps.contains(Member.TreeOp.ANCESTORS)) {
238            for (MondrianOlap4jMember m = member.getParentMember();
239                m != null;
240                m = m.getParentMember())
241            {
242                list.add(0, m);
243            }
244        } else if (treeOps.contains(Member.TreeOp.PARENT)) {
245            final MondrianOlap4jMember parentMember = member.getParentMember();
246            if (parentMember != null) {
247                list.add(parentMember);
248            }
249        }
250
251        // Add siblings. Siblings which occur after the member are deferred,
252        // because they occur after children and descendants in the
253        // hierarchical ordering.
254        List<MondrianOlap4jMember> remainingSiblingsList = null;
255        if (treeOps.contains(Member.TreeOp.SIBLINGS)) {
256            final MondrianOlap4jMember parentMember = member.getParentMember();
257            NamedList<MondrianOlap4jMember> siblingMembers;
258            if (parentMember != null) {
259                siblingMembers =
260                    olap4jConnection.toOlap4j(
261                        schemaReader.getMemberChildren(parentMember.member));
262            } else {
263                siblingMembers =
264                    olap4jConnection.toOlap4j(
265                        schemaReader.getHierarchyRootMembers(
266                            member.member.getHierarchy()));
267            }
268            List<MondrianOlap4jMember> targetList = list;
269            for (MondrianOlap4jMember siblingMember : siblingMembers) {
270                if (siblingMember.equals(member)) {
271                    targetList =
272                        remainingSiblingsList =
273                            new ArrayList<MondrianOlap4jMember>();
274                } else {
275                    targetList.add(siblingMember);
276                }
277            }
278        }
279
280        // Add the member itself.
281        if (treeOps.contains(Member.TreeOp.SELF)) {
282            list.add(member);
283        }
284
285        // Add descendants and/or children.
286        if (treeOps.contains(Member.TreeOp.DESCENDANTS)) {
287            addDescendants(list, schemaReader, olap4jConnection, member, true);
288        } else if (treeOps.contains(Member.TreeOp.CHILDREN)) {
289            addDescendants(list, schemaReader, olap4jConnection, member, false);
290        }
291        // Lastly, add siblings which occur after the member itself. They
292        // occur after all of the descendants in the hierarchical ordering.
293        if (remainingSiblingsList != null) {
294            list.addAll(remainingSiblingsList);
295        }
296        return Olap4jUtil.cast(list);
297    }
298
299    private void addDescendants(
300        List<MondrianOlap4jMember> list,
301        SchemaReader schemaReader,
302        MondrianOlap4jConnection olap4jConnection,
303        MondrianOlap4jMember member,
304        boolean recurse)
305    {
306        for (mondrian.olap.Member m
307            : schemaReader.getMemberChildren(member.member))
308        {
309            MondrianOlap4jMember childMember = olap4jConnection.toOlap4j(m);
310            list.add(childMember);
311            if (recurse) {
312                addDescendants(
313                    list, schemaReader, olap4jConnection, childMember, recurse);
314            }
315        }
316    }
317
318    public boolean isDrillThroughEnabled() {
319        return true;
320    }
321
322    protected OlapElement getOlapElement() {
323        return cube;
324    }
325}
326
327// End MondrianOlap4jCube.java