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