001    /*
002     * Copyright (c) 2007-2014 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    
021    package cascading.tuple.util;
022    
023    import java.util.Comparator;
024    import java.util.List;
025    
026    import cascading.flow.stream.MemorySpliceGate;
027    import cascading.tuple.Fields;
028    import cascading.tuple.Hasher;
029    import cascading.tuple.Tuple;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    /**
034     *
035     */
036    public class TupleHasher
037      {
038      private static final Logger LOG = LoggerFactory.getLogger( MemorySpliceGate.class );
039    
040      private static Hasher DEFAULT = new ObjectHasher();
041      private Hasher[] hashers;
042    
043      public TupleHasher()
044        {
045        }
046    
047      public TupleHasher( Comparator defaultComparator, Comparator[] comparators )
048        {
049        initialize( defaultComparator, comparators );
050        }
051    
052      public static Comparator[] merge( Fields[] keyFields )
053        {
054        Comparator[] comparators = new Comparator[ keyFields[ 0 ].size() ];
055    
056        for( Fields keyField : keyFields )
057          {
058          if( keyField == null )
059            continue;
060    
061          for( int i = 0; i < keyField.getComparators().length; i++ )
062            {
063            Comparator comparator = keyField.getComparators()[ i ];
064    
065            if( !( comparator instanceof Hasher ) )
066              continue;
067    
068            if( comparators[ i ] != null && !comparators[ i ].equals( comparator ) )
069              LOG.warn( "two unequal Hasher instances for the same key field position found: {}, and: {}", comparators[ i ], comparator );
070    
071            comparators[ i ] = comparator;
072            }
073          }
074    
075        return comparators;
076        }
077    
078      public static boolean isNull( Comparator[] comparators )
079        {
080        int count = 0;
081    
082        for( Comparator comparator : comparators )
083          {
084          if( comparator == null )
085            count++;
086          }
087    
088        if( count == comparators.length )
089          return true;
090    
091        return false;
092        }
093    
094      protected void initialize( Comparator defaultComparator, Comparator[] comparators )
095        {
096        Hasher defaultHasher = DEFAULT;
097    
098        if( defaultComparator instanceof Hasher )
099          defaultHasher = (Hasher) defaultComparator;
100    
101        hashers = new Hasher[ comparators.length ];
102    
103        for( int i = 0; i < comparators.length; i++ )
104          {
105          Comparator comparator = comparators[ i ];
106    
107          if( comparator instanceof Hasher )
108            hashers[ i ] = (Hasher) comparator;
109          else
110            hashers[ i ] = defaultHasher;
111          }
112        }
113    
114      public final int hashCode( Tuple tuple )
115        {
116        int hash = 1;
117    
118        List<Object> elements = Tuple.elements( tuple );
119    
120        for( int i = 0; i < elements.size(); i++ )
121          {
122          Object element = elements.get( i );
123    
124          hash = 31 * hash + ( element != null ? hashers[ i % hashers.length ].hashCode( element ) : 0 );
125          }
126    
127        return hash;
128        }
129    
130      private static class ObjectHasher implements Hasher<Object>
131        {
132        @Override
133        public int hashCode( Object value )
134          {
135          return value.hashCode();
136          }
137        }
138      }