001    /*
002    // $Id: //open/mondrian-release/3.0/src/main/mondrian/olap4j/MondrianOlap4jConnection.java#2 $
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-release/3.0/src/main/mondrian/olap4j/MondrianOlap4jConnection.java#2 $
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 OlapConnection connection;
601    
602            public MondrianOlap4jMdxValidator(OlapConnection connection) {
603                this.connection = connection;
604            }
605    
606            public SelectNode validateSelect(SelectNode selectNode) throws OlapException {
607                StringWriter sw = new StringWriter();
608                selectNode.unparse(new ParseTreeWriter(new PrintWriter(sw)));
609                String mdx = sw.toString();
610                final MondrianOlap4jConnection olap4jConnection =
611                    (MondrianOlap4jConnection) connection;
612                Query query =
613                    olap4jConnection.connection
614                        .parseQuery(mdx);
615                query.resolve();
616                return olap4jConnection.toOlap4j(query);
617            }
618        }
619    
620        static Axis toOlap4j(String axisName) {
621            if (axisName.equals("SLICER")) {
622                axisName = "FILTER";
623            }
624            return Axis.valueOf(axisName);
625        }
626    
627        private static class MondrianToOlap4jNodeConverter {
628            private final MondrianOlap4jConnection olap4jConnection;
629    
630            MondrianToOlap4jNodeConverter(
631                MondrianOlap4jConnection olap4jConnection)
632            {
633                this.olap4jConnection = olap4jConnection;
634            }
635    
636            public SelectNode toOlap4j(Query query) {
637                List<IdentifierNode> list = Collections.emptyList();
638                return new SelectNode(
639                    null,
640                    toOlap4j(query.getFormulas()),
641                    toOlap4j(query.getAxes()),
642                    new CubeNode(
643                        null,
644                        olap4jConnection.toOlap4j(query.getCube())),
645                    query.getSlicerAxis() == null
646                        ? null
647                        : toOlap4j(query.getSlicerAxis()),
648                    list);
649            }
650    
651            private AxisNode toOlap4j(QueryAxis axis) {
652                return new AxisNode(
653                    null,
654                    axis.isNonEmpty(),
655                    MondrianOlap4jConnection.toOlap4j(axis.getAxisName()),
656                    toOlap4j(axis.getDimensionProperties()),
657                    toOlap4j(axis.getSet()));
658            }
659    
660            private List<IdentifierNode> toOlap4j(Id[] dimensionProperties) {
661                final List<IdentifierNode> list = new ArrayList<IdentifierNode>();
662                for (Id property : dimensionProperties) {
663                    list.add(toOlap4j(property));
664                }
665                return list;
666            }
667    
668            private ParseTreeNode toOlap4j(Exp exp) {
669                if (exp instanceof Id) {
670                    Id id = (Id) exp;
671                    return toOlap4j(id);
672                }
673                if (exp instanceof ResolvedFunCall) {
674                    ResolvedFunCall call = (ResolvedFunCall) exp;
675                    return toOlap4j(call);
676                }
677                if (exp instanceof DimensionExpr) {
678                    DimensionExpr dimensionExpr = (DimensionExpr) exp;
679                    return new DimensionNode(
680                        null,
681                        olap4jConnection.toOlap4j(dimensionExpr.getDimension()));
682                }
683                if (exp instanceof HierarchyExpr) {
684                    HierarchyExpr hierarchyExpr = (HierarchyExpr) exp;
685                    return new HierarchyNode(
686                        null,
687                        olap4jConnection.toOlap4j(hierarchyExpr.getHierarchy()));
688                }
689                if (exp instanceof LevelExpr) {
690                    LevelExpr levelExpr = (LevelExpr) exp;
691                    return new LevelNode(
692                        null,
693                        olap4jConnection.toOlap4j(levelExpr.getLevel()));
694                }
695                if (exp instanceof MemberExpr) {
696                    MemberExpr memberExpr = (MemberExpr) exp;
697                    return new MemberNode(
698                        null,
699                        olap4jConnection.toOlap4j(memberExpr.getMember()));
700                }
701                if (exp instanceof Literal) {
702                    Literal literal = (Literal) exp;
703                    final Object value = literal.getValue();
704                    if (literal.getCategory() == Category.Symbol) {
705                        return LiteralNode.createSymbol(
706                            null, (String) literal.getValue());
707                    } else if (value instanceof Double) {
708                        return LiteralNode.create(null, (Double) value);
709                    } else if (value instanceof Integer) {
710                        return LiteralNode.create(null, (Integer) value);
711                    } else if (value instanceof String) {
712                        return LiteralNode.createString(null, (String) value);
713                    } else if (value == null) {
714                       return LiteralNode.createNull(null);
715                    } else {
716                        throw new RuntimeException("unknown literal " + literal);
717                    }
718                }
719                throw Util.needToImplement(exp.getClass());
720            }
721    
722            private ParseTreeNode toOlap4j(ResolvedFunCall call) {
723                final CallNode callNode = new CallNode(
724                    null,
725                    call.getFunName(),
726                    toOlap4j(call.getSyntax()),
727                    toOlap4j(Arrays.asList(call.getArgs())));
728                if (call.getType() != null) {
729                    callNode.setType(olap4jConnection.toOlap4j(call.getType()));
730                }
731                return callNode;
732            }
733    
734            private List<ParseTreeNode> toOlap4j(List<Exp> exprList) {
735                final List<ParseTreeNode> result = new ArrayList<ParseTreeNode>();
736                for (Exp expr : exprList) {
737                    result.add(toOlap4j(expr));
738                }
739                return result;
740            }
741    
742            private org.olap4j.mdx.Syntax toOlap4j(mondrian.olap.Syntax syntax) {
743                return org.olap4j.mdx.Syntax.valueOf(syntax.name());
744            }
745    
746            private List<AxisNode> toOlap4j(QueryAxis[] axes) {
747                final ArrayList<AxisNode> axisList = new ArrayList<AxisNode>();
748                for (QueryAxis axis : axes) {
749                    axisList.add(toOlap4j(axis));
750                }
751                return axisList;
752            }
753    
754            private List<ParseTreeNode> toOlap4j(Formula[] formulas) {
755                final List<ParseTreeNode> list = new ArrayList<ParseTreeNode>();
756                for (Formula formula : formulas) {
757                    if (formula.isMember()) {
758                        List<PropertyValueNode> memberPropertyList =
759                            new ArrayList<PropertyValueNode>();
760                        for (Object child : formula.getChildren()) {
761                            if (child instanceof MemberProperty) {
762                                MemberProperty memberProperty =
763                                    (MemberProperty) child;
764                                memberPropertyList.add(
765                                    new PropertyValueNode(
766                                        null,
767                                        memberProperty.getName(),
768                                        toOlap4j(memberProperty.getExp())));
769                            }
770                        }
771                        list.add(
772                            new WithMemberNode(
773                                null,
774                                toOlap4j(formula.getIdentifier()),
775                                toOlap4j(formula.getExpression()),
776                                memberPropertyList));
777                    }
778                }
779                return list;
780            }
781    
782            private IdentifierNode toOlap4j(Id id) {
783                List<IdentifierNode.Segment> list =
784                    new ArrayList<IdentifierNode.Segment>();
785                for (Id.Segment segment : id.getSegments()) {
786                    list.add(
787                        new IdentifierNode.Segment(
788                            null,
789                            segment.name,
790                            toOlap4j(segment.quoting)));
791                }
792                return new IdentifierNode(
793                    list.toArray(
794                        new IdentifierNode.Segment[list.size()]));
795            }
796    
797            private IdentifierNode.Quoting toOlap4j(Id.Quoting quoting) {
798                return IdentifierNode.Quoting.valueOf(quoting.name());
799            }
800        }
801    }
802    
803    // End MondrianOlap4jConnection.java