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