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