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) 2005-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho and others
009// Copyright (C) 2004-2005 SAS Institute, Inc.
010// All Rights Reserved.
011*/
012package mondrian.olap.fun;
013
014import mondrian.calc.*;
015import mondrian.calc.impl.AbstractListCalc;
016import mondrian.mdx.ResolvedFunCall;
017import mondrian.olap.*;
018import mondrian.rolap.RolapEvaluator;
019
020
021/**
022 * Definition of the <code>NonEmptyCrossJoin</code> MDX function.
023 *
024 * @author jhyde
025 * @since Mar 23, 2006
026 *
027 * author 16 December, 2004
028 */
029public class NonEmptyCrossJoinFunDef extends CrossJoinFunDef {
030    static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver(
031        "NonEmptyCrossJoin",
032            "NonEmptyCrossJoin(<Set1>, <Set2>)",
033            "Returns the cross product of two sets, excluding empty tuples and tuples without associated fact table data.",
034            new String[]{"fxxx"},
035            NonEmptyCrossJoinFunDef.class);
036
037    public NonEmptyCrossJoinFunDef(FunDef dummyFunDef) {
038        super(dummyFunDef);
039    }
040
041    public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
042        final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
043        final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
044        return new AbstractListCalc(
045            call, new Calc[] {listCalc1, listCalc2}, false)
046        {
047            public TupleList evaluateList(Evaluator evaluator) {
048                SchemaReader schemaReader = evaluator.getSchemaReader();
049
050                // Evaluate the arguments in non empty mode, but remove from
051                // the slicer any members that will be overridden by args to
052                // the NonEmptyCrossjoin function. For example, in
053                //
054                //   SELECT NonEmptyCrossJoin(
055                //       [Store].[USA].Children,
056                //       [Product].[Beer].Children)
057                //    FROM [Sales]
058                //    WHERE [Store].[Mexico]
059                //
060                // we want all beers, not just those sold in Mexico.
061                final int savepoint = evaluator.savepoint();
062                try {
063                    evaluator.setNonEmpty(true);
064                    for (Member member
065                        : ((RolapEvaluator) evaluator).getSlicerMembers())
066                    {
067                        if (getType().getElementType().usesHierarchy(
068                                member.getHierarchy(), true))
069                        {
070                            evaluator.setContext(
071                                member.getHierarchy().getAllMember());
072                        }
073                    }
074
075                    NativeEvaluator nativeEvaluator =
076                        schemaReader.getNativeSetEvaluator(
077                            call.getFunDef(), call.getArgs(), evaluator, this);
078                    if (nativeEvaluator != null) {
079                        evaluator.restore(savepoint);
080                        return
081                            (TupleList) nativeEvaluator.execute(
082                                ResultStyle.LIST);
083                    }
084
085                    final TupleList list1 = listCalc1.evaluateList(evaluator);
086                    if (list1.isEmpty()) {
087                        evaluator.restore(savepoint);
088                        return list1;
089                    }
090                    final TupleList list2 = listCalc2.evaluateList(evaluator);
091                    TupleList result = mutableCrossJoin(list1, list2);
092
093                    // remove any remaining empty crossings from the result
094                    result = nonEmptyList(evaluator, result, call);
095                    return result;
096                } finally {
097                    evaluator.restore(savepoint);
098                }
099            }
100
101            public boolean dependsOn(Hierarchy hierarchy) {
102                if (super.dependsOn(hierarchy)) {
103                    return true;
104                }
105                // Member calculations generate members, which mask the actual
106                // expression from the inherited context.
107                if (listCalc1.getType().usesHierarchy(hierarchy, true)) {
108                    return false;
109                }
110                if (listCalc2.getType().usesHierarchy(hierarchy, true)) {
111                    return false;
112                }
113                // The implicit value expression, executed to figure out
114                // whether a given tuple is empty, depends upon all dimensions.
115                return true;
116            }
117        };
118    }
119
120}
121
122// End NonEmptyCrossJoinFunDef.java