Package org.cmc.music.myid3.id3v2

Source Code of org.cmc.music.myid3.id3v2.MyID3v2Read

/*
* 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;
  }
}
TOP

Related Classes of org.cmc.music.myid3.id3v2.MyID3v2Read

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.