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) 2010-2012 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap4j;
011
012import mondrian.olap.*;
013import mondrian.olap.Property;
014import mondrian.olap.fun.FunInfo;
015import mondrian.rolap.*;
016import mondrian.xmla.RowsetDefinition;
017import mondrian.xmla.XmlaHandler;
018
019import org.olap4j.*;
020import org.olap4j.Cell;
021import org.olap4j.metadata.*;
022import org.olap4j.metadata.Cube;
023import org.olap4j.metadata.Hierarchy;
024import org.olap4j.metadata.Level;
025import org.olap4j.metadata.Member;
026import org.olap4j.metadata.Schema;
027
028import java.sql.ResultSet;
029import java.sql.SQLException;
030import java.util.*;
031
032/**
033 * Provides access to internals of mondrian's olap4j driver that are not part
034 * of the olap4j API.
035 *
036 * @author jhyde
037 * @since Nov 12, 2010
038 */
039class MondrianOlap4jExtra implements XmlaHandler.XmlaExtra {
040    static final MondrianOlap4jExtra INSTANCE = new MondrianOlap4jExtra();
041
042    public ResultSet executeDrillthrough(
043        OlapStatement olapStatement,
044        String mdx,
045        boolean advanced,
046        String tabFields,
047        int[] rowCountSlot) throws SQLException
048    {
049        return ((MondrianOlap4jStatement) olapStatement).executeQuery2(
050            mdx,
051            advanced,
052            tabFields,
053            rowCountSlot);
054    }
055
056    public void setPreferList(OlapConnection connection) {
057        ((MondrianOlap4jConnection) connection).setPreferList(true);
058    }
059
060    public Date getSchemaLoadDate(Schema schema) {
061        return ((MondrianOlap4jSchema) schema).schema.getSchemaLoadDate();
062    }
063
064    public int getLevelCardinality(Level level) throws OlapException {
065        if (level instanceof MondrianOlap4jLevel) {
066            // Improved implementation if the provider is mondrian.
067            final MondrianOlap4jLevel olap4jLevel = (MondrianOlap4jLevel) level;
068            final mondrian.olap.SchemaReader schemaReader =
069                olap4jLevel.olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
070                    .olap4jConnection.getMondrianConnection().getSchemaReader()
071                    .withLocus();
072            return schemaReader.getLevelCardinality(
073                olap4jLevel.level, true, true);
074        } else {
075            return level.getCardinality();
076        }
077    }
078
079    public void getSchemaFunctionList(
080        List<FunctionDefinition> funDefs,
081        Schema schema,
082        Util.Functor1<Boolean, String> functionFilter)
083    {
084        final FunTable funTable =
085            ((MondrianOlap4jSchema) schema).schema.getFunTable();
086        StringBuilder buf = new StringBuilder(50);
087        for (FunInfo fi : funTable.getFunInfoList()) {
088            switch (fi.getSyntax()) {
089            case Empty:
090            case Internal:
091            case Parentheses:
092                continue;
093            }
094            final Boolean passes = functionFilter.apply(fi.getName());
095            if (passes == null || !passes) {
096                continue;
097            }
098
099            int[][] paramCategories = fi.getParameterCategories();
100            int[] returnCategories = fi.getReturnCategories();
101
102            // Convert Windows newlines in 'description' to UNIX format.
103            String description = fi.getDescription();
104            if (description != null) {
105                description = Util.replace(
106                    fi.getDescription(),
107                    "\r",
108                    "");
109            }
110            if ((paramCategories == null)
111                || (paramCategories.length == 0))
112            {
113                funDefs.add(
114                    new FunctionDefinition(
115                        fi.getName(),
116                        description,
117                        "(none)",
118                        1,
119                        1,
120                        // TODO WHAT VALUE should this have
121                        "",
122                        fi.getName()));
123            } else {
124                for (int i = 0; i < paramCategories.length; i++) {
125                    int[] pc = paramCategories[i];
126                    int returnCategory = returnCategories[i];
127
128                    buf.setLength(0);
129                    for (int j = 0; j < pc.length; j++) {
130                        int v = pc[j];
131                        if (j > 0) {
132                            buf.append(", ");
133                        }
134                        buf.append(
135                            Category.instance.getDescription(
136                                v & Category.Mask));
137                    }
138
139                    RowsetDefinition.MdschemaFunctionsRowset.VarType varType =
140                        RowsetDefinition.MdschemaFunctionsRowset.VarType
141                            .forCategory(returnCategory);
142                    funDefs.add(
143                        new FunctionDefinition(
144                            fi.getName(),
145                            description,
146                            buf.toString(),
147                            //TODO: currently FunInfo can not tell us which
148                            // functions are MDX and which are UDFs.
149                            varType.ordinal(),
150                            1,
151                            // TODO: Name of the interface for UDF and Group
152                            // name for the MDX functions.
153                            // TODO WHAT VALUE should this have
154                            "",
155                            fi.getName()));
156                }
157            }
158        }
159    }
160
161    public int getHierarchyCardinality(Hierarchy hierarchy)
162        throws OlapException
163    {
164        final MondrianOlap4jHierarchy olap4jHierarchy =
165            (MondrianOlap4jHierarchy) hierarchy;
166        final mondrian.olap.SchemaReader schemaReader =
167            olap4jHierarchy.olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
168                .olap4jConnection.getMondrianConnection().getSchemaReader()
169                .withLocus();
170        return RolapMemberBase.getHierarchyCardinality(
171            schemaReader, olap4jHierarchy.hierarchy);
172    }
173
174    public int getHierarchyStructure(Hierarchy hierarchy) {
175        final MondrianOlap4jHierarchy olap4jHierarchy =
176            (MondrianOlap4jHierarchy) hierarchy;
177        return ((HierarchyBase) olap4jHierarchy.hierarchy).isRagged() ? 1 : 0;
178    }
179
180    public boolean isHierarchyParentChild(Hierarchy hierarchy) {
181        Level nonAllFirstLevel = hierarchy.getLevels().get(0);
182        if (nonAllFirstLevel.getLevelType() == Level.Type.ALL) {
183            nonAllFirstLevel = hierarchy.getLevels().get(1);
184        }
185        MondrianOlap4jLevel olap4jLevel =
186            (MondrianOlap4jLevel) nonAllFirstLevel;
187        return ((RolapLevel) olap4jLevel.level).isParentChild();
188    }
189
190    public int getMeasureAggregator(Member member) {
191        MondrianOlap4jMeasure olap4jMeasure =
192            (MondrianOlap4jMeasure) member;
193        Object aggProp =
194            olap4jMeasure.member.getPropertyValue(
195                Property.AGGREGATION_TYPE.name);
196        if (aggProp == null) {
197            return
198                RowsetDefinition.MdschemaMeasuresRowset
199                    .MDMEASURE_AGGR_CALCULATED;
200        }
201        RolapAggregator agg = (RolapAggregator) aggProp;
202        if (agg == RolapAggregator.Sum) {
203            return RowsetDefinition.MdschemaMeasuresRowset.MDMEASURE_AGGR_SUM;
204        } else if (agg == RolapAggregator.Count) {
205            return RowsetDefinition.MdschemaMeasuresRowset.MDMEASURE_AGGR_COUNT;
206        } else if (agg == RolapAggregator.Min) {
207            return RowsetDefinition.MdschemaMeasuresRowset.MDMEASURE_AGGR_MIN;
208        } else if (agg == RolapAggregator.Max) {
209            return RowsetDefinition.MdschemaMeasuresRowset.MDMEASURE_AGGR_MAX;
210        } else if (agg == RolapAggregator.Avg) {
211            return RowsetDefinition.MdschemaMeasuresRowset.MDMEASURE_AGGR_AVG;
212        } else {
213            //TODO: what are VAR and STD
214            return RowsetDefinition.MdschemaMeasuresRowset
215                .MDMEASURE_AGGR_UNKNOWN;
216        }
217    }
218
219    public void checkMemberOrdinal(Member member) throws OlapException {
220        if (member.getOrdinal() == -1) {
221            MondrianOlap4jMember olap4jMember =
222                (MondrianOlap4jMember) member;
223            final mondrian.olap.SchemaReader schemaReader =
224                olap4jMember.olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData
225                    .olap4jConnection.getMondrianConnection().getSchemaReader()
226                    .withLocus();
227            RolapMemberBase.setOrdinals(schemaReader, olap4jMember.member);
228        }
229    }
230
231    public boolean shouldReturnCellProperty(
232        CellSet cellSet,
233        org.olap4j.metadata.Property cellProperty,
234        boolean evenEmpty)
235    {
236        MondrianOlap4jCellSet olap4jCellSet = (MondrianOlap4jCellSet) cellSet;
237        Query query = olap4jCellSet.query;
238        return
239            (evenEmpty
240             && query.isCellPropertyEmpty())
241            || query.hasCellProperty(cellProperty.getName());
242    }
243
244    public List<String> getSchemaRoleNames(Schema schema) {
245        MondrianOlap4jSchema olap4jSchema = (MondrianOlap4jSchema) schema;
246        // TODO: this returns ALL roles, no the current user's roles
247        return new ArrayList<String>(
248            ((RolapSchema) olap4jSchema.schema).roleNames());
249    }
250
251    public String getSchemaId(Schema schema) {
252        return ((MondrianOlap4jSchema)schema).schema.getId();
253    }
254
255    public String getCubeType(Cube cube) {
256        return
257            (cube instanceof MondrianOlap4jCube)
258            && ((RolapCube) ((MondrianOlap4jCube) cube).cube).isVirtual()
259                ? RowsetDefinition.MdschemaCubesRowset.MD_CUBTYPE_VIRTUAL_CUBE
260                : RowsetDefinition.MdschemaCubesRowset.MD_CUBTYPE_CUBE;
261    }
262
263    public boolean isLevelUnique(Level level) {
264        MondrianOlap4jLevel olap4jLevel = (MondrianOlap4jLevel) level;
265        return (olap4jLevel.level instanceof RolapLevel)
266            && ((RolapLevel) olap4jLevel.level).isUnique();
267    }
268
269    public List<org.olap4j.metadata.Property> getLevelProperties(Level level) {
270        MondrianOlap4jLevel olap4jLevel = (MondrianOlap4jLevel) level;
271        return olap4jLevel.getProperties(false);
272    }
273
274    public boolean isPropertyInternal(org.olap4j.metadata.Property property) {
275        MondrianOlap4jProperty olap4jProperty =
276            (MondrianOlap4jProperty) property;
277        return olap4jProperty.property.isInternal();
278    }
279
280    public List<Map<String, Object>> getDataSources(OlapConnection connection)
281        throws OlapException
282    {
283        MondrianOlap4jConnection olap4jConnection =
284            (MondrianOlap4jConnection) connection;
285        MondrianServer server =
286            MondrianServer.forConnection(
287                olap4jConnection.getMondrianConnection());
288        return server.getDatabases(olap4jConnection.getMondrianConnection());
289    }
290
291    public Map<String, Object> getAnnotationMap(MetadataElement element)
292        throws SQLException
293    {
294        if (element instanceof OlapWrapper) {
295            OlapWrapper wrapper = (OlapWrapper) element;
296            if (wrapper.isWrapperFor(Annotated.class)) {
297                final Annotated annotated = wrapper.unwrap(Annotated.class);
298                final Map<String, Object> map = new HashMap<String, Object>();
299                for (Map.Entry<String, Annotation> entry
300                    : annotated.getAnnotationMap().entrySet())
301                {
302                    map.put(entry.getKey(), entry.getValue().getValue());
303                }
304                return map;
305            }
306        }
307        return Collections.emptyMap();
308    }
309
310    public boolean canDrillThrough(Cell cell) {
311        return ((MondrianOlap4jCell)cell).cell.canDrillThrough();
312    }
313
314    public int getDrillThroughCount(Cell cell) {
315        return ((MondrianOlap4jCell)cell).cell.getDrillThroughCount();
316    }
317
318    public void flushSchemaCache(OlapConnection conn) throws OlapException {
319        try {
320            conn.unwrap(RolapConnection.class)
321                .getCacheControl(null).flushSchemaCache();
322        } catch (SQLException e) {
323            throw new OlapException(e);
324        }
325    }
326
327    public Object getMemberKey(Member m) throws OlapException {
328        try {
329            return ((MondrianOlap4jMember)m)
330                .unwrap(RolapMemberBase.class).getKey();
331        } catch (SQLException e) {
332            throw new OlapException(e);
333        }
334    }
335
336    public Object getOrderKey(Member m) throws OlapException {
337        try {
338            return ((MondrianOlap4jMember)m)
339                .unwrap(RolapMemberBase.class).getOrderKey();
340        } catch (SQLException e) {
341            throw new OlapException(e);
342        }
343    }
344}
345
346// End MondrianOlap4jExtra.java