001 /*
002 // $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractCalc.java#1 $
003 // This software is subject to the terms of the Eclipse Public License v1.0
004 // Agreement, available at the following URL:
005 // http://www.eclipse.org/legal/epl-v10.html.
006 // Copyright (C) 2006-2009 Julian Hyde
007 // All Rights Reserved.
008 // You must accept the terms of that agreement to use this software.
009 */
010 package mondrian.calc.impl;
011
012 import mondrian.olap.*;
013 import mondrian.olap.type.Type;
014 import mondrian.calc.Calc;
015 import mondrian.calc.CalcWriter;
016 import mondrian.calc.ResultStyle;
017 import mondrian.mdx.ResolvedFunCall;
018 import mondrian.rolap.RolapEvaluator;
019 import mondrian.rolap.RolapHierarchy;
020
021 import java.io.PrintWriter;
022 import java.util.List;
023 import java.util.Collections;
024
025 /**
026 * Abstract implementation of the {@link mondrian.calc.Calc} interface.
027 *
028 * @author jhyde
029 * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractCalc.java#1 $
030 * @since Sep 27, 2005
031 */
032 public abstract class AbstractCalc implements Calc {
033 private final Calc[] calcs;
034 protected final Type type;
035 protected final Exp exp;
036
037 /**
038 * Creates an AbstractCalc.
039 *
040 * @param exp Source expression
041 * @param calcs Child compiled expressions
042 */
043 protected AbstractCalc(Exp exp, Calc[] calcs) {
044 assert exp != null;
045 this.exp = exp;
046 this.calcs = calcs;
047 this.type = exp.getType();
048 }
049
050 public Type getType() {
051 return type;
052 }
053
054 public void accept(CalcWriter calcWriter) {
055 final PrintWriter pw = calcWriter.getWriter();
056 String name = getName();
057 pw.print(name);
058 final Calc[] calcs = getCalcs();
059 final List<Object> argumentList = getArguments();
060 if (calcs.length > 0 || !argumentList.isEmpty()) {
061 pw.print("(");
062 int k = 0;
063 for (Calc calc : calcs) {
064 if (k++ > 0) {
065 pw.print(", ");
066 }
067 calc.accept(calcWriter);
068 }
069 for (Object o : argumentList) {
070 if (k++ > 0) {
071 pw.print(", ");
072 }
073 pw.print(o);
074 }
075 pw.print(")");
076 }
077 }
078
079 /**
080 * Returns the name of this expression type, used when serializing an
081 * expression to a string.
082 *
083 * <p>The default implementation tries to extract a name from a function
084 * call, if any, then prints the last part of the class name.
085 */
086 protected String getName() {
087 String name;
088 if (exp instanceof ResolvedFunCall) {
089 ResolvedFunCall funCall = (ResolvedFunCall) exp;
090 name = funCall.getFunDef().getName();
091 } else {
092 name = getClass().getName();
093 int dot = name.lastIndexOf('.');
094 int dollar = name.lastIndexOf('$');
095 int dotDollar = Math.max(dot, dollar);
096 if (dotDollar >= 0) {
097 name = name.substring(dotDollar + 1);
098 }
099 }
100 return name;
101 }
102
103 /**
104 * Returns this expression's child expressions.
105 */
106 public Calc[] getCalcs() {
107 return calcs;
108 }
109
110 public boolean dependsOn(Hierarchy hierarchy) {
111 return anyDepends(getCalcs(), hierarchy);
112 }
113
114 /**
115 * Returns true if one of the calcs depends on the given dimension.
116 */
117 public static boolean anyDepends(Calc[] calcs, Hierarchy hierarchy) {
118 for (Calc calc : calcs) {
119 if (calc != null && calc.dependsOn(hierarchy)) {
120 return true;
121 }
122 }
123 return false;
124 }
125
126 /**
127 * Returns true if calc[0] depends on dimension,
128 * else false if calc[0] returns dimension,
129 * else true if any of the other calcs depend on dimension.
130 *
131 * <p>Typical application: <code>Aggregate({Set}, {Value Expression})</code>
132 * depends upon everything {Value Expression} depends upon, except the
133 * dimensions of {Set}.
134 */
135 public static boolean anyDependsButFirst(
136 Calc[] calcs, Hierarchy hierarchy)
137 {
138 if (calcs.length == 0) {
139 return false;
140 }
141 if (calcs[0].dependsOn(hierarchy)) {
142 return true;
143 }
144 if (calcs[0].getType().usesHierarchy(hierarchy, true)) {
145 return false;
146 }
147 for (int i = 1; i < calcs.length; i++) {
148 Calc calc = calcs[i];
149 if (calc != null && calc.dependsOn(hierarchy)) {
150 return true;
151 }
152 }
153 return false;
154 }
155
156 /**
157 * Returns true if any of the calcs depend on dimension,
158 * else false if any of the calcs return dimension,
159 * else true.
160 */
161 public static boolean butDepends(
162 Calc[] calcs, Hierarchy hierarchy)
163 {
164 boolean result = true;
165 for (Calc calc : calcs) {
166 if (calc != null) {
167 if (calc.dependsOn(hierarchy)) {
168 return true;
169 }
170 if (calc.getType().usesHierarchy(hierarchy, true)) {
171 result = false;
172 }
173 }
174 }
175 return result;
176 }
177
178 /**
179 * Returns any other arguments to this calc.
180 * The default implementation returns the empty list.
181 */
182 public List<Object> getArguments() {
183 return Collections.emptyList();
184 }
185
186 /**
187 * Returns a simplified evalator whose context is the same for every
188 * dimension which an expression depends on, and the default member for
189 * every dimension which it does not depend on.
190 *
191 * <p>The default member is often the 'all' member, so this evaluator is
192 * usually the most efficient context in which to evaluate the expression.
193 *
194 * @param calc
195 * @param evaluator
196 */
197 public static Evaluator simplifyEvaluator(Calc calc, Evaluator evaluator) {
198 if (evaluator.isNonEmpty()) {
199 // If NON EMPTY is present, we cannot simplify the context, because
200 // we have to assume that the expression depends on everything.
201 // TODO: Bug 1456418: Convert 'NON EMPTY Crossjoin' to
202 // 'NonEmptyCrossJoin'.
203 return evaluator;
204 }
205 int changeCount = 0;
206 Evaluator ev = evaluator;
207 final List<RolapHierarchy> hierarchies =
208 ((RolapEvaluator) evaluator).getCube().getHierarchies();
209 for (RolapHierarchy hierarchy : hierarchies) {
210 final Member member = ev.getContext(hierarchy);
211 if (member.isAll()) {
212 continue;
213 }
214 if (calc.dependsOn(hierarchy)) {
215 continue;
216 }
217 final Member unconstrainedMember =
218 member.getHierarchy().getDefaultMember();
219 if (member == unconstrainedMember) {
220 // This is a hierarchy without an 'all' member, and the context
221 // is already the default member.
222 continue;
223 }
224 if (changeCount++ == 0) {
225 ev = evaluator.push(unconstrainedMember);
226 } else {
227 ev.setContext(unconstrainedMember);
228 }
229 }
230 return ev;
231 }
232
233 public ResultStyle getResultStyle() {
234 return ResultStyle.VALUE;
235 }
236 }
237
238 // End AbstractCalc.java