001/* 002 * Copyright (C) 2011 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.hash; 016 017import com.google.common.annotations.Beta; 018import com.google.common.base.Preconditions; 019import java.io.InvalidObjectException; 020import java.io.ObjectInputStream; 021import java.io.OutputStream; 022import java.io.Serializable; 023import java.nio.charset.Charset; 024import javax.annotation.CheckForNull; 025import org.checkerframework.checker.nullness.qual.Nullable; 026 027/** 028 * Funnels for common types. All implementations are serializable. 029 * 030 * @author Dimitris Andreou 031 * @since 11.0 032 */ 033@Beta 034public final class Funnels { 035 private Funnels() {} 036 037 /** Returns a funnel that extracts the bytes from a {@code byte} array. */ 038 public static Funnel<byte[]> byteArrayFunnel() { 039 return ByteArrayFunnel.INSTANCE; 040 } 041 042 private enum ByteArrayFunnel implements Funnel<byte[]> { 043 INSTANCE; 044 045 @Override 046 public void funnel(byte[] from, PrimitiveSink into) { 047 into.putBytes(from); 048 } 049 050 @Override 051 public String toString() { 052 return "Funnels.byteArrayFunnel()"; 053 } 054 } 055 056 /** 057 * Returns a funnel that extracts the characters from a {@code CharSequence}, a character at a 058 * time, without performing any encoding. If you need to use a specific encoding, use {@link 059 * Funnels#stringFunnel(Charset)} instead. 060 * 061 * @since 15.0 (since 11.0 as {@code Funnels.stringFunnel()}. 062 */ 063 public static Funnel<CharSequence> unencodedCharsFunnel() { 064 return UnencodedCharsFunnel.INSTANCE; 065 } 066 067 private enum UnencodedCharsFunnel implements Funnel<CharSequence> { 068 INSTANCE; 069 070 @Override 071 public void funnel(CharSequence from, PrimitiveSink into) { 072 into.putUnencodedChars(from); 073 } 074 075 @Override 076 public String toString() { 077 return "Funnels.unencodedCharsFunnel()"; 078 } 079 } 080 081 /** 082 * Returns a funnel that encodes the characters of a {@code CharSequence} with the specified 083 * {@code Charset}. 084 * 085 * @since 15.0 086 */ 087 public static Funnel<CharSequence> stringFunnel(Charset charset) { 088 return new StringCharsetFunnel(charset); 089 } 090 091 private static class StringCharsetFunnel implements Funnel<CharSequence>, Serializable { 092 private final Charset charset; 093 094 StringCharsetFunnel(Charset charset) { 095 this.charset = Preconditions.checkNotNull(charset); 096 } 097 098 @Override 099 public void funnel(CharSequence from, PrimitiveSink into) { 100 into.putString(from, charset); 101 } 102 103 @Override 104 public String toString() { 105 return "Funnels.stringFunnel(" + charset.name() + ")"; 106 } 107 108 @Override 109 public boolean equals(@CheckForNull Object o) { 110 if (o instanceof StringCharsetFunnel) { 111 StringCharsetFunnel funnel = (StringCharsetFunnel) o; 112 return this.charset.equals(funnel.charset); 113 } 114 return false; 115 } 116 117 @Override 118 public int hashCode() { 119 return StringCharsetFunnel.class.hashCode() ^ charset.hashCode(); 120 } 121 122 Object writeReplace() { 123 return new SerializedForm(charset); 124 } 125 126 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 127 throw new InvalidObjectException("Use SerializedForm"); 128 } 129 130 private static class SerializedForm implements Serializable { 131 private final String charsetCanonicalName; 132 133 SerializedForm(Charset charset) { 134 this.charsetCanonicalName = charset.name(); 135 } 136 137 private Object readResolve() { 138 return stringFunnel(Charset.forName(charsetCanonicalName)); 139 } 140 141 private static final long serialVersionUID = 0; 142 } 143 } 144 145 /** 146 * Returns a funnel for integers. 147 * 148 * @since 13.0 149 */ 150 public static Funnel<Integer> integerFunnel() { 151 return IntegerFunnel.INSTANCE; 152 } 153 154 private enum IntegerFunnel implements Funnel<Integer> { 155 INSTANCE; 156 157 @Override 158 public void funnel(Integer from, PrimitiveSink into) { 159 into.putInt(from); 160 } 161 162 @Override 163 public String toString() { 164 return "Funnels.integerFunnel()"; 165 } 166 } 167 168 /** 169 * Returns a funnel that processes an {@code Iterable} by funneling its elements in iteration 170 * order with the specified funnel. No separators are added between the elements. 171 * 172 * @since 15.0 173 */ 174 public static <E extends @Nullable Object> Funnel<Iterable<? extends E>> sequentialFunnel( 175 Funnel<E> elementFunnel) { 176 return new SequentialFunnel<>(elementFunnel); 177 } 178 179 private static class SequentialFunnel<E extends @Nullable Object> 180 implements Funnel<Iterable<? extends E>>, Serializable { 181 private final Funnel<E> elementFunnel; 182 183 SequentialFunnel(Funnel<E> elementFunnel) { 184 this.elementFunnel = Preconditions.checkNotNull(elementFunnel); 185 } 186 187 @Override 188 public void funnel(Iterable<? extends E> from, PrimitiveSink into) { 189 for (E e : from) { 190 elementFunnel.funnel(e, into); 191 } 192 } 193 194 @Override 195 public String toString() { 196 return "Funnels.sequentialFunnel(" + elementFunnel + ")"; 197 } 198 199 @Override 200 public boolean equals(@CheckForNull Object o) { 201 if (o instanceof SequentialFunnel) { 202 SequentialFunnel<?> funnel = (SequentialFunnel<?>) o; 203 return elementFunnel.equals(funnel.elementFunnel); 204 } 205 return false; 206 } 207 208 @Override 209 public int hashCode() { 210 return SequentialFunnel.class.hashCode() ^ elementFunnel.hashCode(); 211 } 212 } 213 214 /** 215 * Returns a funnel for longs. 216 * 217 * @since 13.0 218 */ 219 public static Funnel<Long> longFunnel() { 220 return LongFunnel.INSTANCE; 221 } 222 223 private enum LongFunnel implements Funnel<Long> { 224 INSTANCE; 225 226 @Override 227 public void funnel(Long from, PrimitiveSink into) { 228 into.putLong(from); 229 } 230 231 @Override 232 public String toString() { 233 return "Funnels.longFunnel()"; 234 } 235 } 236 237 /** 238 * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to {@link Funnel#funnel 239 * funnel} an object to a {@code PrimitiveSink} if there is already a way to write the contents of 240 * the object to an {@code OutputStream}. 241 * 242 * <p>The {@code close} and {@code flush} methods of the returned {@code OutputStream} do nothing, 243 * and no method throws {@code IOException}. 244 * 245 * @since 13.0 246 */ 247 public static OutputStream asOutputStream(PrimitiveSink sink) { 248 return new SinkAsStream(sink); 249 } 250 251 private static class SinkAsStream extends OutputStream { 252 final PrimitiveSink sink; 253 254 SinkAsStream(PrimitiveSink sink) { 255 this.sink = Preconditions.checkNotNull(sink); 256 } 257 258 @Override 259 public void write(int b) { 260 sink.putByte((byte) b); 261 } 262 263 @Override 264 public void write(byte[] bytes) { 265 sink.putBytes(bytes); 266 } 267 268 @Override 269 public void write(byte[] bytes, int off, int len) { 270 sink.putBytes(bytes, off, len); 271 } 272 273 @Override 274 public String toString() { 275 return "Funnels.asOutputStream(" + sink + ")"; 276 } 277 } 278}