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