or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

basic-utilities.mdcaching.mdcollections.mdconcurrency.mdgraph-api.mdhash-math.mdimmutable-collections.mdindex.mdio-utilities.mdother-utilities.md

io-utilities.mddocs/

0

# I/O Utilities

1

2

Comprehensive I/O utilities for streams, files, resources, and data processing with proper exception handling and resource management. These utilities simplify common I/O operations while providing robust error handling.

3

4

## Package: com.google.common.io

5

6

### File Operations

7

8

High-level file operations that handle common tasks like reading, writing, copying, and moving files.

9

10

```java { .api }

11

import com.google.common.io.Files;

12

import java.io.File;

13

import java.nio.charset.StandardCharsets;

14

import java.util.List;

15

16

// Reading files

17

File file = new File("data.txt");

18

byte[] bytes = Files.readBytes(file); // Read entire file as bytes

19

List<String> lines = Files.readLines(file, StandardCharsets.UTF_8); // Read as lines

20

String content = Files.asCharSource(file, StandardCharsets.UTF_8).read(); // Read as string

21

22

// Writing files

23

String data = "Hello, World!";

24

Files.write(data.getBytes(StandardCharsets.UTF_8), file); // Write bytes

25

Files.asCharSink(file, StandardCharsets.UTF_8).write(data); // Write string

26

Files.append(data.getBytes(StandardCharsets.UTF_8), file); // Append bytes

27

28

// Line-by-line processing (memory efficient)

29

Files.asCharSource(file, StandardCharsets.UTF_8).readLines(new LineProcessor<Void>() {

30

@Override

31

public boolean processLine(String line) throws IOException {

32

System.out.println("Processing: " + line);

33

return true; // Continue processing (false to stop)

34

}

35

36

@Override

37

public Void getResult() {

38

return null;

39

}

40

});

41

42

// File operations

43

File source = new File("source.txt");

44

File destination = new File("destination.txt");

45

File directory = new File("backup/");

46

47

Files.copy(source, destination); // Copy file

48

Files.move(source, destination); // Move/rename file

49

boolean identical = Files.equal(source, destination); // Compare file contents

50

51

// File metadata

52

Files.touch(file); // Create empty file or update timestamp

53

File tempDir = Files.createTempDir(); // Create temporary directory

54

55

// File extensions and names

56

String extension = Files.getFileExtension("document.pdf"); // "pdf"

57

String nameWithoutExt = Files.getNameWithoutExtension("document.pdf"); // "document"

58

```

59

60

### Modern Path Operations (MoreFiles)

61

62

Modern NIO.2 Path-based file operations that provide enhanced functionality and better error handling compared to legacy File-based operations.

63

64

```java { .api }

65

import com.google.common.io.MoreFiles;

66

import com.google.common.io.RecursiveDeleteOption;

67

import java.nio.file.Path;

68

import java.nio.file.Paths;

69

import java.nio.charset.StandardCharsets;

70

import java.nio.file.StandardOpenOption;

71

72

// Path-based I/O sources and sinks

73

Path file = Paths.get("data.txt");

74

ByteSource source = MoreFiles.asByteSource(file);

75

ByteSink sink = MoreFiles.asByteSink(file, StandardOpenOption.CREATE);

76

CharSource charSource = MoreFiles.asCharSource(file, StandardCharsets.UTF_8);

77

CharSink charSink = MoreFiles.asCharSink(file, StandardCharsets.UTF_8);

78

79

// Directory listing and traversal

80

Path directory = Paths.get("mydir");

81

ImmutableList<Path> files = MoreFiles.listFiles(directory);

82

83

// File tree traversal

84

Traverser<Path> traverser = MoreFiles.fileTraverser();

85

for (Path path : traverser.breadthFirst(directory)) {

86

System.out.println(path);

87

}

88

89

// Predicates for file filtering

90

Predicate<Path> isDirectory = MoreFiles.isDirectory();

91

Predicate<Path> isRegularFile = MoreFiles.isRegularFile();

92

Predicate<Path> isDirNoFollowLinks = MoreFiles.isDirectory(LinkOption.NOFOLLOW_LINKS);

93

94

// File comparison

95

Path file1 = Paths.get("file1.txt");

96

Path file2 = Paths.get("file2.txt");

97

boolean identical = MoreFiles.equal(file1, file2); // Content comparison

98

99

// File creation and modification

100

Path newFile = Paths.get("newfile.txt");

101

MoreFiles.touch(newFile); // Create empty file or update timestamp

102

103

// Parent directory creation

104

Path nested = Paths.get("deep/nested/path/file.txt");

105

MoreFiles.createParentDirectories(nested); // Creates parent directories if needed

106

107

// File name operations

108

Path document = Paths.get("report.pdf");

109

String extension = MoreFiles.getFileExtension(document); // "pdf"

110

String nameOnly = MoreFiles.getNameWithoutExtension(document); // "report"

111

112

// Recursive deletion operations

113

Path targetDir = Paths.get("old_data");

114

115

// Delete directory and all contents

116

MoreFiles.deleteRecursively(targetDir);

117

118

// Delete with options

119

MoreFiles.deleteRecursively(targetDir,

120

RecursiveDeleteOption.ALLOW_INSECURE); // Allow deletion of insecure paths

121

122

// Delete only directory contents (keep directory)

123

MoreFiles.deleteDirectoryContents(targetDir);

124

125

// Safe deletion with options

126

try {

127

MoreFiles.deleteRecursively(targetDir);

128

} catch (InsecureRecursiveDeleteException e) {

129

// Handle insecure path deletion attempt

130

System.err.println("Deletion blocked for security: " + e.getMessage());

131

}

132

```

133

134

**Key Methods:**

135

- `asByteSource(Path, OpenOption...)` - Create ByteSource for Path

136

- `asByteSink(Path, OpenOption...)` - Create ByteSink for Path

137

- `asCharSource(Path, Charset, OpenOption...)` - Create CharSource for Path

138

- `asCharSink(Path, Charset, OpenOption...)` - Create CharSink for Path

139

- `listFiles(Path)` - List files in directory as ImmutableList

140

- `fileTraverser()` - Create Traverser for file tree walking

141

- `isDirectory(LinkOption...)` - Predicate for directory test

142

- `isRegularFile(LinkOption...)` - Predicate for regular file test

143

- `equal(Path, Path)` - Compare file contents for equality

144

- `touch(Path)` - Create empty file or update timestamp

145

- `createParentDirectories(Path, FileAttribute...)` - Create parent directories

146

- `getFileExtension(Path)` - Extract file extension

147

- `getNameWithoutExtension(Path)` - Get filename without extension

148

- `deleteRecursively(Path, RecursiveDeleteOption...)` - Delete directory tree

149

- `deleteDirectoryContents(Path, RecursiveDeleteOption...)` - Delete directory contents

150

151

**Recursive Delete Options:**

152

- `ALLOW_INSECURE` - Allow deletion of symbolic links and files outside directory tree

153

- Default behavior blocks potentially unsafe deletions for security

154

155

**Advantages over Legacy Files Class:**

156

- Uses modern NIO.2 Path API instead of legacy File

157

- Better error handling and exception specificity

158

- Enhanced security for recursive operations

159

- Support for file system features like symbolic links

160

- More efficient directory traversal

161

- Thread-safe operations

162

163

### Base Encoding (BaseEncoding)

164

165

Binary encoding schemes for reversibly translating between byte sequences and printable ASCII strings. Supports Base64, Base32, Base16 and custom encodings.

166

167

```java { .api }

168

import com.google.common.io.BaseEncoding;

169

import java.nio.charset.StandardCharsets;

170

171

// Standard encodings

172

BaseEncoding base64 = BaseEncoding.base64();

173

BaseEncoding base64Url = BaseEncoding.base64Url(); // URL-safe Base64

174

BaseEncoding base32 = BaseEncoding.base32();

175

BaseEncoding base32Hex = BaseEncoding.base32Hex(); // Base32 with hex digits

176

BaseEncoding base16 = BaseEncoding.base16(); // Hexadecimal

177

178

// Basic encoding and decoding

179

String message = "Hello, World!";

180

byte[] data = message.getBytes(StandardCharsets.UTF_8);

181

182

// Base64 encoding

183

String encoded = base64.encode(data); // "SGVsbG8sIFdvcmxkIQ=="

184

byte[] decoded = base64.decode(encoded);

185

String result = new String(decoded, StandardCharsets.UTF_8); // "Hello, World!"

186

187

// Different encoding types

188

String base32Encoded = base32.encode(data); // "JBSWY3DPEBLW64TMMQQQ===="

189

String hexEncoded = base16.encode(data); // "48656C6C6F2C20576F726C6421"

190

191

// Case handling for Base16

192

String lowerHex = base16.lowerCase().encode(data); // "48656c6c6f2c20576f726c6421"

193

String upperHex = base16.upperCase().encode(data); // "48656C6C6F2C20576F726C6421"

194

195

// Padding control

196

BaseEncoding noPadding = base64.omitPadding();

197

String withoutPadding = noPadding.encode(data); // "SGVsbG8sIFdvcmxkIQ"

198

199

BaseEncoding withPadding = noPadding.withPadChar('=');

200

String restored = withPadding.encode(data); // "SGVsbG8sIFdvcmxkIQ=="

201

202

// Separator insertion for readability

203

BaseEncoding withSeparator = base64.withSeparator("-", 4);

204

String separated = withSeparator.encode(data); // "SGVs-bG8s-IFdv-cmxk-IQ=="

205

206

// Stream-based encoding/decoding

207

CharSink charSink = Files.asCharSink(file, StandardCharsets.UTF_8);

208

ByteSink encodingSink = base64.encodingSink(charSink);

209

encodingSink.write(data); // Writes encoded data to file

210

211

CharSource charSource = Files.asCharSource(file, StandardCharsets.UTF_8);

212

ByteSource decodingSource = base64.decodingSource(charSource);

213

byte[] decodedFromFile = decodingSource.read();

214

215

// Custom alphabets for specialized encoding

216

BaseEncoding customBase32 = BaseEncoding.base32().withPadChar('@');

217

String customEncoded = customBase32.encode(data);

218

219

// Handling encoding without padding requirements

220

try {

221

byte[] decoded = base64.decode("Invalid==Input");

222

} catch (IllegalArgumentException e) {

223

System.err.println("Invalid encoding: " + e.getMessage());

224

}

225

226

// Lenient decoding (ignores whitespace and case differences)

227

BaseEncoding lenient = base16.lowerCase();

228

byte[] result1 = lenient.decode("48656c6c"); // Works

229

byte[] result2 = lenient.decode("48656C6C"); // Also works (case insensitive)

230

```

231

232

**Standard Encodings:**

233

- `base64()` - Standard Base64 encoding (RFC 4648)

234

- `base64Url()` - URL-safe Base64 encoding (uses - and _ instead of + and /)

235

- `base32()` - Base32 encoding (RFC 4648)

236

- `base32Hex()` - Base32 encoding with hex alphabet (0-9, A-V)

237

- `base16()` - Base16 (hexadecimal) encoding

238

239

**Encoding Methods:**

240

- `encode(byte[])` - Encode byte array to string

241

- `encode(byte[], int, int)` - Encode byte array slice

242

- `encodingSink(CharSink)` - Create ByteSink that encodes to CharSink

243

244

**Decoding Methods:**

245

- `decode(CharSequence)` - Decode string to byte array

246

- `decodingSource(CharSource)` - Create ByteSource that decodes from CharSource

247

248

**Customization Methods:**

249

- `omitPadding()` - Remove padding characters from output

250

- `withPadChar(char)` - Use custom padding character

251

- `withSeparator(String, int)` - Insert separator every N characters

252

- `lowerCase()` / `upperCase()` - Control case for applicable encodings

253

254

**Key Features:**

255

- **RFC 4648 Compliant**: Standard implementations of Base64, Base32, Base16

256

- **URL-Safe Variants**: Safe for use in URLs and filenames

257

- **Streaming Support**: Encode/decode via ByteSink/ByteSource abstraction

258

- **Customizable**: Control padding, separators, and case

259

- **Error Handling**: Clear exceptions for invalid input

260

- **Thread-Safe**: All encodings are immutable and thread-safe

261

262

### ByteSource and ByteSink

263

264

Abstractions for reading from and writing to byte-oriented data sources and sinks.

265

266

```java { .api }

267

import com.google.common.io.ByteSource;

268

import com.google.common.io.ByteSink;

269

import java.io.InputStream;

270

import java.io.OutputStream;

271

272

// ByteSource - reading bytes

273

ByteSource source = Files.asByteSource(file);

274

275

// Reading operations

276

byte[] allBytes = source.read(); // Read all bytes

277

long size = source.size(); // Get size if known

278

boolean empty = source.isEmpty(); // Check if empty

279

280

// Stream operations

281

try (InputStream inputStream = source.openStream()) {

282

// Process stream

283

byte[] buffer = new byte[1024];

284

int bytesRead = inputStream.read(buffer);

285

}

286

287

// Copy to other destinations

288

ByteSink sink = Files.asByteSink(destinationFile);

289

source.copyTo(sink); // Copy bytes from source to sink

290

291

OutputStream outputStream = new FileOutputStream("output.dat");

292

source.copyTo(outputStream); // Copy to output stream

293

294

// Slice operations

295

ByteSource slice = source.slice(100, 500); // Bytes 100-599

296

297

// ByteSink - writing bytes

298

ByteSink sink2 = Files.asByteSink(file);

299

sink2.write("Hello World".getBytes(StandardCharsets.UTF_8));

300

301

// Open stream for writing

302

try (OutputStream out = sink2.openStream()) {

303

out.write(data);

304

}

305

306

// Concatenating sources

307

ByteSource combined = ByteSource.concat(source1, source2, source3);

308

byte[] allData = combined.read();

309

310

// Empty source

311

ByteSource empty = ByteSource.empty();

312

```

313

314

### CharSource and CharSink

315

316

Abstractions for reading from and writing to character-oriented data sources and sinks.

317

318

```java { .api }

319

import com.google.common.io.CharSource;

320

import com.google.common.io.CharSink;

321

import java.io.Reader;

322

import java.io.Writer;

323

324

// CharSource - reading characters

325

CharSource source = Files.asCharSource(file, StandardCharsets.UTF_8);

326

327

// Reading operations

328

String content = source.read(); // Read entire content

329

List<String> lines = source.readLines(); // Read as list of lines

330

String firstLine = source.readFirstLine(); // Read just first line

331

long length = source.length(); // Get character count if known

332

333

// Stream operations

334

try (Reader reader = source.openStream()) {

335

char[] buffer = new char[1024];

336

int charsRead = reader.read(buffer);

337

}

338

339

// Line processing (memory efficient for large files)

340

Integer lineCount = source.readLines(new LineProcessor<Integer>() {

341

private int count = 0;

342

343

@Override

344

public boolean processLine(String line) throws IOException {

345

count++;

346

return true; // Continue processing

347

}

348

349

@Override

350

public Integer getResult() {

351

return count;

352

}

353

});

354

355

// Copy operations

356

CharSink sink = Files.asCharSink(destinationFile, StandardCharsets.UTF_8);

357

source.copyTo(sink);

358

359

Writer writer = new FileWriter("output.txt");

360

source.copyTo(writer);

361

362

// CharSink - writing characters

363

CharSink sink2 = Files.asCharSink(file, StandardCharsets.UTF_8);

364

sink2.write("Hello World");

365

sink2.writeLines(Arrays.asList("Line 1", "Line 2", "Line 3"));

366

367

// Open stream for writing

368

try (Writer out = sink2.openStream()) {

369

out.write("Content");

370

}

371

372

// Concatenating sources

373

CharSource combined = CharSource.concat(source1, source2, source3);

374

String allContent = combined.read();

375

376

// Wrapping strings as sources

377

CharSource stringSource = CharSource.wrap("This is a string source");

378

List<String> stringLines = stringSource.readLines();

379

```

380

381

### Resource Operations

382

383

Utilities for working with classpath resources and URLs.

384

385

```java { .api }

386

import com.google.common.io.Resources;

387

import java.net.URL;

388

389

// Getting resource URLs

390

URL configUrl = Resources.getResource("config.properties"); // From classpath root

391

URL relativeUrl = Resources.getResource(MyClass.class, "relative-config.properties"); // Relative to class

392

393

// Reading resources

394

byte[] resourceBytes = Resources.toByteArray(configUrl);

395

String resourceContent = Resources.toString(configUrl, StandardCharsets.UTF_8);

396

List<String> resourceLines = Resources.readLines(configUrl, StandardCharsets.UTF_8);

397

398

// Copying resources

399

Resources.copy(configUrl, new FileOutputStream("local-config.properties"));

400

401

// Resource as ByteSource/CharSource

402

ByteSource resourceByteSource = Resources.asByteSource(configUrl);

403

CharSource resourceCharSource = Resources.asCharSource(configUrl, StandardCharsets.UTF_8);

404

405

// Loading properties from resources

406

Properties props = new Properties();

407

try (InputStream in = Resources.getResource("app.properties").openStream()) {

408

props.load(in);

409

}

410

411

// Utility for resource loading with error handling

412

public class ResourceLoader {

413

public static String loadResourceAsString(String resourcePath) {

414

try {

415

URL resource = Resources.getResource(resourcePath);

416

return Resources.toString(resource, StandardCharsets.UTF_8);

417

} catch (IllegalArgumentException e) {

418

throw new RuntimeException("Resource not found: " + resourcePath, e);

419

} catch (IOException e) {

420

throw new RuntimeException("Failed to read resource: " + resourcePath, e);

421

}

422

}

423

424

public static Properties loadProperties(String resourcePath) {

425

Properties props = new Properties();

426

try {

427

URL resource = Resources.getResource(resourcePath);

428

try (InputStream in = resource.openStream()) {

429

props.load(in);

430

}

431

return props;

432

} catch (Exception e) {

433

throw new RuntimeException("Failed to load properties: " + resourcePath, e);

434

}

435

}

436

}

437

```

438

439

### Stream Utilities

440

441

Low-level utilities for working with input and output streams.

442

443

```java { .api }

444

import com.google.common.io.ByteStreams;

445

import com.google.common.io.CharStreams;

446

import java.io.InputStream;

447

import java.io.OutputStream;

448

import java.io.Reader;

449

import java.io.Writer;

450

451

// Byte stream operations

452

InputStream input = new FileInputStream("source.dat");

453

OutputStream output = new FileOutputStream("destination.dat");

454

455

// Copy entire stream

456

long bytesCopied = ByteStreams.copy(input, output);

457

458

// Read operations

459

byte[] buffer = new byte[1024];

460

int bytesRead = ByteStreams.read(input, buffer, 0, buffer.length); // Read exactly

461

ByteStreams.readFully(input, buffer); // Read exactly buffer.length bytes

462

ByteStreams.skipFully(input, 100); // Skip exactly 100 bytes

463

464

// Read entire stream to byte array

465

byte[] allBytes = ByteStreams.toByteArray(input);

466

467

// Exhaust stream (read and discard all data)

468

ByteStreams.exhaust(input);

469

470

// Null output stream (discards all data)

471

OutputStream nullOut = ByteStreams.nullOutputStream();

472

473

// Character stream operations

474

Reader reader = new FileReader("source.txt");

475

Writer writer = new FileWriter("destination.txt");

476

477

// Copy entire stream

478

long charsCopied = CharStreams.copy(reader, writer);

479

480

// Read operations

481

String content = CharStreams.toString(reader); // Read entire stream as string

482

List<String> lines = CharStreams.readLines(reader); // Read as list of lines

483

CharStreams.skipFully(reader, 50); // Skip exactly 50 characters

484

485

// Exhaust stream

486

CharStreams.exhaust(reader);

487

488

// Null writer (discards all data)

489

Writer nullWriter = CharStreams.nullWriter();

490

491

// Limited streams

492

InputStream limited = ByteStreams.limit(originalInputStream, 1024); // Limit to 1024 bytes

493

```

494

495

### Resource Management

496

497

Utilities for proper resource cleanup and exception handling.

498

499

```java { .api }

500

import com.google.common.io.Closer;

501

import com.google.common.io.Closeables;

502

import com.google.common.io.Flushables;

503

504

// Closer - manages multiple resources

505

Closer closer = Closer.create();

506

try {

507

InputStream in = closer.register(new FileInputStream("input.txt"));

508

OutputStream out = closer.register(new FileOutputStream("output.txt"));

509

Reader reader = closer.register(new InputStreamReader(in, StandardCharsets.UTF_8));

510

Writer writer = closer.register(new OutputStreamWriter(out, StandardCharsets.UTF_8));

511

512

// Use resources

513

CharStreams.copy(reader, writer);

514

515

} catch (IOException e) {

516

throw closer.rethrow(e); // Properly handle exceptions

517

} finally {

518

closer.close(); // Closes all registered resources

519

}

520

521

// Simple resource cleanup

522

FileInputStream input = null;

523

try {

524

input = new FileInputStream("file.txt");

525

// Use input stream

526

} finally {

527

Closeables.closeQuietly(input); // Closes and ignores exceptions

528

}

529

530

// Close with exception propagation

531

FileOutputStream output = null;

532

try {

533

output = new FileOutputStream("file.txt");

534

// Use output stream

535

} catch (IOException e) {

536

Closeables.close(output, true); // Close and suppress exceptions

537

throw e;

538

} catch (RuntimeException e) {

539

Closeables.close(output, false); // Close and propagate exceptions

540

throw e;

541

} finally {

542

Closeables.close(output, true);

543

}

544

545

// Flushing with error handling

546

Writer writer = new FileWriter("output.txt");

547

try {

548

writer.write("data");

549

Flushables.flush(writer, false); // Flush and propagate exceptions

550

} catch (IOException e) {

551

Flushables.flushQuietly(writer); // Best effort flush

552

throw e;

553

}

554

```

555

556

### Specialized Streams

557

558

Enhanced input and output streams with additional capabilities.

559

560

```java { .api }

561

import com.google.common.io.CountingInputStream;

562

import com.google.common.io.CountingOutputStream;

563

import com.google.common.io.LittleEndianDataInputStream;

564

import com.google.common.io.LittleEndianDataOutputStream;

565

566

// Counting streams - track bytes read/written

567

InputStream original = new FileInputStream("data.bin");

568

CountingInputStream counting = new CountingInputStream(original);

569

570

byte[] buffer = new byte[1024];

571

counting.read(buffer);

572

long bytesRead = counting.getCount(); // Number of bytes read so far

573

574

// Counting output stream

575

OutputStream originalOut = new FileOutputStream("output.bin");

576

CountingOutputStream countingOut = new CountingOutputStream(originalOut);

577

578

countingOut.write("Hello".getBytes());

579

long bytesWritten = countingOut.getCount(); // Number of bytes written

580

581

// Little-endian data streams (for binary protocol compatibility)

582

LittleEndianDataInputStream littleEndianIn = new LittleEndianDataInputStream(inputStream);

583

int value = littleEndianIn.readInt(); // Reads int in little-endian format

584

long longValue = littleEndianIn.readLong();

585

float floatValue = littleEndianIn.readFloat();

586

587

LittleEndianDataOutputStream littleEndianOut = new LittleEndianDataOutputStream(outputStream);

588

littleEndianOut.writeInt(42); // Writes int in little-endian format

589

littleEndianOut.writeLong(12345L);

590

littleEndianOut.writeFloat(3.14f);

591

```

592

593

### Processing Patterns

594

595

Common patterns for processing data with I/O utilities.

596

597

```java { .api }

598

// Batch processing large files

599

public void processLargeFile(File file) throws IOException {

600

Files.asCharSource(file, StandardCharsets.UTF_8).readLines(new LineProcessor<Void>() {

601

private int lineCount = 0;

602

603

@Override

604

public boolean processLine(String line) throws IOException {

605

lineCount++;

606

607

// Process each line

608

String processed = processLine(line);

609

610

// Write to output or accumulate results

611

if (processed != null) {

612

writeToOutput(processed);

613

}

614

615

// Progress reporting

616

if (lineCount % 10000 == 0) {

617

System.out.println("Processed " + lineCount + " lines");

618

}

619

620

return true; // Continue processing

621

}

622

623

@Override

624

public Void getResult() {

625

System.out.println("Total lines processed: " + lineCount);

626

return null;

627

}

628

});

629

}

630

631

// Safe file operations with backup

632

public void safeFileWrite(File file, String content) throws IOException {

633

File backupFile = new File(file.getAbsolutePath() + ".backup");

634

File tempFile = new File(file.getAbsolutePath() + ".tmp");

635

636

try {

637

// Create backup if original exists

638

if (file.exists()) {

639

Files.copy(file, backupFile);

640

}

641

642

// Write to temporary file first

643

Files.asCharSink(tempFile, StandardCharsets.UTF_8).write(content);

644

645

// Atomic move to final location

646

Files.move(tempFile, file);

647

648

// Clean up backup on success

649

if (backupFile.exists()) {

650

backupFile.delete();

651

}

652

653

} catch (IOException e) {

654

// Clean up temp file on failure

655

if (tempFile.exists()) {

656

tempFile.delete();

657

}

658

659

// Restore from backup if available

660

if (backupFile.exists() && !file.exists()) {

661

Files.move(backupFile, file);

662

}

663

664

throw e;

665

}

666

}

667

668

// Stream transformation pipeline

669

public void transformData(InputStream input, OutputStream output) throws IOException {

670

Closer closer = Closer.create();

671

try {

672

// Create transformation pipeline

673

Reader reader = closer.register(new InputStreamReader(input, StandardCharsets.UTF_8));

674

Writer writer = closer.register(new OutputStreamWriter(output, StandardCharsets.UTF_8));

675

676

// Process data line by line

677

BufferedReader buffered = closer.register(new BufferedReader(reader));

678

PrintWriter printer = closer.register(new PrintWriter(writer));

679

680

String line;

681

while ((line = buffered.readLine()) != null) {

682

String transformed = transformLine(line);

683

if (transformed != null) {

684

printer.println(transformed);

685

}

686

}

687

688

printer.flush();

689

690

} catch (IOException e) {

691

throw closer.rethrow(e);

692

} finally {

693

closer.close();

694

}

695

}

696

697

// Binary data processing

698

public void processBinaryData(ByteSource source, ByteSink sink) throws IOException {

699

try (InputStream in = source.openBufferedStream();

700

OutputStream out = sink.openBufferedStream()) {

701

702

CountingInputStream countingIn = new CountingInputStream(in);

703

CountingOutputStream countingOut = new CountingOutputStream(out);

704

705

byte[] buffer = new byte[8192];

706

int bytesRead;

707

708

while ((bytesRead = countingIn.read(buffer)) != -1) {

709

// Process buffer if needed

710

byte[] processed = processBytes(buffer, 0, bytesRead);

711

countingOut.write(processed);

712

}

713

714

System.out.println("Processed " + countingIn.getCount() + " bytes");

715

System.out.println("Wrote " + countingOut.getCount() + " bytes");

716

}

717

}

718

```

719

720

### Testing I/O Code

721

722

Utilities and patterns for testing I/O operations.

723

724

```java { .api }

725

// Testing with temporary files

726

@Test

727

public void testFileProcessing() throws IOException {

728

File tempDir = Files.createTempDir();

729

File testFile = new File(tempDir, "test.txt");

730

731

try {

732

// Setup test data

733

List<String> testLines = Arrays.asList("line1", "line2", "line3");

734

Files.asCharSink(testFile, StandardCharsets.UTF_8).writeLines(testLines);

735

736

// Test the processing

737

processFile(testFile);

738

739

// Verify results

740

List<String> resultLines = Files.readLines(testFile, StandardCharsets.UTF_8);

741

assertEquals(expected, resultLines);

742

743

} finally {

744

// Cleanup

745

if (tempDir.exists()) {

746

deleteRecursively(tempDir);

747

}

748

}

749

}

750

751

// Testing with in-memory sources/sinks

752

@Test

753

public void testDataTransformation() throws IOException {

754

String inputData = "input line 1\ninput line 2\n";

755

CharSource source = CharSource.wrap(inputData);

756

757

StringBuilder output = new StringBuilder();

758

CharSink sink = CharSink.wrap(output);

759

760

transformData(source, sink);

761

762

String result = output.toString();

763

assertEquals("expected output", result);

764

}

765

```

766

767

### Hash-Aware I/O Streams

768

769

I/O streams that compute hash codes while reading or writing data.

770

771

```java { .api }

772

import com.google.common.io.HashingInputStream;

773

import com.google.common.io.HashingOutputStream;

774

import com.google.common.hash.Hashing;

775

import com.google.common.hash.HashCode;

776

777

// Hash data while reading

778

InputStream originalInput = new FileInputStream("data.bin");

779

HashingInputStream hashingInput = new HashingInputStream(Hashing.sha256(), originalInput);

780

781

byte[] buffer = new byte[1024];

782

int bytesRead;

783

while ((bytesRead = hashingInput.read(buffer)) != -1) {

784

// Process data

785

}

786

787

HashCode inputHash = hashingInput.hash(); // SHA-256 of all data read

788

originalInput.close();

789

790

// Hash data while writing

791

OutputStream originalOutput = new FileOutputStream("output.bin");

792

HashingOutputStream hashingOutput = new HashingOutputStream(Hashing.md5(), originalOutput);

793

794

hashingOutput.write("Hello World".getBytes(StandardCharsets.UTF_8));

795

hashingOutput.flush();

796

797

HashCode outputHash = hashingOutput.hash(); // MD5 of all data written

798

originalOutput.close();

799

800

// Verify data integrity during copy

801

public void copyWithVerification(File source, File destination) throws IOException {

802

try (InputStream in = new FileInputStream(source);

803

OutputStream out = new FileOutputStream(destination)) {

804

805

HashingInputStream hashingIn = new HashingInputStream(Hashing.sha256(), in);

806

HashingOutputStream hashingOut = new HashingOutputStream(Hashing.sha256(), out);

807

808

ByteStreams.copy(hashingIn, hashingOut);

809

810

HashCode sourceHash = hashingIn.hash();

811

HashCode destHash = hashingOut.hash();

812

813

if (!sourceHash.equals(destHash)) {

814

throw new IOException("Hash verification failed during copy");

815

}

816

}

817

}

818

```

819

820

### Processing Interfaces

821

822

Callback interfaces for processing data streams.

823

824

```java { .api }

825

import com.google.common.io.LineProcessor;

826

import com.google.common.io.ByteProcessor;

827

828

// Line processor for text data

829

public class WordCountProcessor implements LineProcessor<Integer> {

830

private int wordCount = 0;

831

832

@Override

833

public boolean processLine(String line) throws IOException {

834

if (!line.trim().isEmpty()) {

835

String[] words = line.trim().split("\\s+");

836

wordCount += words.length;

837

}

838

return true; // Continue processing

839

}

840

841

@Override

842

public Integer getResult() {

843

return wordCount;

844

}

845

}

846

847

// Usage

848

File textFile = new File("document.txt");

849

Integer totalWords = Files.asCharSource(textFile, StandardCharsets.UTF_8)

850

.readLines(new WordCountProcessor());

851

852

// Byte processor for binary data

853

public class ChecksumProcessor implements ByteProcessor<String> {

854

private final Hasher hasher = Hashing.crc32().newHasher();

855

856

@Override

857

public boolean processBytes(byte[] buf, int off, int len) throws IOException {

858

hasher.putBytes(buf, off, len);

859

return true; // Continue processing

860

}

861

862

@Override

863

public String getResult() {

864

return hasher.hash().toString();

865

}

866

}

867

868

// Usage

869

ByteSource source = Files.asByteSource(new File("data.bin"));

870

String checksum = source.read(new ChecksumProcessor());

871

```

872

873

**Additional I/O Classes:**

874

875

- `HashingInputStream` - InputStream that maintains running hash of data read

876

- `HashingOutputStream` - OutputStream that maintains running hash of data written

877

- `LineProcessor<T>` - Interface for processing text files line by line

878

- `ByteProcessor<T>` - Interface for processing byte arrays with result accumulation

879

880

Guava's I/O utilities provide a comprehensive, safe, and efficient way to handle file operations, stream processing, and resource management with proper exception handling and cleanup patterns.