1/*
2 * Copyright (C) 2012 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.io;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20import static com.google.common.io.SourceSinkFactory.ByteSinkFactory;
21import static com.google.common.io.SourceSinkFactory.ByteSourceFactory;
22import static com.google.common.io.SourceSinkFactory.CharSinkFactory;
23import static com.google.common.io.SourceSinkFactory.CharSourceFactory;
24
25import com.google.common.base.Charsets;
26
27import java.io.ByteArrayOutputStream;
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.FileOutputStream;
31import java.io.IOException;
32import java.io.InputStream;
33import java.io.InputStreamReader;
34import java.io.OutputStream;
35import java.io.OutputStreamWriter;
36import java.io.Reader;
37import java.io.Writer;
38import java.nio.CharBuffer;
39import java.util.Arrays;
40import java.util.logging.Logger;
41
42import javax.annotation.Nullable;
43
44/**
45 * {@link SourceSinkFactory} implementations.
46 *
47 * @author Colin Decker
48 */
49public class SourceSinkFactories {
50
51  private SourceSinkFactories() {}
52
53  public static CharSourceFactory stringCharSourceFactory() {
54    return new StringSourceFactory();
55  }
56
57  public static ByteSourceFactory byteArraySourceFactory() {
58    return new ByteArraySourceFactory();
59  }
60
61  public static ByteSourceFactory emptyByteSourceFactory() {
62    return new EmptyByteSourceFactory();
63  }
64
65  public static CharSourceFactory emptyCharSourceFactory() {
66    return new EmptyCharSourceFactory();
67  }
68
69  public static ByteSourceFactory fileByteSourceFactory() {
70    return new FileByteSourceFactory();
71  }
72
73  public static ByteSinkFactory fileByteSinkFactory() {
74    return new FileByteSinkFactory(null);
75  }
76
77  public static ByteSinkFactory appendingFileByteSinkFactory() {
78    String initialString = IoTestCase.ASCII + IoTestCase.I18N;
79    return new FileByteSinkFactory(initialString.getBytes(Charsets.UTF_8));
80  }
81
82  public static CharSourceFactory fileCharSourceFactory() {
83    return new FileCharSourceFactory();
84  }
85
86  public static CharSinkFactory fileCharSinkFactory() {
87    return new FileCharSinkFactory(null);
88  }
89
90  public static CharSinkFactory appendingFileCharSinkFactory() {
91    String initialString = IoTestCase.ASCII + IoTestCase.I18N;
92    return new FileCharSinkFactory(initialString);
93  }
94
95  public static ByteSourceFactory urlByteSourceFactory() {
96    return new UrlByteSourceFactory();
97  }
98
99  public static CharSourceFactory urlCharSourceFactory() {
100    return new UrlCharSourceFactory();
101  }
102
103  public static CharSourceFactory asCharSourceFactory(final ByteSourceFactory factory) {
104    checkNotNull(factory);
105    return new CharSourceFactory() {
106      @Override
107      public CharSource createSource(String string) throws IOException {
108        return factory.createSource(string.getBytes(Charsets.UTF_8))
109            .asCharSource(Charsets.UTF_8);
110      }
111
112      @Override
113      public String getExpected(String data) {
114        return new String(factory.getExpected(data.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
115      }
116
117      @Override
118      public void tearDown() throws IOException {
119        factory.tearDown();
120      }
121    };
122  }
123
124  public static CharSinkFactory asCharSinkFactory(final ByteSinkFactory factory) {
125    checkNotNull(factory);
126    return new CharSinkFactory() {
127      @Override
128      public CharSink createSink() throws IOException {
129        return factory.createSink().asCharSink(Charsets.UTF_8);
130      }
131
132      @Override
133      public String getSinkContents() throws IOException {
134        return new String(factory.getSinkContents(), Charsets.UTF_8);
135      }
136
137      @Override
138      public String getExpected(String data) {
139        /*
140         * Get what the byte sink factory would expect for no written bytes, then append expected
141         * string to that.
142         */
143        byte[] factoryExpectedForNothing = factory.getExpected(new byte[0]);
144        return new String(factoryExpectedForNothing, Charsets.UTF_8) + checkNotNull(data);
145      }
146
147      @Override
148      public void tearDown() throws IOException {
149        factory.tearDown();
150      }
151    };
152  }
153
154  public static ByteSourceFactory asSlicedByteSourceFactory(final ByteSourceFactory factory,
155      final int off, final int len) {
156    checkNotNull(factory);
157    return new ByteSourceFactory() {
158      @Override
159      public ByteSource createSource(byte[] bytes) throws IOException {
160        return factory.createSource(bytes).slice(off, len);
161      }
162
163      @Override
164      public byte[] getExpected(byte[] bytes) {
165        byte[] baseExpected = factory.getExpected(bytes);
166        return Arrays.copyOfRange(baseExpected, off, Math.min(baseExpected.length, off + len));
167      }
168
169      @Override
170      public void tearDown() throws IOException {
171        factory.tearDown();
172      }
173    };
174  }
175
176  private static class StringSourceFactory implements CharSourceFactory {
177
178    @Override
179    public CharSource createSource(String data) throws IOException {
180      return CharSource.wrap(data);
181    }
182
183    @Override
184    public String getExpected(String data) {
185      return data;
186    }
187
188    @Override
189    public void tearDown() throws IOException {
190    }
191  }
192
193  private static class ByteArraySourceFactory implements ByteSourceFactory {
194
195    @Override
196    public ByteSource createSource(byte[] bytes) throws IOException {
197      return ByteSource.wrap(bytes);
198    }
199
200    @Override
201    public byte[] getExpected(byte[] bytes) {
202      return bytes;
203    }
204
205    @Override
206    public void tearDown() throws IOException {
207    }
208  }
209
210  private static class EmptyCharSourceFactory implements CharSourceFactory {
211
212    @Override
213    public CharSource createSource(String data) throws IOException {
214      return CharSource.empty();
215    }
216
217    @Override
218    public String getExpected(String data) {
219      return "";
220    }
221
222    @Override
223    public void tearDown() throws IOException {
224    }
225  }
226
227  private static class EmptyByteSourceFactory implements ByteSourceFactory {
228
229    @Override
230    public ByteSource createSource(byte[] bytes) throws IOException {
231      return ByteSource.empty();
232    }
233
234    @Override
235    public byte[] getExpected(byte[] bytes) {
236      return new byte[0];
237    }
238
239    @Override
240    public void tearDown() throws IOException {
241    }
242  }
243
244  private abstract static class FileFactory {
245
246    private static final Logger logger = Logger.getLogger(FileFactory.class.getName());
247
248    private final ThreadLocal<File> fileThreadLocal = new ThreadLocal<File>();
249
250    protected File createFile() throws IOException {
251      File file = File.createTempFile("SinkSourceFile", "txt");
252      fileThreadLocal.set(file);
253      return file;
254    }
255
256    protected File getFile() {
257      return fileThreadLocal.get();
258    }
259
260    public final void tearDown() throws IOException {
261      if (!fileThreadLocal.get().delete()) {
262        logger.warning("Unable to delete file: " + fileThreadLocal.get());
263      }
264      fileThreadLocal.remove();
265    }
266  }
267
268  private static class FileByteSourceFactory extends FileFactory implements ByteSourceFactory {
269
270    @Override
271    public ByteSource createSource(byte[] bytes) throws IOException {
272      checkNotNull(bytes);
273      File file = createFile();
274      OutputStream out = new FileOutputStream(file);
275      try {
276        out.write(bytes);
277      } finally {
278        out.close();
279      }
280      return Files.asByteSource(file);
281    }
282
283    @Override
284    public byte[] getExpected(byte[] bytes) {
285      return checkNotNull(bytes);
286    }
287  }
288
289  private static class FileByteSinkFactory extends FileFactory implements ByteSinkFactory {
290
291    private final byte[] initialBytes;
292
293    private FileByteSinkFactory(@Nullable byte[] initialBytes) {
294      this.initialBytes = initialBytes;
295    }
296
297    @Override
298    public ByteSink createSink() throws IOException {
299      File file = createFile();
300      if (initialBytes != null) {
301        FileOutputStream out = new FileOutputStream(file);
302        try {
303          out.write(initialBytes);
304        } finally {
305          out.close();
306        }
307        return Files.asByteSink(file, FileWriteMode.APPEND);
308      }
309      return Files.asByteSink(file);
310    }
311
312    @Override
313    public byte[] getExpected(byte[] bytes) {
314      if (initialBytes == null) {
315        return checkNotNull(bytes);
316      } else {
317        byte[] result = new byte[initialBytes.length + bytes.length];
318        System.arraycopy(initialBytes, 0, result, 0, initialBytes.length);
319        System.arraycopy(bytes, 0, result, initialBytes.length, bytes.length);
320        return result;
321      }
322    }
323
324    @Override
325    public byte[] getSinkContents() throws IOException {
326      File file = getFile();
327      InputStream in = new FileInputStream(file);
328      ByteArrayOutputStream out = new ByteArrayOutputStream();
329      byte[] buffer = new byte[100];
330      int read;
331      while ((read = in.read(buffer)) != -1) {
332        out.write(buffer, 0, read);
333      }
334      return out.toByteArray();
335    }
336  }
337
338  private static class FileCharSourceFactory extends FileFactory implements CharSourceFactory {
339
340    @Override
341    public CharSource createSource(String string) throws IOException {
342      checkNotNull(string);
343      File file = createFile();
344      Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8);
345      try {
346        writer.write(string);
347      } finally {
348        writer.close();
349      }
350      return Files.asCharSource(file, Charsets.UTF_8);
351    }
352
353    @Override
354    public String getExpected(String string) {
355      return checkNotNull(string);
356    }
357  }
358
359  private static class FileCharSinkFactory extends FileFactory implements CharSinkFactory {
360
361    private final String initialString;
362
363    private FileCharSinkFactory(@Nullable String initialString) {
364      this.initialString = initialString;
365    }
366
367    @Override
368    public CharSink createSink() throws IOException {
369      File file = createFile();
370      if (initialString != null) {
371        Writer writer = new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8);
372        try {
373          writer.write(initialString);
374        } finally {
375          writer.close();
376        }
377        return Files.asCharSink(file, Charsets.UTF_8, FileWriteMode.APPEND);
378      }
379      return Files.asCharSink(file, Charsets.UTF_8);
380    }
381
382    @Override
383    public String getExpected(String string) {
384      checkNotNull(string);
385      return initialString == null
386          ? string
387          : initialString + string;
388    }
389
390    @Override
391    public String getSinkContents() throws IOException {
392      File file = getFile();
393      Reader reader = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8);
394      StringBuilder builder = new StringBuilder();
395      CharBuffer buffer = CharBuffer.allocate(100);
396      while (reader.read(buffer) != -1) {
397        buffer.flip();
398        builder.append(buffer);
399        buffer.clear();
400      }
401      return builder.toString();
402    }
403  }
404
405  private static class UrlByteSourceFactory extends FileByteSourceFactory {
406
407    @Override
408    public ByteSource createSource(byte[] bytes) throws IOException {
409      super.createSource(bytes);
410      return Resources.asByteSource(getFile().toURI().toURL());
411    }
412  }
413
414  private static class UrlCharSourceFactory extends FileCharSourceFactory {
415
416    @Override
417    public CharSource createSource(String string) throws IOException {
418      super.createSource(string); // just ignore returned CharSource
419      return Resources.asCharSource(getFile().toURI().toURL(), Charsets.UTF_8);
420    }
421  }
422}
423