/*
* APICID3V2Frame.java
*
* Created on Jan 17, 2004
*
* Copyright (C)2004,2005 Paul Grebenc
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: APICID3V2Frame.java,v 1.14 2005/02/06 18:11:17 paul Exp $
*/
package org.blinkenlights.jid3.v2;
import java.io.*;
import java.util.*;
import org.blinkenlights.jid3.*;
import org.blinkenlights.jid3.io.*;
import org.blinkenlights.jid3.util.*;
/**
* @author paul
*
* Frame containing an attached picture.
*/
public class APICID3V2Frame extends ID3V2Frame
{
private TextEncoding m_oTextEncoding;
private String m_sMimeType = null;
private PictureType m_oPictureType;
private String m_sDescription = null;
private byte[] m_abyPictureData = null;
/** Constructor.
*
* Note: It is valid to set the MIME type to "-->", and set the picture data to an URL pointing to the image,
* although this is discouraged.
*
* @param sMimeType the valid MIME type (ie. image/png) describing the format of the contained image (the default
* if null is specified is "image/")
* @param oPictureType the classification of the picture attached
* @param sDescription an optional description of the image, or null if no description required
* @param abyPictureData the data content of the image
*
* @throws ID3Exception if the description is longer than 64 characters
* @throws ID3Exception if the picture data is null, or zero length
*/
public APICID3V2Frame(String sMimeType,
PictureType oPictureType,
String sDescription,
byte[] abyPictureData)
throws ID3Exception
{
m_oTextEncoding = TextEncoding.getDefaultTextEncoding();
m_sMimeType = sMimeType;
if (m_sMimeType == null)
{
m_sMimeType = "image/";
}
m_oPictureType = oPictureType;
if (sDescription.length() > 64)
{
// I have no idea why...
throw new ID3Exception("Description in APIC frame cannot exceed 64 characters.");
}
m_sDescription = sDescription;
if ((abyPictureData == null) || (abyPictureData.length == 0))
{
throw new ID3Exception("APIC frame requires picture data.");
}
m_abyPictureData = abyPictureData;
}
public APICID3V2Frame(InputStream oIS)
throws ID3Exception
{
// Parse out the text encoding and text string from the raw data
try
{
ID3DataInputStream oFrameDataID3DIS = new ID3DataInputStream(oIS);
// text encoding
m_oTextEncoding = TextEncoding.getTextEncoding(oFrameDataID3DIS.readUnsignedByte());
// mime type (read to null)
ByteArrayOutputStream oMimeTypeBAOS = new ByteArrayOutputStream();
int iMimeTypeByte;
do
{
iMimeTypeByte = oFrameDataID3DIS.readUnsignedByte();
if (iMimeTypeByte != 0)
{
oMimeTypeBAOS.write(iMimeTypeByte);
}
}
while (iMimeTypeByte != 0);
if (oMimeTypeBAOS.size() > 0)
{
byte[] abyMimeType = oMimeTypeBAOS.toByteArray();
m_sMimeType = new String(abyMimeType);
}
// picture type
m_oPictureType = new APICID3V2Frame.PictureType((byte)oFrameDataID3DIS.readUnsignedByte());
// description (read to null)
m_sDescription = oFrameDataID3DIS.readStringToNull(m_oTextEncoding);
// picture data
m_abyPictureData = new byte[oFrameDataID3DIS.available()];
oFrameDataID3DIS.readFully(m_abyPictureData);
}
catch (Exception e)
{
throw new InvalidFrameID3Exception(e);
}
}
public void accept(ID3Visitor oID3Visitor)
{
oID3Visitor.visitAPICID3V2Frame(this);
}
/** Set the MIME type for the image contained in this frame.
*
* Note: It is valid to set the MIME type to "-->", and set the picture data to an URL pointing to the image,
* although this is discouraged.
*
* @param sMimeType the valid MIME type (ie. image/png) describing the format of the contained image (the default
* if null is specified is "image/")
*/
public void setMimeType(String sMimeType)
{
m_sMimeType = sMimeType;
if (m_sMimeType == null)
{
m_sMimeType = "image/";
}
}
/** Get the MIME type of the image contained in this frame. A MIME type of "-->" implies that the
* image data contains an URL reference to the actual image data.
*
* @return the specified MIME type
*/
public String getMimeType()
{
return m_sMimeType;
}
/** Set the classification of the picture in this frame.
*
* @param oPictureType the type of the picture in this frame.
*/
public void setPictureType(PictureType oPictureType)
{
m_oPictureType = oPictureType;
}
/** Get the classification of the picture in this frame.
*
* @return the type of the picture in this frame
*/
public PictureType getPictureType()
{
return m_oPictureType;
}
/** Set the description for the picture in this frame.
*
* @param sDescription an optional description of the image, or null if no description required
* @throws ID3Exception if the description is longer than 64 characters
* @throws ID3Exception if this frame is in a tag with another APIC frame which would have the same description
*/
public void setDescription(String sDescription)
throws ID3Exception
{
String sOrigDescription = m_sDescription;
TextEncoding oOrigTextEncoding = m_oTextEncoding;
if (sDescription.length() > 64)
{
// I have no idea why...
throw new ID3Exception("Description in APIC frame cannot exceed 64 characters.");
}
m_oTextEncoding = TextEncoding.getDefaultTextEncoding();
m_sDescription = sDescription;
// try this update, and reverse it if it generates and error
try
{
notifyID3Observers();
}
catch (ID3Exception e)
{
m_sDescription = sOrigDescription;
m_oTextEncoding = oOrigTextEncoding;
throw e;
}
}
/** Get the description for the picture in this frame.
*
* @return the set description, or null if no description has been defined
*/
public String getDescription()
{
return m_sDescription;
}
/** Set the picture data for the image in this frame.
*
* @param abyPictureData the data content of the image
*
* @throws ID3Exception if the picture data is null, or zero length
*/
public void setPictureData(byte[] abyPictureData)
throws ID3Exception
{
if ((abyPictureData == null) || (abyPictureData.length == 0))
{
throw new ID3Exception("APIC frame requires picture data.");
}
m_abyPictureData = abyPictureData;
}
/** Get the picture data for the image in this frame.
*
* @return the picture data for the image
*/
public byte[] getPictureData()
{
return m_abyPictureData;
}
/** Set the text encoding to be used for the description in this frame.
*
* @param oTextEncoding the text encoding to be used for this frame
*/
public void setTextEncoding(TextEncoding oTextEncoding)
{
if (oTextEncoding == null)
{
throw new NullPointerException("Text encoding cannot be null.");
}
m_oTextEncoding = oTextEncoding;
}
/** Get the text encoding used for the description in this frame.
*
* @return the text encoding to be used for this frame
*/
public TextEncoding getTextEncoding()
{
return m_oTextEncoding;
}
protected byte[] getFrameId()
{
return "APIC".getBytes();
}
public String toString()
{
return "Attached picure: Mime type=[" + m_sMimeType + "], Picture type = " +
m_oPictureType.getValue() + ", Description=[" + m_sDescription + "], Picture data length = " +
m_abyPictureData.length;
}
protected void writeBody(ID3DataOutputStream oIDOS)
throws IOException
{
// text encoding
oIDOS.writeUnsignedByte(m_oTextEncoding.getEncodingValue());
// mime type (and trailing null)
oIDOS.write(m_sMimeType.getBytes());
oIDOS.writeUnsignedByte(0);
// picture type
oIDOS.writeUnsignedByte(m_oPictureType.getValue());
// description
if (m_sDescription != null)
{
oIDOS.write(m_sDescription.getBytes(m_oTextEncoding.getEncodingString()));
}
// null separating description from picture data
if (m_oTextEncoding.equals(TextEncoding.ISO_8859_1))
{
oIDOS.writeUnsignedByte(0);
}
else
{
oIDOS.writeUnsignedByte(0);
oIDOS.writeUnsignedByte(0);
}
// actual picture data
oIDOS.write(m_abyPictureData);
}
public boolean equals(Object oOther)
{
if ((oOther == null) || (!(oOther instanceof APICID3V2Frame)))
{
return false;
}
APICID3V2Frame oOtherAPIC = (APICID3V2Frame)oOther;
return (m_oTextEncoding.equals(oOtherAPIC.m_oTextEncoding) &&
m_sMimeType.equals(oOtherAPIC.m_sMimeType) &&
m_oPictureType.equals(oOtherAPIC.m_oPictureType) &&
m_sDescription.equals(oOtherAPIC.m_sDescription) &&
Arrays.equals(m_abyPictureData, oOtherAPIC.m_abyPictureData));
}
public static class PictureType
{
private byte m_byValue;
/** Private constructor. Picture types are predefined. */
private PictureType(byte byValue)
{
m_byValue = byValue;
}
private byte getValue()
{
return m_byValue;
}
/** Equality test returns if two objects represent the same picture type. */
public boolean equals(PictureType oPictureType)
{
if ( (oPictureType == null) || ( ! (oPictureType instanceof PictureType)) )
{
return false;
}
return (oPictureType.m_byValue == this.m_byValue);
}
/** Predefined picture type. */
public static final PictureType Other = new PictureType((byte)0);
/** Predefined picture type. Note, file icon images should be resctricted to 32 by 32 pixel
* images in PNG format.
*/
public static final PictureType FileIcon = new PictureType((byte)1);
/** Predefined picture type. */
public static final PictureType OtherFileIcon = new PictureType((byte)2);
/** Predefined picture type. */
public static final PictureType FrontCover = new PictureType((byte)3);
/** Predefined picture type. */
public static final PictureType BackCover = new PictureType((byte)4);
/** Predefined picture type. */
public static final PictureType LeafletPage = new PictureType((byte)5);
/** Predefined picture type. */
public static final PictureType Media = new PictureType((byte)6);
/** Predefined picture type. */
public static final PictureType LeadArtist = new PictureType((byte)7);
/** Predefined picture type. */
public static final PictureType Artist = new PictureType((byte)8);
/** Predefined picture type. */
public static final PictureType Conductor = new PictureType((byte)9);
/** Predefined picture type. */
public static final PictureType Band = new PictureType((byte)10);
/** Predefined picture type. */
public static final PictureType Composer = new PictureType((byte)11);
/** Predefined picture type. */
public static final PictureType Lyricist = new PictureType((byte)12);
/** Predefined picture type. */
public static final PictureType Location = new PictureType((byte)13);
/** Predefined picture type. */
public static final PictureType DuringRecording = new PictureType((byte)14);
/** Predefined picture type. */
public static final PictureType DuringPerformance = new PictureType((byte)15);
/** Predefined picture type. */
public static final PictureType FrameCapture = new PictureType((byte)16);
/** Predefined picture type. (?!?) */
public static final PictureType BrightColouredFish = new PictureType((byte)17);
/** Predefined picture type. */
public static final PictureType Illustration = new PictureType((byte)18);
/** Predefined picture type. */
public static final PictureType ArtistLogo = new PictureType((byte)19);
/** Predefined picture type. */
public static final PictureType PublisherLogo = new PictureType((byte)20);
}
}