/*
* Written By Charles M. Chen
*
* Created on Sep 2, 2005
*
*/
package org.cmc.music.myid3.id3v2;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import org.cmc.music.myid3.MyID3Listener;
import org.cmc.music.myid3.UnicodeMetrics;
import org.cmc.music.util.Debug;
public class MyID3v2Read implements MyID3v2Constants
{
private static final int HIGH_BIT = 1 << 7;
private final InputStream is;
private final boolean async;
private final MyID3Listener listener;
public MyID3v2Read(final MyID3Listener listener, final InputStream is,
boolean async)
{
this.listener = listener;
this.is = is;
this.async = async;
}
private boolean complete = false, error = false, noTag = false,
streamComplete = false;
public void dump()
{
Debug.debug("complete", complete);
Debug.debug("error", error);
Debug.debug("no_tag", noTag);
Debug.debug("error_msg", errorMessage);
Debug.debug("stream_complete", streamComplete);
Debug.debug("index", index);
Debug.debug("last", last);
Debug.debug("header_read", header_read);
Debug.debug("tag_read", tagRead);
Debug.debug("bytes_read", bytes_read);
Debug.debug("tagLength", tagLength);
Debug.debug("tags", frames);
}
public boolean isComplete()
{
return complete || error || noTag;
}
public boolean isError()
{
return error;
}
public boolean hasTags()
{
return !error && complete && !noTag;
}
// public boolean isSuccess()
// {
// return tag_read && !error;
// }
private boolean header_read = false, tagRead = false;
private int index = 0, last = -1;
public boolean iteration()
{
if (isComplete())
return true;
if (!read())
return false;
if (isComplete())
return true;
if (!header_read)
{
if (bytes_read < TAG_HEADER_LENGTH)
{
if (streamComplete)
error = true;
return true;
}
readHeader();
}
if (!tagRead)
{
if (bytes_read < tagLength)
{
if (streamComplete)
error = true;
return true;
}
readTag();
complete = true;
// Debug.debug("finished v2 file_precis", file_precis);
// if (file_precis != null)
// {
// file_precis.setID3v2(getBytes(), getTags());
// }
}
// complete = true;
return true;
}
private int readInt3(byte bytes[], boolean check_tagLength)
{
if (((index + 2) >= tagLength) && check_tagLength)
{
setError("readInt3(index: " + index + ", tagLength: " + tagLength);
return -1;
}
if ((index + 3) >= bytes.length)
{
setError("readInt3(index: " + index + ", bytes.length: "
+ bytes.length);
return -1;
}
int array[] = { 0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
};
int result = (array[0] << 16) | (array[1] << 8) | (array[2] << 0);
return result;
}
public static Number readSynchsafeInt(byte bytes[], int start)
{
if ((start + 3) >= bytes.length)
{
// setError("readSynchsafeInt(index: " + start + ", bytes.length: "
// + bytes.length);
return null;
}
int index = start;
int array[] = { 0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
};
for (int i = 0; i < array.length; i++)
{
if ((array[i] & HIGH_BIT) > 0)
{
array[i] &= HIGH_BIT;
}
}
int result = (array[0] << 21) | (array[1] << 14) | (array[2] << 7)
| (array[3] << 0);
return new Integer(result);
}
private int readSynchsafeInt(byte bytes[], boolean check_tagLength)
{
if (((index + 3) >= tagLength) && check_tagLength)
{
setError("readSynchsafeInt(index: " + index + ", tagLength: "
+ tagLength);
return -1;
}
if ((index + 3) >= bytes.length)
{
setError("readSynchsafeInt(index: " + index + ", bytes.length: "
+ bytes.length);
return -1;
}
int array[] = { 0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
};
for (int i = 0; i < array.length; i++)
{
if ((array[i] & HIGH_BIT) > 0)
{
array[i] &= HIGH_BIT;
}
}
int result = (array[0] << 21) | (array[1] << 14) | (array[2] << 7)
| (array[3] << 0);
return result;
}
private int readInt(byte bytes[], boolean check_tagLength)
{
if (((index + 3) >= tagLength) && check_tagLength)
{
setError("readInt(index: " + index + ", tagLength: " + tagLength);
return -1;
}
if ((index + 3) >= bytes.length)
{
setError("readInt(index: " + index + ", bytes.length: "
+ bytes.length);
return -1;
}
int array[] = { 0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
0xff & bytes[index++], //
};
int result = (array[0] << 24) | (array[1] << 16) | (array[2] << 8)
| (array[3] << 0);
return result;
}
private int readShort(byte bytes[])
{
if (((index + 1) >= tagLength) || ((index + 1) >= bytes.length))
{
setError("readShort(index: " + index + ", tagLength: " + tagLength
+ ", bytes.length: " + bytes.length);
Debug.debug("bad readShort index", index);
Debug.debug("bytes", bytes, index);
Debug.dumpStack(5);
return -1;
}
byte array[] = { bytes[index++], //
bytes[index++], //
};
int result = (array[0] << 8) | (array[1] << 0);
return result;
}
private byte versionMajor, versionMinor;
private boolean tagUnsynchronization = false, tagCompression = false,
tagExtendedHeader = false, tagExperimentalIndicator = false,
tagFooterPresent = false;
private void readHeader()
{
byte bytes[] = baos.toByteArray();
if (bytes.length < 10)
{
setError("missing header");
return;
}
if (listener != null)
listener.log("id3v2 header");
if (bytes[index++] != 0x49)
noTag = true;
else if (bytes[index++] != 0x44)
noTag = true;
else if (bytes[index++] != 0x33)
noTag = true;
if (error || noTag)
return;
versionMajor = bytes[index++];
versionMinor = bytes[index++];
if (listener != null)
{
listener.log("\t" + "id3v2 versionMajor", versionMajor);
listener.log("\t" + "id3v2 versionMinor", versionMinor);
}
if ((versionMajor < 2) || (versionMajor > 4))
{
setError("Unknown id3v2 Major Version: " + versionMajor);
return;
}
long flags = bytes[index++];
long workingFlags = flags;
if (versionMajor == 2)
{
if ((workingFlags & HEADER_FLAG_ID3v22_UNSYNCHRONISATION) > 0)
{
tagUnsynchronization = true;
workingFlags ^= HEADER_FLAG_ID3v22_UNSYNCHRONISATION;
}
if ((workingFlags & HEADER_FLAG_ID3v22_COMPRESSION) > 0)
{
tagCompression = true;
workingFlags ^= HEADER_FLAG_ID3v22_COMPRESSION;
}
} else if (versionMajor == 3)
{
if ((workingFlags & HEADER_FLAG_ID3v23_UNSYNCHRONISATION) > 0)
{
tagUnsynchronization = true;
workingFlags ^= HEADER_FLAG_ID3v23_UNSYNCHRONISATION;
}
if ((workingFlags & HEADER_FLAG_ID3v23_EXTENDED_HEADER) > 0)
{
tagExtendedHeader = true;
workingFlags ^= HEADER_FLAG_ID3v23_EXTENDED_HEADER;
}
if ((workingFlags & HEADER_FLAG_ID3v23_EXPERIMENTAL_INDICATOR) > 0)
{
tagExperimentalIndicator = true;
workingFlags ^= HEADER_FLAG_ID3v23_EXPERIMENTAL_INDICATOR;
}
// hack to fix old mistake.
if ((workingFlags & HEADER_FLAG_ID3v24_FOOTER_PRESENT) > 0)
workingFlags ^= HEADER_FLAG_ID3v24_FOOTER_PRESENT;
} else if (versionMajor == 4)
{
if ((workingFlags & HEADER_FLAG_ID3v24_UNSYNCHRONISATION) > 0)
{
tagUnsynchronization = true;
workingFlags ^= HEADER_FLAG_ID3v24_UNSYNCHRONISATION;
}
if ((workingFlags & HEADER_FLAG_ID3v24_EXTENDED_HEADER) > 0)
{
tagExtendedHeader = true;
workingFlags ^= HEADER_FLAG_ID3v24_EXTENDED_HEADER;
}
if ((workingFlags & HEADER_FLAG_ID3v24_EXPERIMENTAL_INDICATOR) > 0)
{
tagExperimentalIndicator = true;
workingFlags ^= HEADER_FLAG_ID3v24_EXPERIMENTAL_INDICATOR;
}
if ((workingFlags & HEADER_FLAG_ID3v24_FOOTER_PRESENT) > 0)
{
tagFooterPresent = true;
workingFlags ^= HEADER_FLAG_ID3v24_FOOTER_PRESENT;
}
} else
{
setError("Unknown id3v2 Major Version: " + versionMajor);
return;
}
if (workingFlags > 0)
{
setError("Unknown id3v2 tag flags(id3v2 version: " + versionMajor
+ "): " + Long.toHexString(flags));
return;
}
if (listener != null)
{
listener.log("\t" + "unsynchronization", tagUnsynchronization);
listener.log("\t" + "compression", tagCompression);
listener.log("\t" + "extendedHeader", tagExtendedHeader);
listener.log("\t" + "experimentalIndicator",
tagExperimentalIndicator);
listener.log("\t" + "footerPresent", tagFooterPresent);
}
{
tagLength = readSynchsafeInt(bytes, false);
tagLength += 10;
last = tagLength;
if (tagFooterPresent)
tagLength += 10;
}
header_read = true;
if (index != TAG_HEADER_LENGTH)
setError("index!=kHEADER_SIZE");
if (listener != null)
{
listener.log("\t" + "tagLength", tagLength);
listener.log();
}
}
// private boolean extended_header;
private final Vector frames = new Vector();
private byte[] ununsynchronize(byte bytes[])
{
// Debug.debug("ununsynchronize before", bytes.length);
ByteArrayOutputStream result = new ByteArrayOutputStream();
int i = 0;
for (; i < bytes.length;)
{
byte b = bytes[i++];
result.write(b);
if ((0xff & b) != 0xff)
continue;
if (i >= bytes.length)
break;
// look ahead.
byte b1 = bytes[i];
if ((0xff & b1) == 0)
i++;
}
bytes = result.toByteArray();
// Debug.debug("ununsynchronize after", bytes.length);
return bytes;
}
private static final String LEGAL_FRAME_ID_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private String parseFrameID(byte bytes[])
{
for (int i = 0; i < bytes.length; i++)
{
int b = 0xff & bytes[i];
char c = (char) b;
if (LEGAL_FRAME_ID_CHARACTERS.indexOf(c) < 0)
{
setError("invalid id3v2 frame id byte: "
+ Integer.toHexString(b));
return null;
}
}
return new String(bytes);
}
private boolean isZeroFrameId(byte bytes[])
{
for (int i = 0; i < bytes.length; i++)
{
if ((0xff & bytes[i]) > 0)
return false;
}
return true;
}
private void readTag()
{
byte bytes[] = baos.toByteArray();
if (bytes.length < tagLength)
{
setError("missing tag");
return;
}
if (tagUnsynchronization)
bytes = ununsynchronize(bytes);
if (tagExtendedHeader)
index += 4;
int tagCount = 0;
for (int tag_num = 0; (((index + 7) < last) && (!error)); tag_num++)
{
if ((index + 7) >= last)
break;
byte frameID[];
if (versionMajor >= 3)
{
frameID = new byte[] { bytes[index++], //
bytes[index++], //
bytes[index++], //
bytes[index++], //
};
} else
{
frameID = new byte[] { bytes[index++], //
bytes[index++], //
bytes[index++], //
};
}
if (isZeroFrameId(frameID))
{
// Not a frame, rest of the tag is padding.
if (listener != null)
listener.log("zero frameID", frameID);
break;
}
String frameIDString = parseFrameID(frameID);
if (null == frameIDString)
break;
if (listener != null)
listener.log("id3v2 frameIDString", frameIDString);
int frameLength;
if (versionMajor >= 4)
frameLength = readSynchsafeInt(bytes, true);
else if (versionMajor >= 3)
frameLength = readInt(bytes, true);
else
frameLength = readInt3(bytes, true);
if (listener != null)
listener.log("frameLength", frameLength);
int maxTagLength = tagLength - index;
if (versionMajor >= 3)
maxTagLength += 2;
if (frameLength == 0)
{
if (listener != null)
listener.log("frame has zero length.");
break;
}
if ((frameLength > maxTagLength) || (frameLength < 0))
{
if (listener != null)
{
listener
.log("frame length exceeds tag length", frameLength);
listener.log("bad frameLength versionMajor", versionMajor);
listener.log("bad frameLength versionMinor", versionMinor);
listener
.log("bad frameLength frameIDString", frameIDString);
listener.log("bad frameLength maxTagLength", maxTagLength);
listener.log("bad frameLength frameLength", frameLength
+ " (0x" + Integer.toHexString(frameLength) + ")");
listener.log("bad frameLength tagLength", tagLength
+ " (0x" + Integer.toHexString(tagLength) + ")");
listener.log("bad frameLength index", index);
listener.log("bytes", bytes);
}
setError("bad frame length(" + tag_num + ": " + frameIDString
+ "): " + frameLength + " (" + new String(frameID));
break;
}
ID3v2FrameFlags flags = null;
if (versionMajor == 3 || versionMajor == 4)
{
int value = readShort(bytes);
int workingFlags = value;
flags = new ID3v2FrameFlags();
if (versionMajor == 3)
{
if ((workingFlags & FRAME_FLAG_ID3v23_TAG_ALTER_PRESERVATION) > 0)
{
flags.setTagAlterPreservation(true);
workingFlags ^= FRAME_FLAG_ID3v23_TAG_ALTER_PRESERVATION;
}
if ((workingFlags & FRAME_FLAG_ID3v23_FILE_ALTER_PRESERVATION) > 0)
{
flags.setFileAlterPreservation(true);
workingFlags ^= FRAME_FLAG_ID3v23_FILE_ALTER_PRESERVATION;
}
if ((workingFlags & FRAME_FLAG_ID3v23_READ_ONLY) > 0)
{
flags.setReadOnly(true);
workingFlags ^= FRAME_FLAG_ID3v23_READ_ONLY;
}
if ((workingFlags & FRAME_FLAG_ID3v23_GROUPING_IDENTITY) > 0)
{
flags.setGroupingIdentity(true);
workingFlags ^= FRAME_FLAG_ID3v23_GROUPING_IDENTITY;
}
if ((workingFlags & FRAME_FLAG_ID3v23_COMPRESSION) > 0)
{
flags.setCompression(true);
workingFlags ^= FRAME_FLAG_ID3v23_COMPRESSION;
}
if ((workingFlags & FRAME_FLAG_ID3v23_ENCRYPTION) > 0)
{
flags.setEncryption(true);
workingFlags ^= FRAME_FLAG_ID3v23_ENCRYPTION;
}
} else if (versionMajor == 4)
{
if ((workingFlags & FRAME_FLAG_ID3v24_TAG_ALTER_PRESERVATION) > 0)
{
flags.setTagAlterPreservation(true);
workingFlags ^= FRAME_FLAG_ID3v24_TAG_ALTER_PRESERVATION;
}
if ((workingFlags & FRAME_FLAG_ID3v24_FILE_ALTER_PRESERVATION) > 0)
{
flags.setFileAlterPreservation(true);
workingFlags ^= FRAME_FLAG_ID3v24_FILE_ALTER_PRESERVATION;
}
if ((workingFlags & FRAME_FLAG_ID3v24_READ_ONLY) > 0)
{
flags.setReadOnly(true);
workingFlags ^= FRAME_FLAG_ID3v24_READ_ONLY;
}
if ((workingFlags & FRAME_FLAG_ID3v24_GROUPING_IDENTITY) > 0)
{
flags.setGroupingIdentity(true);
workingFlags ^= FRAME_FLAG_ID3v24_GROUPING_IDENTITY;
}
if ((workingFlags & FRAME_FLAG_ID3v24_COMPRESSION) > 0)
{
flags.setCompression(true);
workingFlags ^= FRAME_FLAG_ID3v24_COMPRESSION;
}
if ((workingFlags & FRAME_FLAG_ID3v24_ENCRYPTION) > 0)
{
flags.setEncryption(true);
workingFlags ^= FRAME_FLAG_ID3v24_ENCRYPTION;
}
if ((workingFlags & FRAME_FLAG_ID3v24_UNSYNCHRONISATION) > 0)
{
flags.setUnsynchronisation(true);
workingFlags ^= FRAME_FLAG_ID3v24_UNSYNCHRONISATION;
}
if ((workingFlags & FRAME_FLAG_ID3v24_DATA_LENGTH_INDICATOR) > 0)
{
flags.setDataLengthIndicator(true);
workingFlags ^= FRAME_FLAG_ID3v24_DATA_LENGTH_INDICATOR;
}
}
if (workingFlags > 0)
{
setError("Unknown id3v2 frame flags(id3v2 version: "
+ versionMajor + "): " + Long.toHexString(value));
return;
}
} else if (versionMajor == 2)
{
flags = new ID3v2FrameFlags();
} else
{
setError("Unknown ID3v2 version: " + versionMajor);
return;
}
if (listener != null)
listener.log("flags", flags.getSummary());
if (frameLength > 0)
{
int dataLengthIndicator = -1;
if (flags != null && flags.getDataLengthIndicator())
{
dataLengthIndicator = readSynchsafeInt(bytes, true);
frameLength -= 4;
if (listener != null)
listener
.log("dataLengthIndicator", dataLengthIndicator);
}
byte frameBytes[] = new byte[frameLength];
System.arraycopy(bytes, index, frameBytes, 0, frameLength);
index += frameLength;
if (flags != null && flags.getUnsynchronisation())
frameBytes = ununsynchronize(frameBytes);
try
{
if (frameID[0] == 'T')
{
if (listener != null)
listener.log("text frame");
readTextTag(frameLength, frameID, frameBytes,
frameIDString);
} else
{
if (listener != null)
listener.log("data frame");
readDataTag(frameLength, frameID, frameBytes,
frameIDString, flags);
}
} catch (IOException e)
{
if (listener != null)
listener.log("IOException", e.getMessage());
setError(e.getMessage());
// TODO: return or break here or what?
return;
}
}
tagCount++;
if (listener != null)
listener.log();
}
tagRead = true;
if (listener != null)
listener.log();
}
private void readDataTag(int frameLength, byte frameID[],
byte frameBytes[], String frameIDString, ID3v2FrameFlags flags)
throws IOException
{
if (frameIDString.equals("COMM") || frameIDString.equals("COM"))
{
if (frameBytes.length < 5)
{
setError("Unexpected COMM frame length(1): " + frameLength
+ " (" + new String(frameID));
return;
}
int frameIndex = 0;
int charEncodingCode = 0xff & frameBytes[frameIndex++];
byte language_1 = frameBytes[frameIndex++];
byte language_2 = frameBytes[frameIndex++];
byte language_3 = frameBytes[frameIndex++];
String summary = readString(frameBytes, frameIndex,
charEncodingCode);
int stringDataLength = findStringDataLength(frameBytes, frameIndex,
charEncodingCode);
frameIndex += stringDataLength;
String comment;
comment = readString(frameBytes, frameIndex, charEncodingCode);
MyID3v2FrameText tag = new MyID3v2FrameText(frameIDString,
frameBytes, comment);
frames.add(tag);
} else if (frameIDString.equals("PIC") || frameIDString.equals("APIC"))
{
int frameIndex = 0;
int charEncodingCode = 0xff & frameBytes[frameIndex++];
String mimeType;
if (frameIDString.equals("PIC"))
{
int imageFormat1 = 0xff & frameBytes[frameIndex++];
int imageFormat2 = 0xff & frameBytes[frameIndex++];
int imageFormat3 = 0xff & frameBytes[frameIndex++];
String extension = "" + (char) imageFormat1
+ (char) imageFormat2 + (char) imageFormat3;
mimeType = extension.toLowerCase();
if (!mimeType.startsWith("image/"))
mimeType = "image/" + mimeType;
} else
{
mimeType = readString(frameBytes, frameIndex, charEncodingCode);
int stringDataLength = findStringDataLength(frameBytes,
frameIndex, charEncodingCode);
frameIndex += stringDataLength;
}
// Debug.debug("PIC imageFormat1", imageFormat1);
// Debug.debug("PIC imageFormat2", imageFormat2);
// Debug.debug("PIC imageFormat3", imageFormat3);
int pictureType = 0xff & frameBytes[frameIndex++];
// Debug.debug("PIC pictureType", pictureType);
String description;
{
description = readString(frameBytes, frameIndex,
charEncodingCode);
int stringDataLength = findStringDataLength(frameBytes,
frameIndex, charEncodingCode);
frameIndex += stringDataLength;
}
byte imageData[] = new byte[frameBytes.length - frameIndex];
System.arraycopy(frameBytes, frameIndex, imageData, 0,
imageData.length);
frames.add(new MyID3v2FrameImage(frameIDString, frameBytes, flags,
imageData, mimeType, description, pictureType));
} else if (frameIDString.equals("PRIV"))
{
int frameIndex = 0;
String owner_identifier;
{
byte charEncodingCode = CHAR_ENCODING_CODE_ISO_8859_1;
owner_identifier = readString(frameBytes, frameIndex,
charEncodingCode);
int stringDataLength = findStringDataLength(frameBytes,
frameIndex, charEncodingCode);
frameIndex += stringDataLength;
}
if (owner_identifier.startsWith("WM/"))
return;
} else
frames.add(new MyID3v2FrameData(frameIDString, frameBytes, flags));
}
private void readTextTag(int frameLength, byte frameID[],
byte frameBytes[], String frameIDString) throws IOException
{
if (frameLength == 1)
{
} else if (frameLength < 2)
{
setError("Unexpected frame length(1): " + frameLength + " ("
+ new String(frameID));
} else
{
int charEncodingCode = 0xff & frameBytes[0];
int frameIndex = 1;
String value = readString(frameBytes, frameIndex, charEncodingCode);
if (listener != null)
listener.logWithLength("value", value);
MyID3v2FrameText tag;
String value2 = null;
if (frameIDString.equals("TXXX"))
{
int stringDataLength = findStringDataLength(frameBytes,
frameIndex, charEncodingCode);
frameIndex += stringDataLength;
value2 = readString(frameBytes, frameIndex, charEncodingCode);
if (listener != null)
listener.logWithLength("value2", value2);
tag = new MyID3v2FrameText(frameIDString, frameBytes, value,
value2);
} else
tag = new MyID3v2FrameText(frameIDString, frameBytes, value);
frames.add(tag);
}
}
private String getCharacterEncodingName(int charEncodingCode)
throws IOException
{
switch (charEncodingCode)
{
case CHAR_ENCODING_CODE_ISO_8859_1:
return CHAR_ENCODING_ISO;
case CHAR_ENCODING_CODE_UTF_16_WITH_BOM:
return CHAR_ENCODING_UTF_16;
case CHAR_ENCODING_CODE_UTF_16_NO_BOM:
return CHAR_ENCODING_UTF_16;
case CHAR_ENCODING_CODE_UTF_8:
return CHAR_ENCODING_UTF_8;
default:
throw new IOException("Unknown charEncodingCode: "
+ charEncodingCode);
}
}
private String getCharacterEncodingFullName(int charEncodingCode)
throws IOException
{
switch (charEncodingCode)
{
case CHAR_ENCODING_CODE_ISO_8859_1:
return CHAR_ENCODING_ISO;
case CHAR_ENCODING_CODE_UTF_16_WITH_BOM:
return CHAR_ENCODING_UTF_16_WITH_BOM;
case CHAR_ENCODING_CODE_UTF_16_NO_BOM:
return CHAR_ENCODING_UTF_16_WITHOUT_BOM;
case CHAR_ENCODING_CODE_UTF_8:
return CHAR_ENCODING_UTF_8;
default:
throw new IOException("Unknown charEncodingCode: "
+ charEncodingCode);
}
}
private String readString(byte bytes[], int start, int charEncodingCode)
throws IOException
{
if (listener != null)
listener.log("reading string with encoding",
getCharacterEncodingFullName(charEncodingCode));
UnicodeMetrics unicodeMetrics = UnicodeMetrics
.getInstance(charEncodingCode);
int unicodeMetricsEnd = unicodeMetrics.findEndWithoutTerminator(bytes,
start);
int unicodeMetricsLength = unicodeMetricsEnd - start;
String charsetName = getCharacterEncodingName(charEncodingCode);
return new String(bytes, start, unicodeMetricsLength, charsetName);
}
private int findStringDataLength(byte bytes[], int start,
int charEncodingCode) throws IOException
{
UnicodeMetrics unicodeMetrics = UnicodeMetrics
.getInstance(charEncodingCode);
int unicodeMetricsEnd = unicodeMetrics.findEndWithTerminator(bytes,
start);
int unicodeMetricsLength = unicodeMetricsEnd - start;
return unicodeMetricsLength;
}
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private long bytes_read = 0;
private int tagLength = 0;
private final byte buffer[] = new byte[1024];
private boolean read()
{
try
{
if (is.available() < 0)
{
streamComplete = true;
return true;
}
if (!async && is.available() < 1)
{
streamComplete = true;
return true;
}
if (is.available() < 1)
return false;
{
int read = is.read(buffer);
if (read < 1)
{
setError("unexpected stream closed");
return true;
}
baos.write(buffer, 0, read);
bytes_read += read;
}
return true;
} catch (IOException e)
{
// Debug.debug(e);
setError(e.getMessage());
return true;
}
}
private String errorMessage = null;
public String getErrorMessage()
{
return errorMessage;
}
private void setError(String s)
{
error = true;
// Debug.debug("error", s);
errorMessage = s;
}
public Vector getTags()
{
return frames;
}
public byte getVersionMajor()
{
return versionMajor;
}
public byte getVersionMinor()
{
return versionMinor;
}
public long getProgress()
{
return bytes_read;
}
public byte[] getBytes()
{
if (error || noTag || !complete)
return null;
byte bytes[] = baos.toByteArray();
if (bytes.length < tagLength)
return null;
// Debug.debug("sought: " + tagLength);
// Debug.debug("actually read: " + bytes.length);
byte result[] = new byte[tagLength];
System.arraycopy(bytes, 0, result, 0, tagLength);
return result;
}
}