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 org.checkerframework.checker.nullness.qual.Nullable;
025
026/**
027 * Funnels for common types. All implementations are serializable.
028 *
029 * @author Dimitris Andreou
030 * @since 11.0
031 */
032@Beta
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(@Nullable 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 void readObject(ObjectInputStream stream) throws InvalidObjectException {
126      throw new InvalidObjectException("Use SerializedForm");
127    }
128
129    private static class SerializedForm implements Serializable {
130      private final String charsetCanonicalName;
131
132      SerializedForm(Charset charset) {
133        this.charsetCanonicalName = charset.name();
134      }
135
136      private Object readResolve() {
137        return stringFunnel(Charset.forName(charsetCanonicalName));
138      }
139
140      private static final long serialVersionUID = 0;
141    }
142  }
143
144  /**
145   * Returns a funnel for integers.
146   *
147   * @since 13.0
148   */
149  public static Funnel<Integer> integerFunnel() {
150    return IntegerFunnel.INSTANCE;
151  }
152
153  private enum IntegerFunnel implements Funnel<Integer> {
154    INSTANCE;
155
156    @Override
157    public void funnel(Integer from, PrimitiveSink into) {
158      into.putInt(from);
159    }
160
161    @Override
162    public String toString() {
163      return "Funnels.integerFunnel()";
164    }
165  }
166
167  /**
168   * Returns a funnel that processes an {@code Iterable} by funneling its elements in iteration
169   * order with the specified funnel. No separators are added between the elements.
170   *
171   * @since 15.0
172   */
173  public static <E extends @Nullable Object> Funnel<Iterable<? extends E>> sequentialFunnel(
174      Funnel<E> elementFunnel) {
175    return new SequentialFunnel<>(elementFunnel);
176  }
177
178  private static class SequentialFunnel<E extends @Nullable Object>
179      implements Funnel<Iterable<? extends E>>, Serializable {
180    private final Funnel<E> elementFunnel;
181
182    SequentialFunnel(Funnel<E> elementFunnel) {
183      this.elementFunnel = Preconditions.checkNotNull(elementFunnel);
184    }
185
186    @Override
187    public void funnel(Iterable<? extends E> from, PrimitiveSink into) {
188      for (E e : from) {
189        elementFunnel.funnel(e, into);
190      }
191    }
192
193    @Override
194    public String toString() {
195      return "Funnels.sequentialFunnel(" + elementFunnel + ")";
196    }
197
198    @Override
199    public boolean equals(@Nullable Object o) {
200      if (o instanceof SequentialFunnel) {
201        SequentialFunnel<?> funnel = (SequentialFunnel<?>) o;
202        return elementFunnel.equals(funnel.elementFunnel);
203      }
204      return false;
205    }
206
207    @Override
208    public int hashCode() {
209      return SequentialFunnel.class.hashCode() ^ elementFunnel.hashCode();
210    }
211  }
212
213  /**
214   * Returns a funnel for longs.
215   *
216   * @since 13.0
217   */
218  public static Funnel<Long> longFunnel() {
219    return LongFunnel.INSTANCE;
220  }
221
222  private enum LongFunnel implements Funnel<Long> {
223    INSTANCE;
224
225    @Override
226    public void funnel(Long from, PrimitiveSink into) {
227      into.putLong(from);
228    }
229
230    @Override
231    public String toString() {
232      return "Funnels.longFunnel()";
233    }
234  }
235
236  /**
237   * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to {@link Funnel#funnel
238   * funnel} an object to a {@code PrimitiveSink} if there is already a way to write the contents of
239   * the object to an {@code OutputStream}.
240   *
241   * <p>The {@code close} and {@code flush} methods of the returned {@code OutputStream} do nothing,
242   * and no method throws {@code IOException}.
243   *
244   * @since 13.0
245   */
246  public static OutputStream asOutputStream(PrimitiveSink sink) {
247    return new SinkAsStream(sink);
248  }
249
250  private static class SinkAsStream extends OutputStream {
251    final PrimitiveSink sink;
252
253    SinkAsStream(PrimitiveSink sink) {
254      this.sink = Preconditions.checkNotNull(sink);
255    }
256
257    @Override
258    public void write(int b) {
259      sink.putByte((byte) b);
260    }
261
262    @Override
263    public void write(byte[] bytes) {
264      sink.putBytes(bytes);
265    }
266
267    @Override
268    public void write(byte[] bytes, int off, int len) {
269      sink.putBytes(bytes, off, len);
270    }
271
272    @Override
273    public String toString() {
274      return "Funnels.asOutputStream(" + sink + ")";
275    }
276  }
277}