class FastTextException extends RuntimeException {
    private final boolean disabled;

    public FastTextException(String message, boolean disabled) {
      super(message);
      this.disabled = disabled;
    }

    /**
     * Should {@link FastTextDetector} be disable after this exception
    */
    public boolean isDisabled() {
      return disabled;
    }
  }

  public FastTextDetector(File modelPath, File binaryPath) throws IOException {
    this.modelPath = modelPath;
    this.binaryPath = binaryPath;
    init();
  }
  
  private void init() throws IOException{
    fasttextProcess = new ProcessBuilder(binaryPath.getPath(), "predict-prob", modelPath.getPath(), "-", "" + K_HIGHEST_SCORES).start();
    // avoid buffering, we want to flush/read all data immediately
    // might cause mixup
    fasttextIn = new InputStreamReader(fasttextProcess.getInputStream(), StandardCharsets.UTF_8);
    fasttextOut = new OutputStreamWriter(fasttextProcess.getOutputStream(), StandardCharsets.UTF_8);
  }

  // for tests only
  FastTextDetector() {
    fasttextProcess = null;
    fasttextIn = null;
    fasttextOut = null;
  }

  public Map<String, Double> runFasttext(String text, List<String> additionalLanguageCodes) throws IOException {
    String joined = text.replace('\n', ' ').toLowerCase(Locale.ROOT);
    char[] cbuf = new char[BUFFER_SIZE];
    synchronized (this) {
      fasttextOut.write(joined + System.lineSeparator());
      fasttextOut.flush();
      long read = fasttextIn.read(cbuf);
      if (read <= 0) {
        // hack to see if this helps us debug the rare case of readLine() returning null:
        try {
          logger.warn("fasttextIn.read() returned no data, trying again after short delay");
          Thread.sleep(10);
          read = fasttextIn.read(cbuf);
          if (read == -1) {
            logger.warn("fasttextIn.read() returned no data again");
          }
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      }
      if (fasttextIn.ready()) {
        logger.warn("More input to read from Fasttext, this should not happen; language detection results might be mixed up");
      }
    }
    return parseBuffer(new String(cbuf), additionalLanguageCodes);
  }

  @NotNull
  Map<String, Double> parseBuffer(String buffer, List<String> additionalLanguageCodes) {<FILL_FUNCTION_BODY>

    String[] values = WHITESPACE.split(buffer.trim());
    if (!buffer.startsWith("__label__")) {
      throw new FastTextException("FastText output is expected to start with '__label__': ''" + buffer + "'", true);
    }
    if (values.length % 2 != 0) {
      throw new FastTextException("Error while parsing fasttext output, expected pairs of '__label_xx' and float: '" + buffer + "'", true);
    }
    if (buffer.trim().contains("\n")) {
      logger.warn("Got multiple lines to read from Fasttext, this should not happen: '" + buffer + "'" );
    }
    Map<String, Double> probabilities = new HashMap<>();
    for (int i = 0; i < values.length; i += 2) {
      String lang = values[i];
      String langCode = lang.substring(lang.lastIndexOf("__") + 2);
      String prob = values[i + 1];
      Double probValue = Double.parseDouble(prob);
      if (LanguageIdentifierService.INSTANCE.canLanguageBeDetected(langCode, additionalLanguageCodes)) {
        probabilities.put(langCode, probValue);
      }
    }
    return probabilities;
  
<no_super_class>