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) 2004-2005 Julian Hyde 008// Copyright (C) 2005-2011 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.AbstractListCalc; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017 018import java.util.*; 019 020/** 021 * Definition of the <code>INTERSECT</code> MDX function. 022 * 023 * @author jhyde 024 * @since Mar 23, 2006 025 */ 026class IntersectFunDef extends FunDefBase 027{ 028 private static final String[] ReservedWords = new String[] {"ALL"}; 029 030 static final Resolver resolver = 031 new ReflectiveMultiResolver( 032 "Intersect", 033 "Intersect(<Set1>, <Set2>[, ALL])", 034 "Returns the intersection of two input sets, optionally retaining duplicates.", 035 new String[] {"fxxxy", "fxxx"}, 036 IntersectFunDef.class, 037 ReservedWords); 038 039 public IntersectFunDef(FunDef dummyFunDef) 040 { 041 super(dummyFunDef); 042 } 043 044 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 045 final String literalArg = getLiteralArg(call, 2, "", ReservedWords); 046 final boolean all = literalArg.equalsIgnoreCase("ALL"); 047 final int arity = call.getType().getArity(); 048 049 final ListCalc listCalc1 = compiler.compileList(call.getArg(0)); 050 final ListCalc listCalc2 = compiler.compileList(call.getArg(1)); 051 return new AbstractListCalc( 052 call, new Calc[] {listCalc1, listCalc2}) 053 { 054 public TupleList evaluateList(Evaluator evaluator) { 055 TupleList leftList = 056 listCalc1.evaluateList(evaluator); 057 if (leftList.isEmpty()) { 058 return leftList; 059 } 060 final TupleList rightList = 061 listCalc2.evaluateList(evaluator); 062 if (rightList.isEmpty()) { 063 return rightList; 064 } 065 066 // Set of members from the right side of the intersect. 067 // We use a RetrievableSet because distinct keys 068 // (regular members and visual totals members) compare 069 // identical using hashCode and equals, we want to retrieve 070 // the actual key, and java.util.Set only has containsKey. 071 RetrievableSet<List<Member>> rightSet = 072 new RetrievableHashSet<List<Member>>( 073 rightList.size() * 3 / 2); 074 for (List<Member> tuple : rightList) { 075 rightSet.add(tuple); 076 } 077 078 final TupleList result = 079 TupleCollections.createList( 080 arity, Math.min(leftList.size(), rightList.size())); 081 final Set<List<Member>> resultSet = 082 all ? null : new HashSet<List<Member>>(); 083 for (List<Member> leftTuple : leftList) { 084 List<Member> rightKey = rightSet.getKey(leftTuple); 085 if (rightKey == null) { 086 continue; 087 } 088 if (resultSet != null && !resultSet.add(leftTuple)) { 089 continue; 090 } 091 result.add( 092 copyTupleWithVisualTotalsMembersOverriding( 093 leftTuple, rightKey)); 094 } 095 return result; 096 } 097 098 /** 099 * Constructs a tuple consisting of members from 100 * {@code leftTuple}, but overridden by any corresponding 101 * members from {@code rightKey} that happen to be visual totals 102 * members. 103 * 104 * <p>Returns the original tuple if there are no visual totals 105 * members on the RHS. 106 * 107 * @param leftTuple Original tuple 108 * @param rightKey Right tuple 109 * @return Copy of original tuple, with any VisualTotalMembers 110 * from right tuple overriding 111 */ 112 private List<Member> copyTupleWithVisualTotalsMembersOverriding( 113 List<Member> leftTuple, List<Member> rightKey) 114 { 115 List<Member> tuple = leftTuple; 116 for (int i = 0; i < rightKey.size(); i++) { 117 Member member = rightKey.get(i); 118 if (!(tuple.get(i) 119 instanceof VisualTotalsFunDef.VisualTotalMember) 120 && member instanceof 121 VisualTotalsFunDef.VisualTotalMember) 122 { 123 if (tuple == leftTuple) { 124 // clone on first VisualTotalMember -- to avoid 125 // alloc/copy in the common case where there are 126 // no VisualTotalMembers 127 tuple = new ArrayList<Member>(leftTuple); 128 } 129 tuple.set(i, member); 130 } 131 } 132 return tuple; 133 } 134 }; 135 } 136 137 /** 138 * Interface similar to the Set interface that allows key values to be 139 * returned. 140 * 141 * <p>Useful if multiple objects can compare equal (using 142 * {@link Object#equals(Object)} and {@link Object#hashCode()}, per the 143 * set contract) and you wish to distinguish them after they have been added 144 * to the set. 145 * 146 * @param <E> element type 147 */ 148 private interface RetrievableSet<E> { 149 /** 150 * Returns the key in this set that compares equal to a given object, 151 * or null if there is no such key. 152 * 153 * @param e Key value 154 * @return Key in the set equal to given key value 155 */ 156 E getKey(E e); 157 158 /** 159 * Analogous to {@link Set#add(Object)}. 160 * 161 * @param e element to be added to this set 162 * @return <tt>true</tt> if this set did not already contain the 163 * specified element 164 */ 165 boolean add(E e); 166 } 167 168 private static class RetrievableHashSet<E> 169 extends HashMap<E, E> 170 implements RetrievableSet<E> 171 { 172 public RetrievableHashSet(int initialCapacity) { 173 super(initialCapacity); 174 } 175 176 public E getKey(E e) { 177 return super.get(e); 178 } 179 180 public boolean add(E e) { 181 return super.put(e, e) == null; 182 } 183 } 184} 185 186// End IntersectFunDef.java