1 package org.codehaus.plexus.util.io;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.nio.Buffer;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.FileChannel;
25 import java.nio.file.Path;
26 import java.nio.file.StandardOpenOption;
27 import java.util.Objects;
28
29
30
31
32
33 public class CachingOutputStream extends OutputStream {
34 private final Path path;
35 private FileChannel channel;
36 private ByteBuffer readBuffer;
37 private ByteBuffer writeBuffer;
38 private boolean modified;
39
40 public CachingOutputStream(File path) throws IOException {
41 this(Objects.requireNonNull(path).toPath());
42 }
43
44 public CachingOutputStream(Path path) throws IOException {
45 this(path, 32 * 1024);
46 }
47
48 public CachingOutputStream(Path path, int bufferSize) throws IOException {
49 this.path = Objects.requireNonNull(path);
50 this.channel =
51 FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
52 this.readBuffer = ByteBuffer.allocate(bufferSize);
53 this.writeBuffer = ByteBuffer.allocate(bufferSize);
54 }
55
56 @Override
57 public void write(int b) throws IOException {
58 if (writeBuffer.remaining() < 1) {
59 ((Buffer) writeBuffer).flip();
60 flushBuffer(writeBuffer);
61 ((Buffer) writeBuffer).clear();
62 }
63 writeBuffer.put((byte) b);
64 }
65
66 @Override
67 public void write(byte[] b) throws IOException {
68 write(b, 0, b.length);
69 }
70
71 @Override
72 public void write(byte[] b, int off, int len) throws IOException {
73 if (writeBuffer.remaining() < len) {
74 ((Buffer) writeBuffer).flip();
75 flushBuffer(writeBuffer);
76 ((Buffer) writeBuffer).clear();
77 }
78 int capacity = writeBuffer.capacity();
79 while (len >= capacity) {
80 flushBuffer(ByteBuffer.wrap(b, off, capacity));
81 off += capacity;
82 len -= capacity;
83 }
84 if (len > 0) {
85 writeBuffer.put(b, off, len);
86 }
87 }
88
89 @Override
90 public void flush() throws IOException {
91 ((Buffer) writeBuffer).flip();
92 flushBuffer(writeBuffer);
93 ((Buffer) writeBuffer).clear();
94 super.flush();
95 }
96
97 private void flushBuffer(ByteBuffer writeBuffer) throws IOException {
98 if (modified) {
99 channel.write(writeBuffer);
100 } else {
101 int len = writeBuffer.remaining();
102 ByteBuffer readBuffer;
103 if (this.readBuffer.capacity() >= len) {
104 readBuffer = this.readBuffer;
105 ((Buffer) readBuffer).clear();
106 readBuffer.limit(len);
107 } else {
108 readBuffer = ByteBuffer.allocate(len);
109 }
110 while (len > 0) {
111 int read = channel.read(readBuffer);
112 if (read <= 0) {
113 modified = true;
114 channel.position(channel.position() - readBuffer.position());
115 channel.write(writeBuffer);
116 return;
117 }
118 len -= read;
119 }
120 ((Buffer) readBuffer).flip();
121 if (readBuffer.compareTo(writeBuffer) != 0) {
122 modified = true;
123 channel.position(channel.position() - readBuffer.remaining());
124 channel.write(writeBuffer);
125 }
126 }
127 }
128
129 @Override
130 public void close() throws IOException {
131 if (channel.isOpen()) {
132 flush();
133 long position = channel.position();
134 if (position != channel.size()) {
135 modified = true;
136 channel.truncate(position);
137 }
138 channel.close();
139 }
140 }
141
142 public boolean isModified() {
143 return modified;
144 }
145 }