1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.io; 19 20 import java.util.Arrays; 21 import libcore.util.SneakyThrow; 22 23 /** 24 * Wraps an existing {@link Writer} and <em>buffers</em> the output. Expensive 25 * interaction with the underlying reader is minimized, since most (smaller) 26 * requests can be satisfied by accessing the buffer alone. The drawback is that 27 * some extra space is required to hold the buffer and that copying takes place 28 * when filling that buffer, but this is usually outweighed by the performance 29 * benefits. 30 * 31 * <p/>A typical application pattern for the class looks like this:<p/> 32 * 33 * <pre> 34 * BufferedWriter buf = new BufferedWriter(new FileWriter("file.java")); 35 * </pre> 36 * 37 * @see BufferedReader 38 */ 39 public class BufferedWriter extends Writer { 40 41 private Writer out; 42 43 private char[] buf; 44 45 private int pos; 46 47 /** 48 * Constructs a new {@code BufferedWriter}, providing {@code out} with a buffer 49 * of 8192 chars. 50 * 51 * @param out the {@code Writer} the buffer writes to. 52 */ 53 public BufferedWriter(Writer out) { 54 this(out, 8192); 55 } 56 57 /** 58 * Constructs a new {@code BufferedWriter}, providing {@code out} with {@code size} chars 59 * of buffer. 60 * 61 * @param out the {@code OutputStream} the buffer writes to. 62 * @param size the size of buffer in chars. 63 * @throws IllegalArgumentException if {@code size <= 0}. 64 */ 65 public BufferedWriter(Writer out, int size) { 66 super(out); 67 if (size <= 0) { 68 throw new IllegalArgumentException("size <= 0"); 69 } 70 this.out = out; 71 this.buf = new char[size]; 72 } 73 74 /** 75 * Closes this writer. The contents of the buffer are flushed, the target 76 * writer is closed, and the buffer is released. Only the first invocation 77 * of close has any effect. 78 * 79 * @throws IOException 80 * if an error occurs while closing this writer. 81 */ 82 @Override 83 public void close() throws IOException { 84 synchronized (lock) { 85 if (isClosed()) { 86 return; 87 } 88 89 Throwable thrown = null; 90 try { 91 flushInternal(); 92 } catch (Throwable e) { 93 thrown = e; 94 } 95 buf = null; 96 97 try { 98 out.close(); 99 } catch (Throwable e) { 100 if (thrown == null) { 101 thrown = e; 102 } 103 } 104 out = null; 105 106 if (thrown != null) { 107 SneakyThrow.sneakyThrow(thrown); 108 } 109 } 110 } 111 112 /** 113 * Flushes this writer. The contents of the buffer are committed to the 114 * target writer and it is then flushed. 115 * 116 * @throws IOException 117 * if an error occurs while flushing this writer. 118 */ 119 @Override 120 public void flush() throws IOException { 121 synchronized (lock) { 122 checkNotClosed(); 123 flushInternal(); 124 out.flush(); 125 } 126 } 127 128 private void checkNotClosed() throws IOException { 129 if (isClosed()) { 130 throw new IOException("BufferedWriter is closed"); 131 } 132 } 133 134 /** 135 * Flushes the internal buffer. 136 */ 137 private void flushInternal() throws IOException { 138 if (pos > 0) { 139 out.write(buf, 0, pos); 140 } 141 pos = 0; 142 } 143 144 /** 145 * Indicates whether this writer is closed. 146 * 147 * @return {@code true} if this writer is closed, {@code false} otherwise. 148 */ 149 private boolean isClosed() { 150 return out == null; 151 } 152 153 /** 154 * Writes a newline to this writer. On Android, this is {@code "\n"}. 155 * The target writer may or may not be flushed when a newline is written. 156 * 157 * @throws IOException 158 * if an error occurs attempting to write to this writer. 159 */ 160 public void newLine() throws IOException { 161 write(System.lineSeparator()); 162 } 163 164 /** 165 * Writes {@code count} characters starting at {@code offset} in 166 * {@code buffer} to this writer. If {@code count} is greater than this 167 * writer's buffer, then the buffer is flushed and the characters are 168 * written directly to the target writer. 169 * 170 * @param buffer 171 * the array containing characters to write. 172 * @param offset 173 * the start position in {@code buffer} for retrieving characters. 174 * @param count 175 * the maximum number of characters to write. 176 * @throws IndexOutOfBoundsException 177 * if {@code offset < 0} or {@code count < 0}, or if 178 * {@code offset + count} is greater than the size of 179 * {@code buffer}. 180 * @throws IOException 181 * if this writer is closed or another I/O error occurs. 182 */ 183 @Override 184 public void write(char[] buffer, int offset, int count) throws IOException { 185 synchronized (lock) { 186 checkNotClosed(); 187 if (buffer == null) { 188 throw new NullPointerException("buffer == null"); 189 } 190 Arrays.checkOffsetAndCount(buffer.length, offset, count); 191 if (pos == 0 && count >= this.buf.length) { 192 out.write(buffer, offset, count); 193 return; 194 } 195 int available = this.buf.length - pos; 196 if (count < available) { 197 available = count; 198 } 199 if (available > 0) { 200 System.arraycopy(buffer, offset, this.buf, pos, available); 201 pos += available; 202 } 203 if (pos == this.buf.length) { 204 out.write(this.buf, 0, this.buf.length); 205 pos = 0; 206 if (count > available) { 207 offset += available; 208 available = count - available; 209 if (available >= this.buf.length) { 210 out.write(buffer, offset, available); 211 return; 212 } 213 214 System.arraycopy(buffer, offset, this.buf, pos, available); 215 pos += available; 216 } 217 } 218 } 219 } 220 221 /** 222 * Writes the character {@code oneChar} to this writer. If the buffer 223 * gets full by writing this character, this writer is flushed. Only the 224 * lower two bytes of the integer {@code oneChar} are written. 225 * 226 * @param oneChar 227 * the character to write. 228 * @throws IOException 229 * if this writer is closed or another I/O error occurs. 230 */ 231 @Override 232 public void write(int oneChar) throws IOException { 233 synchronized (lock) { 234 checkNotClosed(); 235 if (pos >= buf.length) { 236 out.write(buf, 0, buf.length); 237 pos = 0; 238 } 239 buf[pos++] = (char) oneChar; 240 } 241 } 242 243 /** 244 * Writes {@code count} characters starting at {@code offset} in {@code str} 245 * to this writer. If {@code count} is greater than this writer's buffer, 246 * then this writer is flushed and the remaining characters are written 247 * directly to the target writer. If count is negative no characters are 248 * written to the buffer. This differs from the behavior of the superclass. 249 * 250 * @param str 251 * the non-null String containing characters to write. 252 * @param offset 253 * the start position in {@code str} for retrieving characters. 254 * @param count 255 * maximum number of characters to write. 256 * @throws IOException 257 * if this writer has already been closed or another I/O error 258 * occurs. 259 * @throws IndexOutOfBoundsException 260 * if {@code offset < 0} or {@code offset + count} is greater 261 * than the length of {@code str}. 262 */ 263 @Override 264 public void write(String str, int offset, int count) throws IOException { 265 synchronized (lock) { 266 checkNotClosed(); 267 if (count <= 0) { 268 return; 269 } 270 if (offset < 0 || offset > str.length() - count) { 271 throw new StringIndexOutOfBoundsException(str, offset, count); 272 } 273 if (pos == 0 && count >= buf.length) { 274 char[] chars = new char[count]; 275 str.getChars(offset, offset + count, chars, 0); 276 out.write(chars, 0, count); 277 return; 278 } 279 int available = buf.length - pos; 280 if (count < available) { 281 available = count; 282 } 283 if (available > 0) { 284 str.getChars(offset, offset + available, buf, pos); 285 pos += available; 286 } 287 if (pos == buf.length) { 288 out.write(this.buf, 0, this.buf.length); 289 pos = 0; 290 if (count > available) { 291 offset += available; 292 available = count - available; 293 if (available >= buf.length) { 294 char[] chars = new char[count]; 295 str.getChars(offset, offset + available, chars, 0); 296 out.write(chars, 0, available); 297 return; 298 } 299 str.getChars(offset, offset + available, buf, pos); 300 pos += available; 301 } 302 } 303 } 304 } 305 } 306