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