protected void packBundle(ZipOutputStream output, Bundle bundle, String path, Filter filter)
      throws IOException {
    if (filter != null && !filter.accept(path)) {
      log(Level.FINE, "exclude {0}/{1}", new Object[] {bundle.getBundleID(), path});
      return;
    }
    BundleFile bundleFile = bundle.getBundleFile();
    if (bundleFile.isDirectory(path)) {
      List<String> entries = bundleFile.getEntryPaths(path);
      for (String entry : entries) {
        packBundle(output, bundle, entry, filter);
      }
      return;
    }

    // pack the JAR/ZIP
    BundleEntry bundleEntry = bundleFile.getEntry(path);
    if (path.endsWith(".jar") || path.endsWith(".zip")) {
      File tempZipFile = createTempFile(bundleEntry);
      try {
        ZipFile zipFile = new ZipFile(tempZipFile);
        try {
          packZip(output, bundleEntry, zipFile, filter);
        } finally {
          zipFile.close();
        }
      } finally {
        tempZipFile.delete();
      }
      return;
    }

    // pack the normal entry
    if (existingEntries.contains(path)) {
      log(Level.WARNING, "duplicate {0}/{1}", new Object[] {bundle.getBundleID(), path});
      return;
    }
    existingEntries.add(path);

    InputStream input = bundleEntry.getInputStream();
    try {
      ZipEntry zipEntry = new ZipEntry(bundleEntry.getName());
      zipEntry.setTime(bundleEntry.getTime());
      zipEntry.setSize(bundleEntry.getSize());
      output.putNextEntry(zipEntry);
      try {
        copyStream(input, output);
      } finally {
        output.closeEntry();
      }
    } finally {
      input.close();
    }
  }
  protected void packZip(
      ZipOutputStream output, BundleEntry bundleEntry, ZipFile zipFile, Filter filter)
      throws IOException {
    Enumeration<? extends ZipEntry> entries = zipFile.entries();
    while (entries.hasMoreElements()) {
      ZipEntry srcEntry = entries.nextElement();
      String entryName = srcEntry.getName();
      if (filter != null && !filter.accept(bundleEntry.getName() + "/" + entryName)) {
        log(
            Level.FINE,
            "exclude {0}/{1}/{2}",
            new Object[] {bundleEntry.getBundleID(), bundleEntry.getName(), entryName});
        continue;
      }
      if (entryName.endsWith("/")) {
        continue;
      }
      if (existingEntries.contains(entryName)) {
        log(
            Level.WARNING,
            "duplicate {0}/{1}/{2}",
            new Object[] {bundleEntry.getBundleID(), bundleEntry.getName(), entryName});
        continue;
      }
      existingEntries.add(entryName);

      ZipEntry tgtEntry = new ZipEntry(entryName);
      tgtEntry.setTime(srcEntry.getTime());
      output.putNextEntry(tgtEntry);
      try {
        InputStream input = zipFile.getInputStream(srcEntry);
        try {
          copyStream(input, output);
        } finally {
          input.close();
        }
      } finally {
        output.closeEntry();
      }
    }
  }