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