001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/QueryAxis.java#28 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 1998-2002 Kana Software, Inc. 007 // Copyright (C) 2001-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, 20 January, 1999 012 */ 013 014 package mondrian.olap; 015 016 import mondrian.calc.*; 017 import mondrian.mdx.*; 018 import mondrian.olap.type.*; 019 import mondrian.resource.MondrianResource; 020 021 import java.io.PrintWriter; 022 import java.util.List; 023 024 /** 025 * An axis in an MDX query. For example, the typical MDX query has two axes, 026 * which appear as the "ON COLUMNS" and "ON ROWS" clauses. 027 * 028 * @version $Id: //open/mondrian/src/main/mondrian/olap/QueryAxis.java#28 $ 029 */ 030 public class QueryAxis extends QueryPart { 031 032 private boolean nonEmpty; 033 private boolean ordered; 034 private Exp exp; 035 private final AxisOrdinal axisOrdinal; 036 037 /** 038 * Whether to show subtotals on this axis. 039 * The "(show\hide)Subtotals" operation changes its valud. 040 */ 041 private SubtotalVisibility subtotalVisibility; 042 private final Id[] dimensionProperties; 043 044 /** 045 * Creates an axis. 046 * 047 * @param nonEmpty Whether to filter out members of this axis whose cells 048 * are all empty 049 * @param set Expression to populate the axis 050 * @param axisDef Which axis (ROWS, COLUMNS, etc.) 051 * @param subtotalVisibility Whether to show subtotals 052 * @param dimensionProperties List of dimension properties 053 */ 054 public QueryAxis( 055 boolean nonEmpty, 056 Exp set, 057 AxisOrdinal axisDef, 058 SubtotalVisibility subtotalVisibility, 059 Id[] dimensionProperties) { 060 assert dimensionProperties != null; 061 this.nonEmpty = nonEmpty || 062 MondrianProperties.instance().EnableNonEmptyOnAllAxis.get(); 063 this.exp = set; 064 this.axisOrdinal = axisDef; 065 this.subtotalVisibility = subtotalVisibility; 066 this.dimensionProperties = dimensionProperties; 067 this.ordered = false; 068 } 069 070 /** 071 * Creates an axis with no dimension properties. 072 * 073 * @see #QueryAxis(boolean,Exp,AxisOrdinal,mondrian.olap.QueryAxis.SubtotalVisibility,Id[]) 074 */ 075 public QueryAxis( 076 boolean nonEmpty, 077 Exp set, 078 AxisOrdinal axisDef, 079 SubtotalVisibility subtotalVisibility) { 080 this(nonEmpty, set, axisDef, subtotalVisibility, new Id[0]); 081 } 082 083 public Object clone() { 084 return new QueryAxis(nonEmpty, exp.clone(), axisOrdinal, 085 subtotalVisibility, dimensionProperties.clone()); 086 } 087 088 static QueryAxis[] cloneArray(QueryAxis[] a) { 089 QueryAxis[] a2 = new QueryAxis[a.length]; 090 for (int i = 0; i < a.length; i++) { 091 a2[i] = (QueryAxis) a[i].clone(); 092 } 093 return a2; 094 } 095 096 public Object accept(MdxVisitor visitor) { 097 final Object o = visitor.visit(this); 098 099 // visit the expression which forms the axis 100 exp.accept(visitor); 101 102 return o; 103 } 104 105 public Calc compile(ExpCompiler compiler, List<ResultStyle> resultStyles) { 106 Exp exp = this.exp; 107 if (axisOrdinal == AxisOrdinal.SLICER) { 108 exp = normalizeSlicerExpression(exp); 109 exp = exp.accept(compiler.getValidator()); 110 } 111 return compiler.compileAs(exp, null, resultStyles); 112 } 113 114 private static Exp normalizeSlicerExpression(Exp exp) { 115 Exp slicer = exp; 116 if (slicer instanceof LevelExpr || 117 slicer instanceof HierarchyExpr || 118 slicer instanceof DimensionExpr) { 119 120 slicer = new UnresolvedFunCall( 121 "DefaultMember", Syntax.Property, new Exp[] { 122 slicer}); 123 } 124 if (slicer == null) { 125 ; 126 } else if (slicer instanceof FunCall && 127 ((FunCall) slicer).getSyntax() == Syntax.Parentheses) { 128 slicer = new UnresolvedFunCall( 129 "{}", Syntax.Braces, new Exp[] { 130 slicer}); 131 } else { 132 slicer = new UnresolvedFunCall( 133 "{}", Syntax.Braces, new Exp[] { 134 new UnresolvedFunCall( 135 "()", Syntax.Parentheses, new Exp[] { 136 slicer})}); 137 } 138 139 return slicer; 140 } 141 142 public String getAxisName() { 143 return axisOrdinal.name(); 144 } 145 146 /** 147 * Returns the ordinal of this axis, for example {@link AxisOrdinal#ROWS}. 148 */ 149 public AxisOrdinal getAxisOrdinal() { 150 return axisOrdinal; 151 } 152 153 /** 154 * Returns whether the axis has the <code>NON EMPTY</code> property set. 155 */ 156 public boolean isNonEmpty() { 157 return nonEmpty; 158 } 159 160 /** 161 * Sets whether the axis has the <code>NON EMPTY</code> property set. 162 * See {@link #isNonEmpty()}. 163 */ 164 public void setNonEmpty(boolean nonEmpty) { 165 this.nonEmpty = nonEmpty; 166 } 167 168 /** 169 * Returns whether the axis has the <code>ORDER</code> property set. 170 */ 171 public boolean isOrdered() { 172 return ordered; 173 } 174 175 /** 176 * Sets whether the axis has the <code>ORDER</code> property set. 177 */ 178 public void setOrdered(boolean ordered) { 179 this.ordered = ordered; 180 } 181 182 /** 183 * Returns the expression which is used to compute the value of this axis. 184 */ 185 public Exp getSet() { 186 return exp; 187 } 188 189 /** 190 * Sets the expression which is used to compute the value of this axis. 191 * See {@link #getSet()}. 192 */ 193 public void setSet(Exp set) { 194 this.exp = set; 195 } 196 197 public void resolve(Validator validator) { 198 exp = validator.validate(exp, false); 199 final Type type = exp.getType(); 200 if (!TypeUtil.isSet(type)) { 201 // If expression is a member or a tuple, implicitly convert it 202 // into a set. 203 if (type instanceof MemberType || 204 type instanceof TupleType) { 205 exp = 206 new UnresolvedFunCall( 207 "{}", 208 Syntax.Braces, 209 new Exp[] {exp}); 210 exp = validator.validate(exp, false); 211 } else { 212 throw MondrianResource.instance().MdxAxisIsNotSet.ex( 213 axisOrdinal.name()); 214 } 215 } 216 } 217 218 public Object[] getChildren() { 219 return new Object[] {exp}; 220 } 221 222 public void unparse(PrintWriter pw) { 223 if (nonEmpty) { 224 pw.print("NON EMPTY "); 225 } 226 if (exp != null) { 227 exp.unparse(pw); 228 } 229 if (dimensionProperties.length > 0) { 230 pw.print(" DIMENSION PROPERTIES "); 231 for (int i = 0; i < dimensionProperties.length; i++) { 232 Id dimensionProperty = dimensionProperties[i]; 233 if (i > 0) { 234 pw.print(", "); 235 } 236 dimensionProperty.unparse(pw); 237 } 238 } 239 if (axisOrdinal != AxisOrdinal.SLICER) { 240 pw.print(" ON " + axisOrdinal); 241 } 242 } 243 244 public void addLevel(Level level) { 245 Util.assertTrue(level != null, "addLevel needs level"); 246 exp = new UnresolvedFunCall("Crossjoin", Syntax.Function, new Exp[]{ 247 exp, 248 new UnresolvedFunCall("Members", Syntax.Property, new Exp[]{ 249 new LevelExpr(level)})}); 250 } 251 252 void setSubtotalVisibility(boolean bShowSubtotals) { 253 subtotalVisibility = bShowSubtotals ? 254 SubtotalVisibility.Show : 255 SubtotalVisibility.Hide; 256 } 257 258 public SubtotalVisibility getSubtotalVisibility() { 259 return subtotalVisibility; 260 } 261 262 public void resetSubtotalVisibility() { 263 this.subtotalVisibility = SubtotalVisibility.Undefined; 264 } 265 266 public void validate(Validator validator) { 267 if (axisOrdinal == AxisOrdinal.SLICER) { 268 if (exp != null) { 269 exp = validator.validate(exp, false); 270 } 271 } 272 } 273 274 public Id[] getDimensionProperties() { 275 return dimensionProperties; 276 } 277 278 /** 279 * <code>SubtotalVisibility</code> enumerates the allowed values of 280 * whether subtotals are visible. 281 */ 282 public enum SubtotalVisibility { 283 Undefined, 284 Hide, 285 Show; 286 } 287 288 } 289 290 // End QueryAxis.java