001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap4j/MondrianOlap4jConnection.java#9 $
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) 2007-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.olap4j;
011    
012    import mondrian.mdx.*;
013    import mondrian.olap.*;
014    import mondrian.rolap.RolapMeasure;
015    import org.olap4j.Axis;
016    import org.olap4j.Cell;
017    import org.olap4j.*;
018    import org.olap4j.impl.Olap4jUtil;
019    import org.olap4j.mdx.*;
020    import org.olap4j.mdx.parser.*;
021    import org.olap4j.mdx.parser.impl.DefaultMdxParserImpl;
022    import org.olap4j.metadata.*;
023    import org.olap4j.metadata.Schema;
024    import org.olap4j.type.*;
025    import org.olap4j.type.DimensionType;
026    
027    import java.io.PrintWriter;
028    import java.io.StringWriter;
029    import java.sql.*;
030    import java.util.*;
031    
032    /**
033     * Implementation of {@link org.olap4j.OlapConnection}
034     * for the Mondrian OLAP engine.
035     *
036     * <p>This class has sub-classes which implement JDBC 3.0 and JDBC 4.0 APIs;
037     * it is instantiated using {@link Factory#newConnection}.</p>
038     *
039     * @author jhyde
040     * @version $Id: //open/mondrian/src/main/mondrian/olap4j/MondrianOlap4jConnection.java#9 $
041     * @since May 23, 2007
042     */
043    abstract class MondrianOlap4jConnection implements OlapConnection {
044        /**
045         * Handler for errors.
046         */
047        final Helper helper = new Helper();
048    
049        /**
050         * Underlying mondrian connection. Set on creation, cleared on close.
051         */
052        mondrian.olap.Connection connection;
053    
054        /**
055         * Current schema.
056         */
057        MondrianOlap4jSchema olap4jSchema;
058    
059        /**
060         * Map from mondrian schema objects to olap4j schemas.
061         */
062        final Map<mondrian.olap.Schema,MondrianOlap4jSchema> schemaMap =
063            new HashMap<mondrian.olap.Schema, MondrianOlap4jSchema>();
064    
065        private final MondrianOlap4jDatabaseMetaData olap4jDatabaseMetaData;
066    
067        /**
068         * The name of the sole catalog.
069         */
070        static final String LOCALDB_CATALOG_NAME = "LOCALDB";
071        private static final String CONNECT_STRING_PREFIX = "jdbc:mondrian:";
072    
073        final Factory factory;
074        private Locale locale;
075        private String roleName;
076        private boolean autoCommit;
077        private boolean readOnly;
078    
079        /**
080         * Creates an Olap4j connection to Mondrian.
081         *
082         * <p>This method is intentionally package-protected. The public API
083         * uses the traditional JDBC {@link java.sql.DriverManager}.
084         * See {@link mondrian.olap4j.MondrianOlap4jDriver} for more details.
085         *
086         * @pre acceptsURL(url)
087         *
088         * @param factory Factory
089         * @param url Connect-string URL
090         * @param info Additional properties
091         * @throws SQLException if there is an error
092         */
093        MondrianOlap4jConnection(
094            Factory factory,
095            String url,
096            Properties info)
097            throws SQLException
098        {
099            this.factory = factory;
100            if (!acceptsURL(url)) {
101                // This is not a URL we can handle.
102                // DriverManager should not have invoked us.
103                throw new AssertionError(
104                    "does not start with '" + CONNECT_STRING_PREFIX + "'");
105            }
106            String x = url.substring(CONNECT_STRING_PREFIX.length());
107            Util.PropertyList list = Util.parseConnectString(x);
108            for (Map.Entry<String,String> entry : toMap(info).entrySet()) {
109                list.put(entry.getKey(), entry.getValue());
110            }
111            this.connection =
112                mondrian.olap.DriverManager.getConnection(list, null);
113            this.olap4jDatabaseMetaData =
114                factory.newDatabaseMetaData(this);
115            this.olap4jSchema = toOlap4j(connection.getSchema());
116        }
117    
118        static boolean acceptsURL(String url) {
119            return url.startsWith(CONNECT_STRING_PREFIX);
120        }
121    
122        public OlapStatement createStatement() {
123            return new MondrianOlap4jStatement(this);
124        }
125    
126        public PreparedStatement prepareStatement(String sql) throws SQLException {
127            throw new UnsupportedOperationException();
128        }
129    
130        public CallableStatement prepareCall(String sql) throws SQLException {
131            throw new UnsupportedOperationException();
132        }
133    
134        public String nativeSQL(String sql) throws SQLException {
135            throw new UnsupportedOperationException();
136        }
137    
138        public void setAutoCommit(boolean autoCommit) throws SQLException {
139            this.autoCommit = autoCommit;
140        }
141    
142        public boolean getAutoCommit() throws SQLException {
143            return autoCommit;
144        }
145    
146        public void commit() throws SQLException {
147            throw new UnsupportedOperationException();
148        }
149    
150        public void rollback() throws SQLException {
151            throw new UnsupportedOperationException();
152        }
153    
154        public void close() throws SQLException {
155            if (connection != null) {
156                mondrian.olap.Connection c = connection;
157                connection = null;
158                c.close();
159            }
160        }
161    
162        public boolean isClosed() throws SQLException {
163            return connection == null;
164        }
165    
166        public OlapDatabaseMetaData getMetaData() {
167            return olap4jDatabaseMetaData;
168        }
169    
170        public NamedList<Catalog> getCatalogs() {
171            return olap4jDatabaseMetaData.getCatalogObjects();
172        }
173    
174        public void setReadOnly(boolean readOnly) throws SQLException {
175            this.readOnly = readOnly;
176        }
177    
178        public boolean isReadOnly() throws SQLException {
179            return readOnly;
180        }
181    
182        public void setCatalog(String catalog) throws SQLException {
183            if (!catalog.equals(LOCALDB_CATALOG_NAME)) {
184                throw new UnsupportedOperationException();
185            }
186        }
187    
188        public String getCatalog() throws SQLException {
189            return LOCALDB_CATALOG_NAME;
190        }
191    
192        public void setTransactionIsolation(int level) throws SQLException {
193            throw new UnsupportedOperationException();
194        }
195    
196        public int getTransactionIsolation() throws SQLException {
197            throw new UnsupportedOperationException();
198        }
199    
200        public SQLWarning getWarnings() throws SQLException {
201            throw new UnsupportedOperationException();
202        }
203    
204        public void clearWarnings() throws SQLException {
205        }
206    
207        public Statement createStatement(
208            int resultSetType, int resultSetConcurrency) throws SQLException {
209            throw new UnsupportedOperationException();
210        }
211    
212        public PreparedStatement prepareStatement(
213            String sql,
214            int resultSetType,
215            int resultSetConcurrency) throws SQLException {
216            throw new UnsupportedOperationException();
217        }
218    
219        public CallableStatement prepareCall(
220            String sql,
221            int resultSetType,
222            int resultSetConcurrency) throws SQLException {
223            throw new UnsupportedOperationException();
224        }
225    
226        public Map<String, Class<?>> getTypeMap() throws SQLException {
227            throw new UnsupportedOperationException();
228        }
229    
230        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
231            throw new UnsupportedOperationException();
232        }
233    
234        public void setHoldability(int holdability) throws SQLException {
235            throw new UnsupportedOperationException();
236        }
237    
238        public int getHoldability() throws SQLException {
239            throw new UnsupportedOperationException();
240        }
241    
242        public Savepoint setSavepoint() throws SQLException {
243            throw new UnsupportedOperationException();
244        }
245    
246        public Savepoint setSavepoint(String name) throws SQLException {
247            throw new UnsupportedOperationException();
248        }
249    
250        public void rollback(Savepoint savepoint) throws SQLException {
251            throw new UnsupportedOperationException();
252        }
253    
254        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
255            throw new UnsupportedOperationException();
256        }
257    
258        public Statement createStatement(
259            int resultSetType,
260            int resultSetConcurrency,
261            int resultSetHoldability) throws SQLException {
262            throw new UnsupportedOperationException();
263        }
264    
265        public PreparedStatement prepareStatement(
266            String sql,
267            int resultSetType,
268            int resultSetConcurrency,
269            int resultSetHoldability) throws SQLException {
270            throw new UnsupportedOperationException();
271        }
272    
273        public CallableStatement prepareCall(
274            String sql,
275            int resultSetType,
276            int resultSetConcurrency,
277            int resultSetHoldability) throws SQLException {
278            throw new UnsupportedOperationException();
279        }
280    
281        public PreparedStatement prepareStatement(
282            String sql, int autoGeneratedKeys) throws SQLException {
283            throw new UnsupportedOperationException();
284        }
285    
286        public PreparedStatement prepareStatement(
287            String sql, int columnIndexes[]) throws SQLException {
288            throw new UnsupportedOperationException();
289        }
290    
291        public PreparedStatement prepareStatement(
292            String sql, String columnNames[]) throws SQLException {
293            throw new UnsupportedOperationException();
294        }
295    
296        // implement Wrapper
297    
298        public <T> T unwrap(Class<T> iface) throws SQLException {
299            if (iface.isInstance(this)) {
300                return iface.cast(this);
301            } else if (iface.isInstance(connection)) {
302                return iface.cast(connection);
303            }
304            throw helper.createException("does not implement '" + iface + "'");
305        }
306    
307        public boolean isWrapperFor(Class<?> iface) throws SQLException {
308            return iface.isInstance(this) ||
309                iface.isInstance(connection);
310        }
311    
312        // implement OlapConnection
313    
314        public PreparedOlapStatement prepareOlapStatement(
315            String mdx)
316            throws OlapException
317        {
318            return factory.newPreparedStatement(mdx, this);
319        }
320    
321        public MdxParserFactory getParserFactory() {
322            return new MdxParserFactory() {
323                public MdxParser createMdxParser(OlapConnection connection) {
324                    return new DefaultMdxParserImpl(connection);
325                }
326    
327                public MdxValidator createMdxValidator(OlapConnection connection) {
328                    return new MondrianOlap4jMdxValidator(connection);
329                }
330            };
331        }
332    
333        public Schema getSchema() throws OlapException {
334            return olap4jSchema;
335        }
336    
337        MondrianOlap4jCube toOlap4j(mondrian.olap.Cube cube) {
338            MondrianOlap4jSchema schema = toOlap4j(cube.getSchema());
339            return new MondrianOlap4jCube(cube, schema);
340        }
341    
342        MondrianOlap4jDimension toOlap4j(mondrian.olap.Dimension dimension) {
343            return new MondrianOlap4jDimension(
344                toOlap4j(dimension.getSchema()),
345                dimension);
346        }
347    
348        synchronized MondrianOlap4jSchema toOlap4j(mondrian.olap.Schema schema) {
349            MondrianOlap4jSchema olap4jSchema = schemaMap.get(schema);
350            if (olap4jSchema == null) {
351                final MondrianOlap4jCatalog olap4jCatalog =
352                    (MondrianOlap4jCatalog) getCatalogs().get(LOCALDB_CATALOG_NAME);
353                olap4jSchema =
354                    new MondrianOlap4jSchema(
355                        olap4jCatalog,
356                        schema.getSchemaReader(),
357                        schema);
358                schemaMap.put(schema, olap4jSchema);
359            }
360            return olap4jSchema;
361        }
362    
363        Type toOlap4j(mondrian.olap.type.Type type) {
364            if (type instanceof mondrian.olap.type.BooleanType) {
365                return new BooleanType();
366            } else if (type instanceof mondrian.olap.type.CubeType) {
367                final mondrian.olap.Cube mondrianCube =
368                    ((mondrian.olap.type.CubeType) type).getCube();
369                return new CubeType(toOlap4j(mondrianCube));
370            } else if (type instanceof mondrian.olap.type.DecimalType) {
371                mondrian.olap.type.DecimalType decimalType =
372                    (mondrian.olap.type.DecimalType) type;
373                return new DecimalType(
374                    decimalType.getPrecision(),
375                    decimalType.getScale());
376            } else if (type instanceof mondrian.olap.type.DimensionType) {
377                mondrian.olap.type.DimensionType dimensionType =
378                    (mondrian.olap.type.DimensionType) type;
379                return new DimensionType(
380                    toOlap4j(dimensionType.getDimension()));
381            } else if (type instanceof mondrian.olap.type.HierarchyType) {
382                return new BooleanType();
383            } else if (type instanceof mondrian.olap.type.LevelType) {
384                return new BooleanType();
385            } else if (type instanceof mondrian.olap.type.MemberType) {
386                final mondrian.olap.type.MemberType memberType =
387                    (mondrian.olap.type.MemberType) type;
388                return new MemberType(
389                    toOlap4j(memberType.getDimension()),
390                    toOlap4j(memberType.getHierarchy()),
391                    toOlap4j(memberType.getLevel()),
392                    toOlap4j(memberType.getMember()));
393            } else if (type instanceof mondrian.olap.type.NullType) {
394                return new NullType();
395            } else if (type instanceof mondrian.olap.type.NumericType) {
396                return new NumericType();
397            } else if (type instanceof mondrian.olap.type.SetType) {
398                final mondrian.olap.type.SetType setType =
399                    (mondrian.olap.type.SetType) type;
400                return new SetType(toOlap4j(setType.getElementType()));
401            } else if (type instanceof mondrian.olap.type.StringType) {
402                return new StringType();
403            } else if (type instanceof mondrian.olap.type.TupleType) {
404                mondrian.olap.type.TupleType tupleType =
405                    (mondrian.olap.type.TupleType) type;
406                final Type[] types = toOlap4j(tupleType.elementTypes);
407                return new TupleType(types);
408            } else if (type instanceof mondrian.olap.type.SymbolType) {
409                return new SymbolType();
410            } else {
411                throw new UnsupportedOperationException();
412            }
413        }
414    
415        MondrianOlap4jMember toOlap4j(mondrian.olap.Member member) {
416            if (member == null) {
417                return null;
418            }
419            if (member instanceof RolapMeasure) {
420                RolapMeasure measure = (RolapMeasure) member;
421                return new MondrianOlap4jMeasure(
422                    toOlap4j(member.getDimension().getSchema()),
423                    measure);
424            }
425            return new MondrianOlap4jMember(
426                toOlap4j(member.getDimension().getSchema()),
427                member);
428        }
429    
430        MondrianOlap4jLevel toOlap4j(mondrian.olap.Level level) {
431            if (level == null) {
432                return null;
433            }
434            return new MondrianOlap4jLevel(
435                toOlap4j(level.getDimension().getSchema()),
436                level);
437        }
438    
439        MondrianOlap4jHierarchy toOlap4j(mondrian.olap.Hierarchy hierarchy) {
440            if (hierarchy == null) {
441                return null;
442            }
443            return new MondrianOlap4jHierarchy(
444                toOlap4j(hierarchy.getDimension().getSchema()),
445                hierarchy);
446        }
447    
448        Type[] toOlap4j(mondrian.olap.type.Type[] mondrianTypes) {
449            final Type[] types = new Type[mondrianTypes.length];
450            for (int i = 0; i < types.length; i++) {
451                types[i] = toOlap4j(mondrianTypes[i]);
452            }
453            return types;
454        }
455    
456        /**
457         * Converts a Properties object to a Map with String keys and values.
458         *
459         * @param properties Properties
460         * @return Map backed by the given Properties object
461         */
462        public static Map<String, String> toMap(final Properties properties) {
463            return new AbstractMap<String, String>() {
464                public Set<Entry<String, String>> entrySet() {
465                    return Olap4jUtil.cast(properties.entrySet());
466                }
467            };
468        }
469    
470        MondrianOlap4jNamedSet toOlap4j(
471            mondrian.olap.Cube cube,
472            mondrian.olap.NamedSet namedSet)
473        {
474            if (namedSet == null) {
475                return null;
476            }
477            return new MondrianOlap4jNamedSet(
478                toOlap4j(cube),
479                namedSet);
480        }
481    
482        ParseTreeNode toOlap4j(Exp exp) {
483            return new MondrianToOlap4jNodeConverter(this).toOlap4j(exp);
484        }
485    
486        SelectNode toOlap4j(Query query) {
487            return new MondrianToOlap4jNodeConverter(this).toOlap4j(query);
488        }
489    
490        public void setLocale(Locale locale) {
491            if (locale == null) {
492                throw new IllegalArgumentException("locale must not be null");
493            }
494            this.locale = locale;
495        }
496    
497        public Locale getLocale() {
498            if (locale == null) {
499                return Locale.getDefault();
500            }
501            return locale;
502        }
503    
504        public void setRoleName(String roleName) throws OlapException {
505            final Role role;
506            if (roleName == null) {
507                role = null;
508            } else {
509                role = this.connection.getSchema().lookupRole(roleName);
510                if (role == null) {
511                    throw helper.createException("Unknown role '" + roleName + "'");
512                }
513            }
514            // Remember the name of the role, because mondrian roles don't know
515            // their own name.
516            this.roleName = roleName;
517            this.connection.setRole(role);
518        }
519    
520        public String getRoleName() {
521            return roleName;
522        }
523    
524        // inner classes
525    
526        /**
527         * Package-private helper class which encapsulates policies which are
528         * common throughout the driver. These policies include exception handling
529         * and factory methods.
530         */
531        static class Helper {
532            OlapException createException(String msg) {
533                return new OlapException(msg);
534            }
535    
536            /**
537             * Creates an exception in the context of a particular Cell.
538             *
539             * @param context Cell context for exception
540             * @param msg Message
541             * @return New exception
542             */
543            OlapException createException(Cell context, String msg) {
544                OlapException exception = new OlapException(msg);
545                exception.setContext(context);
546                return exception;
547            }
548    
549            /**
550             * Creates an exception in the context of a particular Cell and with
551             * a given cause.
552             *
553             * @param context Cell context for exception
554             * @param msg Message
555             * @param cause Causing exception
556             * @return New exception
557             */
558            OlapException createException(
559                Cell context, String msg, Throwable cause)
560            {
561                OlapException exception = new OlapException(msg, cause);
562                exception.setContext(context);
563                return exception;
564            }
565    
566            /**
567             * Creates an exception with a given cause.
568             *
569             * @param msg Message
570             * @param cause Causing exception
571             * @return New exception
572             */
573            OlapException createException(
574                String msg, Throwable cause)
575            {
576                return new OlapException(msg, cause);
577            }
578    
579            /**
580             * Converts a SQLException to an OlapException. Casts the exception
581             * if it is already an OlapException, wraps otherwise.
582             *
583             * <p>This method is typically used as an adapter for SQLException
584             * instances coming from a base class, where derived interface declares
585             * that it throws the more specific OlapException.
586             *
587             * @param e Exception
588             * @return Exception as an OlapException
589             */
590            public OlapException toOlapException(SQLException e) {
591                if (e instanceof OlapException) {
592                    return (OlapException) e;
593                } else {
594                    return new OlapException(null, e);
595                }
596            }
597        }
598    
599        private static class MondrianOlap4jMdxValidator implements MdxValidator {
600            private final MondrianOlap4jConnection connection;
601    
602            public MondrianOlap4jMdxValidator(OlapConnection connection) {
603                this.connection = (MondrianOlap4jConnection) connection;
604            }
605    
606            public SelectNode validateSelect(SelectNode selectNode)
607                throws OlapException
608            {
609                try {
610                    // A lot of mondrian's validation happens during parsing.
611                    // Therefore to do effective validation, we need to go back to
612                    // the MDX string. Someday we will reshape mondrian's
613                    // parse/validation process to fit the olap4j model better.
614                    StringWriter sw = new StringWriter();
615                    selectNode.unparse(new ParseTreeWriter(new PrintWriter(sw)));
616                    String mdx = sw.toString();
617                    Query query =
618                        connection.connection
619                            .parseQuery(mdx);
620                    query.resolve();
621                    return connection.toOlap4j(query);
622                } catch (MondrianException e) {
623                    throw connection.helper.createException("Validation error", e);
624                }
625            }
626        }
627    
628        static Axis toOlap4j(String axisName) {
629            if (axisName.equals("SLICER")) {
630                axisName = "FILTER";
631            }
632            return Axis.valueOf(axisName);
633        }
634    
635        private static class MondrianToOlap4jNodeConverter {
636            private final MondrianOlap4jConnection olap4jConnection;
637    
638            MondrianToOlap4jNodeConverter(
639                MondrianOlap4jConnection olap4jConnection)
640            {
641                this.olap4jConnection = olap4jConnection;
642            }
643    
644            public SelectNode toOlap4j(Query query) {
645                List<IdentifierNode> list = Collections.emptyList();
646                return new SelectNode(
647                    null,
648                    toOlap4j(query.getFormulas()),
649                    toOlap4j(query.getAxes()),
650                    new CubeNode(
651                        null,
652                        olap4jConnection.toOlap4j(query.getCube())),
653                    query.getSlicerAxis() == null
654                        ? null
655                        : toOlap4j(query.getSlicerAxis()),
656                    list);
657            }
658    
659            private AxisNode toOlap4j(QueryAxis axis) {
660                return new AxisNode(
661                    null,
662                    axis.isNonEmpty(),
663                    MondrianOlap4jConnection.toOlap4j(axis.getAxisName()),
664                    toOlap4j(axis.getDimensionProperties()),
665                    toOlap4j(axis.getSet()));
666            }
667    
668            private List<IdentifierNode> toOlap4j(Id[] dimensionProperties) {
669                final List<IdentifierNode> list = new ArrayList<IdentifierNode>();
670                for (Id property : dimensionProperties) {
671                    list.add(toOlap4j(property));
672                }
673                return list;
674            }
675    
676            private ParseTreeNode toOlap4j(Exp exp) {
677                if (exp instanceof Id) {
678                    Id id = (Id) exp;
679                    return toOlap4j(id);
680                }
681                if (exp instanceof ResolvedFunCall) {
682                    ResolvedFunCall call = (ResolvedFunCall) exp;
683                    return toOlap4j(call);
684                }
685                if (exp instanceof DimensionExpr) {
686                    DimensionExpr dimensionExpr = (DimensionExpr) exp;
687                    return new DimensionNode(
688                        null,
689                        olap4jConnection.toOlap4j(dimensionExpr.getDimension()));
690                }
691                if (exp instanceof HierarchyExpr) {
692                    HierarchyExpr hierarchyExpr = (HierarchyExpr) exp;
693                    return new HierarchyNode(
694                        null,
695                        olap4jConnection.toOlap4j(hierarchyExpr.getHierarchy()));
696                }
697                if (exp instanceof LevelExpr) {
698                    LevelExpr levelExpr = (LevelExpr) exp;
699                    return new LevelNode(
700                        null,
701                        olap4jConnection.toOlap4j(levelExpr.getLevel()));
702                }
703                if (exp instanceof MemberExpr) {
704                    MemberExpr memberExpr = (MemberExpr) exp;
705                    return new MemberNode(
706                        null,
707                        olap4jConnection.toOlap4j(memberExpr.getMember()));
708                }
709                if (exp instanceof Literal) {
710                    Literal literal = (Literal) exp;
711                    final Object value = literal.getValue();
712                    if (literal.getCategory() == Category.Symbol) {
713                        return LiteralNode.createSymbol(
714                            null, (String) literal.getValue());
715                    } else if (value instanceof Double) {
716                        return LiteralNode.create(null, (Double) value);
717                    } else if (value instanceof Integer) {
718                        return LiteralNode.create(null, (Integer) value);
719                    } else if (value instanceof String) {
720                        return LiteralNode.createString(null, (String) value);
721                    } else if (value == null) {
722                       return LiteralNode.createNull(null);
723                    } else {
724                        throw new RuntimeException("unknown literal " + literal);
725                    }
726                }
727                throw Util.needToImplement(exp.getClass());
728            }
729    
730            private ParseTreeNode toOlap4j(ResolvedFunCall call) {
731                final CallNode callNode = new CallNode(
732                    null,
733                    call.getFunName(),
734                    toOlap4j(call.getSyntax()),
735                    toOlap4j(Arrays.asList(call.getArgs())));
736                if (call.getType() != null) {
737                    callNode.setType(olap4jConnection.toOlap4j(call.getType()));
738                }
739                return callNode;
740            }
741    
742            private List<ParseTreeNode> toOlap4j(List<Exp> exprList) {
743                final List<ParseTreeNode> result = new ArrayList<ParseTreeNode>();
744                for (Exp expr : exprList) {
745                    result.add(toOlap4j(expr));
746                }
747                return result;
748            }
749    
750            private org.olap4j.mdx.Syntax toOlap4j(mondrian.olap.Syntax syntax) {
751                return org.olap4j.mdx.Syntax.valueOf(syntax.name());
752            }
753    
754            private List<AxisNode> toOlap4j(QueryAxis[] axes) {
755                final ArrayList<AxisNode> axisList = new ArrayList<AxisNode>();
756                for (QueryAxis axis : axes) {
757                    axisList.add(toOlap4j(axis));
758                }
759                return axisList;
760            }
761    
762            private List<ParseTreeNode> toOlap4j(Formula[] formulas) {
763                final List<ParseTreeNode> list = new ArrayList<ParseTreeNode>();
764                for (Formula formula : formulas) {
765                    if (formula.isMember()) {
766                        List<PropertyValueNode> memberPropertyList =
767                            new ArrayList<PropertyValueNode>();
768                        for (Object child : formula.getChildren()) {
769                            if (child instanceof MemberProperty) {
770                                MemberProperty memberProperty =
771                                    (MemberProperty) child;
772                                memberPropertyList.add(
773                                    new PropertyValueNode(
774                                        null,
775                                        memberProperty.getName(),
776                                        toOlap4j(memberProperty.getExp())));
777                            }
778                        }
779                        list.add(
780                            new WithMemberNode(
781                                null,
782                                toOlap4j(formula.getIdentifier()),
783                                toOlap4j(formula.getExpression()),
784                                memberPropertyList));
785                    }
786                }
787                return list;
788            }
789    
790            private IdentifierNode toOlap4j(Id id) {
791                List<IdentifierNode.Segment> list =
792                    new ArrayList<IdentifierNode.Segment>();
793                for (Id.Segment segment : id.getSegments()) {
794                    list.add(
795                        new IdentifierNode.Segment(
796                            null,
797                            segment.name,
798                            toOlap4j(segment.quoting)));
799                }
800                return new IdentifierNode(
801                    list.toArray(
802                        new IdentifierNode.Segment[list.size()]));
803            }
804    
805            private IdentifierNode.Quoting toOlap4j(Id.Quoting quoting) {
806                return IdentifierNode.Quoting.valueOf(quoting.name());
807            }
808        }
809    }
810    
811    // End MondrianOlap4jConnection.java