AudioFileWriterpublic abstract class AudioFileWriter extends Object This abstract class is the skeleton for tag writers.
It handles the creation/closing of the randomaccessfile objects and then call
the subclass method writeTag or deleteTag. These two method have to be
implemented in the subclass. |
Fields Summary |
---|
private static final String | TEMP_FILENAME_SUFFIX | private static final String | WRITE_MODE | private static final int | MINIMUM_FILESIZE | public static Logger | logger | private AudioFileModificationListener | modificationListenerIf not null , this listener is used to notify the listener
about modification events.
|
Methods Summary |
---|
public synchronized void | delete(org.jaudiotagger.audio.AudioFile af)Delete the tag (if any) present in the given file
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)
{
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)
{
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);
}
}
| public synchronized void | delete(java.io.RandomAccessFile raf, java.io.RandomAccessFile tempRaf)Delete the tag (if any) present in the given randomaccessfile, and do not
close it at the end.
raf.seek(0);
tempRaf.seek(0);
deleteTag(raf, tempRaf);
| protected abstract void | deleteTag(java.io.RandomAccessFile raf, java.io.RandomAccessFile tempRaf)Same as above, but delete tag in the file.
| private void | precheckWrite(org.jaudiotagger.audio.AudioFile af)Prechecks before normal write
- If the tag is actually empty, remove the tag
- if the file is not writable, throw exception
-
- If the file is too small to be a valid file, throw exception
-
// 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()));
}
| public synchronized void | setAudioFileModificationListener(AudioFileModificationListener listener)This method sets the {@link AudioFileModificationListener}.
There is only one listener allowed, if you want more instances to be
supported, use the {@link ModificationHandler} to broadcast those events.
this.modificationListener = listener;
| public synchronized void | write(org.jaudiotagger.audio.AudioFile af)Write the tag (if not empty) present in the AudioFile in the associated
File
logger.config("Started writing tag data for file:" + af.getFile().getName());
// Prechecks
precheckWrite(af);
//mp3's use a different mechanism to the other formats
if(af instanceof MP3File)
{
af.commit();
return;
}
RandomAccessFile raf = null;
RandomAccessFile rafTemp = null;
File newFile;
File result;
// 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 delete until closed corresponding
// 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().getAbsoluteFile().getParentFile().getPath(),
AudioFile.getBaseFilename(af.getFile()) + ".old");
//If already exists modify the suffix
int count=1;
while(originalFileBackup.exists())
{
originalFileBackup = new File(af.getFile().getAbsoluteFile().getParentFile().getPath(), AudioFile.getBaseFilename(af.getFile())+ ".old"+count);
count++;
}
boolean renameResult = Utils.rename(af.getFile(),originalFileBackup);
if (!renameResult)
{
logger
.log(Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP
.getMsg(af.getFile().getAbsolutePath(), originalFileBackup.getName()));
//Delete the temp file because write has failed
if(newFile!=null)
{
newFile.delete();
}
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 = Utils.rename(newFile,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);
}
| protected abstract void | writeTag(org.jaudiotagger.tag.Tag tag, java.io.RandomAccessFile raf, java.io.RandomAccessFile rafTemp)This is called when a tag has to be written in a file. Three parameters
are provided, the tag to write (not empty) Two randomaccessfiles, the
first points to the file where we want to write the given tag, and the
second is an empty temporary file that can be used if e.g. the file has
to be bigger than the original.
If something has been written in the temporary file, when this method
returns, the original file is deleted, and the temporary file is renamed
the the original name
If nothing has been written to it, it is simply deleted.
This method can assume the raf, rafTemp are pointing to the first byte of
the file. The subclass must not close these two files when the method
returns.
|
|