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