001 /*
002 // $Id: //open/mondrian-release/3.2/src/main/mondrian/calc/impl/AbstractExpCompiler.java#3 $
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-2010 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.*;
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.2/src/main/mondrian/calc/impl/AbstractExpCompiler.java#3 $
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 HierarchyType) {
153 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
154 return hierarchyToMember(hierarchyCalc);
155 } else if (type instanceof NullType) {
156 throw MondrianResource.instance().NullNotSupported.ex();
157 } else if (type instanceof DimensionType) {
158 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
159 return hierarchyToMember(hierarchyCalc);
160 }
161 assert type instanceof MemberType : type;
162 return (MemberCalc) compile(exp);
163 }
164
165 private MemberCalc hierarchyToMember(
166 HierarchyCalc hierarchyCalc)
167 {
168 final Hierarchy hierarchy = hierarchyCalc.getType().getHierarchy();
169 if (hierarchy != null) {
170 return new HierarchyCurrentMemberFunDef.FixedCalcImpl(
171 new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())),
172 hierarchy);
173 } else {
174 return new HierarchyCurrentMemberFunDef.CalcImpl(
175 new DummyExp(TypeUtil.toMemberType(hierarchyCalc.getType())),
176 hierarchyCalc);
177 }
178 }
179
180 public LevelCalc compileLevel(Exp exp) {
181 final Type type = exp.getType();
182 if (type instanceof MemberType) {
183 // <Member> --> <Member>.Level
184 final MemberCalc memberCalc = compileMember(exp);
185 return new MemberLevelFunDef.CalcImpl(
186 new DummyExp(LevelType.forType(type)),
187 memberCalc);
188 }
189 assert type instanceof LevelType;
190 return (LevelCalc) compile(exp);
191 }
192
193 public DimensionCalc compileDimension(Exp exp) {
194 final Type type = exp.getType();
195 if (type instanceof HierarchyType) {
196 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
197 return new HierarchyDimensionFunDef.CalcImpl(
198 new DummyExp(new DimensionType(type.getDimension())),
199 hierarchyCalc);
200 }
201 assert type instanceof DimensionType : type;
202 return (DimensionCalc) compile(exp);
203 }
204
205 public HierarchyCalc compileHierarchy(Exp exp) {
206 final Type type = exp.getType();
207 if (type instanceof DimensionType) {
208 // <Dimension> --> unique Hierarchy else error
209 // Resolve at compile time if constant
210 final Dimension dimension = type.getDimension();
211 if (dimension != null) {
212 final Hierarchy hierarchy =
213 FunUtil.getDimensionDefaultHierarchy(dimension);
214 if (hierarchy != null) {
215 return (HierarchyCalc) ConstantCalc.constantHierarchy(
216 hierarchy);
217 } else {
218 // SSAS gives error at run time (often as an error in a
219 // cell) but we prefer to give an error at validate time.
220 throw MondrianResource.instance()
221 .CannotImplicitlyConvertDimensionToHierarchy.ex(
222 dimension.getName());
223 }
224 }
225 final DimensionCalc dimensionCalc = compileDimension(exp);
226 return new DimensionHierarchyCalc(
227 new DummyExp(HierarchyType.forType(type)),
228 dimensionCalc);
229 }
230 if (type instanceof MemberType) {
231 // <Member> --> <Member>.Hierarchy
232 final MemberCalc memberCalc = compileMember(exp);
233 return new MemberHierarchyFunDef.CalcImpl(
234 new DummyExp(HierarchyType.forType(type)),
235 memberCalc);
236 }
237 if (type instanceof LevelType) {
238 // <Level> --> <Level>.Hierarchy
239 final LevelCalc levelCalc = compileLevel(exp);
240 return new LevelHierarchyFunDef.CalcImpl(
241 new DummyExp(HierarchyType.forType(type)),
242 levelCalc);
243 }
244 assert type instanceof HierarchyType;
245 return (HierarchyCalc) compile(exp);
246 }
247
248 public IntegerCalc compileInteger(Exp exp) {
249 final Calc calc = compileScalar(exp, false);
250 final Type type = calc.getType();
251 if (type instanceof DecimalType
252 && ((DecimalType) type).getScale() == 0)
253 {
254 return (IntegerCalc) calc;
255 } else if (type instanceof NumericType) {
256 if (calc instanceof ConstantCalc) {
257 ConstantCalc constantCalc = (ConstantCalc) calc;
258 return new ConstantCalc(
259 new DecimalType(Integer.MAX_VALUE, 0),
260 constantCalc.evaluateInteger(null));
261 } else if (calc instanceof DoubleCalc) {
262 final DoubleCalc doubleCalc = (DoubleCalc) calc;
263 return new AbstractIntegerCalc(exp, new Calc[] {doubleCalc}) {
264 public int evaluateInteger(Evaluator evaluator) {
265 return (int) doubleCalc.evaluateDouble(evaluator);
266 }
267 };
268 }
269 }
270 return (IntegerCalc) calc;
271 }
272
273 public StringCalc compileString(Exp exp) {
274 return (StringCalc) compileScalar(exp, false);
275 }
276
277 public DateTimeCalc compileDateTime(Exp exp) {
278 return (DateTimeCalc) compileScalar(exp, false);
279 }
280
281 public ListCalc compileList(Exp exp) {
282 return compileList(exp, false);
283 }
284
285 public ListCalc compileList(Exp exp, boolean mutable) {
286 assert exp.getType() instanceof SetType : "must be a set: " + exp;
287 final List<ResultStyle> resultStyleList;
288 if (mutable) {
289 resultStyleList = ResultStyle.MUTABLELIST_ONLY;
290 } else {
291 resultStyleList = ResultStyle.LIST_ONLY;
292 }
293 Calc calc = compileAs(exp, null, resultStyleList);
294 if (calc instanceof ListCalc) {
295 return (ListCalc) calc;
296 }
297 if (calc == null) {
298 calc = compileAs(exp, null, ResultStyle.ITERABLE_ANY);
299 assert calc != null;
300 }
301 // If expression is an iterator, convert it to a list. Don't check
302 // 'calc instanceof IterCalc' because some generic calcs implement both
303 // ListCalc and IterCalc.
304 if (!(calc instanceof ListCalc)) {
305 if (((SetType) calc.getType()).getArity() == 1) {
306 return toList((MemberIterCalc) calc);
307 } else {
308 return toList((TupleIterCalc) calc);
309 }
310 } else {
311 // A set can only be implemented as a list or an iterable.
312 throw Util.newInternal("Cannot convert calc to list: " + calc);
313 }
314 }
315
316 /**
317 * Converts an iterable over members to a list of members.
318 *
319 * @param calc Calc
320 * @return List calculation.
321 */
322 public MemberListCalc toList(MemberIterCalc calc) {
323 return new IterableMemberListCalc(calc);
324 }
325
326 /**
327 * Converts an iterable over tuples to a list of tuples.
328 *
329 * @param calc Calc
330 * @return List calculation.
331 */
332 public TupleListCalc toList(TupleIterCalc calc) {
333 return new IterableTupleListCalc(calc);
334 }
335
336 public IterCalc compileIter(Exp exp) {
337 Calc calc = compileAs(exp, null, ResultStyle.ITERABLE_ONLY);
338 if (calc == null) {
339 calc = compileAs(exp, null, ResultStyle.ANY_ONLY);
340 assert calc != null;
341 }
342 if (calc instanceof IterCalc) {
343 return (IterCalc) calc;
344 } else {
345 if (((SetType) calc.getType()).getArity() == 1) {
346 return toIter((MemberListCalc) calc);
347 } else {
348 return toIter((TupleListCalc) calc);
349 }
350 }
351 }
352
353 /**
354 * Converts a list of members to an iterable over members.
355 *
356 * @param memberListCalc Calc
357 * @return Iterable calculation
358 */
359 public MemberIterCalc toIter(final MemberListCalc memberListCalc) {
360 return new MemberListIterCalc(memberListCalc);
361 }
362
363 /**
364 * Converts a list of tuples to an iterable over tuples.
365 *
366 * @param tupleListCalc Calc
367 * @return Iterable calculation
368 */
369 public TupleIterCalc toIter(final TupleListCalc tupleListCalc) {
370 return new TupleListIterCalc(tupleListCalc);
371 }
372
373 public BooleanCalc compileBoolean(Exp exp) {
374 final Calc calc = compileScalar(exp, false);
375 if (calc instanceof BooleanCalc) {
376 if (calc instanceof ConstantCalc) {
377 final Object o = calc.evaluate(null);
378 if (!(o instanceof Boolean)) {
379 return ConstantCalc.constantBoolean(
380 CastFunDef.toBoolean(o, new BooleanType()));
381 }
382 }
383 return (BooleanCalc) calc;
384 } else if (calc instanceof DoubleCalc) {
385 final DoubleCalc doubleCalc = (DoubleCalc) calc;
386 return new AbstractBooleanCalc(exp, new Calc[] {doubleCalc}) {
387 public boolean evaluateBoolean(Evaluator evaluator) {
388 return doubleCalc.evaluateDouble(evaluator) != 0;
389 }
390 };
391 } else if (calc instanceof IntegerCalc) {
392 final IntegerCalc integerCalc = (IntegerCalc) calc;
393 return new AbstractBooleanCalc(exp, new Calc[] {integerCalc}) {
394 public boolean evaluateBoolean(Evaluator evaluator) {
395 return integerCalc.evaluateInteger(evaluator) != 0;
396 }
397 };
398 } else {
399 return (BooleanCalc) calc;
400 }
401 }
402
403 public DoubleCalc compileDouble(Exp exp) {
404 final DoubleCalc calc = (DoubleCalc) compileScalar(exp, false);
405 if (calc instanceof ConstantCalc
406 && !(calc.evaluate(null) instanceof Double))
407 {
408 return ConstantCalc.constantDouble(
409 calc.evaluateDouble(null));
410 }
411 return calc;
412 }
413
414 public TupleCalc compileTuple(Exp exp) {
415 return (TupleCalc) compile(exp);
416 }
417
418 public Calc compileScalar(Exp exp, boolean specific) {
419 final Type type = exp.getType();
420 if (type instanceof MemberType) {
421 MemberCalc calc = compileMember(exp);
422 return memberToScalar(calc);
423 } else if (type instanceof DimensionType) {
424 HierarchyCalc hierarchyCalc = compileHierarchy(exp);
425 return hierarchyToScalar(hierarchyCalc);
426 } else if (type instanceof HierarchyType) {
427 final HierarchyCalc hierarchyCalc = compileHierarchy(exp);
428 return hierarchyToScalar(hierarchyCalc);
429 } else if (type instanceof TupleType) {
430 TupleType tupleType = (TupleType) type;
431 TupleCalc tupleCalc = compileTuple(exp);
432 final TupleValueCalc scalarCalc =
433 new TupleValueCalc(
434 new DummyExp(tupleType.getValueType()), tupleCalc);
435 return scalarCalc.optimize();
436 } else if (type instanceof ScalarType) {
437 if (specific) {
438 if (type instanceof BooleanType) {
439 return compileBoolean(exp);
440 } else if (type instanceof NumericType) {
441 return compileDouble(exp);
442 } else if (type instanceof StringType) {
443 return compileString(exp);
444 } else {
445 return compile(exp);
446 }
447 } else {
448 return compile(exp);
449 }
450 } else {
451 return compile(exp);
452 }
453 }
454
455 private Calc hierarchyToScalar(HierarchyCalc hierarchyCalc) {
456 final MemberCalc memberCalc = hierarchyToMember(hierarchyCalc);
457 return memberToScalar(memberCalc);
458 }
459
460 private Calc memberToScalar(MemberCalc memberCalc) {
461 MemberType memberType = (MemberType) memberCalc.getType();
462 return new MemberValueCalc(
463 new DummyExp(memberType.getValueType()),
464 new MemberCalc[] {memberCalc});
465 }
466
467 public ParameterSlot registerParameter(Parameter parameter) {
468 ParameterSlot slot = parameterSlots.get(parameter);
469 if (slot != null) {
470 return slot;
471 }
472 int index = parameterSlots.size();
473 ParameterSlotImpl slot2 = new ParameterSlotImpl(parameter, index);
474 parameterSlots.put(parameter, slot2);
475 slot2.value = parameter.getValue();
476
477 // Compile the expression only AFTER the parameter has been
478 // registered with a slot. Otherwise a cycle is possible.
479 final Type type = parameter.getType();
480 Exp defaultExp = parameter.getDefaultExp();
481 Calc calc;
482 if (type instanceof ScalarType) {
483 if (!defaultExp.getType().equals(type)) {
484 defaultExp =
485 new UnresolvedFunCall(
486 "Cast",
487 Syntax.Cast,
488 new Exp[] {
489 defaultExp,
490 Literal.createSymbol(
491 Category.instance.getName(
492 TypeUtil.typeToCategory(type)))});
493 defaultExp = getValidator().validate(defaultExp, true);
494 }
495 calc = compileScalar(defaultExp, true);
496 } else {
497 calc = compileAs(defaultExp, type, resultStyles);
498 }
499 slot2.setDefaultValueCalc(calc);
500 return slot2;
501 }
502
503 public List<ResultStyle> getAcceptableResultStyles() {
504 return resultStyles;
505 }
506
507 /**
508 * Implementation of {@link ParameterSlot}.
509 */
510 private static class ParameterSlotImpl implements ParameterSlot {
511 private final Parameter parameter;
512 private final int index;
513 private Calc defaultValueCalc;
514 private Object value;
515 private boolean assigned;
516 private Object cachedDefaultValue;
517
518 /**
519 * Creates a ParameterSlotImpl.
520 *
521 * @param parameter Parameter
522 * @param index Unique index of the slot
523 */
524 public ParameterSlotImpl(
525 Parameter parameter, int index)
526 {
527 this.parameter = parameter;
528 this.index = index;
529 }
530
531 public int getIndex() {
532 return index;
533 }
534
535 public Calc getDefaultValueCalc() {
536 return defaultValueCalc;
537 }
538
539 public Parameter getParameter() {
540 return parameter;
541 }
542
543 /**
544 * Sets a compiled expression to compute the default value of the
545 * parameter.
546 *
547 * @param calc Compiled expression to compute default value of
548 * parameter
549 *
550 * @see #getDefaultValueCalc()
551 */
552 private void setDefaultValueCalc(Calc calc) {
553 this.defaultValueCalc = calc;
554 }
555
556 public void setParameterValue(Object value, boolean assigned) {
557 this.value = value;
558 this.assigned = assigned;
559 }
560
561 public Object getParameterValue() {
562 return value;
563 }
564
565 public boolean isParameterSet() {
566 return assigned;
567 }
568
569 public void unsetParameterValue() {
570 this.value = null;
571 this.assigned = false;
572 }
573
574 public void setCachedDefaultValue(Object value) {
575 this.cachedDefaultValue = value;
576 }
577
578 public Object getCachedDefaultValue() {
579 return cachedDefaultValue;
580 }
581 }
582
583 /**
584 * Adapter that converts a member list calc into a member iter calc.
585 */
586 private static class MemberListIterCalc extends AbstractMemberIterCalc {
587 private final MemberListCalc memberListCalc;
588
589 public MemberListIterCalc(MemberListCalc memberListCalc) {
590 super(
591 new DummyExp(memberListCalc.getType()),
592 new Calc[]{memberListCalc});
593 this.memberListCalc = memberListCalc;
594 }
595
596 public Iterable<Member> evaluateMemberIterable(Evaluator evaluator) {
597 return memberListCalc.evaluateMemberList(evaluator);
598 }
599 }
600
601 /**
602 * Adapter that converts a tuple list calc into a tuple iter calc.
603 */
604 private static class TupleListIterCalc extends AbstractTupleIterCalc {
605 private final TupleListCalc tupleListCalc;
606
607 public TupleListIterCalc(TupleListCalc tupleListCalc) {
608 super(
609 new DummyExp(tupleListCalc.getType()),
610 new Calc[]{tupleListCalc});
611 this.tupleListCalc = tupleListCalc;
612 }
613
614 public Iterable<Member[]> evaluateTupleIterable(Evaluator evaluator) {
615 return tupleListCalc.evaluateTupleList(evaluator);
616 }
617 }
618
619 /**
620 * Computes the hierarchy of a dimension.
621 */
622 private static class DimensionHierarchyCalc extends AbstractHierarchyCalc {
623 private final DimensionCalc dimensionCalc;
624
625 protected DimensionHierarchyCalc(Exp exp, DimensionCalc dimensionCalc) {
626 super(exp, new Calc[] {dimensionCalc});
627 this.dimensionCalc = dimensionCalc;
628 }
629
630 public Hierarchy evaluateHierarchy(Evaluator evaluator) {
631 Dimension dimension =
632 dimensionCalc.evaluateDimension(evaluator);
633 final Hierarchy hierarchy =
634 FunUtil.getDimensionDefaultHierarchy(dimension);
635 if (hierarchy != null) {
636 return hierarchy;
637 }
638 throw FunUtil.newEvalException(
639 MondrianResource.instance()
640 .CannotImplicitlyConvertDimensionToHierarchy.ex(
641 dimension.getName()));
642 }
643 }
644 }
645
646 // End AbstractExpCompiler.java