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-2013 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.olap.fun;
011
012import mondrian.calc.*;
013import mondrian.calc.impl.AbstractListCalc;
014import mondrian.mdx.ResolvedFunCall;
015import mondrian.olap.*;
016
017import java.util.ArrayList;
018import java.util.List;
019
020/**
021 * Definition of the <code>EXISTS</code> MDX function.
022 *
023 * @author kvu
024 * @since Mar 23, 2008
025 */
026class ExistsFunDef extends FunDefBase
027{
028    static final Resolver resolver =
029        new ReflectiveMultiResolver(
030            "Exists",
031            "Exists(<Set1>, <Set2>])",
032            "Returns the the set of tuples of the first set that exist with one or more tuples of the second set.",
033            new String[] {"fxxx"},
034            ExistsFunDef.class);
035
036    public ExistsFunDef(FunDef dummyFunDef)
037    {
038        super(dummyFunDef);
039    }
040
041    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
042        final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
043        final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
044
045        return new AbstractListCalc(call, new Calc[] {listCalc1, listCalc2}) {
046            public TupleList evaluateList(Evaluator evaluator) {
047                TupleList leftTuples = listCalc1.evaluateList(evaluator);
048                if (leftTuples.isEmpty()) {
049                    return TupleCollections.emptyList(leftTuples.getArity());
050                }
051                TupleList rightTuples = listCalc2.evaluateList(evaluator);
052                if (rightTuples.isEmpty()) {
053                    return TupleCollections.emptyList(leftTuples.getArity());
054                }
055                TupleList result =
056                    TupleCollections.createList(leftTuples.getArity());
057
058                List<Hierarchy> leftDims = getHierarchies(leftTuples.get(0));
059                List<Hierarchy> rightDims = getHierarchies(rightTuples.get(0));
060
061                leftLoop:
062                for (List<Member> leftTuple : leftTuples) {
063                    for (List<Member> rightTuple : rightTuples) {
064                        if (existsInTuple(leftTuple, rightTuple,
065                            leftDims, rightDims))
066                        {
067                            result.add(leftTuple);
068                            continue leftLoop;
069                        }
070                    }
071                }
072                return result;
073            }
074        };
075    }
076
077    private static boolean isOnSameHierarchyChain(Member mA, Member mB)
078    {
079        return (FunUtil.isAncestorOf(mA, mB, false))||
080            (FunUtil.isAncestorOf(mB, mA, false));
081    }
082
083
084    /**
085     * Returns true if leftTuple Exists w/in rightTuple
086     *
087     *
088     *
089     * @param leftTuple tuple from arg one of EXISTS()
090     * @param rightTuple tuple from arg two of EXISTS()
091     * @param leftHierarchies list of hierarchies from leftTuple, in the same
092     *                        order
093     * @param rightHierarchies list of the hiearchies from rightTuple,
094     *                         in the same order
095     * @return true if each member from leftTuple is somewhere in the
096     *         hierarchy chain of the corresponding member from rightTuple,
097     *         false otherwise.
098     *         If there is no explicit corresponding member from either
099     *         right or left, then the default member is used.
100     */
101    private boolean existsInTuple(
102        final List<Member> leftTuple, final List<Member> rightTuple,
103        final List<Hierarchy> leftHierarchies,
104        final List<Hierarchy> rightHierarchies)
105    {
106        List<Member> checkedMembers = new ArrayList<Member>();
107
108        for (Member leftMember : leftTuple) {
109            Member rightMember = getCorrespondingMember(
110                leftMember, rightTuple, rightHierarchies);
111            checkedMembers.add(rightMember);
112            if (!isOnSameHierarchyChain(leftMember, rightMember)) {
113                return false;
114            }
115        }
116        // this loop handles members in the right tuple not present in left
117        // Such a member could only impact the resulting tuple list if the
118        // default member of the hierarchy is not the all member.
119        for (Member rightMember : rightTuple) {
120            if (checkedMembers.contains(rightMember)) {
121                // already checked in the previous loop
122                continue;
123            }
124            Member leftMember = getCorrespondingMember(
125                rightMember, leftTuple, leftHierarchies);
126            if (!isOnSameHierarchyChain(leftMember, rightMember)) {
127                return false;
128            }
129        }
130        return true;
131    }
132
133    /**
134     * Returns the corresponding member from tuple, or the default member
135     * for the hierarchy if member is not explicitly contained in the tuple.
136     *
137     *
138     * @param member source member
139     * @param tuple tuple containing the target member
140     * @param tupleHierarchies list of the hierarchies explicitly contained
141     *                         in the tuple, in the same order.
142     * @return target member
143     */
144    private Member getCorrespondingMember(
145        final Member member, final List<Member> tuple,
146        final List<Hierarchy> tupleHierarchies)
147    {
148        assert tuple.size() == tupleHierarchies.size();
149        int dimPos = tupleHierarchies.indexOf(member.getHierarchy());
150        if (dimPos >= 0) {
151            return tuple.get(dimPos);
152        } else {
153            return member.getHierarchy().getDefaultMember();
154        }
155    }
156
157    private static List<Hierarchy> getHierarchies(final List<Member> members)
158    {
159        List<Hierarchy> hierarchies = new ArrayList<Hierarchy>();
160        for (Member member : members) {
161            hierarchies.add(member.getHierarchy());
162        }
163        return hierarchies;
164    }
165
166}
167
168// End ExistsFunDef.java