1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.hash;
16 
17 import com.google.common.annotations.Beta;
18 import com.google.common.base.Preconditions;
19 
20 import java.io.OutputStream;
21 import java.io.Serializable;
22 import java.nio.charset.Charset;
23 
24 import javax.annotation.Nullable;
25 
26 /**
27  * Funnels for common types. All implementations are serializable.
28  *
29  * @author Dimitris Andreou
30  * @since 11.0
31  */
32 @Beta
33 public final class Funnels {
Funnels()34   private Funnels() {}
35 
36   /**
37    * Returns a funnel that extracts the bytes from a {@code byte} array.
38    */
byteArrayFunnel()39   public static Funnel<byte[]> byteArrayFunnel() {
40     return ByteArrayFunnel.INSTANCE;
41   }
42 
43   private enum ByteArrayFunnel implements Funnel<byte[]> {
44     INSTANCE;
45 
funnel(byte[] from, PrimitiveSink into)46     public void funnel(byte[] from, PrimitiveSink into) {
47       into.putBytes(from);
48     }
49 
toString()50     @Override public String toString() {
51       return "Funnels.byteArrayFunnel()";
52     }
53   }
54 
55   /**
56    * Returns a funnel that extracts the characters from a {@code CharSequence}, a character at a
57    * time, without performing any encoding. If you need to use a specific encoding, use
58    * {@link Funnels#stringFunnel(Charset)} instead.
59    *
60    * @since 15.0 (since 11.0 as {@code Funnels.stringFunnel()}.
61    */
unencodedCharsFunnel()62   public static Funnel<CharSequence> unencodedCharsFunnel() {
63     return UnencodedCharsFunnel.INSTANCE;
64   }
65 
66   /**
67    * Returns a funnel that extracts the characters from a {@code CharSequence}.
68    *
69    * @deprecated Use {@link Funnels#unencodedCharsFunnel} instead. This method is scheduled for
70    *     removal in Guava 16.0.
71    */
72   @Deprecated
stringFunnel()73   public static Funnel<CharSequence> stringFunnel() {
74     return unencodedCharsFunnel();
75   }
76 
77   private enum UnencodedCharsFunnel implements Funnel<CharSequence> {
78     INSTANCE;
79 
funnel(CharSequence from, PrimitiveSink into)80     public void funnel(CharSequence from, PrimitiveSink into) {
81       into.putUnencodedChars(from);
82     }
83 
toString()84     @Override public String toString() {
85       return "Funnels.unencodedCharsFunnel()";
86     }
87   }
88 
89   /**
90    * Returns a funnel that encodes the characters of a {@code CharSequence} with the specified
91    * {@code Charset}.
92    *
93    * @since 15.0
94    */
stringFunnel(Charset charset)95   public static Funnel<CharSequence> stringFunnel(Charset charset) {
96     return new StringCharsetFunnel(charset);
97   }
98 
99   private static class StringCharsetFunnel implements Funnel<CharSequence>, Serializable {
100     private final Charset charset;
101 
StringCharsetFunnel(Charset charset)102     StringCharsetFunnel(Charset charset) {
103       this.charset = Preconditions.checkNotNull(charset);
104     }
105 
funnel(CharSequence from, PrimitiveSink into)106     public void funnel(CharSequence from, PrimitiveSink into) {
107       into.putString(from, charset);
108     }
109 
toString()110     @Override public String toString() {
111       return "Funnels.stringFunnel(" + charset.name() + ")";
112     }
113 
equals(@ullable Object o)114     @Override public boolean equals(@Nullable Object o) {
115       if (o instanceof StringCharsetFunnel) {
116         StringCharsetFunnel funnel = (StringCharsetFunnel) o;
117         return this.charset.equals(funnel.charset);
118       }
119       return false;
120     }
121 
hashCode()122     @Override public int hashCode() {
123       return StringCharsetFunnel.class.hashCode() ^ charset.hashCode();
124     }
125 
writeReplace()126     Object writeReplace() {
127       return new SerializedForm(charset);
128     }
129 
130     private static class SerializedForm implements Serializable {
131       private final String charsetCanonicalName;
132 
SerializedForm(Charset charset)133       SerializedForm(Charset charset) {
134         this.charsetCanonicalName = charset.name();
135       }
136 
readResolve()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    */
integerFunnel()150   public static Funnel<Integer> integerFunnel() {
151     return IntegerFunnel.INSTANCE;
152   }
153 
154   private enum IntegerFunnel implements Funnel<Integer> {
155     INSTANCE;
156 
funnel(Integer from, PrimitiveSink into)157     public void funnel(Integer from, PrimitiveSink into) {
158       into.putInt(from);
159     }
160 
toString()161     @Override public String toString() {
162       return "Funnels.integerFunnel()";
163     }
164   }
165 
166   /**
167    * Returns a funnel that processes an {@code Iterable} by funneling its elements in iteration
168    * order with the specified funnel.  No separators are added between the elements.
169    *
170    * @since 15.0
171    */
sequentialFunnel(Funnel<E> elementFunnel)172   public static <E> Funnel<Iterable<? extends E>> sequentialFunnel(Funnel<E> elementFunnel) {
173     return new SequentialFunnel<E>(elementFunnel);
174   }
175 
176   private static class SequentialFunnel<E> implements Funnel<Iterable<? extends E>>, Serializable {
177     private final Funnel<E> elementFunnel;
178 
SequentialFunnel(Funnel<E> elementFunnel)179     SequentialFunnel(Funnel<E> elementFunnel) {
180       this.elementFunnel = Preconditions.checkNotNull(elementFunnel);
181     }
182 
funnel(Iterable<? extends E> from, PrimitiveSink into)183     public void funnel(Iterable<? extends E> from, PrimitiveSink into) {
184       for (E e : from) {
185         elementFunnel.funnel(e, into);
186       }
187     }
188 
toString()189     @Override public String toString() {
190       return "Funnels.sequentialFunnel(" + elementFunnel + ")";
191     }
192 
equals(@ullable Object o)193     @Override public boolean equals(@Nullable Object o) {
194       if (o instanceof SequentialFunnel) {
195         SequentialFunnel<?> funnel = (SequentialFunnel<?>) o;
196         return elementFunnel.equals(funnel.elementFunnel);
197       }
198       return false;
199     }
200 
hashCode()201     @Override public int hashCode() {
202       return SequentialFunnel.class.hashCode() ^ elementFunnel.hashCode();
203     }
204   }
205 
206   /**
207    * Returns a funnel for longs.
208    *
209    * @since 13.0
210    */
longFunnel()211   public static Funnel<Long> longFunnel() {
212     return LongFunnel.INSTANCE;
213   }
214 
215   private enum LongFunnel implements Funnel<Long> {
216     INSTANCE;
217 
funnel(Long from, PrimitiveSink into)218     public void funnel(Long from, PrimitiveSink into) {
219       into.putLong(from);
220     }
221 
toString()222     @Override public String toString() {
223       return "Funnels.longFunnel()";
224     }
225   }
226 
227   /**
228    * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to
229    * {@link Funnel#funnel funnel} an object to a {@code PrimitiveSink}
230    * if there is already a way to write the contents of the object to an {@code OutputStream}.
231    *
232    * <p>The {@code close} and {@code flush} methods of the returned {@code OutputStream}
233    * do nothing, and no method throws {@code IOException}.
234    *
235    * @since 13.0
236    */
asOutputStream(PrimitiveSink sink)237   public static OutputStream asOutputStream(PrimitiveSink sink) {
238     return new SinkAsStream(sink);
239   }
240 
241   private static class SinkAsStream extends OutputStream {
242     final PrimitiveSink sink;
SinkAsStream(PrimitiveSink sink)243     SinkAsStream(PrimitiveSink sink) {
244       this.sink = Preconditions.checkNotNull(sink);
245     }
246 
write(int b)247     @Override public void write(int b) {
248       sink.putByte((byte) b);
249     }
250 
write(byte[] bytes)251     @Override public void write(byte[] bytes) {
252       sink.putBytes(bytes);
253     }
254 
write(byte[] bytes, int off, int len)255     @Override public void write(byte[] bytes, int off, int len) {
256       sink.putBytes(bytes, off, len);
257     }
258 
toString()259     @Override public String toString() {
260       return "Funnels.asOutputStream(" + sink + ")";
261     }
262   }
263 }
264