/**
   * @param startByte
   * @param endByte
   * @return
   * @throws Exception
   * @return true if all the bytes between in the file between startByte and endByte are null, false
   *     otherwise
   */
  private boolean isFilePortionNull(int startByte, int endByte) throws IOException {
    logger.config("Checking file portion:" + Hex.asHex(startByte) + ":" + Hex.asHex(endByte));
    FileInputStream fis = null;
    FileChannel fc = null;
    try {
      fis = new FileInputStream(file);
      fc = fis.getChannel();
      fc.position(startByte);
      ByteBuffer bb = ByteBuffer.allocateDirect(endByte - startByte);
      fc.read(bb);
      while (bb.hasRemaining()) {
        if (bb.get() != 0) {
          return false;
        }
      }
    } finally {
      if (fc != null) {
        fc.close();
      }

      if (fis != null) {
        fis.close();
      }
    }
    return true;
  }
  /**
   * Extracts the raw ID3v2 tag data into a file.
   *
   * <p>This provides access to the raw data before manipulation, the data is written from the start
   * of the file to the start of the Audio Data. This is primarily useful for manipulating corrupted
   * tags that are not (fully) loaded using the standard methods.
   *
   * @param outputFile to write the data to
   * @return
   * @throws TagNotFoundException
   * @throws IOException
   */
  public File extractID3v2TagDataIntoFile(File outputFile)
      throws TagNotFoundException, IOException {
    int startByte = (int) ((MP3AudioHeader) audioHeader).getMp3StartByte();
    if (startByte >= 0) {

      // Read byte into buffer
      FileInputStream fis = new FileInputStream(file);
      FileChannel fc = fis.getChannel();
      ByteBuffer bb = ByteBuffer.allocate(startByte);
      fc.read(bb);

      // Write bytes to outputFile
      FileOutputStream out = new FileOutputStream(outputFile);
      out.write(bb.array());
      out.close();
      fc.close();
      fis.close();
      return outputFile;
    }
    throw new TagNotFoundException("There is no ID3v2Tag data in this file");
  }
  /**
   * Read V2tag if exists
   *
   * <p>TODO:shouldn't we be handing TagExceptions:when will they be thrown
   *
   * @param file
   * @param loadOptions
   * @throws IOException
   * @throws TagException
   */
  private void readV2Tag(File file, int loadOptions, int startByte)
      throws IOException, TagException {
    // We know where the actual Audio starts so load all the file from start to that point into
    // a buffer then we can read the IDv2 information without needing any more File I/O
    if (startByte >= AbstractID3v2Tag.TAG_HEADER_LENGTH) {
      logger.finer("Attempting to read id3v2tags");
      FileInputStream fis = null;
      FileChannel fc = null;
      ByteBuffer bb;
      try {
        fis = new FileInputStream(file);
        fc = fis.getChannel();
        bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, startByte);
      }
      // #JAUDIOTAGGER-419:If reading networked file map can fail so just copy bytes instead
      catch (IOException ioe) {
        bb = ByteBuffer.allocate(startByte);
        fc.read(bb, 0);
      } finally {
        if (fc != null) {
          fc.close();
        }

        if (fis != null) {
          fis.close();
        }
      }

      try {
        bb.rewind();

        if ((loadOptions & LOAD_IDV2TAG) != 0) {
          logger.config("Attempting to read id3v2tags");
          try {
            this.setID3v2Tag(new ID3v24Tag(bb, file.getName()));
          } catch (TagNotFoundException ex) {
            logger.config("No id3v24 tag found");
          }

          try {
            if (id3v2tag == null) {
              this.setID3v2Tag(new ID3v23Tag(bb, file.getName()));
            }
          } catch (TagNotFoundException ex) {
            logger.config("No id3v23 tag found");
          }

          try {
            if (id3v2tag == null) {
              this.setID3v2Tag(new ID3v22Tag(bb, file.getName()));
            }
          } catch (TagNotFoundException ex) {
            logger.config("No id3v22 tag found");
          }
        }
      } finally {
        // Workaround for 4724038 on Windows
        bb.clear();
        if (bb.isDirect() && !TagOptionSingleton.getInstance().isAndroid()) {
          // Reflection substitute for following code:
          //    ((sun.nio.ch.DirectBuffer) bb).cleaner().clean();
          // which causes exception on Android - Sun NIO classes are not available
          try {
            Class<?> clazz = Class.forName("sun.nio.ch.DirectBuffer");
            Method cleanerMethod = clazz.getMethod("cleaner");
            Object cleaner = cleanerMethod.invoke(bb); // cleaner = bb.cleaner()
            if (cleaner != null) {
              Method cleanMethod = cleaner.getClass().getMethod("clean");
              cleanMethod.invoke(cleaner); // cleaner.clean()
            }
          } catch (ClassNotFoundException e) {
            logger.severe("Could not load sun.nio.ch.DirectBuffer.");
          } catch (NoSuchMethodException e) {
            logger.severe("Could not invoke DirectBuffer method - " + e.getMessage());
          } catch (InvocationTargetException e) {
            logger.severe("Could not invoke DirectBuffer method - target exception");
          } catch (IllegalAccessException e) {
            logger.severe("Could not invoke DirectBuffer method - illegal access");
          }
        }
      }
    } else {
      logger.config("Not enough room for valid id3v2 tag:" + startByte);
    }
  }