in src/main/java/net/hydromatic/linq4j/EnumerableDefaults.java [873:961]
private static <TSource, TInner, TKey, TResult> Enumerable<TResult> join_(
final Enumerable<TSource> outer, final Enumerable<TInner> inner,
final Function1<TSource, TKey> outerKeySelector,
final Function1<TInner, TKey> innerKeySelector,
final Function2<TSource, TInner, TResult> resultSelector,
final EqualityComparer<TKey> comparer, final boolean generateNullsOnLeft,
final boolean generateNullsOnRight) {
return new AbstractEnumerable<TResult>() {
public Enumerator<TResult> enumerator() {
final Lookup<TKey, TInner> innerLookup =
comparer == null
? inner.toLookup(innerKeySelector)
: inner.toLookup(innerKeySelector, comparer);
return new Enumerator<TResult>() {
Enumerator<TSource> outers = outer.enumerator();
Enumerator<TInner> inners = Linq4j.emptyEnumerator();
Set<TKey> unmatchedKeys =
generateNullsOnLeft
? new HashSet<TKey>(innerLookup.keySet())
: null;
public TResult current() {
return resultSelector.apply(outers.current(), inners.current());
}
public boolean moveNext() {
for (;;) {
if (inners.moveNext()) {
return true;
}
if (!outers.moveNext()) {
if (unmatchedKeys != null) {
// We've seen everything else. If we are doing a RIGHT or FULL
// join (leftNull = true) there are any keys which right but
// not the left.
List<TInner> list = new ArrayList<TInner>();
for (TKey key : unmatchedKeys) {
for (TInner tInner : innerLookup.get(key)) {
list.add(tInner);
}
}
list = new ArrayList<TInner>();
for (TKey key : unmatchedKeys) {
for (TInner inner : innerLookup.get(key)) {
list.add(inner);
}
}
inners = Linq4j.enumerator(list);
outers = Linq4j.singletonNullEnumerator();
unmatchedKeys = null; // don't do the 'leftovers' again
continue;
}
return false;
}
final TSource outer = outers.current();
if (outer == null) {
continue;
}
final TKey outerKey = outerKeySelector.apply(outer);
if (unmatchedKeys != null) {
unmatchedKeys.remove(outerKey);
}
final Enumerable<TInner> innerEnumerable =
innerLookup.get(outerKey);
if (innerEnumerable == null
|| !innerEnumerable.any()) {
if (generateNullsOnRight) {
inners = Linq4j.singletonNullEnumerator();
} else {
inners = Linq4j.emptyEnumerator();
}
} else {
inners = innerEnumerable.enumerator();
}
}
}
public void reset() {
outers.reset();
}
public void close() {
outers.close();
}
};
}
};
}