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