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