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-2012 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.rolap;
011
012import mondrian.calc.*;
013import mondrian.olap.*;
014
015import java.util.List;
016
017/**
018 * Evaluation context for a particular named set.
019 *
020 * @author jhyde
021 * @since November 11, 2008
022 */
023class RolapNamedSetEvaluator
024    implements Evaluator.NamedSetEvaluator, TupleList.PositionCallback
025{
026    private final RolapResult.RolapResultEvaluatorRoot rrer;
027    private final NamedSet namedSet;
028
029    private final int RECURSION_TOLERANCE =
030        MondrianProperties.instance().IterationLimit.get();
031
032    private int recursionCount;
033
034    /** Value of this named set; set on first use. */
035    private TupleList list;
036
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    /**
045     * Ordinal of current iteration through the named set. Used to implement
046     * the <Named Set>.CurrentOrdinal and <Named Set>.Current
047     * functions.
048     */
049    private int currentOrdinal;
050
051    /**
052     * Creates a RolapNamedSetEvaluator.
053     *
054     * @param rrer Evaluation root context
055     * @param namedSet Named set
056     */
057    public RolapNamedSetEvaluator(
058        RolapResult.RolapResultEvaluatorRoot rrer,
059        NamedSet namedSet)
060    {
061        this.rrer = rrer;
062        this.namedSet = namedSet;
063    }
064
065    public TupleIterable evaluateTupleIterable(Evaluator evaluator) {
066        ensureList(evaluator);
067        return list;
068    }
069
070    /**
071     * Evaluates and saves the value of this named set, if it has not been
072     * evaluated already.
073     */
074    private void ensureList(Evaluator evaluator) {
075        if (list != null) {
076            if (list == DUMMY_LIST) {
077                recursionCount ++;
078                if (RECURSION_TOLERANCE > 0
079                    && recursionCount > RECURSION_TOLERANCE)
080                {
081                    throw rrer.result.slicerEvaluator.newEvalException(
082                        null,
083                        "Illegal attempt to reference value of named set '"
084                        + namedSet.getName() + "' while evaluating itself");
085                }
086            }
087            return;
088        }
089        if (RolapResult.LOGGER.isDebugEnabled()) {
090            RolapResult.LOGGER.debug(
091                "Named set " + namedSet.getName() + ": starting evaluation");
092        }
093        list = DUMMY_LIST; // recursion detection
094        try {
095            final Calc calc =
096                rrer.getCompiled(
097                    namedSet.getExp(), false, ResultStyle.ITERABLE);
098            TupleIterable iterable =
099                (TupleIterable)
100                    rrer.result.evaluateExp(
101                        calc,
102                        rrer.result.slicerEvaluator,
103                        evaluator);
104
105            // Axes can be in two forms: list or iterable. If iterable, we
106            // need to materialize it, to ensure that all cell values are in
107            // cache.
108            final TupleList rawList;
109            if (iterable instanceof TupleList) {
110                rawList = (TupleList) iterable;
111            } else {
112                rawList = TupleCollections.createList(iterable.getArity());
113                TupleCursor cursor = iterable.tupleCursor();
114                while (cursor.forward()) {
115                    rawList.addCurrent(cursor);
116                }
117            }
118            if (RolapResult.LOGGER.isDebugEnabled()) {
119                RolapResult.LOGGER.debug(generateDebugMessage(calc, rawList));
120            }
121            // Wrap list so that currentOrdinal is updated whenever the list
122            // is accessed. The list is immutable, because we don't override
123            // AbstractList.set(int, Object).
124            this.list = rawList.withPositionCallback(this);
125        } finally {
126            if (this.list == DUMMY_LIST) {
127                this.list = null;
128            }
129            recursionCount = 0;
130        }
131    }
132
133    private String generateDebugMessage(Calc calc, TupleList rawList) {
134        final StringBuilder buf = new StringBuilder();
135        buf.append(this);
136        buf.append(": ");
137        buf.append("Named set ");
138        buf.append(namedSet.getName());
139        buf.append(" evaluated to:");
140        buf.append(Util.nl);
141        int arity = calc.getType().getArity();
142        int rowCount = 0;
143        final int maxRowCount = 100;
144        if (arity == 1) {
145            for (Member t : rawList.slice(0)) {
146                if (rowCount++ > maxRowCount) {
147                    buf.append("...");
148                    buf.append(Util.nl);
149                    break;
150                }
151                buf.append(t);
152                buf.append(Util.nl);
153            }
154        } else {
155            for (List<Member> t : rawList) {
156                if (rowCount++ > maxRowCount) {
157                    buf.append("...");
158                    buf.append(Util.nl);
159                    break;
160                }
161                int k = 0;
162                for (Member member : t) {
163                    if (k++ > 0) {
164                        buf.append(", ");
165                    }
166                    buf.append(member);
167                }
168                buf.append(Util.nl);
169            }
170        }
171        return buf.toString();
172    }
173
174    public int currentOrdinal() {
175        return currentOrdinal;
176    }
177
178    public void onPosition(int index) {
179        this.currentOrdinal = index;
180    }
181
182    public Member[] currentTuple() {
183        final List<Member> tuple = list.get(currentOrdinal);
184        return tuple.toArray(new Member[tuple.size()]);
185    }
186
187    public Member currentMember() {
188        return list.get(0, currentOrdinal);
189    }
190}
191
192// End RolapNamedSetEvaluator.java