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