/*
* Entagged Audio Tag library
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package entagged.audioformats.mp3.util.id3frames;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import entagged.audioformats.generic.TagField;
import entagged.audioformats.mp3.Id3v2Tag;
import entagged.audioformats.mp3.util.Id3v2TagCreator;
public abstract class Id3Frame implements TagField {
protected byte[] flags;
protected byte version;
public Id3Frame() {
this.version = Id3v2Tag.ID3V23;
createDefaultFlags();
}
public Id3Frame(byte[] raw, byte version)
throws UnsupportedEncodingException {
byte[] rawNew;
if (version == Id3v2Tag.ID3V23 || version == Id3v2Tag.ID3V24) {
byte size = 2;
if ((raw[1] & 0x80) == 0x80) {
// Compression zlib, 4 bytes uncompressed size.
size += 4;
}
if ((raw[1] & 0x80) == 0x40) {
// Encryption method byte
size += 1;
}
if ((raw[1] & 0x80) == 0x20) {
// Group identity byte
size += 1;
}
this.flags = new byte[size];
for (int i = 0; i < size; i++)
this.flags[i] = raw[i];
rawNew = raw;
} else {
createDefaultFlags();
rawNew = new byte[this.flags.length + raw.length];
copy(this.flags, rawNew, 0);
copy(raw, rawNew, this.flags.length);
}
this.version = version;
populate(rawNew);
}
protected abstract byte[] build() throws UnsupportedEncodingException;
/**
* (overridden)
*
* @see java.lang.Object#clone()
*/
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
protected void copy(byte[] src, byte[] dst, int dstOffset) {
for (int i = 0; i < src.length; i++)
dst[i + dstOffset] = src[i];
}
private void createDefaultFlags() {
this.flags = new byte[2];
this.flags[0] = 0;
this.flags[1] = 0;
}
/**
* (overridden) For Id3Frame objects the comparison can be easily done by
* comparing their binary representation which can be retrieved by invoking
* {@link Id3Frame#build()}.<br>
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof Id3Frame) {
Id3Frame other = (Id3Frame) obj;
try {
return Arrays.equals(this.build(), other.build());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return false;
}
protected byte[] getBytes(String s, String encoding)
throws UnsupportedEncodingException {
byte[] result = null;
if ("UTF-16".equalsIgnoreCase(encoding)) {
result = s.getBytes("UTF-16LE");
// 2 for BOM and 2 for terminal character
byte[] tmp = new byte[result.length + 4];
System.arraycopy(result, 0, tmp, 2, result.length);
// Create the BOM
tmp[0] = (byte) 0xFF;
tmp[1] = (byte) 0xFE;
result = tmp;
} else {
// this is encoding ISO-8859-1, for the time of this change.
result = s.getBytes(encoding);
int zeroTerm = 1;
if ("UTF-16BE".equals(encoding)) {
zeroTerm = 2;
}
byte[] tmp = new byte[result.length + zeroTerm];
System.arraycopy(result, 0, tmp, 0, result.length);
result = tmp;
}
return result;
}
public byte[] getFlags() {
return this.flags;
}
public abstract String getId();
protected byte[] getIdBytes() {
return getId().getBytes();
}
public byte[] getRawContent() throws UnsupportedEncodingException {
return build();
}
protected byte[] getSize(int size) {
byte[] b = null;
if (this.version == Id3v2Tag.ID3V24) {
b = Id3v2TagCreator.getSyncSafe(size);
} else {
b = new byte[4];
b[0] = (byte) ((size >> 24) & 0xFF);
b[1] = (byte) ((size >> 16) & 0xFF);
b[2] = (byte) ((size >> 8) & 0xFF);
b[3] = (byte) (size & 0xFF);
}
return b;
}
protected String getString(byte[] b, int offset, int length, String encoding)
throws UnsupportedEncodingException {
String result = null;
if ("UTF-16".equalsIgnoreCase(encoding)) {
int zerochars = 0;
// do we have zero terminating chars (old entagged did not)
if (b[offset + length - 2] == 0x00
&& b[offset + length - 1] == 0x00) {
zerochars = 2;
}
if (b[offset] == (byte) 0xFE && b[offset + 1] == (byte) 0xFF) {
result = new String(b, offset + 2, length - 2 - zerochars,
"UTF-16BE");
} else if (b[offset] == (byte) 0xFF && b[offset + 1] == (byte) 0xFE) {
result = new String(b, offset + 2, length - 2 - zerochars,
"UTF-16LE");
} else {
/*
* Now we have a little problem. The tag is not id3-spec
* conform. And since I don't have a way to see if its little or
* big endian, i decide for the windows default little endian.
*/
result = new String(b, offset, length - zerochars, "UTF-16LE");
}
} else {
int zerochars = 0;
if ("UTF-16BE".equals(encoding)) {
if (b[offset + length - 2] == 0x00
&& b[offset + length - 1] == 0x00) {
zerochars = 2;
}
} else if (b[offset + length - 1] == 0x00) {
zerochars = 1;
}
if (length == 0 || offset + length > b.length) {
result = "";
} else {
result = new String(b, offset, length - zerochars, encoding);
}
}
return result;
}
protected int indexOfFirstNull(byte[] b, int offset) {
for (int i = offset; i < b.length; i++)
if (b[i] == 0)
return i;
return -1;
}
public abstract boolean isBinary();
public void isBinary(boolean b) { /* Never can choose if binary or not */
}
public abstract boolean isCommon();
protected abstract void populate(byte[] raw)
throws UnsupportedEncodingException;
}