    001/*
    002 * Copyright (c) 2007-2016 Concurrent, Inc. All Rights Reserved.
    003 *
    004 * Project and contact information: http://www.cascading.org/
    005 *
    006 * This file is part of the Cascading project.
    007 *
    008 * Licensed under the Apache License, Version 2.0 (the "License");
    009 * you may not use this file except in compliance with the License.
    010 * You may obtain a copy of the License at
    011 *
    012 *     http://www.apache.org/licenses/LICENSE-2.0
    013 *
    014 * Unless required by applicable law or agreed to in writing, software
    015 * distributed under the License is distributed on an "AS IS" BASIS,
    016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017 * See the License for the specific language governing permissions and
    018 * limitations under the License.
    019 */
    020
    021package cascading.pipe.joiner;
    022
    023import java.beans.ConstructorProperties;
    024import java.util.Collections;
    025import java.util.Iterator;
    026import java.util.List;
    027
    028import cascading.tuple.Fields;
    029import cascading.tuple.Tuple;
    030
    031/**
    032 * Class OuterJoin will return an {@link Iterator} that will iterate over a given {@link Joiner} and return tuples that represent
    033 * and outer join of the CoGrouper internal grouped tuple collections.
    034 * <p/>
    035 * Joins perform based on the equality of the join keys. In the case of null values, Java treats two
    036 * null values as equivalent. SQL does not treat null values as equal. To produce SQL like results in a given
    037 * join, a new {@link java.util.Comparator} will need to be used on the joined values to prevent null from
    038 * equaling null. As a convenience, see the {@link cascading.util.NullNotEquivalentComparator} class.
    039 */
    040public class OuterJoin extends BaseJoiner
    041  {
    042  public OuterJoin()
    043    {
    044    }
    045
    046  @ConstructorProperties({"fieldDeclaration"})
    047  public OuterJoin( Fields fieldDeclaration )
    048    {
    049    super( fieldDeclaration );
    050    }
    051
    052  public Iterator<Tuple> getIterator( JoinerClosure closure )
    053    {
    054    return new JoinIterator( closure );
    055    }
    056
    057  public int numJoins()
    058    {
    059    return -1;
    060    }
    061
    062  public static class JoinIterator extends InnerJoin.JoinIterator
    063    {
    064    List[] singletons;
    065
    066    public JoinIterator( JoinerClosure closure )
    067      {
    068      super( closure );
    069      }
    070
    071    @Override
    072    protected void init()
    073      {
    074      singletons = new List[ closure.size() ];
    075
    076      for( int i = 0; i < singletons.length; i++ )
    077        {
    078        if( isOuter( i ) )
    079          singletons[ i ] = Collections.singletonList( Tuple.size( closure.getValueFields()[ i ].size() ) );
    080        }
    081
    082      super.init();
    083      }
    084
    085    protected boolean isOuter( int i )
    086      {
    087      return closure.isEmpty( i );
    088      }
    089
    090    @Override
    091    protected Iterator getIterator( int i )
    092      {
    093      if( singletons[ i ] == null ) // let init() decide
    094        return super.getIterator( i );
    095
    096      return singletons[ i ].iterator();
    097      }
    098    }
    099  }