Coverage Report - org.jaudiotagger.audio.generic.AudioFileWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
AudioFileWriter
62%
101/162
46%
35/76
10.286
 
 1  
 /*
 2  
  * Entagged Audio Tag library
 3  
  * Copyright (c) 2003-2005 RaphaĆ«l Slinckx <raphael@slinckx.net>
 4  
  * 
 5  
  * This library is free software; you can redistribute it and/or
 6  
  * modify it under the terms of the GNU Lesser General Public
 7  
  * License as published by the Free Software Foundation; either
 8  
  * version 2.1 of the License, or (at your option) any later version.
 9  
  *  
 10  
  * This library is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
  * Lesser General Public License for more details.
 14  
  * 
 15  
  * You should have received a copy of the GNU Lesser General Public
 16  
  * License along with this library; if not, write to the Free Software
 17  
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 18  
  */
 19  
 package org.jaudiotagger.audio.generic;
 20  
 
 21  
 import org.jaudiotagger.audio.AudioFile;
 22  
 import org.jaudiotagger.audio.exceptions.CannotReadException;
 23  
 import org.jaudiotagger.audio.exceptions.CannotWriteException;
 24  
 import org.jaudiotagger.audio.exceptions.ModifyVetoException;
 25  
 import org.jaudiotagger.logging.ErrorMessage;
 26  
 import org.jaudiotagger.tag.Tag;
 27  
 
 28  
 import java.io.File;
 29  
 import java.io.IOException;
 30  
 import java.io.RandomAccessFile;
 31  
 import java.util.logging.Level;
 32  
 import java.util.logging.Logger;
 33  
 
 34  
 /**
 35  
  * This abstract class is the skeleton for tag writers.
 36  
  * <p/>
 37  
  * <p/>
 38  
  * It handles the creation/closing of the randomaccessfile objects and then call
 39  
  * the subclass method writeTag or deleteTag. These two method have to be
 40  
  * implemented in the subclass.
 41  
  *
 42  
  * @author Raphael Slinckx
 43  
  * @version $Id: AudioFileWriter.java,v 1.21 2009/05/05 15:59:14 paultaylor Exp
 44  
  *          $
 45  
  * @since v0.02
 46  
  */
 47  36
 public abstract class AudioFileWriter
 48  
 {
 49  
     private static final String TEMP_FILENAME_SUFFIX = ".tmp";
 50  
     private static final String WRITE_MODE = "rws";
 51  
     private static final int MINIMUM_FILESIZE = 150;
 52  
 
 53  
     // Logger Object
 54  4
     public static Logger logger = Logger
 55  
             .getLogger("org.jaudiotagger.audio.generic");
 56  
 
 57  
     /**
 58  
      * If not <code>null</code>, this listener is used to notify the listener
 59  
      * about modification events.<br>
 60  
      */
 61  36
     private AudioFileModificationListener modificationListener = null;
 62  
 
 63  
     /**
 64  
      * Delete the tag (if any) present in the given file
 65  
      *
 66  
      * @param af The file to process
 67  
      * @throws CannotWriteException if anything went wrong
 68  
      * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 69  
      */
 70  
     public synchronized void delete(AudioFile af) throws CannotReadException, CannotWriteException
 71  
     {
 72  76
         if (!af.getFile().canWrite())
 73  
         {
 74  0
             throw new CannotWriteException(ErrorMessage.GENERAL_DELETE_FAILED
 75  
                     .getMsg(af.getFile().getPath()));
 76  
         }
 77  
 
 78  76
         if (af.getFile().length() <= MINIMUM_FILESIZE)
 79  
         {
 80  0
             throw new CannotWriteException(ErrorMessage.GENERAL_DELETE_FAILED
 81  
                     .getMsg(af.getFile().getPath()));
 82  
         }
 83  
 
 84  76
         RandomAccessFile raf = null;
 85  76
         RandomAccessFile rafTemp = null;
 86  76
         File tempF = null;
 87  
 
 88  
         // Will be set to true on VetoException, causing the finally block to
 89  
         // discard the tempfile.
 90  76
         boolean revert = false;
 91  
 
 92  
         try
 93  
         {
 94  
 
 95  76
             tempF = File.createTempFile(af.getFile().getName()
 96  
                     .replace('.', '_'), TEMP_FILENAME_SUFFIX, af.getFile()
 97  
                     .getParentFile());
 98  76
             rafTemp = new RandomAccessFile(tempF, WRITE_MODE);
 99  76
             raf = new RandomAccessFile(af.getFile(), WRITE_MODE);
 100  76
             raf.seek(0);
 101  76
             rafTemp.seek(0);
 102  
 
 103  
             try
 104  
             {
 105  76
                 if (this.modificationListener != null)
 106  
                 {
 107  76
                     this.modificationListener.fileWillBeModified(af, true);
 108  
                 }
 109  76
                 deleteTag(raf, rafTemp);
 110  76
                 if (this.modificationListener != null)
 111  
                 {
 112  76
                     this.modificationListener.fileModified(af, tempF);
 113  
                 }
 114  
             }
 115  0
             catch (ModifyVetoException veto)
 116  
             {
 117  0
                 throw new CannotWriteException(veto);
 118  76
             }
 119  
 
 120  76
         }
 121  0
         catch (Exception e)
 122  
         {
 123  0
             revert = true;
 124  0
             throw new CannotWriteException("\"" + af.getFile().getAbsolutePath() + "\" :" + e, e);
 125  
         }
 126  
         finally
 127  
         {
 128  
             // will be set to the remaining file.
 129  0
             File result = af.getFile();
 130  
             try
 131  
             {
 132  76
                 if (raf != null)
 133  
                 {
 134  76
                     raf.close();
 135  
                 }
 136  76
                 if (rafTemp != null)
 137  
                 {
 138  76
                     rafTemp.close();
 139  
                 }
 140  
 
 141  76
                 if (tempF.length() > 0 && !revert)
 142  
                 {
 143  68
                     boolean deleteResult = af.getFile().delete();
 144  68
                     if (!deleteResult)
 145  
                     {
 146  0
                         logger
 147  
                                 .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE
 148  
                                         .getMsg(af.getFile().getPath(), tempF
 149  
                                         .getPath()));
 150  0
                         throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE
 151  
                                 .getMsg(af.getFile().getPath(), tempF
 152  
                                 .getPath()));
 153  
                     }
 154  68
                     boolean renameResult = tempF.renameTo(af.getFile());
 155  68
                     if (!renameResult)
 156  
                     {
 157  0
                         logger
 158  
                                 .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE
 159  
                                         .getMsg(af.getFile().getPath(), tempF
 160  
                                         .getPath()));
 161  0
                         throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE
 162  
                                 .getMsg(af.getFile().getPath(), tempF
 163  
                                 .getPath()));
 164  
                     }
 165  68
                     result = tempF;
 166  
 
 167  
                     // If still exists we can now delete
 168  68
                     if (tempF.exists())
 169  
                     {
 170  0
                         if (!tempF.delete())
 171  
                         {
 172  
                             // Non critical failed deletion
 173  0
                             logger
 174  
                                     .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 175  
                                             .getMsg(tempF.getPath()));
 176  
                         }
 177  
                     }
 178  68
                 }
 179  
                 else
 180  
                 {
 181  
                     // It was created but never used
 182  8
                     if (!tempF.delete())
 183  
                     {
 184  
                         // Non critical failed deletion
 185  0
                         logger
 186  
                                 .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 187  
                                         .getMsg(tempF.getPath()));
 188  
                     }
 189  
                 }
 190  
             }
 191  0
             catch (Exception ex)
 192  
             {
 193  0
                 logger.severe("AudioFileWriter exception cleaning up delete:" + af.getFile().getPath() + " or" + tempF.getAbsolutePath() + ":" + ex);
 194  76
             }
 195  
             // Notify listener
 196  76
             if (this.modificationListener != null)
 197  
             {
 198  76
                 this.modificationListener.fileOperationFinished(result);
 199  
             }
 200  152
         }
 201  76
     }
 202  
 
 203  
     /**
 204  
      * Delete the tag (if any) present in the given randomaccessfile, and do not
 205  
      * close it at the end.
 206  
      *
 207  
      * @param raf     The source file, already opened in r-write mode
 208  
      * @param tempRaf The temporary file opened in r-write mode
 209  
      * @throws CannotWriteException if anything went wrong
 210  
      * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 211  
      * @throws java.io.IOException
 212  
      */
 213  
     public synchronized void delete(RandomAccessFile raf, RandomAccessFile tempRaf) throws CannotReadException, CannotWriteException, IOException
 214  
     {
 215  0
         raf.seek(0);
 216  0
         tempRaf.seek(0);
 217  0
         deleteTag(raf, tempRaf);
 218  0
     }
 219  
 
 220  
     /**
 221  
      * Same as above, but delete tag in the file.
 222  
      *
 223  
      * @param raf
 224  
      * @param tempRaf
 225  
      * @throws IOException          is thrown when the RandomAccessFile operations throw it (you
 226  
      *                              should never throw them manually)
 227  
      * @throws CannotWriteException when an error occured during the deletion of the tag
 228  
      * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 229  
      */
 230  
     protected abstract void deleteTag(RandomAccessFile raf, RandomAccessFile tempRaf) throws CannotReadException, CannotWriteException, IOException;
 231  
 
 232  
     /**
 233  
      * This method sets the {@link AudioFileModificationListener}.<br>
 234  
      * There is only one listener allowed, if you want more instances to be
 235  
      * supported, use the {@link ModificationHandler} to broadcast those events.<br>
 236  
      *
 237  
      * @param listener The listener. <code>null</code> allowed to deregister.
 238  
      */
 239  
     public synchronized void setAudioFileModificationListener(AudioFileModificationListener listener)
 240  
     {
 241  36
         this.modificationListener = listener;
 242  36
     }
 243  
 
 244  
     /**
 245  
      * Prechecks before normal write
 246  
      * <p/>
 247  
      * <ul>
 248  
      * <li>If the tag is actually empty, remove the tag</li>
 249  
      * <li>if the file is not writable, throw exception
 250  
      * <li>
 251  
      * <li>If the file is too small to be a valid file, throw exception
 252  
      * <li>
 253  
      * </ul>
 254  
      *
 255  
      * @param af
 256  
      * @throws CannotWriteException
 257  
      */
 258  
     private void precheckWrite(AudioFile af) throws CannotWriteException
 259  
     {
 260  
         // Preliminary checks
 261  
         try
 262  
         {
 263  367
             if (af.getTag().isEmpty())
 264  
             {
 265  4
                 delete(af);
 266  4
                 return;
 267  
             }
 268  
         }
 269  0
         catch (CannotReadException re)
 270  
         {
 271  0
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED
 272  
                     .getMsg(af.getFile().getPath()));
 273  363
         }
 274  
 
 275  363
         if (!af.getFile().canWrite())
 276  
         {
 277  0
             logger.severe(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile()
 278  
                     .getPath()));
 279  0
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED
 280  
                     .getMsg(af.getFile().getPath()));
 281  
         }
 282  
 
 283  363
         if (af.getFile().length() <= MINIMUM_FILESIZE)
 284  
         {
 285  0
             logger
 286  
                     .severe(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL
 287  
                             .getMsg(af.getFile().getPath()));
 288  0
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL
 289  
                     .getMsg(af.getFile().getPath()));
 290  
         }
 291  363
     }
 292  
 
 293  
     /**
 294  
      * Write the tag (if not empty) present in the AudioFile in the associated
 295  
      * File
 296  
      *
 297  
      * @param af The file we want to process
 298  
      * @throws CannotWriteException if anything went wrong
 299  
      */
 300  
     // TODO Creates temp file in same folder as the original file, this is safe
 301  
     // but would impose a performance overhead if the original file is on a networked drive
 302  
     public synchronized void write(AudioFile af) throws CannotWriteException
 303  
     {
 304  367
         logger.info("Started writing tag data for file:" + af.getFile().getName());
 305  
 
 306  
         // Prechecks
 307  367
         precheckWrite(af);
 308  
 
 309  367
         RandomAccessFile raf = null;
 310  367
         RandomAccessFile rafTemp = null;
 311  
         File newFile;
 312  
         File result;
 313  
 
 314  
         // Create temporary File
 315  
         try
 316  
         {
 317  367
             newFile = File.createTempFile(af.getFile().getName().replace('.', '_'), TEMP_FILENAME_SUFFIX, af.getFile().getParentFile());
 318  
         }
 319  
         // Unable to create temporary file, can happen in Vista if have Create
 320  
         // Files/Write Data set to Deny
 321  0
         catch (IOException ioe)
 322  
         {
 323  0
             logger
 324  
                     .log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER
 325  
                             .getMsg(af.getFile().getName(), af
 326  
                             .getFile().getParentFile()
 327  
                             .getAbsolutePath()), ioe);
 328  0
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER
 329  
                     .getMsg(af.getFile().getName(), af.getFile()
 330  
                     .getParentFile().getAbsolutePath()));
 331  367
         }
 332  
 
 333  
         // Open temporary file and actual file for editing
 334  
         try
 335  
         {
 336  367
             rafTemp = new RandomAccessFile(newFile, WRITE_MODE);
 337  367
             raf = new RandomAccessFile(af.getFile(), WRITE_MODE);
 338  
 
 339  
         }
 340  
         // Unable to write to writable file, can happen in Vista if have Create
 341  
         // Folders/Append Data set to Deny
 342  0
         catch (IOException ioe)
 343  
         {
 344  0
             logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING
 345  
                     .getMsg(af.getFile().getAbsolutePath()), ioe);
 346  
 
 347  
             // If we managed to open either file, delete it.
 348  
             try
 349  
             {
 350  0
                 if (raf != null)
 351  
                 {
 352  0
                     raf.close();
 353  
                 }
 354  0
                 if (rafTemp != null)
 355  
                 {
 356  0
                     rafTemp.close();
 357  
                 }
 358  
             }
 359  0
             catch (IOException ioe2)
 360  
             {
 361  
                 // Warn but assume has worked okay
 362  0
                 logger.log(Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE
 363  
                         .getMsg(af.getFile(), ioe.getMessage()), ioe2);
 364  0
             }
 365  
 
 366  
             // Delete the temp file ( we cannot delete until closed corresponding
 367  
             // rafTemp)
 368  0
             if (!newFile.delete())
 369  
             {
 370  
                 // Non critical failed deletion
 371  0
                 logger
 372  
                         .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 373  
                                 .getMsg(newFile.getAbsolutePath()));
 374  
             }
 375  
 
 376  0
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING
 377  
                     .getMsg(af.getFile().getAbsolutePath()));
 378  367
         }
 379  
 
 380  
         // Write data to File
 381  
         try
 382  
         {
 383  
 
 384  367
             raf.seek(0);
 385  367
             rafTemp.seek(0);
 386  
             try
 387  
             {
 388  367
                 if (this.modificationListener != null)
 389  
                 {
 390  367
                     this.modificationListener.fileWillBeModified(af, false);
 391  
                 }
 392  367
                 writeTag(af.getTag(), raf, rafTemp);
 393  366
                 if (this.modificationListener != null)
 394  
                 {
 395  366
                     this.modificationListener.fileModified(af, newFile);
 396  
                 }
 397  
             }
 398  0
             catch (ModifyVetoException veto)
 399  
             {
 400  0
                 throw new CannotWriteException(veto);
 401  366
             }
 402  366
         }
 403  1
         catch (Exception e)
 404  
         {
 405  1
             logger.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE
 406  
                     .getMsg(af.getFile(), e.getMessage()), e);
 407  
 
 408  
             try
 409  
             {
 410  1
                 if (raf != null)
 411  
                 {
 412  1
                     raf.close();
 413  
                 }
 414  1
                 if (rafTemp != null)
 415  
                 {
 416  1
                     rafTemp.close();
 417  
                 }
 418  
             }
 419  0
             catch (IOException ioe)
 420  
             {
 421  
                 // Warn but assume has worked okay
 422  0
                 logger.log(Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE
 423  
                         .getMsg(af.getFile().getAbsolutePath(), ioe
 424  
                         .getMessage()), ioe);
 425  1
             }
 426  
 
 427  
             // Delete the temporary file because either it was never used so
 428  
             // lets just tidy up or we did start writing to it but
 429  
             // the write failed and we havent renamed it back to the original
 430  
             // file so we can just delete it.
 431  1
             if (!newFile.delete())
 432  
             {
 433  
                 // Non critical failed deletion
 434  0
                 logger
 435  
                         .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 436  
                                 .getMsg(newFile.getAbsolutePath()));
 437  
             }
 438  1
             throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(af
 439  
                     .getFile(), e.getMessage()));
 440  
         }
 441  
         finally
 442  
         {
 443  1
             try
 444  
             {
 445  367
                 if (raf != null)
 446  
                 {
 447  367
                     raf.close();
 448  
                 }
 449  367
                 if (rafTemp != null)
 450  
                 {
 451  367
                     rafTemp.close();
 452  
                 }
 453  
             }
 454  0
             catch (IOException ioe)
 455  
             {
 456  
                 // Warn but assume has worked okay
 457  0
                 logger.log(Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE
 458  
                         .getMsg(af.getFile().getAbsolutePath(), ioe
 459  
                         .getMessage()), ioe);
 460  734
             }
 461  366
         }
 462  
 
 463  
         // Result held in this file
 464  366
         result = af.getFile();
 465  
 
 466  
         // If the temporary file was used
 467  366
         if (newFile.length() > 0)
 468  
         {
 469  
 
 470  
             // Rename Original File
 471  
             // Can fail on Vista if have Special Permission 'Delete' set Deny
 472  345
             File originalFileBackup = new File(af.getFile().getAbsoluteFile().getParentFile().getPath(),
 473  
                                                AudioFile.getBaseFilename(af.getFile()) + ".old");
 474  
 
 475  
             //If already exists modify the suffix
 476  345
             int count=1;
 477  349
             while(originalFileBackup.exists())
 478  
             {
 479  4
                 originalFileBackup = new File(af.getFile().getAbsoluteFile().getParentFile().getPath(), AudioFile.getBaseFilename(af.getFile())+ ".old"+count);
 480  4
                 count++;
 481  
             }               
 482  
 
 483  345
             boolean renameResult = Utils.rename(af.getFile(),originalFileBackup);
 484  345
             if (!renameResult)
 485  
             {
 486  0
                 logger
 487  
                         .log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP
 488  
                                 .getMsg(af.getFile().getAbsolutePath(), originalFileBackup.getName()));
 489  0
                 throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP
 490  
                         .getMsg(af.getFile().getPath(), originalFileBackup.getName()));
 491  
             }
 492  
 
 493  
             // Rename Temp File to Original File
 494  345
             renameResult = Utils.rename(newFile,af.getFile());
 495  345
             if (!renameResult)
 496  
             {
 497  
                 // Renamed failed so lets do some checks rename the backup back to the original file
 498  
                 // New File doesnt exist
 499  0
                 if (!newFile.exists())
 500  
                 {
 501  0
                     logger
 502  
                             .warning(ErrorMessage.GENERAL_WRITE_FAILED_NEW_FILE_DOESNT_EXIST
 503  
                                     .getMsg(newFile.getAbsolutePath()));
 504  
                 }
 505  
 
 506  
                 // Rename the backup back to the original
 507  0
                 if (!originalFileBackup.renameTo(af.getFile()))
 508  
                 {
 509  
                     // TODO now if this happens we are left with testfile.old
 510  
                     // instead of testfile.mp4
 511  0
                     logger
 512  
                             .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_BACKUP_TO_ORIGINAL
 513  
                                     .getMsg(originalFileBackup
 514  
                                     .getAbsolutePath(), af.getFile()
 515  
                                     .getName()));
 516  
                 }
 517  
 
 518  0
                 logger
 519  
                         .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE
 520  
                                 .getMsg(af.getFile().getAbsolutePath(), newFile
 521  
                                 .getName()));
 522  0
                 throw new CannotWriteException(ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE
 523  
                         .getMsg(af.getFile().getAbsolutePath(), newFile
 524  
                         .getName()));
 525  
             }
 526  
             else
 527  
             {
 528  
                 // Rename was okay so we can now delete the backup of the
 529  
                 // original
 530  345
                 boolean deleteResult = originalFileBackup.delete();
 531  345
                 if (!deleteResult)
 532  
                 {
 533  
                     // Not a disaster but can't delete the backup so make a
 534  
                     // warning
 535  0
                     logger
 536  
                             .warning(ErrorMessage.GENERAL_WRITE_WARNING_UNABLE_TO_DELETE_BACKUP_FILE
 537  
                                     .getMsg(originalFileBackup
 538  
                                     .getAbsolutePath()));
 539  
                 }
 540  
             }
 541  
 
 542  
             // Delete the temporary file if still exists
 543  345
             if (newFile.exists())
 544  
             {
 545  0
                 if (!newFile.delete())
 546  
                 {
 547  
                     // Non critical failed deletion
 548  0
                     logger
 549  
                             .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 550  
                                     .getMsg(newFile.getPath()));
 551  
                 }
 552  
             }
 553  345
         }
 554  
         else
 555  
         {
 556  
             // Delete the temporary file that wasn't ever used
 557  21
             if (!newFile.delete())
 558  
             {
 559  
                 // Non critical failed deletion
 560  0
                 logger
 561  
                         .warning(ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE
 562  
                                 .getMsg(newFile.getPath()));
 563  
             }
 564  
         }
 565  
 
 566  366
         if (this.modificationListener != null)
 567  
         {
 568  366
             this.modificationListener.fileOperationFinished(result);
 569  
         }
 570  366
     }
 571  
 
 572  
     /**
 573  
      * This is called when a tag has to be written in a file. Three parameters
 574  
      * are provided, the tag to write (not empty) Two randomaccessfiles, the
 575  
      * first points to the file where we want to write the given tag, and the
 576  
      * second is an empty temporary file that can be used if e.g. the file has
 577  
      * to be bigger than the original.
 578  
      * <p/>
 579  
      * If something has been written in the temporary file, when this method
 580  
      * returns, the original file is deleted, and the temporary file is renamed
 581  
      * the the original name
 582  
      * <p/>
 583  
      * If nothing has been written to it, it is simply deleted.
 584  
      * <p/>
 585  
      * This method can assume the raf, rafTemp are pointing to the first byte of
 586  
      * the file. The subclass must not close these two files when the method
 587  
      * returns.
 588  
      *
 589  
      * @param tag
 590  
      * @param raf
 591  
      * @param rafTemp
 592  
      * @throws IOException          is thrown when the RandomAccessFile operations throw it (you
 593  
      *                              should never throw them manually)
 594  
      * @throws CannotWriteException when an error occured during the generation of the tag
 595  
      * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 596  
      */
 597  
     protected abstract void writeTag(Tag tag, RandomAccessFile raf, RandomAccessFile rafTemp) throws CannotReadException, CannotWriteException, IOException;
 598  
 
 599  
 }