View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.spi;
6   
7   import java.io.File;
8   import java.io.IOException;
9   import java.io.InputStream;
10  import java.nio.file.Files;
11  
12  /**
13   * A file that is considered binary.
14   *
15   * <p>Detects binary files using the classic NULL-byte heuristic: if any
16   * of the first {@link #SNIFF} bytes of the file is {@code 0x00}, the file
17   * is treated as binary. This is the same rule used by {@code git diff}
18   * and GNU {@code grep} to decide whether a file is text or binary. It
19   * intentionally misclassifies nothing for typical source code (Java, XML,
20   * YAML, JSON, UTF-8) while correctly ignoring images, archives, compiled
21   * classes and similar blobs that should not be fed to text-based
22   * validators such as Checkstyle's {@code RegexpSinglelineCheck}.</p>
23   *
24   * <p>A file that does not exist, is not a regular file, or is empty is
25   * considered non-binary, so the caller may still attempt to open it and
26   * fail fast with a clear error, rather than silently skipping it here.</p>
27   *
28   * @since 0.24
29   */
30  public final class Binary {
31  
32      /**
33       * Amount of bytes to sniff from the head of the file.
34       */
35      private static final int SNIFF = 8192;
36  
37      /**
38       * The file to inspect.
39       */
40      private final File file;
41  
42      /**
43       * Ctor.
44       * @param src File to inspect
45       */
46      public Binary(final File src) {
47          this.file = src;
48      }
49  
50      /**
51       * Is the file binary?
52       * @return TRUE if the first {@value #SNIFF} bytes contain a NULL byte
53       */
54      public boolean yes() {
55          boolean binary = false;
56          if (this.file.isFile()) {
57              try (InputStream stream = Files.newInputStream(this.file.toPath())) {
58                  final byte[] buffer = new byte[Binary.SNIFF];
59                  final int read = stream.read(buffer);
60                  for (int idx = 0; idx < read; ++idx) {
61                      if (buffer[idx] == 0) {
62                          binary = true;
63                          break;
64                      }
65                  }
66              } catch (final IOException ex) {
67                  throw new IllegalStateException(
68                      String.format(
69                          "Failed to inspect file '%s' for binary content",
70                          this.file
71                      ),
72                      ex
73                  );
74              }
75          }
76          return binary;
77      }
78  }