/**
* @author : Paul Taylor
* @author : Eric Farng
*
* Version @version:$Id: FrameBodyTDRC.java 932 2010-11-26 13:13:15Z paultaylor $
*
* MusicTag Copyright (C)2003,2004
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not,
* you can get a copy from http://www.opensource.org/licenses/lgpl-license.php or write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Description:
*
*/
package org.jaudiotagger.tag.id3.framebody;
import org.jaudiotagger.tag.InvalidTagException;
import org.jaudiotagger.tag.datatype.DataTypes;
import org.jaudiotagger.tag.id3.ID3v23Frames;
import org.jaudiotagger.tag.id3.ID3v24Frames;
import org.jaudiotagger.tag.id3.valuepair.TextEncoding;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
public class FrameBodyTDRC extends AbstractFrameBodyTextInfo implements ID3v24FrameBody {
/**
* Used when converting from v3 tags , these fields should ALWAYS hold the v23 value
*/
private String originalID;
private String year = "";
private String time = "";
private String date = "";
private boolean monthOnly = false;
private boolean hoursOnly = false;
private static SimpleDateFormat formatYearIn, formatYearOut;
private static SimpleDateFormat formatDateIn, formatDateOut, formatMonthOut;
private static SimpleDateFormat formatTimeIn, formatTimeOut, formatHoursOut;
private static final List<SimpleDateFormat> formatters = new ArrayList<SimpleDateFormat>();
private static final int PRECISION_SECOND = 0;
private static final int PRECISION_MINUTE = 1;
private static final int PRECISION_HOUR = 2;
private static final int PRECISION_DAY = 3;
private static final int PRECISION_MONTH = 4;
private static final int PRECISION_YEAR = 5;
static {
//This is allowable v24 format , we use UK Locale not because we are restricting to UK
//but because these formats are fixed in ID3 spec, and could possibly get unexpected results if library
//used with a default locale that has Date Format Symbols that interfere with the pattern
formatters.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.UK));
formatters.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm", Locale.UK));
formatters.add(new SimpleDateFormat("yyyy-MM-dd'T'HH", Locale.UK));
formatters.add(new SimpleDateFormat("yyyy-MM-dd", Locale.UK));
formatters.add(new SimpleDateFormat("yyyy-MM", Locale.UK));
formatters.add(new SimpleDateFormat("yyyy", Locale.UK));
//These are formats used by v23 Frames
formatYearIn = new SimpleDateFormat("yyyy", Locale.UK);
formatDateIn = new SimpleDateFormat("ddMM", Locale.UK);
formatTimeIn = new SimpleDateFormat("HHmm", Locale.UK);
//These are the separate components of the v24 format that the v23 formats map to
formatYearOut = new SimpleDateFormat("yyyy", Locale.UK);
formatDateOut = new SimpleDateFormat("-MM-dd", Locale.UK);
formatMonthOut = new SimpleDateFormat("-MM", Locale.UK);
formatTimeOut = new SimpleDateFormat("'T'HH:mm", Locale.UK);
formatHoursOut = new SimpleDateFormat("'T'HH", Locale.UK);
}
/**
* Creates a new FrameBodyTDRC datatype.
*/
public FrameBodyTDRC() {
super();
}
public FrameBodyTDRC(FrameBodyTDRC body) {
super(body);
}
/**
* Retrieve the original identifier
*
* @return
*/
public String getOriginalID() {
return originalID;
}
/**
* When this has been generated as an amalgamation of v3 frames assumes
* the v3 frames match the the format in specification and convert them
* to their equivalent v4 format and return the generated String.
* i.e if the v3 frames contain a valid value this will return a valid
* v4 value, if not this won't.
*/
/**
* Synchronized because SimpleDatFormat aren't thread safe
*
* @param formatDate
* @param parseDate
* @param text
* @return
*/
private static synchronized String formatAndParse(SimpleDateFormat formatDate, SimpleDateFormat parseDate, String text) {
try {
Date date = parseDate.parse(text);
String result = formatDate.format(date);
return result;
} catch (ParseException e) {
//logger.warning("Unable to parse:" + text);
}
return "";
}
public String getFormattedText() {
StringBuffer sb = new StringBuffer();
if (originalID == null) {
return this.getText();
} else {
if (year != null && !(year.equals(""))) {
sb.append(formatAndParse(formatYearOut, formatYearIn, year));
}
if (!date.equals("")) {
if (isMonthOnly()) {
sb.append(formatAndParse(formatMonthOut, formatDateIn, date));
} else {
sb.append(formatAndParse(formatDateOut, formatDateIn, date));
}
}
if (!time.equals("")) {
if (isHoursOnly()) {
sb.append(formatAndParse(formatHoursOut, formatTimeIn, time));
} else {
sb.append(formatAndParse(formatTimeOut, formatTimeIn, time));
}
}
return sb.toString();
}
}
public void setYear(String year) {
//logger.finest("Setting year to" + year);
this.year = year;
}
public void setTime(String time) {
//logger.finest("Setting time to:" + time);
this.time = time;
}
public void setDate(String date) {
//logger.finest("Setting date to:" + date);
this.date = date;
}
public String getYear() {
return year;
}
public String getTime() {
return time;
}
public String getDate() {
return date;
}
/**
* When converting v3 TYER to v4 TDRC frame
*
* @param body
*/
public FrameBodyTDRC(FrameBodyTYER body) {
originalID = ID3v23Frames.FRAME_ID_V3_TYER;
year = body.getText();
setObjectValue(DataTypes.OBJ_TEXT_ENCODING, TextEncoding.ISO_8859_1);
setObjectValue(DataTypes.OBJ_TEXT, getFormattedText());
}
/**
* When converting v3 TIME to v4 TDRC frame
*
* @param body
*/
public FrameBodyTDRC(FrameBodyTIME body) {
originalID = ID3v23Frames.FRAME_ID_V3_TIME;
time = body.getText();
setHoursOnly(body.isHoursOnly());
setObjectValue(DataTypes.OBJ_TEXT_ENCODING, TextEncoding.ISO_8859_1);
setObjectValue(DataTypes.OBJ_TEXT, getFormattedText());
}
/**
* When converting v3 TDAT to v4 TDRC frame
*
* @param body
*/
public FrameBodyTDRC(FrameBodyTDAT body) {
originalID = ID3v23Frames.FRAME_ID_V3_TDAT;
date = body.getText();
setMonthOnly(body.isMonthOnly());
setObjectValue(DataTypes.OBJ_TEXT_ENCODING, TextEncoding.ISO_8859_1);
setObjectValue(DataTypes.OBJ_TEXT, getFormattedText());
}
/**
* Creates a new FrameBodyTDRC dataType.
* <p/>
* Tries to decode the text to find the v24 date mask being used, and store the v3 components of the mask
*
* @param textEncoding
* @param text
*/
public FrameBodyTDRC(byte textEncoding, String text) {
super(textEncoding, text);
findMatchingMaskAndExtractV3Values();
}
/**
* Creates a new FrameBodyTDRC datatype from File
*
* @param byteBuffer
* @param frameSize
* @throws InvalidTagException
*/
public FrameBodyTDRC(ByteBuffer byteBuffer, int frameSize) throws InvalidTagException {
super(byteBuffer, frameSize);
findMatchingMaskAndExtractV3Values();
}
private void findMatchingMaskAndExtractV3Values() {
//Find the date format of the text
for (int i = 0; i < formatters.size(); i++) {
try {
Date d;
synchronized (formatters.get(i)) {
d = formatters.get(i).parse(getText());
}
//If able to parse a date from the text
if (d != null) {
extractID3v23Formats(d, i);
break;
}
}
//Dont display will occur for each failed format
catch (ParseException e) {
//Do nothing;
} catch (NumberFormatException nfe) {
//Do nothing except log warning because not really expecting this to happen
logger.log(Level.WARNING, "Date Formatter:" + formatters.get(i).toPattern() + "failed to parse:" + getText() + "with " + nfe.getMessage(), nfe);
}
}
}
/**
* Format Date
* <p/>
* Synchronized because SimpleDateFormat is invalid
*
* @param d
* @return
*/
private static synchronized String formatDateAsYear(Date d) {
return formatYearIn.format(d);
}
/**
* Format Date
* <p/>
* Synchronized because SimpleDateFormat is invalid
*
* @param d
* @return
*/
private static synchronized String formatDateAsDate(Date d) {
return formatDateIn.format(d);
}
/**
* Format Date
* <p/>
* Synchronized because SimpleDateFormat is invalid
*
* @param d
* @return
*/
private static synchronized String formatDateAsTime(Date d) {
return formatTimeIn.format(d);
}
/**
* Extract the components ans store the v23 version of the various values
*
* @param dateRecord
* @param precision
*/
//TODO currently if user has entered Year and Month, we only store in v23, should we store month with
//first day
private void extractID3v23Formats(final Date dateRecord, final int precision) {
//logger.fine("Precision is:" + precision + "for date:" + dateRecord.toString());
Date d = dateRecord;
//Precision Year
if (precision == PRECISION_YEAR) {
setYear(formatDateAsYear(d));
}
//Precision Month
else if (precision == PRECISION_MONTH) {
setYear(formatDateAsYear(d));
setDate(formatDateAsDate(d));
monthOnly = true;
}
//Precision Day
else if (precision == PRECISION_DAY) {
setYear(formatDateAsYear(d));
setDate(formatDateAsDate(d));
}
//Precision Hour
else if (precision == PRECISION_HOUR) {
setYear(formatDateAsYear(d));
setDate(formatDateAsDate(d));
setTime(formatDateAsTime(d));
hoursOnly = true;
}
//Precision Minute
else if (precision == PRECISION_MINUTE) {
setYear(formatDateAsYear(d));
setDate(formatDateAsDate(d));
setTime(formatDateAsTime(d));
}
//Precision Minute
else if (precision == PRECISION_SECOND) {
setYear(formatDateAsYear(d));
setDate(formatDateAsDate(d));
setTime(formatDateAsTime(d));
}
}
/**
* The ID3v2 frame identifier
*
* @return the ID3v2 frame identifier for this frame type
*/
public String getIdentifier() {
return ID3v24Frames.FRAME_ID_YEAR;
}
public boolean isMonthOnly() {
return monthOnly;
}
public void setMonthOnly(boolean monthOnly) {
this.monthOnly = monthOnly;
}
public boolean isHoursOnly() {
return hoursOnly;
}
public void setHoursOnly(boolean hoursOnly) {
this.hoursOnly = hoursOnly;
}
}