1414 * See the License for the specific language governing permissions and
1515 * limitations under the License.
1616 */
17+
1718package org .apache .commons .io .input ;
1819
1920import static org .apache .commons .io .IOUtils .EOF ;
3132/**
3233 * This class is used to wrap a stream that includes an encoded {@link ByteOrderMark} as its first bytes.
3334 * <p>
34- * This class detects these bytes and, if required, can automatically skip them and return the subsequent byte as the
35- * first byte in the stream.
35+ * This class detects these bytes and, if required, can automatically skip them and return the subsequent byte as the first byte in the stream.
3636 * </p>
3737 * <p>
3838 * The {@link ByteOrderMark} implementation has the following predefined BOMs:
6060 *
6161 * <pre>
6262 * boolean include = true;
63- * BOMInputStream bomIn = BOMInputStream.builder()
64- * .setInputStream(in)
65- * .setInclude(include)
66- * .get();
63+ * BOMInputStream bomIn = BOMInputStream.builder().setInputStream(in).setInclude(include).get();
6764 * if (bomIn.hasBOM()) {
6865 * // has a UTF-8 BOM
6966 * }
7269 * <h2>Example 3 - Detecting Multiple BOMs</h2>
7370 *
7471 * <pre>
75- * BOMInputStream bomIn = BOMInputStream.builder()
76- * .setInputStream(in)
77- * .setByteOrderMarks(ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE)
78- * .get();
72+ * BOMInputStream bomIn = BOMInputStream.builder().setInputStream(in)
73+ * .setByteOrderMarks(ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE).get();
7974 * if (bomIn.hasBOM() == false) {
8075 * // No BOM found
8176 * } else if (bomIn.hasBOM(ByteOrderMark.UTF_16LE)) {
@@ -141,7 +136,6 @@ static ByteOrderMark getDefaultByteOrderMark() {
141136 }
142137
143138 private ByteOrderMark [] byteOrderMarks = DEFAULT ;
144-
145139 private boolean include ;
146140
147141 /**
@@ -207,7 +201,6 @@ public Builder setInclude(final boolean include) {
207201 this .include = include ;
208202 return this ;
209203 }
210-
211204 }
212205
213206 /**
@@ -229,65 +222,68 @@ public static Builder builder() {
229222 * BOMs are sorted from longest to shortest.
230223 */
231224 private final List <ByteOrderMark > bomList ;
232-
233- private ByteOrderMark byteOrderMark ;
225+ private final ByteOrderMark byteOrderMark ;
234226 private int fbIndex ;
235227 private int [] firstBytes ;
236228 private final boolean include ;
237229 private boolean markedAtStart ;
238230 private int markFbIndex ;
239231
232+ /**
233+ * Constructs a new instance.
234+ *
235+ * @param builder The builder.
236+ * @throws IOException if an error reading the first bytes of the stream occurs.
237+ */
240238 private BOMInputStream (final Builder builder ) throws IOException {
241239 super (builder );
242240 if (IOUtils .length (builder .byteOrderMarks ) == 0 ) {
243241 throw new IllegalArgumentException ("No ByteOrderMark specified." );
244242 }
245243 this .include = builder .include ;
246- final List <ByteOrderMark > list = Arrays .asList (builder .byteOrderMarks );
244+ final List <ByteOrderMark > bomList = Arrays .asList (builder .byteOrderMarks );
247245 // Sort the BOMs to match the longest BOM first because some BOMs have the same starting two bytes.
248- list .sort (ByteOrderMarkLengthComparator );
249- this .bomList = list ;
246+ bomList .sort (ByteOrderMarkLengthComparator );
247+ this .bomList = bomList ;
248+ this .byteOrderMark = readBom ();
250249 }
251250
252251 /**
253252 * Constructs a new BOM InputStream that excludes a {@link ByteOrderMark#UTF_8} BOM.
254253 *
255- * @param delegate
256- * the InputStream to delegate to .
257- * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
254+ * @param delegate the InputStream to delegate to.
255+ * @throws IOException if an error reading the first bytes of the stream occurs .
256+ * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
258257 */
259258 @ Deprecated
260- public BOMInputStream (final InputStream delegate ) {
259+ public BOMInputStream (final InputStream delegate ) throws IOException {
261260 this (delegate , false , Builder .DEFAULT );
262261 }
263262
264263 /**
265264 * Constructs a new BOM InputStream that detects a {@link ByteOrderMark#UTF_8} and optionally includes it.
266265 *
267- * @param delegate
268- * the InputStream to delegate to.
269- * @param include
270- * true to include the UTF-8 BOM or false to exclude it.
271- * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
266+ * @param delegate the InputStream to delegate to.
267+ * @param include true to include the UTF-8 BOM or false to exclude it.
268+ * @throws IOException if an error reading the first bytes of the stream occurs.
269+ * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
272270 */
273271 @ Deprecated
274- public BOMInputStream (final InputStream delegate , final boolean include ) {
272+ public BOMInputStream (final InputStream delegate , final boolean include ) throws IOException {
275273 this (delegate , include , Builder .DEFAULT );
276274 }
277275
278276 /**
279277 * Constructs a new BOM InputStream that detects the specified BOMs and optionally includes them.
280278 *
281- * @param delegate
282- * the InputStream to delegate to.
283- * @param include
284- * true to include the specified BOMs or false to exclude them.
285- * @param boms
286- * The BOMs to detect and optionally exclude.
287- * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
279+ * @param delegate the InputStream to delegate to.
280+ * @param include true to include the specified BOMs or false to exclude them.
281+ * @param boms The BOMs to detect and optionally exclude.
282+ * @throws IOException if an error reading the first bytes of the stream occurs.
283+ * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}.
288284 */
289285 @ Deprecated
290- public BOMInputStream (final InputStream delegate , final boolean include , final ByteOrderMark ... boms ) {
286+ public BOMInputStream (final InputStream delegate , final boolean include , final ByteOrderMark ... boms ) throws IOException {
291287 super (delegate );
292288 if (IOUtils .length (boms ) == 0 ) {
293289 throw new IllegalArgumentException ("No BOMs specified" );
@@ -297,19 +293,19 @@ public BOMInputStream(final InputStream delegate, final boolean include, final B
297293 // Sort the BOMs to match the longest BOM first because some BOMs have the same starting two bytes.
298294 list .sort (ByteOrderMarkLengthComparator );
299295 this .bomList = list ;
296+ this .byteOrderMark = readBom ();
300297 }
301298
302299 /**
303300 * Constructs a new BOM InputStream that excludes the specified BOMs.
304301 *
305- * @param delegate
306- * the InputStream to delegate to.
307- * @param boms
308- * The BOMs to detect and exclude.
302+ * @param delegate the InputStream to delegate to.
303+ * @param boms The BOMs to detect and exclude.
304+ * @throws IOException if an error reading the first bytes of the stream occurs.
309305 * @deprecated Use {@link #builder()}, {@link Builder}, and {@link Builder#get()}
310306 */
311307 @ Deprecated
312- public BOMInputStream (final InputStream delegate , final ByteOrderMark ... boms ) {
308+ public BOMInputStream (final InputStream delegate , final ByteOrderMark ... boms ) throws IOException {
313309 this (delegate , false , boms );
314310 }
315311
@@ -326,34 +322,26 @@ private ByteOrderMark find() {
326322 * Gets the ByteOrderMark (Byte Order Mark).
327323 *
328324 * @return The BOM or null if none matched.
329- * @throws IOException
330- * if an error reading the first bytes of the stream occurs.
331325 */
332- public ByteOrderMark getBOM () throws IOException {
333- if (firstBytes == null ) {
334- byteOrderMark = readBom ();
335- }
326+ public ByteOrderMark getBOM () {
336327 return byteOrderMark ;
337328 }
338329
339330 /**
340331 * Gets the BOM charset Name - {@link ByteOrderMark#getCharsetName()}.
341332 *
342333 * @return The BOM charset Name or null if no BOM found.
343- * @throws IOException
344- * if an error reading the first bytes of the stream occurs.
334+ * @throws IOException if an error reading the first bytes of the stream occurs.
345335 */
346336 public String getBOMCharsetName () throws IOException {
347- getBOM ();
348337 return byteOrderMark == null ? null : byteOrderMark .getCharsetName ();
349338 }
350339
351340 /**
352341 * Tests whether the stream contains one of the specified BOMs.
353342 *
354343 * @return true if the stream has one of the specified BOMs, otherwise false if it does not.
355- * @throws IOException
356- * if an error reading the first bytes of the stream occurs.
344+ * @throws IOException if an error reading the first bytes of the stream occurs.
357345 */
358346 public boolean hasBOM () throws IOException {
359347 return getBOM () != null ;
@@ -362,13 +350,10 @@ public boolean hasBOM() throws IOException {
362350 /**
363351 * Tests whether the stream contains the specified BOM.
364352 *
365- * @param bom
366- * The BOM to check for.
353+ * @param bom The BOM to check for.
367354 * @return true if the stream has the specified BOM, otherwise false if it does not.
368- * @throws IllegalArgumentException
369- * if the BOM is not one the stream is configured to detect.
370- * @throws IOException
371- * if an error reading the first bytes of the stream occurs.
355+ * @throws IllegalArgumentException if the BOM is not one the stream is configured to detect.
356+ * @throws IOException if an error reading the first bytes of the stream occurs.
372357 */
373358 public boolean hasBOM (final ByteOrderMark bom ) throws IOException {
374359 if (!bomList .contains (bom )) {
@@ -378,10 +363,9 @@ public boolean hasBOM(final ByteOrderMark bom) throws IOException {
378363 }
379364
380365 /**
381- * Invokes the delegate's {@code mark(int)} method.
366+ * Invokes the delegate's {@link InputStream# mark(int)} method.
382367 *
383- * @param readLimit
384- * read ahead limit.
368+ * @param readLimit read ahead limit.
385369 */
386370 @ Override
387371 public synchronized void mark (final int readLimit ) {
@@ -393,9 +377,8 @@ public synchronized void mark(final int readLimit) {
393377 /**
394378 * Checks if the bytes match a BOM.
395379 *
396- * @param bom
397- * The BOM.
398- * @return true if the bytes match the bom, otherwise false.
380+ * @param bom The BOM.
381+ * @return true if the bytes match the BOM, otherwise false.
399382 */
400383 private boolean matches (final ByteOrderMark bom ) {
401384 return bom .matches (firstBytes );
@@ -405,8 +388,7 @@ private boolean matches(final ByteOrderMark bom) {
405388 * Invokes the delegate's {@code read()} method, detecting and optionally skipping BOM.
406389 *
407390 * @return the byte read (excluding BOM) or -1 if the end of stream.
408- * @throws IOException
409- * if an I/O error occurs.
391+ * @throws IOException if an I/O error occurs.
410392 */
411393 @ Override
412394 public int read () throws IOException {
@@ -416,37 +398,28 @@ public int read() throws IOException {
416398 }
417399
418400 /**
419- * Invokes the delegate's {@code read(byte[])} method, detecting and optionally skipping BOM.
401+ * Invokes the delegate's {@link InputStream# read(byte[])} method, detecting and optionally skipping BOM.
420402 *
421- * @param buf
422- * the buffer to read the bytes into, never {@code null}
403+ * @param buf the buffer to read the bytes into, never {@code null}
423404 * @return the number of bytes read (excluding BOM) or -1 if the end of stream.
424- * @throws NullPointerException
425- * if the buffer is {@code null}
426- * @throws IOException
427- * if an I/O error occurs.
405+ * @throws NullPointerException if the buffer is {@code null}
406+ * @throws IOException if an I/O error occurs.
428407 */
429408 @ Override
430409 public int read (final byte [] buf ) throws IOException {
431410 return read (buf , 0 , buf .length );
432411 }
433412
434413 /**
435- * Invokes the delegate's {@code read(byte[], int, int)} method, detecting and optionally skipping BOM.
414+ * Invokes the delegate's {@link InputStream# read(byte[], int, int)} method, detecting and optionally skipping BOM.
436415 *
437- * @param buf
438- * the buffer to read the bytes into.
439- * @param off
440- * The start offset.
441- * @param len
442- * The number of bytes to read (excluding BOM).
416+ * @param buf the buffer to read the bytes into.
417+ * @param off The start offset.
418+ * @param len The number of bytes to read (excluding BOM).
443419 * @return the number of bytes read or -1 if the end of stream.
444- * @throws NullPointerException
445- * if the buffer is {@code null}
446- * @throws IndexOutOfBoundsException
447- * if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code buf.length}
448- * @throws IOException
449- * if an I/O error occurs.
420+ * @throws NullPointerException if the buffer is {@code null}.
421+ * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code buf.length}.
422+ * @throws IOException if an I/O error occurs.
450423 */
451424 @ Override
452425 public int read (final byte [] buf , int off , int len ) throws IOException {
@@ -469,6 +442,12 @@ public int read(final byte[] buf, int off, int len) throws IOException {
469442 return secondCount < 0 ? firstCount > 0 ? firstCount : EOF : firstCount + secondCount ;
470443 }
471444
445+ /**
446+ * Reads the byte order mark.
447+ *
448+ * @return the byte order mark.
449+ * @throws IOException if an error reading the first bytes of the stream occurs.
450+ */
472451 private ByteOrderMark readBom () throws IOException {
473452 int fbLength = 0 ;
474453 // BOMs are sorted from longest to shortest
@@ -504,15 +483,13 @@ private ByteOrderMark readBom() throws IOException {
504483 * @throws IOException if an I/O error occurs.
505484 */
506485 private int readFirstBytes () throws IOException {
507- getBOM ();
508486 return fbIndex < firstBytes .length ? firstBytes [fbIndex ++] : EOF ;
509487 }
510488
511489 /**
512- * Invokes the delegate's {@code reset()} method.
490+ * Invokes the delegate's {@link InputStream# reset()} method.
513491 *
514- * @throws IOException
515- * if an I/O error occurs.
492+ * @throws IOException if an I/O error occurs.
516493 */
517494 @ Override
518495 public synchronized void reset () throws IOException {
@@ -524,13 +501,11 @@ public synchronized void reset() throws IOException {
524501 }
525502
526503 /**
527- * Invokes the delegate's {@code skip(long)} method, detecting and optionally skipping BOM.
504+ * Invokes the delegate's {@link InputStream# skip(long)} method, detecting and optionally skipping BOM.
528505 *
529- * @param n
530- * the number of bytes to skip.
506+ * @param n the number of bytes to skip.
531507 * @return the number of bytes to skipped or -1 if the end of stream.
532- * @throws IOException
533- * if an I/O error occurs.
508+ * @throws IOException if an I/O error occurs.
534509 */
535510 @ Override
536511 public long skip (final long n ) throws IOException {
0 commit comments