001    /*
002    // $Id: SelectNode.java 333 2010-07-30 00:43:55Z jhyde $
003    // This software is subject to the terms of the Eclipse Public License v1.0
004    // Agreement, available at the following URL:
005    // http://www.eclipse.org/legal/epl-v10.html.
006    // Copyright (C) 2007-2010 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package org.olap4j.mdx;
011    
012    import org.olap4j.type.Type;
013    import org.olap4j.Axis;
014    
015    import java.io.PrintWriter;
016    import java.io.StringWriter;
017    import java.util.*;
018    
019    /**
020     * Parse tree model for an MDX SELECT statement.
021     *
022     * @author jhyde
023     * @version $Id: SelectNode.java 333 2010-07-30 00:43:55Z jhyde $
024     * @since Jun 4, 2007
025     */
026    public class SelectNode implements ParseTreeNode {
027        private final ParseRegion region;
028        private final List<ParseTreeNode> withList;
029        private final List<AxisNode> axisList;
030        private final AxisNode filterAxis;
031        private final List<IdentifierNode> cellPropertyList;
032        private ParseTreeNode from;
033    
034        /**
035         * Creates a SelectNode.
036         *
037         * @param region Region of source code from which this node was created
038         * @param withList List of members and sets defined in this query using
039         *   a <code>WITH</code> clause
040         * @param axisList List of axes
041         * @param from Contents of FROM clause (name of cube, or subquery)
042         * @param filterAxis Filter axis
043         * @param cellPropertyList List of properties
044         */
045        public SelectNode(
046            ParseRegion region,
047            List<ParseTreeNode> withList,
048            List<AxisNode> axisList,
049            ParseTreeNode from,
050            AxisNode filterAxis,
051            List<IdentifierNode> cellPropertyList)
052        {
053            this.region = region;
054            this.withList = withList;
055            this.axisList = axisList;
056            this.from = from;
057            if (filterAxis == null) {
058                filterAxis =
059                    new AxisNode(
060                        null,
061                        false,
062                        Axis.FILTER,
063                        Collections.<IdentifierNode>emptyList(),
064                        null);
065            }
066            if (filterAxis.getAxis() != Axis.FILTER) {
067                throw new IllegalArgumentException(
068                    "Filter axis must have type FILTER");
069            }
070            this.filterAxis = filterAxis;
071            this.cellPropertyList = cellPropertyList;
072        }
073    
074        /**
075         * Creates an empty SelectNode.
076         *
077         * <p>The contents of the SelectNode, such as the axis list, can be
078         * populated after construction.
079         */
080        public SelectNode() {
081            this(
082                null,
083                new ArrayList<ParseTreeNode>(),
084                new ArrayList<AxisNode>(),
085                null,
086                null,
087                new ArrayList<IdentifierNode>());
088        }
089    
090        public ParseRegion getRegion() {
091            return region;
092        }
093    
094        public <T> T accept(ParseTreeVisitor<T> visitor) {
095            return visitor.visit(this);
096        }
097    
098        public Type getType() {
099            // not an expression, so has no type
100            return null;
101        }
102    
103        public String toString() {
104            StringWriter sw = new StringWriter();
105            ParseTreeWriter pw = new ParseTreeWriter(sw);
106            unparse(pw);
107            return sw.toString();
108        }
109    
110        public void unparse(ParseTreeWriter writer) {
111            final PrintWriter pw = writer.getPrintWriter();
112            if (!withList.isEmpty()) {
113                pw.println("WITH");
114                for (ParseTreeNode with : withList) {
115                    with.unparse(writer);
116                    pw.println();
117                }
118            }
119            pw.print("SELECT");
120            int k = 0;
121            for (AxisNode axis : axisList) {
122                if (k++ > 0) {
123                    pw.println(",");
124                } else {
125                    pw.println();
126                }
127                axis.unparse(writer);
128            }
129            pw.println();
130            pw.print("FROM ");
131            if (from instanceof SelectNode) {
132                writer.indent();
133                pw.println("(");
134                from.unparse(writer);
135                pw.print(")");
136                writer.outdent();
137            } else {
138                from.unparse(writer);
139            }
140            if (filterAxis.getExpression() != null) {
141                pw.println();
142                pw.print("WHERE ");
143                filterAxis.unparse(writer);
144            }
145            if (!cellPropertyList.isEmpty()) {
146                pw.println();
147                pw.print("CELL PROPERTIES ");
148                k = 0;
149                for (IdentifierNode cellProperty : cellPropertyList) {
150                    if (k++ > 0) {
151                        pw.print(", ");
152                    }
153                    cellProperty.unparse(writer);
154                }
155            }
156        }
157    
158        /**
159         * Returns a list of calculated members and sets defined as the WITH
160         * clause of this SelectNode.
161         *
162         * <p>For example, the WITH clause of query
163         *
164         * <blockquote>
165         * <code>WITH MEMBER [Measures].[Foo] AS ' [Measures].[Unit Sales] * 2 '
166         *   SET [Customers].[Top] AS ' TopCount([Customers].Members, 10) '
167         * SELECT FROM [Sales]</code>
168         * </blockquote>
169         *
170         * contains one {@link org.olap4j.mdx.WithMemberNode} and one
171         * {@link org.olap4j.mdx.WithSetNode}.
172         *
173         * <p>The returned list is mutable.
174         *
175         * @return list of calculated members and sets
176         */
177        public List<ParseTreeNode> getWithList() {
178            return withList;
179        }
180    
181        /**
182         * Returns a list of axes in this SelectNode.
183         *
184         * <p>The returned list is mutable.
185         *
186         * @return list of axes
187         */
188        public List<AxisNode> getAxisList() {
189            return axisList;
190        }
191    
192        /**
193         * Returns the filter axis defined by the WHERE clause of this SelectNode.
194         *
195         * <p>Never returns {@code null}. If there is no WHERE clause, returns an
196         * AxisNode for which {@link org.olap4j.mdx.AxisNode#getExpression()}
197         * returns null.
198         *
199         * <p>You can modify the filter expression by calling
200         * {@link org.olap4j.mdx.AxisNode#getExpression()} on the filter AxisNode;
201         * {@code null} means that there is no filter axis.
202         *
203         * @return filter axis
204         */
205        public AxisNode getFilterAxis() {
206            return filterAxis;
207        }
208    
209        /**
210         * Returns the node representing the FROM clause of this SELECT statement.
211         * The node is typically an {@link IdentifierNode}, a {@link CubeNode} or
212         * a {@link SelectNode}.
213         *
214         * @return FROM clause
215         */
216        public ParseTreeNode getFrom() {
217            return from;
218        }
219    
220        /**
221         * Sets the FROM clause of this SELECT statement.
222         *
223         * <p><code>fromNode</code> should typically by an
224         * {@link org.olap4j.mdx.IdentifierNode} containing the cube name, or
225         * a {@link org.olap4j.mdx.CubeNode} referencing an explicit
226         * {@link org.olap4j.metadata.Cube} object.
227         *
228         * @param from FROM clause
229         */
230        public void setFrom(ParseTreeNode from) {
231            this.from = from;
232        }
233    
234        /**
235         * Returns a list of cell properties in this SelectNode.
236         *
237         * <p>The returned list is mutable.
238         *
239         * @return list of cell properties
240         */
241        public List<IdentifierNode> getCellPropertyList() {
242            return cellPropertyList;
243        }
244    
245        public SelectNode deepCopy() {
246            return new SelectNode(
247                this.region,
248                MdxUtil.deepCopyList(withList),
249                MdxUtil.deepCopyList(axisList),
250                this.from != null ? this.from.deepCopy() : null,
251                this.filterAxis.deepCopy(),
252                MdxUtil.deepCopyList(cellPropertyList));
253        }
254    }
255    
256    // End SelectNode.java