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