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