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      }