001 /*
002 // $Id: //open/mondrian-release/3.1/src/main/mondrian/calc/impl/AbstractExpCompiler.java#4 $
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.fun.*;
014 import mondrian.olap.type.*;
015 import mondrian.olap.type.DimensionType;
016 import mondrian.olap.type.LevelType;
017 import mondrian.resource.MondrianResource;
018 import mondrian.calc.*;
019 import mondrian.mdx.UnresolvedFunCall;
020
021 import java.util.*;
022
023 /**
024 * Abstract implementation of the {@link mondrian.calc.ExpCompiler} interface.
025 *
026 * @author jhyde
027 * @version $Id: //open/mondrian-release/3.1/src/main/mondrian/calc/impl/AbstractExpCompiler.java#4 $
028 * @since Sep 29, 2005
029 */
030 public class AbstractExpCompiler implements ExpCompiler {
031 private final Evaluator evaluator;
032 private final Validator validator;
033 private final Map<Parameter, ParameterSlotImpl> parameterSlots =
034 new HashMap<Parameter, ParameterSlotImpl>();
035 private List<ResultStyle> resultStyles;
036
037 /**
038 * Creates an AbstractExpCompiler
039 *
040 * @param evaluator Evaluator
041 * @param validator Validator
042 */
043 public AbstractExpCompiler(Evaluator evaluator, Validator validator) {
044 this(evaluator, validator, ResultStyle.ANY_LIST);
045 }
046
047 /**
048 * Creates an AbstractExpCompiler which is constrained to produce one of
049 * a set of result styles.
050 *
051 * @param evaluator Evaluator
052 * @param validator Validator
053 * @param resultStyles List of result styles, preferred first, must not be
054 */
055 public AbstractExpCompiler(
056 Evaluator evaluator,
057 Validator validator,
058 List<ResultStyle> resultStyles)
059 {
060 this.evaluator = evaluator;
061 this.validator = validator;
062 this.resultStyles = (resultStyles == null)
063 ? ResultStyle.ANY_LIST : resultStyles;
064 }
065
066 public Evaluator getEvaluator() {
067 return evaluator;
068 }
069
070 public Validator getValidator() {
071 return validator;
072 }
073
074 /**
075 * {@inheritDoc}
076 *
077 * Uses the current ResultStyle to compile the expression.
078 */
079 public Calc compile(Exp exp) {
080 return exp.accept(this);
081 }
082
083 /**
084 * {@inheritDoc}
085 *
086 * Uses a new ResultStyle to compile the expression.
087 */
088 public Calc compileAs(
089 Exp exp,
090 Type resultType,
091 List<ResultStyle> preferredResultTypes)
092 {
093 assert preferredResultTypes != null;
094 int substitutions = 0;
095 if (Util.Retrowoven) {
096 // Copy and replace ITERABLE
097 // A number of functions declare that they can accept
098 // ITERABLEs so here is where that those are converted to innocent
099 // LISTs for jdk1.4 and other retrowoven code.
100 List<ResultStyle> tmp =
101 new ArrayList<ResultStyle>(preferredResultTypes.size());
102 for (ResultStyle preferredResultType : preferredResultTypes) {
103 if (preferredResultType == ResultStyle.ITERABLE) {
104 preferredResultType = ResultStyle.LIST;
105 ++substitutions;
106 }
107 tmp.add(preferredResultType);
108 }
109 preferredResultTypes = tmp;
110 }
111 List<ResultStyle> save = this.resultStyles;
112 try {
113 this.resultStyles = preferredResultTypes;
114 if (resultType != null && resultType != exp.getType()) {
115 if (resultType instanceof MemberType) {
116 return compileMember(exp);
117 } else if (resultType instanceof LevelType) {
118 return compileLevel(exp);
119 } else if (resultType instanceof HierarchyType) {
120 return compileHierarchy(exp);
121 } else if (resultType instanceof DimensionType) {
122 return compileDimension(exp);
123 } else if (resultType instanceof ScalarType) {
124 return compileScalar(exp, false);
125 }
126 }
127 final Calc calc = compile(exp);
128 if (substitutions > 0) {
129 if (calc == null) {
130 this.resultStyles =
131 Collections.singletonList(ResultStyle.ITERABLE);
132 return compile(exp);
133 } else if (calc instanceof IterCalc) {
134 return calc;
135 } else {
136 assert calc instanceof ListCalc;
137 if (((SetType) calc.getType()).getArity() == 1) {
138 return toIter((MemberListCalc) calc);
139 } else {
140 return toIter((TupleListCalc) calc);
141 }
142 }
143 }
144 return calc;
145 } finally {
146 this.resultStyles = save;
147 }
148 }
149
150 public MemberCalc compileMember(Exp exp) {
151 final Type type = exp.getType();
152 if (type instanceof DimensionType) {
153 final DimensionCalc dimensionCalc = compileDimension(exp);
154 return new DimensionCurrentMemberCalc(
155 new DummyExp(TypeUtil.toMemberType(type)), dimensionCalc);
156 } else if (type instanceof HierarchyType) {
157 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
158 return new HierarchyCurrentMemberFunDef.CalcImpl(
159 new DummyExp(TypeUtil.toMemberType(type)), hierarchyCalc);
160 } else if (type instanceof NullType) {
161 throw MondrianResource.instance().NullNotSupported.ex();
162 }
163 assert type instanceof MemberType;
164 return (MemberCalc) compile(exp);
165 }
166
167 public LevelCalc compileLevel(Exp exp) {
168 final Type type = exp.getType();
169 if (type instanceof MemberType) {
170 // <Member> --> <Member>.Level
171 final MemberCalc memberCalc = compileMember(exp);
172 return new MemberLevelFunDef.CalcImpl(
173 new DummyExp(LevelType.forType(type)),
174 memberCalc);
175 }
176 assert type instanceof LevelType;
177 return (LevelCalc) compile(exp);
178 }
179
180 public DimensionCalc compileDimension(Exp exp) {
181 final Type type = exp.getType();
182 if (type instanceof HierarchyType) {
183 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
184 return new HierarchyDimensionFunDef.CalcImpl(
185 new DummyExp(new DimensionType(type.getDimension())),
186 hierarchyCalc);
187 }
188 assert type instanceof DimensionType : type;
189 return (DimensionCalc) compile(exp);
190 }
191
192 public HierarchyCalc compileHierarchy(Exp exp) {
193 final Type type = exp.getType();
194 if (type instanceof DimensionType) {
195 // <Dimension> --> unique Hierarchy else error
196 // Resolve at compile time if constant
197 final Dimension dimension = type.getDimension();
198 if (dimension != null) {
199 final Hierarchy hierarchy =
200 FunUtil.getDimensionDefaultHierarchy(dimension);
201 if (hierarchy != null) {
202 return (HierarchyCalc) ConstantCalc.constantHierarchy(
203 hierarchy);
204 }
205 }
206 final DimensionCalc dimensionCalc = compileDimension(exp);
207 return new DimensionHierarchyCalc(
208 new DummyExp(HierarchyType.forType(type)),
209 dimensionCalc);
210 }
211 if (type instanceof MemberType) {
212 // <Member> --> <Member>.Hierarchy
213 final MemberCalc memberCalc = compileMember(exp);
214 return new MemberHierarchyFunDef.CalcImpl(
215 new DummyExp(HierarchyType.forType(type)),
216 memberCalc);
217 }
218 if (type instanceof LevelType) {
219 // <Level> --> <Level>.Hierarchy
220 final LevelCalc levelCalc = compileLevel(exp);
221 return new LevelHierarchyFunDef.CalcImpl(
222 new DummyExp(HierarchyType.forType(type)),
223 levelCalc);
224 }
225 assert type instanceof HierarchyType;
226 return (HierarchyCalc) compile(exp);
227 }
228
229 public IntegerCalc compileInteger(Exp exp) {
230 final Calc calc = compileScalar(exp, false);
231 final Type type = calc.getType();
232 if (type instanceof DecimalType
233 && ((DecimalType) type).getScale() == 0)
234 {
235 return (IntegerCalc) calc;
236 } else if (type instanceof NumericType) {
237 if (calc instanceof ConstantCalc) {
238 ConstantCalc constantCalc = (ConstantCalc) calc;
239 return new ConstantCalc(
240 new DecimalType(Integer.MAX_VALUE, 0),
241 constantCalc.evaluateInteger(null));
242 } else if (calc instanceof DoubleCalc) {
243 final DoubleCalc doubleCalc = (DoubleCalc) calc;
244 return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) {
245 public int evaluateInteger(Evaluator evaluator) {
246 return (int) doubleCalc.evaluateDouble(evaluator);
247 }
248 };
249 }
250 }
251 return (IntegerCalc) calc;
252 }
253
254 public StringCalc compileString(Exp exp) {
255 return (StringCalc) compileScalar(exp, false);
256 }
257
258 public DateTimeCalc compileDateTime(Exp exp) {
259 return (DateTimeCalc) compileScalar(exp, false);
260 }
261
262 public ListCalc compileList(Exp exp) {
263 return compileList(exp, false);
264 }
265
266 public ListCalc compileList(Exp exp, boolean mutable) {
267 assert exp.getType() instanceof SetType : "must be a set: " + exp;
268 final List<ResultStyle> resultStyleList;
269 if (mutable) {
270 resultStyleList = ResultStyle.MUTABLELIST_ONLY;
271 } else {
272 resultStyleList = ResultStyle.LIST_ONLY;
273 }
274 Calc calc = compileAs(exp, null, resultStyleList);
275 if (calc instanceof ListCalc) {
276 return (ListCalc) calc;
277 }
278 if (calc == null) {
279 calc = compileAs(exp, null, ResultStyle.ITERABLE_ANY);
280 assert calc != null;
281 }
282 if (calc instanceof IterCalc) {
283 if (((SetType) calc.getType()).getArity() == 1) {
284 return toList((MemberIterCalc) calc);
285 } else {
286 return toList((TupleIterCalc) calc);
287 }
288 } else {
289 // A set can only be implemented as a list or an iterable.
290 throw Util.newInternal("Cannot convert calc to list: " + calc);
291 }
292 }
293
294 /**
295 * Converts an iterable over members to a list of members.
296 *
297 * @param calc Calc
298 * @return List calculation.
299 */
300 public MemberListCalc toList(MemberIterCalc calc) {
301 return new IterableMemberListCalc(calc);
302 }
303
304 /**
305 * Converts an iterable over tuples to a list of tuples.
306 *
307 * @param calc Calc
308 * @return List calculation.
309 */
310 public TupleListCalc toList(TupleIterCalc calc) {
311 return new IterableTupleListCalc(calc);
312 }
313
314 public IterCalc compileIter(Exp exp) {
315 Calc calc = compileAs(exp, null, ResultStyle.ITERABLE_ONLY);
316 if (calc == null) {
317 calc = compileAs(exp, null, ResultStyle.ANY_ONLY);
318 assert calc != null;
319 }
320 if (calc instanceof IterCalc) {
321 return (IterCalc) calc;
322 } else {
323 if (((SetType) calc.getType()).getArity() == 1) {
324 return toIter((MemberListCalc) calc);
325 } else {
326 return toIter((TupleListCalc) calc);
327 }
328 }
329 }
330
331 /**
332 * Converts a list of members to an iterable over members.
333 *
334 * @param memberListCalc Calc
335 * @return Iterable calculation
336 */
337 public MemberIterCalc toIter(final MemberListCalc memberListCalc) {
338 return new MemberListIterCalc(memberListCalc);
339 }
340
341 /**
342 * Converts a list of tuples to an iterable over tuples.
343 *
344 * @param tupleListCalc Calc
345 * @return Iterable calculation
346 */
347 public TupleIterCalc toIter(final TupleListCalc tupleListCalc) {
348 return new TupleListIterCalc(tupleListCalc);
349 }
350
351 public BooleanCalc compileBoolean(Exp exp) {
352 final Calc calc = compileScalar(exp, false);
353 if (calc instanceof BooleanCalc) {
354 if (calc instanceof ConstantCalc) {
355 final Object o = calc.evaluate(null);
356 if (!(o instanceof Boolean)) {
357 return ConstantCalc.constantBoolean(
358 CastFunDef.toBoolean(o, new BooleanType()));
359 }
360 }
361 return (BooleanCalc) calc;
362 } else if (calc instanceof DoubleCalc) {
363 final DoubleCalc doubleCalc = (DoubleCalc) calc;
364 return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) {
365 public boolean evaluateBoolean(Evaluator evaluator) {
366 return doubleCalc.evaluateDouble(evaluator) != 0;
367 }
368 };
369 } else if (calc instanceof IntegerCalc) {
370 final IntegerCalc integerCalc = (IntegerCalc) calc;
371 return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) {
372 public boolean evaluateBoolean(Evaluator evaluator) {
373 return integerCalc.evaluateInteger(evaluator) != 0;
374 }
375 };
376 } else {
377 return (BooleanCalc) calc;
378 }
379 }
380
381 public DoubleCalc compileDouble(Exp exp) {
382 final DoubleCalc calc = (DoubleCalc) compileScalar(exp, false);
383 if (calc instanceof ConstantCalc
384 && !(calc.evaluate(null) instanceof Double))
385 {
386 return ConstantCalc.constantDouble(
387 calc.evaluateDouble(null));
388 }
389 return calc;
390 }
391
392 public TupleCalc compileTuple(Exp exp) {
393 return (TupleCalc) compile(exp);
394 }
395
396 public Calc compileScalar(Exp exp, boolean specific) {
397 final Type type = exp.getType();
398 if (type instanceof MemberType) {
399 MemberType memberType = (MemberType) type;
400 MemberCalc calc = compileMember(exp);
401 return new MemberValueCalc(
402 new DummyExp(memberType.getValueType()),
403 new MemberCalc[] {calc});
404 } else if (type instanceof DimensionType) {
405 final DimensionCalc dimensionCalc = compileDimension(exp);
406 MemberType memberType = MemberType.forType(type);
407 final MemberCalc dimensionCurrentMemberCalc =
408 new DimensionCurrentMemberCalc(
409 new DummyExp(memberType),
410 dimensionCalc);
411 return new MemberValueCalc(
412 new DummyExp(memberType.getValueType()),
413 new MemberCalc[] {dimensionCurrentMemberCalc});
414 } else if (type instanceof HierarchyType) {
415 HierarchyType hierarchyType = (HierarchyType) type;
416 MemberType memberType =
417 MemberType.forHierarchy(hierarchyType.getHierarchy());
418 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
419 final MemberCalc hierarchyCurrentMemberCalc =
420 new HierarchyCurrentMemberFunDef.CalcImpl(
421 new DummyExp(memberType), hierarchyCalc);
422 return new MemberValueCalc(
423 new DummyExp(memberType.getValueType()),
424 new MemberCalc[] {hierarchyCurrentMemberCalc});
425 } else if (type instanceof TupleType) {
426 TupleType tupleType = (TupleType) type;
427 TupleCalc tupleCalc = compileTuple(exp);
428 final TupleValueCalc scalarCalc = new TupleValueCalc(
429 new DummyExp(tupleType.getValueType()), tupleCalc);
430 return scalarCalc.optimize();
431 } else if (type instanceof ScalarType) {
432 if (specific) {
433 if (type instanceof BooleanType) {
434 return compileBoolean(exp);
435 } else if (type instanceof NumericType) {
436 return compileDouble(exp);
437 } else if (type instanceof StringType) {
438 return compileString(exp);
439 } else {
440 return compile(exp);
441 }
442 } else {
443 return compile(exp);
444 }
445 } else {
446 return compile(exp);
447 }
448 }
449
450 public ParameterSlot registerParameter(Parameter parameter) {
451 ParameterSlot slot = parameterSlots.get(parameter);
452 if (slot != null) {
453 return slot;
454 }
455 int index = parameterSlots.size();
456 ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index);
457 parameterSlots.put(parameter, slot2);
458 slot2.value = parameter.getValue();
459
460 // Compile the expression only AFTER the parameter has been
461 // registered with a slot. Otherwise a cycle is possible.
462 final Type type = parameter.getType();
463 Exp defaultExp = parameter.getDefaultExp();
464 Calc calc;
465 if (type instanceof ScalarType) {
466 if (!defaultExp.getType().equals(type)) {
467 defaultExp =
468 new UnresolvedFunCall(
469 "Cast",
470 Syntax.Cast,
471 new Exp[] {
472 defaultExp,
473 Literal.createSymbol(
474 Category.instance.getName(
475 TypeUtil.typeToCategory(type)))});
476 defaultExp = getValidator().validate(defaultExp, true);
477 }
478 calc = compileScalar(defaultExp, true);
479 } else {
480 calc = compileAs(defaultExp, type, resultStyles);
481 }
482 slot2.setDefaultValueCalc(calc);
483 return slot2;
484 }
485
486 public List<ResultStyle> getAcceptableResultStyles() {
487 return resultStyles;
488 }
489
490 /**
491 * Implementation of {@link ParameterSlot}.
492 */
493 private static class ParameterSlotImpl implements ParameterSlot {
494 private final Parameter parameter;
495 private final int index;
496 private Calc defaultValueCalc;
497 private Object value;
498 private Object cachedDefaultValue;
499
500 /**
501 * Creates a ParameterSlotImpl.
502 *
503 * @param parameter Parameter
504 * @param index Unique index of the slot
505 */
506 public ParameterSlotImpl(
507 Parameter parameter, int index)
508 {
509 this.parameter = parameter;
510 this.index = index;
511 }
512
513 public int getIndex() {
514 return index;
515 }
516
517 public Calc getDefaultValueCalc() {
518 return defaultValueCalc;
519 }
520
521 public Parameter getParameter() {
522 return parameter;
523 }
524
525 /**
526 * Sets a compiled expression to compute the default value of the
527 * parameter.
528 *
529 * @param calc Compiled expression to compute default value of
530 * parameter
531 *
532 * @see #getDefaultValueCalc()
533 */
534 private void setDefaultValueCalc(Calc calc) {
535 this.defaultValueCalc = calc;
536 }
537
538 public void setParameterValue(Object value) {
539 this.value = value;
540 }
541
542 public Object getParameterValue() {
543 return value;
544 }
545
546 public void setCachedDefaultValue(Object value) {
547 this.cachedDefaultValue = value;
548 }
549
550 public Object getCachedDefaultValue() {
551 return cachedDefaultValue;
552 }
553 }
554
555 /**
556 * Adapter that converts a member list calc into a member iter calc.
557 */
558 private static class MemberListIterCalc extends AbstractMemberIterCalc {
559 private final MemberListCalc memberListCalc;
560
561 public MemberListIterCalc(MemberListCalc memberListCalc) {
562 super(
563 new DummyExp(memberListCalc.getType()),
564 new Calc[]{memberListCalc});
565 this.memberListCalc = memberListCalc;
566 }
567
568 public Iterable<Member> evaluateMemberIterable(Evaluator evaluator) {
569 return memberListCalc.evaluateMemberList(evaluator);
570 }
571 }
572
573 /**
574 * Adapter that converts a tuple list calc into a tuple iter calc.
575 */
576 private static class TupleListIterCalc extends AbstractTupleIterCalc {
577 private final TupleListCalc tupleListCalc;
578
579 public TupleListIterCalc(TupleListCalc tupleListCalc) {
580 super(
581 new DummyExp(tupleListCalc.getType()),
582 new Calc[]{tupleListCalc});
583 this.tupleListCalc = tupleListCalc;
584 }
585
586 public Iterable<Member[]> evaluateTupleIterable(Evaluator evaluator) {
587 return tupleListCalc.evaluateTupleList(evaluator);
588 }
589 }
590
591 /**
592 * Computes the hierarchy of a dimension
593 */
594 private static class DimensionHierarchyCalc extends AbstractHierarchyCalc {
595 private final DimensionCalc dimensionCalc;
596
597 protected DimensionHierarchyCalc(Exp exp, DimensionCalc dimensionCalc) {
598 super(exp, new Calc[] {dimensionCalc});
599 this.dimensionCalc = dimensionCalc;
600 }
601
602 public Hierarchy evaluateHierarchy(Evaluator evaluator) {
603 Dimension dimension =
604 dimensionCalc.evaluateDimension(evaluator);
605 final Hierarchy hierarchy =
606 FunUtil.getDimensionDefaultHierarchy(dimension);
607 if (hierarchy != null) {
608 return hierarchy;
609 }
610 throw FunUtil.newEvalException(
611 MondrianResource.instance()
612 .CannotImplicitlyConvertDimensionToHierarchy
613 .ex(
614 dimension.getName()));
615 }
616 }
617
618 /**
619 * Computation that returns the current member of a dimension.
620 */
621 public static class DimensionCurrentMemberCalc extends AbstractMemberCalc {
622 private final DimensionCalc dimensionCalc;
623
624 public DimensionCurrentMemberCalc(
625 Exp exp,
626 DimensionCalc dimensionCalc)
627 {
628 super(exp, new Calc[] {dimensionCalc});
629 this.dimensionCalc = dimensionCalc;
630 }
631
632 protected String getName() {
633 return "CurrentMember";
634 }
635
636 public Member evaluateMember(Evaluator evaluator) {
637 Dimension dimension =
638 dimensionCalc.evaluateDimension(evaluator);
639 return evaluator.getContext(dimension);
640 }
641
642 public boolean dependsOn(Dimension dimension) {
643 return dimensionCalc.getType().usesDimension(dimension, true);
644 }
645 }
646 }
647
648 // End AbstractExpCompiler.java