Methods Summary |
---|
public void | addID3Observer(ID3Observer oID3Observer)
m_oID3ObserverSet.add(oID3Observer);
|
private int | getActualLength()Returns the length in bytes that the body of the frame will require when actually written to
a file. This may be shorter than the default length, if the frame is compressed.
ByteArrayOutputStream oBAOS = new ByteArrayOutputStream();
ID3DataOutputStream oIDOS = new ID3DataOutputStream(oBAOS);
writeBody(oIDOS);
byte[] abyBody = oBAOS.toByteArray();
if (m_bCompressionFlag)
{
ByteArrayOutputStream oCompressedBAOS = new ByteArrayOutputStream();
DeflaterOutputStream oDeflaterOS = new DeflaterOutputStream(oCompressedBAOS);
oDeflaterOS.write(abyBody);
oDeflaterOS.finish();
abyBody = oCompressedBAOS.toByteArray();
}
if (m_bEncryptionFlag)
{
if (m_oCryptoAgent == null)
{
throw new ID3Exception("Crypto agent for method " + m_byEncryptionMethod + " not registered. Cannot write frame.");
}
abyBody = m_oCryptoAgent.encrypt(abyBody, m_abyEncryptionData);
}
return abyBody.length + (m_bCompressionFlag ? 4 : 0) + (m_bEncryptionFlag ? 1 : 0);
|
public byte | getEncryptionMethod()Get the encryption method symbol used to encrypt this frame.
if (! isEncrypted())
{
throw new ID3Exception("This frame is not encrypted.");
}
return m_byEncryptionMethod;
|
protected abstract byte[] | getFrameId()Get the four bytes which uniquely specify of which type this frame is.
|
private int | getLength()Return number of bytes required to store the body of this frame.
ByteArrayOutputStream oBAOS = new ByteArrayOutputStream();
ID3DataOutputStream oLengthIDOS = new ID3DataOutputStream(oBAOS);
writeBody(oLengthIDOS);
return oBAOS.size();
|
public boolean | isEncrypted()Check whether this frame is encrypted.
return m_bEncryptionFlag;
|
public void | notifyID3Observers()
Iterator oIter = m_oID3ObserverSet.iterator();
while (oIter.hasNext())
{
ID3Observer oID3Observer = (ID3Observer)oIter.next();
oID3Observer.update(this);
}
|
static org.blinkenlights.jid3.v2.ID3V2Frame | read(ID3DataInputStream oID3DIS)Read an ID3 v2 frame from an ID3DataInputStream.
return read(oID3DIS, new ENCRID3V2Frame[0]);
|
static org.blinkenlights.jid3.v2.ID3V2Frame | read(ID3DataInputStream oID3DIS, ENCRID3V2Frame[] aoENCRID3V2Frame)Read an ID3 v2 frame from an ID3DataInputStream, providing the possibility for decryption.
String sFrameId = null;
try
{
// read frame id
byte[] abyFrameId = new byte[4];
oID3DIS.readFully(abyFrameId);
if (abyFrameId[0] == 0) // we're reading into the padding past the frames
{
return null;
}
sFrameId = new String(abyFrameId);
//HACK: This is a work-around for a bug in the MP3ext Windows explorer extension. It repeatedly
// writes the string "MP3ext V3.3.18(unicode)" into the padding area after the frames in a v2 tag.
// This is invalid, and the resulting frames are corrupt, according to the ID3 specification, which
// requires that padding contain only nulls.
if (sFrameId.equals("MP3e"))
{
return null;
}
if (( ! sFrameId.matches("[A-Z0-9]+")) && ID3V2Tag.usingStrict())
{
throw new InvalidFrameID3Exception("Invalid frame id [" + sFrameId + "].");
}
// read size
int iFrameSize = oID3DIS.readBE32();
// read first flags byte
int iFirstFlags = oID3DIS.readUnsignedByte();
boolean bTagAlterPreservationFlag = ((iFirstFlags & 0x80) != 0);
boolean bFileAlterPreservationFlag = ((iFirstFlags & 0x40) != 0);
boolean bReadOnlyFlag = ((iFirstFlags & 0x20) != 0);
boolean bUnknownFirstByteFlags = ((iFirstFlags & 0x1f) != 0);
// read second flags byte
int iSecondFlags = oID3DIS.readUnsignedByte();
boolean bCompressionFlag = ((iSecondFlags & 0x80) != 0);
boolean bEncryptionFlag = ((iSecondFlags & 0x40) != 0);
boolean bGroupingIdentityFlag = ((iSecondFlags & 0x20) != 0);
boolean bUnknownSecondByteFlags = ((iSecondFlags & 0x1f) != 0);
// get length of uncompressed frame if compression set
int iUncompressedSize = iFrameSize;
if (bCompressionFlag)
{
iUncompressedSize = oID3DIS.readBE32();
iFrameSize -= 4; // FIX: four bytes read for frame size
}
// read encryption method byte, if used
int iEncryptionMethodSymbol = 0;
ICryptoAgent oCryptoAgent = null;
byte[] abyEncryptionData = null;
if (bEncryptionFlag)
{
iEncryptionMethodSymbol = oID3DIS.readUnsignedByte();
iFrameSize -= 1; // FIX: one byte for encryption method
// this frame is encrypted.. do we have a means of decrypting it?
for (int i=0; i < aoENCRID3V2Frame.length; i++)
{
if ((aoENCRID3V2Frame[i].getEncryptionMethodSymbol() & 0xff) == iEncryptionMethodSymbol)
{
// we can decrypt this frame now
oCryptoAgent = ID3Encryption.getInstance().lookupCryptoAgent(aoENCRID3V2Frame[i].getOwnerIdentifier());
abyEncryptionData = aoENCRID3V2Frame[i].getEncryptionData();
break;
}
}
if (oCryptoAgent == null)
{
ByteArrayOutputStream oEncryptedBAOS = new ByteArrayOutputStream();
ID3DataOutputStream oEncryptedIDOS = new ID3DataOutputStream(oEncryptedBAOS);
oEncryptedIDOS.write(abyFrameId);
oEncryptedIDOS.writeBE32(iFrameSize + (bEncryptionFlag ? 1 : 0));
oEncryptedIDOS.writeUnsignedByte(iFirstFlags);
oEncryptedIDOS.writeUnsignedByte(iSecondFlags);
if (bCompressionFlag)
{
oEncryptedIDOS.writeID3Four(iUncompressedSize);
}
oEncryptedIDOS.writeUnsignedByte(iEncryptionMethodSymbol);
// determine the length of the compressed/encrypted data to be read in (minus the after header bytes we've already read)
int iFrameDataLength = iFrameSize; // initial length of frame data before data we have already read
// read compressed/encrypted data
byte[] abyEncryptedFrameData = new byte[iFrameDataLength];
oID3DIS.readFully(abyEncryptedFrameData);
oEncryptedIDOS.write(abyEncryptedFrameData);
// we cannot decrypt this frame at this time, so return it, as we read it, as a special encrypted frame object
EncryptedID3V2Frame oEncryptedFrame = new EncryptedID3V2Frame(sFrameId, oEncryptedBAOS.toByteArray());
return oEncryptedFrame;
}
}
// read frame data
byte[] abyFrameData = null;
if (bCompressionFlag)
{
// read compressed data
byte[] abyCompressedFrameData = new byte[iFrameSize];
oID3DIS.readFully(abyCompressedFrameData);
// decrypt compressed data first, if encrypted
if (bEncryptionFlag)
{
abyCompressedFrameData = oCryptoAgent.decrypt(abyCompressedFrameData, abyEncryptionData);
}
// deflate data
ByteArrayInputStream oBAIS = new ByteArrayInputStream(abyCompressedFrameData);
InflaterInputStream oInflaterIS = new InflaterInputStream(oBAIS);
ID3DataInputStream oInflaterID3DIS = new ID3DataInputStream(oInflaterIS);
abyFrameData = new byte[iUncompressedSize];
oInflaterID3DIS.readFully(abyFrameData);
}
else
{
abyFrameData = new byte[iFrameSize];
oID3DIS.readFully(abyFrameData);
// decrypt data, if encrypted
if (bEncryptionFlag)
{
abyFrameData = oCryptoAgent.decrypt(abyFrameData, abyEncryptionData);
}
}
// create a frame object here based on what we've read
ID3V2Frame oID3V2Frame;
if (sFrameId.startsWith("T"))
{
// text information frame
String sClassName = "org.blinkenlights.jid3.v2." + sFrameId + "TextInformationID3V2Frame";
// if this class exists, then create such an object
try
{
Class oID3V2FrameClass = Class.forName(sClassName);
Class[] aoArgClassTypes = { InputStream.class };
Constructor oConstructor = oID3V2FrameClass.getConstructor(aoArgClassTypes);
Object[] aoConstructorArgs = { new ByteArrayInputStream(abyFrameData) };
oID3V2Frame = (ID3V2Frame)oConstructor.newInstance(aoConstructorArgs);
}
catch (ClassNotFoundException e)
{
// unknown frame type
oID3V2Frame = new UnknownTextInformationID3V2Frame(sFrameId, new ByteArrayInputStream(abyFrameData));
}
catch (NoSuchMethodException e)
{
// unknown frame type
oID3V2Frame = new UnknownTextInformationID3V2Frame(sFrameId, new ByteArrayInputStream(abyFrameData));
}
catch (InvocationTargetException e)
{
// constructor threw an exception
if (e.getCause() instanceof Exception)
{
throw (Exception)e.getCause();
}
else
{
throw e;
}
}
}
else if (sFrameId.startsWith("W"))
{
// URL link frame
String sClassName = "org.blinkenlights.jid3.v2." + sFrameId + "UrlLinkID3V2Frame";
// if this class exists, then create such an object
try
{
Class oID3V2FrameClass = Class.forName(sClassName);
Class[] aoArgClassTypes = { InputStream.class };
Constructor oConstructor = oID3V2FrameClass.getConstructor(aoArgClassTypes);
Object[] aoConstructorArgs = { new ByteArrayInputStream(abyFrameData) };
oID3V2Frame = (ID3V2Frame)oConstructor.newInstance(aoConstructorArgs);
}
catch (ClassNotFoundException e)
{
// unknown frame type
oID3V2Frame = new UnknownUrlLinkID3V2Frame(sFrameId, new ByteArrayInputStream(abyFrameData));
}
catch (NoSuchMethodException e)
{
// unknown frame type
oID3V2Frame = new UnknownUrlLinkID3V2Frame(sFrameId, new ByteArrayInputStream(abyFrameData));
}
catch (InvocationTargetException e)
{
// constructor threw an exception
if (e.getCause() instanceof Exception)
{
throw (Exception)e.getCause();
}
else
{
throw e;
}
}
}
else
{
// unique frame
String sClassName = "org.blinkenlights.jid3.v2." + sFrameId + "ID3V2Frame";
// if this class exists, then create such an object
try
{
Class oID3V2FrameClass = Class.forName(sClassName);
Class[] aoArgClassTypes = { InputStream.class };
Constructor oConstructor = oID3V2FrameClass.getConstructor(aoArgClassTypes);
Object[] aoConstructorArgs = { new ByteArrayInputStream(abyFrameData) };
oID3V2Frame = (ID3V2Frame)oConstructor.newInstance(aoConstructorArgs);
}
catch (ClassNotFoundException e)
{
// unknown frame
oID3V2Frame = new UnknownID3V2Frame(sFrameId, abyFrameData);
}
catch (NoSuchMethodException e)
{
// unknown frame type
oID3V2Frame = new UnknownID3V2Frame(sFrameId, abyFrameData);
}
}
// set flags applicable to all v2 frames
oID3V2Frame.setTagAlterPreservationFlag(bTagAlterPreservationFlag);
oID3V2Frame.setFileAlterPreservationFlag(bFileAlterPreservationFlag);
oID3V2Frame.setReadOnlyFlag(bReadOnlyFlag);
oID3V2Frame.setCompressionFlag(bCompressionFlag);
if (bEncryptionFlag)
{
oID3V2Frame.setEncryption((byte)iEncryptionMethodSymbol);
}
oID3V2Frame.setGroupingIdentityFlag(bGroupingIdentityFlag);
return oID3V2Frame;
}
catch (ID3Exception e)
{
throw e;
}
catch (Exception e)
{
if (sFrameId == null)
{
throw new ID3Exception("Error reading v2 frame.", e);
}
else
{
throw new ID3Exception("Error reading " + sFrameId + " v2 frame.", e);
}
}
|
public void | removeID3Observer(ID3Observer oID3Observer)
m_oID3ObserverSet.remove(oID3Observer);
|
public void | setCompressionFlag(boolean bCompressionFlagValue)Specify whether this frame is compressed or not. The compression method used is zlib.
m_bCompressionFlag = bCompressionFlagValue;
|
void | setCryptoAgent(ICryptoAgent oCryptoAgent, byte[] abyEncryptionData)Set the crypto agent to be used in this frame.
m_oCryptoAgent = oCryptoAgent;
m_abyEncryptionData = abyEncryptionData;
|
public void | setEncryption(byte byEncryptionMethod)Set encryption method for this frame. When an encryption method is specified, the
frame will be encrypted before being written to a file.
m_bEncryptionFlag = true;
m_byEncryptionMethod = byEncryptionMethod;
notifyID3Observers();
|
public void | setFileAlterPreservationFlag(boolean bFileAlterPreservationFlagValue)Specify what should happen to this frame, if this file (other than the tag) is modified by a
program which does not recognize it. If set to true, then this frame should be discarded. If
set to false, then the frame should be preserved as is.
m_bFileAlterPreservationFlag = bFileAlterPreservationFlagValue;
|
public void | setGroupingIdentityFlag(boolean bGroupingIdentityFlagValue)Specify whether this frame belongs to a group of other frames or not. If set, the group
identifier must be specified.
m_bGroupingIdentityFlag = bGroupingIdentityFlagValue;
|
public void | setReadOnlyFlag(boolean bReadOnlyFlagValue)Specify whether this frame should be considered read only or not.
m_bReadOnlyFlag = bReadOnlyFlagValue;
|
public void | setTagAlterPreservationFlag(boolean bTagAlterPreservationFlagValue)Specify what should happen to this frame, if the tag it is in is modified by another program
which does not recognize it. If set to true, then this frame should be discarded by a program
which does not recognize it. If set to false, it should be left as is.
m_bTagAlterPreservationFlag = bTagAlterPreservationFlagValue;
|
public abstract java.lang.String | toString()Represent the contents of this frame as a string. For debugging purposes.
|
public void | write(java.io.OutputStream oOS)Write this frame to an output stream.
ID3DataOutputStream oIDOS = new ID3DataOutputStream(oOS);
// write header
writeHeader(oIDOS);
// write body
byte[] abyBody = null;
// put original body bytes in abyBody
ByteArrayOutputStream oBodyBAOS = new ByteArrayOutputStream();
ID3DataOutputStream oBodyIDOS = new ID3DataOutputStream(oBodyBAOS);
writeBody(oBodyIDOS);
abyBody = oBodyBAOS.toByteArray();
// if compression used, compress body byte array
if (m_bCompressionFlag)
{
ByteArrayOutputStream oCompressedBAOS = new ByteArrayOutputStream();
DeflaterOutputStream oDeflaterOS = new DeflaterOutputStream(oCompressedBAOS);
oDeflaterOS.write(abyBody);
oDeflaterOS.finish();
abyBody = oCompressedBAOS.toByteArray();
}
// if encryption used, encrypt body byte array
if (m_bEncryptionFlag)
{
if (m_oCryptoAgent == null)
{
throw new ID3Exception("Crypto agent for method " + m_byEncryptionMethod + " not registered. Cannot write frame.");
}
abyBody = m_oCryptoAgent.encrypt(abyBody, m_abyEncryptionData);
}
oIDOS.write(abyBody);
|
protected abstract void | writeBody(ID3DataOutputStream oIDOS)Write the body of the frame to an ID3 data output stream.
|
protected void | writeHeader(java.io.OutputStream oOS)Write the header of this frame to an output stream.
try
{
ID3DataOutputStream oIDOS = new ID3DataOutputStream(oOS);
// frame id
oIDOS.write(getFrameId());
// size
int iActualLength = getActualLength();
oIDOS.writeBE32(iActualLength);
//oIDOS.writeBE32(getLength());
// first flags
int iFirstFlags = 0;
if (m_bTagAlterPreservationFlag)
{
iFirstFlags |= (1 << 7);
}
if (m_bFileAlterPreservationFlag)
{
iFirstFlags |= (1 << 6);
}
if (m_bReadOnlyFlag)
{
iFirstFlags |= (1 << 5);
}
oIDOS.writeUnsignedByte(iFirstFlags);
// second flags
int iSecondFlags = 0;
if (m_bCompressionFlag)
{
iSecondFlags |= (1 << 7);
}
if (m_bEncryptionFlag)
{
iSecondFlags |= (1 << 6);
}
if (m_bGroupingIdentityFlag)
{
iSecondFlags |= (1 << 5);
}
oIDOS.writeUnsignedByte(iSecondFlags);
// write uncompressed length of the body, if it is compressed
if (m_bCompressionFlag)
{
oIDOS.writeBE32(getLength());
}
// write encrypted method, if used
if (m_bEncryptionFlag)
{
oIDOS.writeUnsignedByte(m_byEncryptionMethod & 0xff);
}
}
catch (Exception e)
{
throw new ID3Exception("Error writing frame: " + e.getMessage(), e);
}
|