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 }