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.Files;
26 import java.nio.file.Path;
27 import java.nio.file.StandardOpenOption;
28 import java.nio.file.attribute.FileTime;
29 import java.time.Instant;
30 import java.util.Objects;
31
32
33
34
35
36 public class CachingOutputStream extends OutputStream
37 {
38 private final Path path;
39 private FileChannel channel;
40 private ByteBuffer readBuffer;
41 private ByteBuffer writeBuffer;
42 private boolean modified;
43
44 public CachingOutputStream( File path ) throws IOException
45 {
46 this( Objects.requireNonNull( path ).toPath() );
47 }
48
49 public CachingOutputStream( Path path ) throws IOException
50 {
51 this( path, 32 * 1024 );
52 }
53
54 public CachingOutputStream( Path path, int bufferSize ) throws IOException
55 {
56 this.path = Objects.requireNonNull( path );
57 this.channel = FileChannel.open( path,
58 StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );
59 this.readBuffer = ByteBuffer.allocate( bufferSize );
60 this.writeBuffer = ByteBuffer.allocate( bufferSize );
61 }
62
63 @Override
64 public void write( int b ) throws IOException
65 {
66 if ( writeBuffer.remaining() < 1 )
67 {
68 ( ( Buffer ) writeBuffer ).flip();
69 flushBuffer( writeBuffer );
70 ( ( Buffer ) writeBuffer ).clear();
71 }
72 writeBuffer.put( ( byte ) b );
73 }
74
75 @Override
76 public void write( byte[] b ) throws IOException
77 {
78 write( b, 0, b.length );
79 }
80
81 @Override
82 public void write( byte[] b, int off, int len ) throws IOException
83 {
84 if ( writeBuffer.remaining() < len )
85 {
86 ( ( Buffer ) writeBuffer ).flip();
87 flushBuffer( writeBuffer );
88 ( ( Buffer ) writeBuffer ).clear();
89 }
90 int capacity = writeBuffer.capacity();
91 while ( len >= capacity )
92 {
93 flushBuffer( ByteBuffer.wrap( b, off, capacity ) );
94 off += capacity;
95 len -= capacity;
96 }
97 if ( len > 0 )
98 {
99 writeBuffer.put( b, off, len );
100 }
101 }
102
103 @Override
104 public void flush() throws IOException
105 {
106 ( ( Buffer ) writeBuffer ).flip();
107 flushBuffer( writeBuffer );
108 ( ( Buffer ) writeBuffer ).clear();
109 super.flush();
110 }
111
112 private void flushBuffer( ByteBuffer writeBuffer ) throws IOException
113 {
114 if ( modified )
115 {
116 channel.write( writeBuffer );
117 }
118 else
119 {
120 int len = writeBuffer.remaining();
121 ByteBuffer readBuffer;
122 if ( this.readBuffer.capacity() >= len )
123 {
124 readBuffer = this.readBuffer;
125 ( ( Buffer ) readBuffer ).clear();
126 }
127 else
128 {
129 readBuffer = ByteBuffer.allocate( len );
130 }
131 while ( len > 0 )
132 {
133 int read = channel.read( readBuffer );
134 if ( read <= 0 )
135 {
136 modified = true;
137 channel.position( channel.position() - readBuffer.position() );
138 channel.write( writeBuffer );
139 return;
140 }
141 len -= read;
142 }
143 ( ( Buffer ) readBuffer ).flip();
144 if ( readBuffer.compareTo( writeBuffer ) != 0 )
145 {
146 modified = true;
147 channel.position( channel.position() - readBuffer.remaining() );
148 channel.write( writeBuffer );
149 }
150 }
151 }
152
153 @Override
154 public void close() throws IOException
155 {
156 if ( channel.isOpen() )
157 {
158 flush();
159 long position = channel.position();
160 if ( position != channel.size() )
161 {
162 modified = true;
163 channel.truncate( position );
164 }
165 channel.close();
166 }
167 }
168
169 public boolean isModified()
170 {
171 return modified;
172 }
173 }