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