/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.services.tags;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jajuk.base.PropertyMetaInformation;
import org.jajuk.base.TrackManager;
import org.jajuk.base.Type;
import org.jajuk.base.TypeManager;
import org.jajuk.services.covers.Cover;
import org.jajuk.ui.widgets.InformationJPanel;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.Messages;
import org.jajuk.util.UtilString;
import org.jajuk.util.UtilSystem;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.log.Log;
/**
* abstract tag, independent from real implementation.
*/
public class Tag {
/** Current tag impl*. */
private ITagImpl tagImpl;
/** Current file*. */
private File fio;
/** Is this tag corrupted ?. */
private boolean bCorrupted = false;
/** File -> tag cache This is required by the autocommit=false operations. */
static private Map<File, Tag> tagsCache = new HashMap<File, Tag>(10);
private static List<String> supportedTagFields = null;
/**
* Tag constructor.
*
* @param fio
* @param bIgnoreErrors ignore error and keep instance anyway
*
* @throws JajukException the jajuk exception
*
*/
public Tag(java.io.File fio, boolean bIgnoreErrors) throws JajukException {
try {
this.fio = fio;
Type type = TypeManager.getInstance().getTypeByExtension(UtilSystem.getExtension(fio));
if (type == null) {
constructionError(fio, bIgnoreErrors, "No type for file: ");
return;
}
tagImpl = type.getTagImpl();
if (tagImpl == null) {
constructionError(fio, bIgnoreErrors, "No TagImpl for file: ");
return;
}
tagImpl.setFile(fio);
} catch (Exception e) {
bCorrupted = true;
if (!bIgnoreErrors) {
throw new JajukException(103, (fio == null ? "<null>" : fio.getName()), e);
}
}
}
/**
* @param fio
* @param bIgnoreErrors
* @throws JajukException
*/
private final void constructionError(java.io.File fio, boolean bIgnoreErrors, String error)
throws JajukException {
if (!bIgnoreErrors) {
throw new JajukException(103, error + (fio == null ? "<null>" : fio.getName()));
}
bCorrupted = true;
}
/**
* Gets the track name.
*
* @return track name as defined in tags are file name otherwise
*/
public String getTrackName() {
// by default, track name is the file name without extension
String sTrackName = UtilSystem.removeExtension(fio.getName());
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return sTrackName;
}
try {
String sTemp = tagImpl.getTrackName().trim();
if (!"".equals(sTemp)) {
// remove the extension
sTrackName = UtilString.formatTag(sTemp);
}
} catch (Exception e) {
Log.info("Wrong track name:{{" + fio.getName() + "}}");
}
return sTrackName;
}
/**
* Gets the album name.
*
* @return album name
*/
public String getAlbumName() {
if (tagImpl == null || !tagImpl.isTagAvailable()) {
if (Conf.getBoolean(Const.CONF_TAGS_USE_PARENT_DIR)) {
return fio.getParentFile().getName();
// if album is not found, take current directory as album name
} else {
return Messages.getString(Const.UNKNOWN_ALBUM);
}
}
String sAlbumlName = null;
try {
String sTemp = tagImpl.getAlbumName().trim();
if (Messages.getString(Const.UNKNOWN_ALBUM).equals(sTemp)) {
// it is done to avoid duplicates unknown genres if
// the tag is the real string "unknown" in the
// current language
sAlbumlName = Const.UNKNOWN_ALBUM;
} else if (!"".equals(sTemp)) {
sAlbumlName = sTemp;
}
} catch (Exception e) {
Log.info("Wrong album name:{{" + fio.getName() + "}}");
}
if (sAlbumlName == null) { // album tag cannot be found
if (Conf.getBoolean(Const.CONF_TAGS_USE_PARENT_DIR)) {
sAlbumlName = fio.getParentFile().getName();
// if album is not found, take current directory as album name
} else {
sAlbumlName = Messages.getString(Const.UNKNOWN_ALBUM);
}
}
sAlbumlName = UtilString.formatTag(sAlbumlName);
// We internalize the album name for memory saving reasons
return sAlbumlName.intern();
}
/**
* Gets the artist name.
*
* @return artist name
*/
public String getArtistName() {
String sArtistName = Const.UNKNOWN_ARTIST;
// if the type doesn't support tags ( like wav )
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return sArtistName;
}
try {
String sTemp = tagImpl.getArtistName().trim();
if (Messages.getString(Const.UNKNOWN_ARTIST).equals(sTemp)) {
// it is done to avoid duplicates unknown genres if
// the tag is the real string "unknown" in the
// current language
sArtistName = Const.UNKNOWN_ARTIST;
} else if (!"".equals(sTemp)) {
sArtistName = UtilString.formatTag(sTemp);
}
} catch (Exception e) {
Log.info("Wrong artist name:{{" + fio.getName() + "}}");
}
// We internalize the artist name for memory saving reasons
return sArtistName.intern();
}
/**
* Gets the album artist.
*
* @return album artist
*/
public String getAlbumArtist() {
String sAlbumArtist = Const.UNKNOWN_ARTIST;
// if the type doesn't support tags ( like wav )
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return sAlbumArtist;
}
try {
String sTemp = tagImpl.getAlbumArtist().trim();
if (Messages.getString(Const.UNKNOWN_ARTIST).equals(sTemp)) {
sAlbumArtist = Const.UNKNOWN_ARTIST;
} else if (!"".equals(sTemp)) {
sAlbumArtist = UtilString.formatTag(sTemp);
}
} catch (Exception e) {
Log.info("Wrong album artist:{{" + fio.getName() + "}}");
}
// We internalize the artist name for memory saving reasons
return sAlbumArtist.intern();
}
/**
* Gets the genre name.
*
* @return genre name
*/
public String getGenreName() {
String genre = Const.UNKNOWN_GENRE;
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return genre;
}
try {
String sTemp = tagImpl.getGenreName().trim();
if (Messages.getString(Const.UNKNOWN_GENRE).equals(sTemp)) {
// it is done to avoid duplicates unknown genres if
// the tag is the real string "unknown" in the
// current language
genre = Const.UNKNOWN_GENRE;
} else if (!"".equals(sTemp)) {
if ("unknown".equals(sTemp)) {
sTemp = genre;
}
genre = UtilString.formatTag(sTemp);
}
} catch (Exception e) {
Log.info("Wrong genre name: {{" + fio.getName() + "}}");
}
// We internalize the genre name for memory saving reasons
return genre.intern();
}
/**
* Gets the length.
*
* @return length in sec
*/
public long getLength() {
long length = 0;
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return 0;
}
try {
length = tagImpl.getLength();
} catch (Exception e) {
Log.info("Wrong length:{{" + fio.getName() + "}}");
}
return length;
}
/**
* Gets the disc number.
*
* @return disc number, by default, return 0
*/
public long getDiscNumber() {
long l = 0l;
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return l;
}
try {
l = tagImpl.getDiscNumber();
} catch (Exception e) {
Log.info("Wrong disc number:{{" + fio.getName() + "}}");
}
return l;
}
/**
* Gets the year.
*
* @return creation year
*/
public String getYear() {
String year = "0";
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return year;
}
try {
year = tagImpl.getYear(); // check it is an integer
} catch (Exception e) {
Log.info("Wrong year:{{" + fio.getName() + "}}");
}
// We internalize the year name for memory saving reasons
return year.intern();
}
/**
* Gets the quality.
*
* @return quality
*/
public long getQuality() {
long lQuality = 0l;
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return lQuality;
}
try {
lQuality = tagImpl.getQuality();
} catch (Exception e) {
Log.info("Wrong quality:{{" + fio.getName() + "}}");
}
return lQuality;
}
/**
* Gets the comment.
*
* @return comment
*/
public String getComment() {
String sComment = "";
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return sComment;
}
try {
String sTemp = tagImpl.getComment();
if (sTemp != null && !sTemp.equals("")) {
sComment = UtilString.formatTag(sTemp);
}
} catch (Exception e) {
Log.info("Wrong comment:{{" + fio.getName() + "}}");
}
// We internalize the comments for memory saving reasons
return sComment.intern();
}
/**
* Gets the order.
*
* @return comment
*/
public long getOrder() {
long order = 0l;
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return order;
}
try {
order = tagImpl.getOrder();
if (order < 0) {
throw new Exception("Negative Order");
}
} catch (Exception e) {
Log.info("Wrong order:{{" + fio.getName() + "}}");
order = 0;
}
return order;
}
/**
* Gets the lyrics.
*
* @return the lyrics
*/
public String getLyrics() {
String sLyrics = "";
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return sLyrics;
}
try {
sLyrics = tagImpl.getLyrics();
} catch (Exception e) {
Log.info("Wrong lyrics:{{" + fio.getName() + "}}");
}
return sLyrics;
}
/**
* Gets the embedded covers.
*
* @return the covers or a void list if none.
*/
public List<Cover> getCovers() {
List<Cover> covers = new ArrayList<Cover>(1);
// if the type doesn't support tags ( like wav )
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return covers;
}
try {
covers = tagImpl.getCovers();
if (covers == null) {
covers = new ArrayList<Cover>(1);
}
} catch (Exception e) {
Log.info("Wrong covers:{{" + fio.getName() + "}}");
}
return covers;
}
/**
* Sets the track name.
*
* @param sTrackName
*
* @throws JajukException the jajuk exception
*/
public void setTrackName(String sTrackName) throws JajukException {
try {
tagImpl.setTrackName(sTrackName);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the album name.
*
* @param sAlbumName
*
* @throws JajukException the jajuk exception
*/
public void setAlbumName(String sAlbumName) throws JajukException {
try {
tagImpl.setAlbumName(sAlbumName);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the artist name.
*
* @param sArtistName
*
* @throws JajukException the jajuk exception
*/
public void setArtistName(String sArtistName) throws JajukException {
try {
tagImpl.setArtistName(sArtistName);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the album artist.
*
* @param sAlbumArtist
*
* @throws JajukException the jajuk exception
*/
public void setAlbumArtist(String sAlbumArtist) throws JajukException {
try {
tagImpl.setAlbumArtist(sAlbumArtist);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the genre name.
*
* @param genre
*
* @throws JajukException the jajuk exception
*/
public void setGenreName(String genre) throws JajukException {
try {
tagImpl.setGenreName(genre);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the order.
*
* @param lOrder
*
* @throws JajukException the jajuk exception
*/
public void setOrder(long lOrder) throws JajukException {
try {
tagImpl.setOrder(lOrder);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the year.
*
* @param sYear
*
* @throws JajukException the jajuk exception
*/
public void setYear(String sYear) throws JajukException {
try {
tagImpl.setYear(sYear);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the disc number.
*
* @param discnumber
*
* @throws JajukException the jajuk exception
*/
public void setDiscNumber(long discnumber) throws JajukException {
try {
tagImpl.setDiscNumber(discnumber);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the comment.
*
* @param sComment
*
* @throws JajukException the jajuk exception
*/
public void setComment(String sComment) throws JajukException {
try {
tagImpl.setComment(sComment);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Sets the lyrics.
*
* @param sLyrics the new lyrics
*
* @throws JajukException the jajuk exception
*/
public void setLyrics(String sLyrics) throws JajukException {
try {
if (NoTagsTagImpl.class.equals(tagImpl.getClass())) {
throw new Exception();
}
tagImpl.setLyrics(sLyrics);
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Delete lyrics.
*
* @throws JajukException the jajuk exception
*/
public void deleteLyrics() throws JajukException {
try {
tagImpl.deleteLyrics();
} catch (Exception e) {
throw new JajukException(104, fio.getName(), e);
}
}
/**
* Commit tags.
*
* @throws JajukException the jajuk exception
*/
public synchronized void commit() throws JajukException {
try {
// Show a commit message except if the tag impl is "no tag" (does nothing)
if (Log.isDebugEnabled() && !(tagImpl.getClass().equals(NoTagsTagImpl.class))) {
Log.debug(Messages.getString("PropertiesWizard.11") + " {{" + fio.getAbsolutePath() + "}}");
}
// Store file date, can be 0 if a problem occurs
long dateLastChange = fio.lastModified();
// Actual commit
tagImpl.commit();
// Display written file full path. Note that we use a limited string for
// parent to make sure the file name itself is visible in information
// panel
InformationJPanel.getInstance().setMessage(
Messages.getString("PropertiesWizard.11") + " "
+ UtilString.getLimitedString(fio.getParentFile().getAbsolutePath(), 60)
+ File.separatorChar + fio.getName(), InformationJPanel.MessageType.INFORMATIVE);
//Keep last change date if required
if (Conf.getBoolean(Const.CONF_PRESERVE_FILE_DATES) && dateLastChange != 0) {
fio.setLastModified(dateLastChange);
}
} catch (Exception e) {
// reset information panel to avoid leaving with a "writting xxx message"
InformationJPanel.getInstance().setMessage("", InformationJPanel.MessageType.INFORMATIVE);
throw new JajukException(104, fio.getName() + "\n" + e.getMessage(), e);
}
}
/**
* Checks if is corrupted.
*
* @return true, if is corrupted
*/
public boolean isCorrupted() {
return bCorrupted;
}
/**
* Sets the corrupted.
*
* @param corrupted the new corrupted
*/
public void setCorrupted(boolean corrupted) {
bCorrupted = corrupted;
}
/**
* Gets the fio.
*
* @return the fio
*/
public File getFio() {
return this.fio;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof Tag)) {
return false;
}
Tag otherTag = (Tag) other;
if (fio == null && otherTag.fio == null) {
return true;
}
if (fio == null || otherTag.fio == null) {
return false;
}
return this.fio.equals(otherTag.getFio());
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
// use an arbitrary primary number for hashCode if fio is null...
return fio == null ? 13 : fio.getAbsolutePath().hashCode();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Tag of : " + (fio == null ? "<null>" : fio.getAbsolutePath());
}
/**
* Return cached tag or new tag if non already in cache.
*
* @param fio the audio file containing the tag
* @param bIgnoreErrors
*
* @return cached tag or new tag if non already in cache
*
* @throws JajukException the jajuk exception
*
* @bIgnoreError : ignore any error and keep instance in cache
*/
public static Tag getTagForFio(File fio, boolean bIgnoreErrors) throws JajukException {
Tag tag = tagsCache.get(fio);
if (tag == null) {
tag = new Tag(fio, bIgnoreErrors);
// Cache the tag
tagsCache.put(fio, tag);
}
return tag;
}
/**
* Clear the tags cache.
*/
public static void clearCache() {
tagsCache.clear();
}
/**
* Gets the tag field.
*
* @param tagFieldKey
*
* @return the tag field
*/
public String getTagField(String tagFieldKey) {
if (tagImpl == null || !tagImpl.isTagAvailable()) {
return "";
}
try {
return tagImpl.getTagField(tagFieldKey).trim();
} catch (Exception e) {
Log.error(e);
}
return "";
}
/**
* Set the tag field.
*
* @param tagFieldKey
* @param tagFieldValue
*
*/
public void setTagField(String tagFieldKey, String tagFieldValue) {
try {
tagImpl.setTagField(tagFieldKey, tagFieldValue);
} catch (Exception e) {
Log.error(e);
}
}
/**
* Gets the supported tag fields.
*
* @return the supported tag fields
*/
public static List<String> getSupportedTagFields() {
if (supportedTagFields == null) {
supportedTagFields = new ArrayList<String>();
// get all available tag impls
List<ITagImpl> tagImplList = new ArrayList<ITagImpl>(2);
for (Type t : TypeManager.getInstance().getAllMusicTypes()) {
if (t.getTaggerClass() != null && !tagImplList.contains(t.getTaggerClass())) {
try {
tagImplList.add(t.getTaggerClass().newInstance());
} catch (InstantiationException e) {
Log.error(e);
} catch (IllegalAccessException e) {
Log.error(e);
}
}
}
for (ITagImpl t : tagImplList) {
for (String s : t.getSupportedTagFields()) {
if (!supportedTagFields.contains(s)) {
supportedTagFields.add(s);
}
}
}
}
return supportedTagFields;
}
/**
* Gets the activated extra tags.
*
* @return the activatedExtraTags
*/
public static List<String> getActivatedExtraTags() {
List<String> activeExtraTagsArrayList = new ArrayList<String>();
// check all custom properties
for (PropertyMetaInformation m : TrackManager.getInstance().getCustomProperties()) {
if (getSupportedTagFields().contains(m.getName())) {
activeExtraTagsArrayList.add(m.getName());
}
}
return activeExtraTagsArrayList;
}
}