001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RolapAggregator.java#17 $
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) 2003-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.rolap;
011    
012    import mondrian.calc.Calc;
013    import mondrian.olap.*;
014    import mondrian.olap.fun.FunUtil;
015    
016    import java.util.List;
017    
018    /**
019     * Describes an aggregation operator, such as "sum" or "count".
020     *
021     * @author jhyde
022     * @since Jul 9, 2003
023     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapAggregator.java#17 $
024     */
025    public abstract class RolapAggregator
026            extends EnumeratedValues.BasicValue
027            implements Aggregator {
028    
029        private static int index = 0;
030    
031        public static final RolapAggregator Sum =
032                new RolapAggregator("sum", index++, false) {
033                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
034                        return FunUtil.sum(evaluator, members, exp);
035                    }
036                };
037    
038        public static final RolapAggregator Count =
039                new RolapAggregator("count", index++, false) {
040                    public Aggregator getRollup() {
041                        return Sum;
042                    }
043                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
044                        return FunUtil.count(evaluator, members, false);
045                    }
046                };
047    
048        public static final RolapAggregator Min =
049                new RolapAggregator("min", index++, false) {
050                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
051                        return FunUtil.min(evaluator, members, exp);
052                    }
053                };
054    
055        public static final RolapAggregator Max =
056                new RolapAggregator("max", index++, false) {
057                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
058                        return FunUtil.max(evaluator, members, exp);
059                    }
060                };
061    
062        public static final RolapAggregator Avg =
063                new RolapAggregator("avg", index++, false) {
064                    public Aggregator getRollup() {
065                        return null;
066                    }
067                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
068                        return FunUtil.avg(evaluator, members, exp);
069                    }
070                };
071    
072        public static final RolapAggregator DistinctCount =
073                new RolapAggregator("distinct-count", index++, true) {
074                    public Aggregator getRollup() {
075                        // Distinct counts cannot always be rolled up, when they can,
076                        // it's using Sum.
077                        return Sum;
078                    }
079                    public RolapAggregator getNonDistinctAggregator() {
080                        return Count;
081                    }
082                    public Object aggregate(Evaluator evaluator, List members, Calc exp) {
083                        throw new UnsupportedOperationException();
084                    }
085                    public String getExpression(String operand) {
086                        return "count(distinct " + operand + ")";
087                    }
088                };
089    
090        /**
091         * List of all valid aggregation operators.
092         */
093        public static final EnumeratedValues<RolapAggregator> enumeration =
094            new EnumeratedValues<RolapAggregator> (
095                new RolapAggregator[] {Sum, Count, Min, Max, Avg, DistinctCount});
096    
097        /**
098         * This is the base class for implementing aggregators over sum and
099         * average columns in an aggregate table. These differ from the above
100         * aggregators in that these require not oly the operand to create
101         * the aggregation String expression, but also, the aggregate table's
102         * fact count column expression.
103         * These aggregators are NOT singletons like the above aggregators; rather,
104         * each is different because of the fact count column expression.
105         */
106        protected abstract static class BaseAggor extends RolapAggregator {
107            protected final String factCountExpr;
108    
109            protected BaseAggor(final String name, final String factCountExpr) {
110                super(name, index++, false);
111                this.factCountExpr = factCountExpr;
112            }
113            public Object aggregate(Evaluator evaluator, List members, Calc exp) {
114                throw new UnsupportedOperationException();
115            }
116        }
117    
118        /**
119         * Aggregator used for aggregate tables implementing the
120         * average aggregator.
121         *
122         * <p>It uses the aggregate table fact_count column
123         * and a sum measure to create the query used to generate an average:
124         * <blockquote>
125         * <code>
126         *    avg == sum(column_sum) / sum(factcount).
127         * </code>
128         * </blockquote>
129         *
130         * <p>If the fact table has both a sum and average over the same column and
131         * the aggregate table only has a sum and fact count column, then the
132         * average aggregator can be generated using this aggregator.
133         */
134        public static class AvgFromSum extends BaseAggor {
135            public AvgFromSum(String factCountExpr) {
136                super("AvgFromSum", factCountExpr);
137            }
138            public String getExpression(String operand) {
139                StringBuilder buf = new StringBuilder(64);
140                buf.append("sum(");
141                buf.append(operand);
142                buf.append(") / sum(");
143                buf.append(factCountExpr);
144                buf.append(')');
145                return buf.toString();
146            }
147        }
148    
149        /**
150         * Aggregator used for aggregate tables implementing the
151         * average aggregator.
152         *
153         * <p>It uses the aggregate table fact_count column
154         * and an average measure to create the query used to generate an average:
155         * <blockquote>
156         * <code>
157         *    avg == sum(column_sum * factcount) / sum(factcount).
158         * </code>
159         * </blockquote>
160         *
161         * <p>If the fact table has both a sum and average over the same column and
162         * the aggregate table only has a average and fact count column, then the
163         * average aggregator can be generated using this aggregator.
164         */
165        public static class AvgFromAvg extends BaseAggor {
166            public AvgFromAvg(String factCountExpr) {
167                super("AvgFromAvg", factCountExpr);
168            }
169            public String getExpression(String operand) {
170                StringBuilder buf = new StringBuilder(64);
171                buf.append("sum(");
172                buf.append(operand);
173                buf.append(" * ");
174                buf.append(factCountExpr);
175                buf.append(") / sum(");
176                buf.append(factCountExpr);
177                buf.append(')');
178                return buf.toString();
179            }
180        }
181        /**
182         * This is an aggregator used for aggregate tables implementing the
183         * sum aggregator. It uses the aggregate table fact_count column
184         * and an average measure to create the query used to generate a sum:
185         * <pre>
186         *    sum == sum(column_avg * factcount)
187         * </pre>
188         * If the fact table has both a sum and average over the same column and
189         * the aggregate table only has an average and fact count column, then the
190         * sum aggregator can be generated using this aggregator.
191         */
192        public static class SumFromAvg extends BaseAggor {
193            public SumFromAvg(String factCountExpr) {
194                super("SumFromAvg", factCountExpr);
195            }
196            public String getExpression(String operand) {
197                StringBuilder buf = new StringBuilder(64);
198                buf.append("sum(");
199                buf.append(operand);
200                buf.append(" * ");
201                buf.append(factCountExpr);
202                buf.append(')');
203                return buf.toString();
204            }
205        }
206    
207    
208    
209    
210        private final boolean distinct;
211    
212        public RolapAggregator(String name, int ordinal, boolean distinct) {
213            super(name, ordinal, null);
214            this.distinct = distinct;
215        }
216    
217        public boolean isDistinct() {
218            return distinct;
219        }
220    
221        /**
222         * Returns the expression to apply this aggregator to an operand.
223         * For example, <code>getExpression("emp.sal")</code> returns
224         * <code>"sum(emp.sal)"</code>.
225         */
226        public String getExpression(String operand) {
227            StringBuilder buf = new StringBuilder(64);
228            buf.append(name);
229            buf.append('(');
230            if (distinct) {
231                buf.append("distinct ");
232            }
233            buf.append(operand);
234            buf.append(')');
235            return buf.toString();
236        }
237    
238        /**
239         * If this is a distinct aggregator, returns the corresponding non-distinct
240         * aggregator, otherwise throws an error.
241         */
242        public RolapAggregator getNonDistinctAggregator() {
243            throw new UnsupportedOperationException();
244        }
245    
246        /**
247         * Returns the aggregator used to roll up. By default, aggregators roll up
248         * themselves.
249         */
250        public Aggregator getRollup() {
251            return this;
252        }
253    }
254    
255    // End RolapAggregator.java