Exemplo n.º 1
0
  /**
   * Saves the tags in this dataType to the file argument. It will be saved as
   * TagConstants.MP3_FILE_SAVE_WRITE
   *
   * @param fileToSave file to save the this dataTypes tags to
   * @throws FileNotFoundException if unable to find file
   * @throws IOException on any I/O error
   */
  public void save(File fileToSave) throws IOException {
    // Ensure we are dealing with absolute filepaths not relative ones
    File file = fileToSave.getAbsoluteFile();

    logger.config("Saving  : " + file.getPath());

    // Checks before starting write
    precheck(file);

    RandomAccessFile rfile = null;
    try {
      // ID3v2 Tag
      if (TagOptionSingleton.getInstance().isId3v2Save()) {
        if (id3v2tag == null) {
          rfile = new RandomAccessFile(file, "rw");
          (new ID3v24Tag()).delete(rfile);
          (new ID3v23Tag()).delete(rfile);
          (new ID3v22Tag()).delete(rfile);
          logger.config("Deleting ID3v2 tag:" + file.getName());
          rfile.close();
        } else {
          logger.config("Writing ID3v2 tag:" + file.getName());
          final MP3AudioHeader mp3AudioHeader = (MP3AudioHeader) this.getAudioHeader();
          final long mp3StartByte = mp3AudioHeader.getMp3StartByte();
          final long newMp3StartByte = id3v2tag.write(file, mp3StartByte);
          if (mp3StartByte != newMp3StartByte) {
            logger.config("New mp3 start byte: " + newMp3StartByte);
            mp3AudioHeader.setMp3StartByte(newMp3StartByte);
          }
        }
      }
      rfile = new RandomAccessFile(file, "rw");

      // Lyrics 3 Tag
      if (TagOptionSingleton.getInstance().isLyrics3Save()) {
        if (lyrics3tag != null) {
          lyrics3tag.write(rfile);
        }
      }
      // ID3v1 tag
      if (TagOptionSingleton.getInstance().isId3v1Save()) {
        logger.config("Processing ID3v1");
        if (id3v1tag == null) {
          logger.config("Deleting ID3v1");
          (new ID3v1Tag()).delete(rfile);
        } else {
          logger.config("Saving ID3v1");
          id3v1tag.write(rfile);
        }
      }
    } catch (FileNotFoundException ex) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_NOT_FOUND.getMsg(file.getName()),
          ex);
      throw ex;
    } catch (IOException iex) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), iex.getMessage()),
          iex);
      throw iex;
    } catch (RuntimeException re) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(file.getName(), re.getMessage()),
          re);
      throw re;
    } finally {
      if (rfile != null) {
        rfile.close();
      }
    }
  }
  // TODO Creates temp file in same folder as the original file, this is safe but would impose a
  // performance
  // overhead if the original file is on a networked drive
  public synchronized void write(AudioFile af) throws CannotWriteException {
    logger.info("Started writing tag data for file:" + af.getFile().getName());

    // Prechecks
    precheckWrite(af);

    RandomAccessFile raf = null;
    RandomAccessFile rafTemp = null;
    File newFile = null;
    File result = null;

    // Create temporary File
    try {
      newFile =
          File.createTempFile(
              af.getFile().getName().replace('.', '_'),
              TEMP_FILENAME_SUFFIX,
              af.getFile().getParentFile());
    }
    // Unable to create temporary file, can happen in Vista if have Create Files/Write Data set to
    // Deny
    catch (IOException ioe) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER.getMsg(
              af.getFile().getName(), af.getFile().getParentFile().getAbsolutePath()),
          ioe);
      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER.getMsg(
              af.getFile().getName(), af.getFile().getParentFile().getAbsolutePath()));
    }

    // Open temporary file and actual file for Editing
    try {
      rafTemp = new RandomAccessFile(newFile, WRITE_MODE);
      raf = new RandomAccessFile(af.getFile(), WRITE_MODE);

    }
    // Unable to write to writable file, can happen in Vista if have Create Folders/Append Data set
    // to Deny
    catch (IOException ioe) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING.getMsg(
              af.getFile().getAbsolutePath()),
          ioe);

      // If we managed to open either file, delete it.
      try {
        if (raf != null) {
          raf.close();
        }
        if (rafTemp != null) {
          rafTemp.close();
        }
      } catch (IOException ioe2) {
        // Warn but assume has worked okay
        logger.log(
            Level.WARNING,
            ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg(
                af.getFile(), ioe.getMessage()),
            ioe2);
      }

      // Delete the temp file ( we cannot delet until closed correpsonding rafTemp)
      if (!newFile.delete()) {
        // Non critical failed deletion
        logger.warning(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(
                newFile.getAbsolutePath()));
      }

      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING.getMsg(
              af.getFile().getAbsolutePath()));
    }

    // Write data to File
    try {

      raf.seek(0);
      rafTemp.seek(0);
      try {
        if (this.modificationListener != null) {
          this.modificationListener.fileWillBeModified(af, false);
        }
        writeTag(af.getTag(), raf, rafTemp);
        if (this.modificationListener != null) {
          this.modificationListener.fileModified(af, newFile);
        }
      } catch (ModifyVetoException veto) {
        throw new CannotWriteException(veto);
      }
    } catch (Exception e) {
      logger.log(
          Level.SEVERE,
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(af.getFile(), e.getMessage()),
          e);

      try {
        if (raf != null) {
          raf.close();
        }
        if (rafTemp != null) {
          rafTemp.close();
        }
      } catch (IOException ioe) {
        // Warn but assume has worked okay
        logger.log(
            Level.WARNING,
            ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg(
                af.getFile().getAbsolutePath(), ioe.getMessage()),
            ioe);
      }

      // Delete the temporary file because either it was never used so lets just tidy up or we did
      // start writing to it but
      // the write failed and we havent renamed it back to the original file so we can just delete
      // it.
      if (!newFile.delete()) {
        // Non critical failed deletion
        logger.warning(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(
                newFile.getAbsolutePath()));
      }
      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(af.getFile(), e.getMessage()));
    } finally {
      try {
        if (raf != null) {
          raf.close();
        }
        if (rafTemp != null) {
          rafTemp.close();
        }
      } catch (IOException ioe) {
        // Warn but assume has worked okay
        logger.log(
            Level.WARNING,
            ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg(
                af.getFile().getAbsolutePath(), ioe.getMessage()),
            ioe);
      }
    }

    // Result held in this file
    result = af.getFile();

    // If the temporary file was used
    if (newFile.length() > 0) {
      // Rename Original File
      // Can fail on Vista if have Special Permission 'Delete' set Deny
      File originalFileBackup =
          new File(
              af.getFile().getParentFile().getPath(),
              AudioFile.getBaseFilename(af.getFile()) + ".old");
      boolean renameResult = af.getFile().renameTo(originalFileBackup);
      if (renameResult == false) {
        logger.log(
            Level.SEVERE,
            ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP.getMsg(
                af.getFile().getPath(), originalFileBackup.getName()));
        throw new CannotWriteException(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP.getMsg(
                af.getFile().getPath(), originalFileBackup.getName()));
      }

      // Rename Temp File to Original File
      renameResult = newFile.renameTo(af.getFile());
      if (!renameResult) {
        // Renamed failed so lets do some checks rename the backup back to the original file
        // New File doesnt exist
        if (!newFile.exists()) {
          logger.warning(
              ErrorMessage.GENERAL_WRITE_FAILED_NEW_FILE_DOESNT_EXIST.getMsg(
                  newFile.getAbsolutePath()));
        }

        // Rename the backup back to the original
        if (!originalFileBackup.renameTo(af.getFile())) {
          // TODO now if this happens we are left with testfile.old instead of testfile.mp4
          logger.warning(
              ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_BACKUP_TO_ORIGINAL.getMsg(
                  originalFileBackup.getAbsolutePath(), af.getFile().getName()));
        }

        logger.warning(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg(
                af.getFile().getAbsolutePath(), newFile.getName()));
        throw new CannotWriteException(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg(
                af.getFile().getAbsolutePath(), newFile.getName()));
      } else {
        // Rename was okay so we can now delete the backup of the original
        boolean deleteResult = originalFileBackup.delete();
        if (!deleteResult) {
          // Not a disaster but can't delete the backup so make a warning
          logger.warning(
              ErrorMessage.GENERAL_WRITE_WARNING_UNABLE_TO_DELETE_BACKUP_FILE.getMsg(
                  originalFileBackup.getAbsolutePath()));
        }
      }

      // Delete the temporary file if still exists
      if (newFile.exists()) {
        if (!newFile.delete()) {
          // Non critical failed deletion
          logger.warning(
              ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(newFile.getPath()));
        }
      }
    } else {
      // Delete the temporary file that wasn't ever used
      if (!newFile.delete()) {
        // Non critical failed deletion
        logger.warning(
            ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(newFile.getPath()));
      }
    }

    if (this.modificationListener != null) {
      this.modificationListener.fileOperationFinished(result);
    }
  }