• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static java.nio.charset.StandardCharsets.UTF_8;
25 
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.io.OutputStream;
34 import java.io.OutputStreamWriter;
35 import java.io.Reader;
36 import java.io.Writer;
37 import java.nio.CharBuffer;
38 import java.nio.file.Path;
39 import java.nio.file.StandardOpenOption;
40 import java.util.Arrays;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import org.checkerframework.checker.nullness.qual.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     return new FileByteSinkFactory(initialString.getBytes(UTF_8));
81   }
82 
fileCharSourceFactory()83   public static CharSourceFactory fileCharSourceFactory() {
84     return new FileCharSourceFactory();
85   }
86 
fileCharSinkFactory()87   public static CharSinkFactory fileCharSinkFactory() {
88     return new FileCharSinkFactory(null);
89   }
90 
appendingFileCharSinkFactory()91   public static CharSinkFactory appendingFileCharSinkFactory() {
92     String initialString = IoTestCase.ASCII + IoTestCase.I18N;
93     return new FileCharSinkFactory(initialString);
94   }
95 
urlByteSourceFactory()96   public static ByteSourceFactory urlByteSourceFactory() {
97     return new UrlByteSourceFactory();
98   }
99 
urlCharSourceFactory()100   public static CharSourceFactory urlCharSourceFactory() {
101     return new UrlCharSourceFactory();
102   }
103 
104   @AndroidIncompatible
pathByteSourceFactory()105   public static ByteSourceFactory pathByteSourceFactory() {
106     return new PathByteSourceFactory();
107   }
108 
109   @AndroidIncompatible
pathByteSinkFactory()110   public static ByteSinkFactory pathByteSinkFactory() {
111     return new PathByteSinkFactory(null);
112   }
113 
114   @AndroidIncompatible
appendingPathByteSinkFactory()115   public static ByteSinkFactory appendingPathByteSinkFactory() {
116     String initialString = IoTestCase.ASCII + IoTestCase.I18N;
117     return new PathByteSinkFactory(initialString.getBytes(UTF_8));
118   }
119 
120   @AndroidIncompatible
pathCharSourceFactory()121   public static CharSourceFactory pathCharSourceFactory() {
122     return new PathCharSourceFactory();
123   }
124 
125   @AndroidIncompatible
pathCharSinkFactory()126   public static CharSinkFactory pathCharSinkFactory() {
127     return new PathCharSinkFactory(null);
128   }
129 
130   @AndroidIncompatible
appendingPathCharSinkFactory()131   public static CharSinkFactory appendingPathCharSinkFactory() {
132     String initialString = IoTestCase.ASCII + IoTestCase.I18N;
133     return new PathCharSinkFactory(initialString);
134   }
135 
asByteSourceFactory(final CharSourceFactory factory)136   public static ByteSourceFactory asByteSourceFactory(final CharSourceFactory factory) {
137     checkNotNull(factory);
138     return new ByteSourceFactory() {
139       @Override
140       public ByteSource createSource(byte[] data) throws IOException {
141         return factory.createSource(new String(data, UTF_8)).asByteSource(UTF_8);
142       }
143 
144       @Override
145       public byte[] getExpected(byte[] data) {
146         return factory.getExpected(new String(data, UTF_8)).getBytes(UTF_8);
147       }
148 
149       @Override
150       public void tearDown() throws IOException {
151         factory.tearDown();
152       }
153     };
154   }
155 
156   public static CharSourceFactory asCharSourceFactory(final ByteSourceFactory factory) {
157     checkNotNull(factory);
158     return new CharSourceFactory() {
159       @Override
160       public CharSource createSource(String string) throws IOException {
161         return factory.createSource(string.getBytes(UTF_8)).asCharSource(UTF_8);
162       }
163 
164       @Override
165       public String getExpected(String data) {
166         return new String(factory.getExpected(data.getBytes(UTF_8)), UTF_8);
167       }
168 
169       @Override
170       public void tearDown() throws IOException {
171         factory.tearDown();
172       }
173     };
174   }
175 
176   public static CharSinkFactory asCharSinkFactory(final ByteSinkFactory factory) {
177     checkNotNull(factory);
178     return new CharSinkFactory() {
179       @Override
180       public CharSink createSink() throws IOException {
181         return factory.createSink().asCharSink(UTF_8);
182       }
183 
184       @Override
185       public String getSinkContents() throws IOException {
186         return new String(factory.getSinkContents(), UTF_8);
187       }
188 
189       @Override
190       public String getExpected(String data) {
191         /*
192          * Get what the byte sink factory would expect for no written bytes, then append expected
193          * string to that.
194          */
195         byte[] factoryExpectedForNothing = factory.getExpected(new byte[0]);
196         return new String(factoryExpectedForNothing, UTF_8) + checkNotNull(data);
197       }
198 
199       @Override
200       public void tearDown() throws IOException {
201         factory.tearDown();
202       }
203     };
204   }
205 
206   public static ByteSourceFactory asSlicedByteSourceFactory(
207       final ByteSourceFactory factory, final long off, final long len) {
208     checkNotNull(factory);
209     return new ByteSourceFactory() {
210       @Override
211       public ByteSource createSource(byte[] bytes) throws IOException {
212         return factory.createSource(bytes).slice(off, len);
213       }
214 
215       @Override
216       public byte[] getExpected(byte[] bytes) {
217         byte[] baseExpected = factory.getExpected(bytes);
218         int startOffset = (int) Math.min(off, baseExpected.length);
219         int actualLen = (int) Math.min(len, baseExpected.length - startOffset);
220         return Arrays.copyOfRange(baseExpected, startOffset, startOffset + actualLen);
221       }
222 
223       @Override
224       public void tearDown() throws IOException {
225         factory.tearDown();
226       }
227     };
228   }
229 
230   private static class StringSourceFactory implements CharSourceFactory {
231 
232     @Override
233     public CharSource createSource(String data) throws IOException {
234       return CharSource.wrap(data);
235     }
236 
237     @Override
238     public String getExpected(String data) {
239       return data;
240     }
241 
242     @Override
243     public void tearDown() throws IOException {}
244   }
245 
246   private static class ByteArraySourceFactory implements ByteSourceFactory {
247 
248     @Override
249     public ByteSource createSource(byte[] bytes) throws IOException {
250       return ByteSource.wrap(bytes);
251     }
252 
253     @Override
254     public byte[] getExpected(byte[] bytes) {
255       return bytes;
256     }
257 
258     @Override
259     public void tearDown() throws IOException {}
260   }
261 
262   private static class EmptyCharSourceFactory implements CharSourceFactory {
263 
264     @Override
265     public CharSource createSource(String data) throws IOException {
266       return CharSource.empty();
267     }
268 
269     @Override
270     public String getExpected(String data) {
271       return "";
272     }
273 
274     @Override
275     public void tearDown() throws IOException {}
276   }
277 
278   private static class EmptyByteSourceFactory implements ByteSourceFactory {
279 
280     @Override
281     public ByteSource createSource(byte[] bytes) throws IOException {
282       return ByteSource.empty();
283     }
284 
285     @Override
286     public byte[] getExpected(byte[] bytes) {
287       return new byte[0];
288     }
289 
290     @Override
291     public void tearDown() throws IOException {}
292   }
293 
294   private abstract static class FileFactory {
295 
296     private static final Logger logger = Logger.getLogger(FileFactory.class.getName());
297 
298     private final ThreadLocal<File> fileThreadLocal = new ThreadLocal<>();
299 
300     protected File createFile() throws IOException {
301       File file = File.createTempFile("SinkSourceFile", "txt");
302       fileThreadLocal.set(file);
303       return file;
304     }
305 
306     protected File getFile() {
307       return fileThreadLocal.get();
308     }
309 
310     public final void tearDown() throws IOException {
311       if (!fileThreadLocal.get().delete()) {
312         logger.warning("Unable to delete file: " + fileThreadLocal.get());
313       }
314       fileThreadLocal.remove();
315     }
316   }
317 
318   private static class FileByteSourceFactory extends FileFactory implements ByteSourceFactory {
319 
320     @Override
321     public ByteSource createSource(byte[] bytes) throws IOException {
322       checkNotNull(bytes);
323       File file = createFile();
324       OutputStream out = new FileOutputStream(file);
325       try {
326         out.write(bytes);
327       } finally {
328         out.close();
329       }
330       return Files.asByteSource(file);
331     }
332 
333     @Override
334     public byte[] getExpected(byte[] bytes) {
335       return checkNotNull(bytes);
336     }
337   }
338 
339   private static class FileByteSinkFactory extends FileFactory implements ByteSinkFactory {
340 
341     private final byte[] initialBytes;
342 
343     private FileByteSinkFactory(byte @Nullable [] initialBytes) {
344       this.initialBytes = initialBytes;
345     }
346 
347     @Override
348     public ByteSink createSink() throws IOException {
349       File file = createFile();
350       if (initialBytes != null) {
351         FileOutputStream out = new FileOutputStream(file);
352         try {
353           out.write(initialBytes);
354         } finally {
355           out.close();
356         }
357         return Files.asByteSink(file, FileWriteMode.APPEND);
358       }
359       return Files.asByteSink(file);
360     }
361 
362     @Override
363     public byte[] getExpected(byte[] bytes) {
364       if (initialBytes == null) {
365         return checkNotNull(bytes);
366       } else {
367         byte[] result = new byte[initialBytes.length + bytes.length];
368         System.arraycopy(initialBytes, 0, result, 0, initialBytes.length);
369         System.arraycopy(bytes, 0, result, initialBytes.length, bytes.length);
370         return result;
371       }
372     }
373 
374     @Override
375     public byte[] getSinkContents() throws IOException {
376       File file = getFile();
377       InputStream in = new FileInputStream(file);
378       ByteArrayOutputStream out = new ByteArrayOutputStream();
379       byte[] buffer = new byte[100];
380       int read;
381       while ((read = in.read(buffer)) != -1) {
382         out.write(buffer, 0, read);
383       }
384       return out.toByteArray();
385     }
386   }
387 
388   private static class FileCharSourceFactory extends FileFactory implements CharSourceFactory {
389 
390     @Override
391     public CharSource createSource(String string) throws IOException {
392       checkNotNull(string);
393       File file = createFile();
394       Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF_8);
395       try {
396         writer.write(string);
397       } finally {
398         writer.close();
399       }
400       return Files.asCharSource(file, UTF_8);
401     }
402 
403     @Override
404     public String getExpected(String string) {
405       return checkNotNull(string);
406     }
407   }
408 
409   private static class FileCharSinkFactory extends FileFactory implements CharSinkFactory {
410 
411     private final String initialString;
412 
413     private FileCharSinkFactory(@Nullable String initialString) {
414       this.initialString = initialString;
415     }
416 
417     @Override
418     public CharSink createSink() throws IOException {
419       File file = createFile();
420       if (initialString != null) {
421         Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF_8);
422         try {
423           writer.write(initialString);
424         } finally {
425           writer.close();
426         }
427         return Files.asCharSink(file, UTF_8, FileWriteMode.APPEND);
428       }
429       return Files.asCharSink(file, UTF_8);
430     }
431 
432     @Override
433     public String getExpected(String string) {
434       checkNotNull(string);
435       return initialString == null ? string : initialString + string;
436     }
437 
438     @Override
439     public String getSinkContents() throws IOException {
440       File file = getFile();
441       Reader reader = new InputStreamReader(new FileInputStream(file), UTF_8);
442       StringBuilder builder = new StringBuilder();
443       CharBuffer buffer = CharBuffer.allocate(100);
444       while (reader.read(buffer) != -1) {
445         Java8Compatibility.flip(buffer);
446         builder.append(buffer);
447         Java8Compatibility.clear(buffer);
448       }
449       return builder.toString();
450     }
451   }
452 
453   private static class UrlByteSourceFactory extends FileByteSourceFactory {
454 
455     @SuppressWarnings("CheckReturnValue") // only using super.createSource to create a file
456     @Override
457     public ByteSource createSource(byte[] bytes) throws IOException {
458       super.createSource(bytes);
459       return Resources.asByteSource(getFile().toURI().toURL());
460     }
461   }
462 
463   private static class UrlCharSourceFactory extends FileCharSourceFactory {
464 
465     @SuppressWarnings("CheckReturnValue") // only using super.createSource to create a file
466     @Override
467     public CharSource createSource(String string) throws IOException {
468       super.createSource(string); // just ignore returned CharSource
469       return Resources.asCharSource(getFile().toURI().toURL(), UTF_8);
470     }
471   }
472 
473   @AndroidIncompatible
474   private abstract static class Jdk7FileFactory {
475 
476     private static final Logger logger = Logger.getLogger(Jdk7FileFactory.class.getName());
477 
478     private final ThreadLocal<Path> fileThreadLocal = new ThreadLocal<>();
479 
480     protected Path createFile() throws IOException {
481       Path file = java.nio.file.Files.createTempFile("SinkSourceFile", "txt");
482       fileThreadLocal.set(file);
483       return file;
484     }
485 
486     protected Path getPath() {
487       return fileThreadLocal.get();
488     }
489 
490     public final void tearDown() throws IOException {
491       try {
492         java.nio.file.Files.delete(fileThreadLocal.get());
493       } catch (IOException e) {
494         logger.log(Level.WARNING, "Unable to delete file: " + fileThreadLocal.get(), e);
495       }
496       fileThreadLocal.remove();
497     }
498   }
499 
500   @AndroidIncompatible
501   private static class PathByteSourceFactory extends Jdk7FileFactory implements ByteSourceFactory {
502 
503     @Override
504     public ByteSource createSource(byte[] bytes) throws IOException {
505       checkNotNull(bytes);
506       Path file = createFile();
507 
508       java.nio.file.Files.write(file, bytes);
509       return MoreFiles.asByteSource(file);
510     }
511 
512     @Override
513     public byte[] getExpected(byte[] bytes) {
514       return checkNotNull(bytes);
515     }
516   }
517 
518   @AndroidIncompatible
519   private static class PathByteSinkFactory extends Jdk7FileFactory implements ByteSinkFactory {
520 
521     private final byte[] initialBytes;
522 
523     private PathByteSinkFactory(byte @Nullable [] initialBytes) {
524       this.initialBytes = initialBytes;
525     }
526 
527     @Override
528     public ByteSink createSink() throws IOException {
529       Path file = createFile();
530       if (initialBytes != null) {
531         java.nio.file.Files.write(file, initialBytes);
532         return MoreFiles.asByteSink(file, StandardOpenOption.APPEND);
533       }
534       return MoreFiles.asByteSink(file);
535     }
536 
537     @Override
538     public byte[] getExpected(byte[] bytes) {
539       if (initialBytes == null) {
540         return checkNotNull(bytes);
541       } else {
542         byte[] result = new byte[initialBytes.length + bytes.length];
543         System.arraycopy(initialBytes, 0, result, 0, initialBytes.length);
544         System.arraycopy(bytes, 0, result, initialBytes.length, bytes.length);
545         return result;
546       }
547     }
548 
549     @Override
550     public byte[] getSinkContents() throws IOException {
551       Path file = getPath();
552       return java.nio.file.Files.readAllBytes(file);
553     }
554   }
555 
556   @AndroidIncompatible
557   private static class PathCharSourceFactory extends Jdk7FileFactory implements CharSourceFactory {
558 
559     @Override
560     public CharSource createSource(String string) throws IOException {
561       checkNotNull(string);
562       Path file = createFile();
563       try (Writer writer = java.nio.file.Files.newBufferedWriter(file, UTF_8)) {
564         writer.write(string);
565       }
566       return MoreFiles.asCharSource(file, UTF_8);
567     }
568 
569     @Override
570     public String getExpected(String string) {
571       return checkNotNull(string);
572     }
573   }
574 
575   @AndroidIncompatible
576   private static class PathCharSinkFactory extends Jdk7FileFactory implements CharSinkFactory {
577 
578     private final String initialString;
579 
580     private PathCharSinkFactory(@Nullable String initialString) {
581       this.initialString = initialString;
582     }
583 
584     @Override
585     public CharSink createSink() throws IOException {
586       Path file = createFile();
587       if (initialString != null) {
588         try (Writer writer = java.nio.file.Files.newBufferedWriter(file, UTF_8)) {
589           writer.write(initialString);
590         }
591         return MoreFiles.asCharSink(file, UTF_8, StandardOpenOption.APPEND);
592       }
593       return MoreFiles.asCharSink(file, UTF_8);
594     }
595 
596     @Override
597     public String getExpected(String string) {
598       checkNotNull(string);
599       return initialString == null ? string : initialString + string;
600     }
601 
602     @Override
603     public String getSinkContents() throws IOException {
604       Path file = getPath();
605       try (Reader reader = java.nio.file.Files.newBufferedReader(file, UTF_8)) {
606         StringBuilder builder = new StringBuilder();
607         for (int c = reader.read(); c != -1; c = reader.read()) {
608           builder.append((char) c);
609         }
610         return builder.toString();
611       }
612     }
613   }
614 }
615