    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.io.Serializable;
    024    import java.util.Comparator;
    025    import java.util.List;
    026
    027    import cascading.flow.stream.MemorySpliceGate;
    028    import cascading.tuple.Fields;
    029    import cascading.tuple.Hasher;
    030    import cascading.tuple.Tuple;
    031    import org.slf4j.Logger;
    032    import org.slf4j.LoggerFactory;
    033
    034    /**
    035     *
    036     */
    037    public class TupleHasher implements Serializable
    038      {
    039      private static final Logger LOG = LoggerFactory.getLogger( MemorySpliceGate.class );
    040
    041      private static Hasher DEFAULT = new ObjectHasher();
    042      private Hasher[] hashers;
    043
    044      public TupleHasher()
    045        {
    046        }
    047
    048      public TupleHasher( Comparator defaultComparator, Comparator[] comparators )
    049        {
    050        initialize( defaultComparator, comparators );
    051        }
    052
    053      public static Comparator[] merge( Fields[] keyFields )
    054        {
    055        Comparator[] comparators = new Comparator[ keyFields[ 0 ].size() ];
    056
    057        for( Fields keyField : keyFields )
    058          {
    059          if( keyField == null )
    060            continue;
    061
    062          for( int i = 0; i < keyField.getComparators().length; i++ )
    063            {
    064            Comparator comparator = keyField.getComparators()[ i ];
    065
    066            if( !( comparator instanceof Hasher ) )
    067              continue;
    068
    069            if( comparators[ i ] != null && !comparators[ i ].equals( comparator ) )
    070              LOG.warn( "two unequal Hasher instances for the same key field position found: {}, and: {}", comparators[ i ], comparator );
    071
    072            comparators[ i ] = comparator;
    073            }
    074          }
    075
    076        return comparators;
    077        }
    078
    079      public static boolean isNull( Comparator[] comparators )
    080        {
    081        int count = 0;
    082
    083        for( Comparator comparator : comparators )
    084          {
    085          if( comparator == null )
    086            count++;
    087          }
    088
    089        if( count == comparators.length )
    090          return true;
    091
    092        return false;
    093        }
    094
    095      protected void initialize( Comparator defaultComparator, Comparator[] comparators )
    096        {
    097        Hasher defaultHasher = DEFAULT;
    098
    099        if( defaultComparator instanceof Hasher )
    100          defaultHasher = (Hasher) defaultComparator;
    101
    102        hashers = new Hasher[ comparators.length ];
    103
    104        for( int i = 0; i < comparators.length; i++ )
    105          {
    106          Comparator comparator = comparators[ i ];
    107
    108          if( comparator instanceof Hasher )
    109            hashers[ i ] = (Hasher) comparator;
    110          else
    111            hashers[ i ] = defaultHasher;
    112          }
    113        }
    114
    115      public final int hashCode( Tuple tuple )
    116        {
    117        int hash = 1;
    118
    119        List<Object> elements = Tuple.elements( tuple );
    120
    121        for( int i = 0; i < elements.size(); i++ )
    122          {
    123          Object element = elements.get( i );
    124
    125          hash = 31 * hash + ( element != null ? hashers[ i % hashers.length ].hashCode( element ) : 0 );
    126          }
    127
    128        return hash;
    129        }
    130
    131      private static class ObjectHasher implements Hasher<Object>
    132        {
    133        @Override
    134        public int hashCode( Object value )
    135          {
    136          return value.hashCode();
    137          }
    138        }
    139
    140      static class WrappedTuple extends Tuple
    141        {
    142        private final TupleHasher tupleHasher;
    143
    144        public WrappedTuple( TupleHasher tupleHasher, Tuple input )
    145          {
    146          super( Tuple.elements( input ) );
    147          this.tupleHasher = tupleHasher;
    148          }
    149
    150        @Override
    151        public int hashCode()
    152          {
    153          return tupleHasher.hashCode( this );
    154          }
    155        }
    156
    157      /**
    158       * Wraps the given Tuple in a subtype, that uses the provided Hasher for hashCode calculations. If the given Hahser
    159       * is <code>null</code> the input Tuple will be returned.
    160       *
    161       * @param tupleHasher A TupleHasher instance.
    162       * @param input       A Tuple instance.
    163       * @return A tuple using the provided TupleHasher for hashCode calculations if the TupleHasher is not null.
    164       */
    165      public static Tuple wrapTuple( TupleHasher tupleHasher, Tuple input )
    166        {
    167        if( tupleHasher == null )
    168          return input;
    169
    170        return new WrappedTuple( tupleHasher, input );
    171        }
    172      }