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*/
020package org.olap4j.mdx;
021
022import org.olap4j.Axis;
023import org.olap4j.type.Type;
024
025import java.io.PrintWriter;
026import java.io.StringWriter;
027import 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 */
036public 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