/**
   * Write (either create or replace) a binary file on the local machine with one read from the
   * server.
   *
   * @param path the absolute path of the file, (including the file name).
   * @param mode the mode of the file
   * @param dis the stream to read the file from, as bytes
   * @param length the number of bytes to read
   */
  @Override
  public void writeBinaryFile(String path, String mode, LoggedDataInputStream dis, int length)
      throws IOException {
    if (DEBUG) {
      System.err.println("[writeBinaryFile] writing: " + path); // NOI18N
      System.err.println("[writeBinaryFile] length: " + length); // NOI18N
      System.err.println("Reader object is: " + dis.hashCode()); // NOI18N
    }

    File file = new File(path);

    boolean readOnly = resetReadOnly(file);

    createNewFile(file);
    // FUTURE: optimisation possible - no need to use a temp file if there
    // is no post processing required (e.g. unzipping). So perhaps enhance
    // the interface to allow this stage to be optional
    File cvsDir = new File(file.getParentFile(), "CVS");
    cvsDir.mkdir();
    File tempFile = File.createTempFile("cvsPostConversion", "tmp", cvsDir); // NOI18N

    try {
      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tempFile));

      byte[] chunk = new byte[CHUNK_SIZE];
      try {
        while (length > 0) {
          int bytesToRead = length >= CHUNK_SIZE ? CHUNK_SIZE : (int) length;
          int count = dis.read(chunk, 0, bytesToRead);
          if (count == -1) {
            throw new IOException(
                "Unexpected end of stream: "
                    + path
                    + "\nMissing "
                    + length
                    + " bytes. Probably network communication failure.\nPlease try again."); // NOI18N
          }
          if (count < 0) {
            break;
          }

          length -= count;
          if (DEBUG) {
            System.err.println("Still got: " + length + " to read"); // NOI18N
          }
          bos.write(chunk, 0, count);
        }
      } finally {
        bos.close();
      }

      // Here we read the temp file in, taking the opportunity to process
      // the file, e.g. unzip the data
      BufferedInputStream tempIS = new BufferedInputStream(getProcessedInputStream(tempFile));
      bos = new BufferedOutputStream(createOutputStream(file));

      try {
        for (int count = tempIS.read(chunk, 0, CHUNK_SIZE);
            count > 0;
            count = tempIS.read(chunk, 0, CHUNK_SIZE)) {
          bos.write(chunk, 0, count);
        }
      } finally {
        bos.close();
        tempIS.close();
      }

      // now we need to modifiy the timestamp on the file, if specified
      if (modifiedDate != null) {
        file.setLastModified(modifiedDate.getTime());
        modifiedDate = null;
      }
    } finally {
      tempFile.delete();
    }

    if (readOnly) {
      FileUtils.setFileReadOnly(file, true);
    }
  }
  /**
   * Common code for writeTextFile() and writeRcsDiffFile() methods. Differs only in the passed file
   * processor.
   */
  private void writeAndPostProcessTextFile(
      String path,
      String mode,
      LoggedDataInputStream dis,
      int length,
      WriteTextFilePreprocessor processor)
      throws IOException {
    if (DEBUG) {
      System.err.println("[writeTextFile] writing: " + path); // NOI18N
      System.err.println("[writeTextFile] length: " + length); // NOI18N
      System.err.println("Reader object is: " + dis.hashCode()); // NOI18N
    }

    File file = new File(path);

    boolean readOnly = resetReadOnly(file);

    createNewFile(file);
    // For CRLF conversion, we have to read the file
    // into a temp file, then do the conversion. This is because we cannot
    // perform a sequence of readLines() until we've read the file from
    // the server - the file transmission is not followed by a newline.
    // Bah.
    File tempFile = File.createTempFile("cvsCRLF", "tmp"); // NOI18N

    try {
      OutputStream os = null;
      try {
        os = new BufferedOutputStream(new FileOutputStream(tempFile));
        byte[] chunk = new byte[CHUNK_SIZE];
        while (length > 0) {
          int count = length >= CHUNK_SIZE ? CHUNK_SIZE : length;
          count = dis.read(chunk, 0, count);
          if (count == -1) {
            throw new IOException(
                "Unexpected end of stream: "
                    + path
                    + "\nMissing "
                    + length
                    + " bytes. Probably network communication failure.\nPlease try again."); // NOI18N
          }
          length -= count;
          if (DEBUG) {
            System.err.println("Still got: " + length + " to read"); // NOI18N
          }
          os.write(chunk, 0, count);
        }
      } finally {
        if (os != null) {
          try {
            os.close();
          } catch (IOException ex) {
            // ignore
          }
        }
      }

      // Here we read the temp file in again, doing any processing required
      // (for example, unzipping). We must not convert bytes to characters
      // because it would break characters that are not in the current encoding
      InputStream tempInput = getProcessedInputStream(tempFile);

      try {
        // BUGLOG - assert the processor is not null..
        processor.copyTextFileToLocation(tempInput, file, new StreamProvider(file));
      } finally {
        tempInput.close();
      }

      if (modifiedDate != null) {
        file.setLastModified(modifiedDate.getTime());
        modifiedDate = null;
      }
    } finally {
      tempFile.delete();
    }

    if (readOnly) {
      FileUtils.setFileReadOnly(file, true);
    }
  }