001/*
002 // This software is subject to the terms of the Eclipse Public License v1.0
003 // Agreement, available at the following URL:
004 // http://www.eclipse.org/legal/epl-v10.html.
005 // You must accept the terms of that agreement to use this software.
006 //
007 // Copyright (C) 2008-2011 Pentaho and others
008 // All Rights Reserved.
009 */
010package mondrian.rolap;
011
012import mondrian.calc.*;
013import mondrian.olap.*;
014
015import java.io.PrintWriter;
016import java.io.StringWriter;
017import java.io.Writer;
018import java.util.List;
019
020/**
021 * Evaluation context to be able to expand a generic expression that returns a
022 * set
023 *
024 * @author pedro alves
025 * @since September 14, 2012
026 */
027class RolapSetEvaluator
028    implements Evaluator.SetEvaluator, TupleList.PositionCallback
029{
030
031    private final RolapResult.RolapResultEvaluatorRoot rrer;
032    private final Exp exp;
033    /**
034     * Value of this named set; set on first use.
035     */
036    private TupleList list;
037    /**
038     * Dummy list used as a marker to detect re-entrant calls to
039     * {@link #ensureList}.
040     */
041    private static final TupleList DUMMY_LIST =
042            TupleCollections.createList(1);
043    /**
044     * Ordinal of current iteration through the named set. Used to implement the
045     * <Named Set>.CurrentOrdinal and <Named Set>.Current functions.
046     */
047    private int currentOrdinal;
048
049    /**
050     * Creates a RolapNamedSetEvaluator.
051     *
052     * @param rrer Evaluation root context
053     * @param exp Expression
054     */
055    public RolapSetEvaluator(
056        RolapResult.RolapResultEvaluatorRoot rrer,
057        Exp exp)
058    {
059        this.rrer = rrer;
060        this.exp = exp;
061    }
062
063    public TupleIterable evaluateTupleIterable() {
064        ensureList();
065        return list;
066    }
067
068    /**
069     * Evaluates and saves the value of this named set, if it has not been
070     * evaluated already.
071     */
072    private void ensureList() {
073        if (list != null) {
074            if (list == DUMMY_LIST) {
075                throw rrer.result.slicerEvaluator.newEvalException(
076                    null,
077                    "Illegal attempt to reference value of a set '"
078                    + getExpression() + "' while evaluating itself");
079            }
080            return;
081        }
082        if (RolapResult.LOGGER.isDebugEnabled()) {
083            RolapResult.LOGGER.debug(
084                "Set " + exp + ": starting evaluation");
085        }
086        list = DUMMY_LIST; // recursion detection
087        try {
088            final Calc calc =
089                rrer.getCompiled(
090                    exp, false, ResultStyle.ITERABLE);
091            TupleIterable iterable =
092                    (TupleIterable) rrer.result.evaluateExp(
093                        calc,
094                        rrer.result.slicerEvaluator, null);
095
096            // Axes can be in two forms: list or iterable. If iterable, we
097            // need to materialize it, to ensure that all cell values are in
098            // cache.
099            final TupleList rawList;
100            if (iterable instanceof TupleList) {
101                rawList = (TupleList) iterable;
102            } else {
103                rawList = TupleCollections.createList(iterable.getArity());
104                TupleCursor cursor = iterable.tupleCursor();
105                while (cursor.forward()) {
106                    rawList.addCurrent(cursor);
107                }
108            }
109            if (RolapResult.LOGGER.isDebugEnabled()) {
110                RolapResult.LOGGER.debug(generateDebugMessage(calc, rawList));
111            }
112            // Wrap list so that currentOrdinal is updated whenever the list
113            // is accessed. The list is immutable, because we don't override
114            // AbstractList.set(int, Object).
115            this.list = rawList.withPositionCallback(this);
116        } finally {
117            if (this.list == DUMMY_LIST) {
118                this.list = null;
119            }
120        }
121    }
122
123    private String generateDebugMessage(Calc calc, TupleList rawList) {
124        final StringBuilder buf = new StringBuilder();
125        buf.append(this);
126        buf.append(": ");
127        buf.append("Set expression ");
128        buf.append(getExpression());
129        buf.append(" evaluated to:");
130        buf.append(Util.nl);
131        int arity = calc.getType().getArity();
132        int rowCount = 0;
133        final int maxRowCount = 100;
134        if (arity == 1) {
135            for (Member t : rawList.slice(0)) {
136                if (rowCount++ > maxRowCount) {
137                    buf.append("...");
138                    buf.append(Util.nl);
139                    break;
140                }
141                buf.append(t);
142                buf.append(Util.nl);
143            }
144        } else {
145            for (List<Member> t : rawList) {
146                if (rowCount++ > maxRowCount) {
147                    buf.append("...");
148                    buf.append(Util.nl);
149                    break;
150                }
151                int k = 0;
152                for (Member member : t) {
153                    if (k++ > 0) {
154                        buf.append(", ");
155                    }
156                    buf.append(member);
157                }
158                buf.append(Util.nl);
159            }
160        }
161        return buf.toString();
162    }
163
164    private String getExpression() {
165        final Writer result = new StringWriter();
166        final PrintWriter printWriter = new PrintWriter(result);
167        exp.unparse(printWriter);
168        return result.toString();
169    }
170
171    public int currentOrdinal() {
172        return currentOrdinal;
173    }
174
175    public void onPosition(int index) {
176        this.currentOrdinal = index;
177    }
178
179    public Member[] currentTuple() {
180        final List<Member> tuple = list.get(currentOrdinal);
181        return tuple.toArray(new Member[tuple.size()]);
182    }
183
184    public Member currentMember() {
185        return list.get(0, currentOrdinal);
186    }
187}
188
189// End RolapSetEvaluator.java