private void fillMetaInfo() {
   if (tag == null) {
     title = audioFile.getFile().getName();
     artist = "";
     album = "";
     album_artist = "";
     track = "";
     composer = "";
     year = "";
     diskNumber = "";
     genre = "";
   } else {
     title = tag.getFirst(FieldKey.TITLE);
     if (title.equals("")) {
       title = audioFile.getFile().getName();
     }
     artist = tag.getFirst(FieldKey.ARTIST);
     album = tag.getFirst(FieldKey.ALBUM);
     album_artist = tag.getFirst(FieldKey.ALBUM_ARTIST);
     track = tag.getFirst(FieldKey.TRACK);
     composer = tag.getFirst(FieldKey.COMPOSER);
     year = tag.getFirst(FieldKey.YEAR);
     diskNumber = tag.getFirst(FieldKey.DISC_NO);
     genre = tag.getFirst(FieldKey.GENRE);
   }
 }
  /**
   * Prechecks before normal write
   *
   * <p>
   *
   * <ul>
   *   <li>If the tag is actually empty, remove the tag
   *   <li>if the file is not writable, throw exception
   *   <li>
   *   <li>If the file is too small to be a valid file, throw exception
   *   <li>
   * </ul>
   *
   * @param af
   * @throws CannotWriteException
   */
  private void precheckWrite(AudioFile af) throws CannotWriteException {
    // Preliminary checks
    try {
      if (af.getTag().isEmpty()) {
        delete(af);
        return;
      }
    } catch (CannotReadException re) {
      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath()));
    }

    if (!af.getFile().canWrite()) {
      logger.severe(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath()));
      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath()));
    }

    if (af.getFile().length() <= MINIMUM_FILESIZE) {
      logger.severe(
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(
              af.getFile().getPath()));
      throw new CannotWriteException(
          ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg(
              af.getFile().getPath()));
    }
  }
  /**
   * Update the GridFSDBFile in the associated DB with the key/values in updateKeys
   *
   * @param updateKeys Map of new tag data
   * @param file GridFSDBFile to update with tag data
   * @param db
   * @param songId ID of Song to update with tag data
   * @return
   */
  public static boolean updateFile(
      Map<String, String> updateKeys,
      GridFSDBFile file,
      DB db,
      ObjectId songId) { // TODO updateKeys?
    File audioFile = null;
    try {
      audioFile = File.createTempFile("tmp", ".mp3");
    } catch (IOException e) {
      log.error("tmp file not created", e);
    }

    audioFile.deleteOnExit();
    AudioFile f = null;
    ObjectId id = (ObjectId) file.getId();
    ObjectId oid = null;
    try {
      file.writeTo(audioFile);
      f = AudioFileIO.read(audioFile);
      Tag tag = f.getTagOrCreateAndSetDefault();
      DBObject q = new BasicDBObject("_id", songId);
      DBObject o = new BasicDBObject("$set", new BasicDBObject(updateKeys));

      if (updateKeys.get("artist") != null) {
        tag.setField(FieldKey.ARTIST, updateKeys.get("artist"));
      }
      if (updateKeys.get("album") != null) {
        tag.setField(FieldKey.ALBUM, updateKeys.get("album"));
      }
      if (updateKeys.get("title") != null) {
        tag.setField(FieldKey.TITLE, updateKeys.get("title"));
      }
      if (updateKeys.get("track") != null) {
        tag.setField(FieldKey.TRACK, updateKeys.get("track"));
      }
      if (updateKeys.get("year") != null) {
        tag.setField(FieldKey.YEAR, updateKeys.get("year"));
      }
      AudioFileIO.write(f);
      GridFS myFS = new GridFS(db);
      myFS.remove(id);
      GridFSInputFile inputFile =
          putSongFileInDB(f.getFile(), db, file.getContentType(), file.getFilename(), id);
      oid = (ObjectId) inputFile.getId();
      if (oid.equals(id)) {
        db.getCollection("songs").update(q, o);
      }
    } catch (KeyNotFoundException knfe) {
      log.error("key not found", knfe);
    } catch (FieldDataInvalidException fdie) {
      log.error("tried to set field with invalid value", fdie);
    } catch (Exception e) {
      log.error("error reading/writing file", e);
    }
    return (oid.equals(id));
  }
  /**
   * This will write a custom ID3 tag (TXXX). This works only with MP3 files (Flac with ID3-Tag not
   * tested).
   *
   * @param description The description of the custom tag i.e. "catalognr" There can only be one
   *     custom TXXX tag with that description in one MP3 file
   * @param text The actual text to be written into the new tag field
   * @return True if the tag has been properly written, false otherwise
   */
  public static boolean setCustomTag(AudioFile audioFile, String description, String text)
      throws IOException {
    FrameBodyTXXX txxxBody = new FrameBodyTXXX();
    txxxBody.setDescription(description);
    txxxBody.setText(text);

    // Get the tag from the audio file
    // If there is no ID3Tag create an ID3v2.3 tag
    Tag tag = audioFile.getTagOrCreateAndSetDefault();
    if (tag instanceof AbstractID3Tag) {
      // If there is only a ID3v1 tag, copy data into new ID3v2.3 tag
      if (!(tag instanceof ID3v23Tag || tag instanceof ID3v24Tag)) {
        Tag newTagV23 = null;
        if (tag instanceof ID3v1Tag) {
          newTagV23 =
              new ID3v23Tag((ID3v1Tag) audioFile.getTag()); // Copy old tag data
        }
        if (tag instanceof ID3v22Tag) {
          newTagV23 =
              new ID3v23Tag((ID3v22Tag) audioFile.getTag()); // Copy old tag data
        }
        audioFile.setTag(newTagV23);
        tag = newTagV23;
      }

      AbstractID3v2Frame frame = null;
      if (tag instanceof ID3v23Tag) {
        if (((ID3v23Tag) audioFile.getTag()).getInvalidFrames() > 0) {
          throw new IOException("read some invalid frames!");
        }
        frame = new ID3v23Frame("TXXX");
      } else if (tag instanceof ID3v24Tag) {
        if (((ID3v24Tag) audioFile.getTag()).getInvalidFrames() > 0) {
          throw new IOException("read some invalid frames!");
        }
        frame = new ID3v24Frame("TXXX");
      }

      frame.setBody(txxxBody);

      try {
        tag.setField(frame);
      } catch (FieldDataInvalidException e) {
        Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, e);
        return false;
      }
    } else if (tag instanceof FlacTag) {
      try {
        ((FlacTag) tag).setField(description, text);
      } catch (KeyNotFoundException ex) {
        Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
        return false;
      } catch (FieldDataInvalidException ex) {
        return false;
      }
    } else if (tag instanceof Mp4Tag) {
      // TagField field = new Mp4TagTextField("----:com.apple.iTunes:"+description, text);
      TagField field;
      field =
          new Mp4TagReverseDnsField(
              Mp4TagReverseDnsField.IDENTIFIER + ":" + "com.apple.iTunes" + ":" + description,
              "com.apple.iTunes",
              description,
              text);
      // TagField field = new Mp4TagTextField(description, text);
      try {
        tag.setField(field);
      } catch (FieldDataInvalidException ex) {
        Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
        return false;
      }
    } else if (tag instanceof VorbisCommentTag) {
      try {
        ((VorbisCommentTag) tag).setField(description, text);
      } catch (KeyNotFoundException ex) {
        Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
        return false;
      } catch (FieldDataInvalidException ex) {
        Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex);
        return false;
      }
    } else {
      // tag not implented
      Logger.getLogger(TrackAnalyzer.class.getName())
          .log(
              Level.WARNING,
              "couldn't write key information for "
                  + audioFile.getFile().getName()
                  + " to tag, because this format is not supported.");
      return false;
    }

    // write changes in tag to file
    try {
      audioFile.commit();
    } catch (CannotWriteException e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }
  /**
   * Delete the tag (if any) present in the given file
   *
   * @param af The file to process
   * @throws CannotWriteException if anything went wrong
   */
  public synchronized void delete(AudioFile af) throws CannotReadException, CannotWriteException {
    if (!af.getFile().canWrite()) {
      throw new CannotWriteException(
          ErrorMessage.GENERAL_DELETE_FAILED.getMsg(af.getFile().getPath()));
    }

    if (af.getFile().length() <= MINIMUM_FILESIZE) {
      throw new CannotWriteException(
          ErrorMessage.GENERAL_DELETE_FAILED.getMsg(af.getFile().getPath()));
    }

    RandomAccessFile raf = null;
    RandomAccessFile rafTemp = null;
    File tempF = null;

    // Will be set to true on VetoException, causing the finally block to discard the tempfile.
    boolean revert = false;

    try {

      tempF =
          File.createTempFile(
              af.getFile().getName().replace('.', '_'),
              TEMP_FILENAME_SUFFIX,
              af.getFile().getParentFile());
      rafTemp = new RandomAccessFile(tempF, WRITE_MODE);
      raf = new RandomAccessFile(af.getFile(), WRITE_MODE);
      raf.seek(0);
      rafTemp.seek(0);

      try {
        if (this.modificationListener != null) {
          this.modificationListener.fileWillBeModified(af, true);
        }
        deleteTag(raf, rafTemp);
        if (this.modificationListener != null) {
          this.modificationListener.fileModified(af, tempF);
        }
      } catch (ModifyVetoException veto) {
        throw new CannotWriteException(veto);
      }

    } catch (Exception e) {
      revert = true;
      throw new CannotWriteException("\"" + af.getFile().getAbsolutePath() + "\" :" + e, e);
    } finally {
      // will be set to the remaining file.
      File result = af.getFile();
      try {
        if (raf != null) {
          raf.close();
        }
        if (rafTemp != null) {
          rafTemp.close();
        }

        if (tempF.length() > 0 && !revert) {
          boolean deleteResult = af.getFile().delete();
          if (deleteResult == false) {
            logger.warning(
                ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE.getMsg(
                    af.getFile().getPath(), tempF.getPath()));
            throw new CannotWriteException(
                ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE.getMsg(
                    af.getFile().getPath(), tempF.getPath()));
          }
          boolean renameResult = tempF.renameTo(af.getFile());
          if (renameResult == false) {
            logger.warning(
                ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg(
                    af.getFile().getPath(), tempF.getPath()));
            throw new CannotWriteException(
                ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg(
                    af.getFile().getPath(), tempF.getPath()));
          }
          result = tempF;

          // If still exists we can now delete
          if (tempF.exists()) {
            if (!tempF.delete()) {
              // Non critical failed deletion
              logger.warning(
                  ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(
                      tempF.getPath()));
            }
          }
        } else {
          // It was created but never used
          if (!tempF.delete()) {
            // Non critical failed deletion
            logger.warning(
                ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(tempF.getPath()));
          }
        }
      } catch (Exception ex) {
        logger.severe(
            "AudioFileWriter exception cleaning up delete:"
                + af.getFile().getPath()
                + " or"
                + tempF.getAbsolutePath()
                + ":"
                + ex);
      }
      // Notify listener
      if (this.modificationListener != null) {
        this.modificationListener.fileOperationFinished(result);
      }
    }
  }
  // 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);
    }
  }