title: tika PackageParser does not work with directories
body: <p>I am writing a class to recursively extract files from inside a zip file and produce them to a Kafka queue for further processing. My intent is to be able to extract files from multiple levels of zip. The code below is my implementation of the tika ContainerExtractor to do this. </p>
  
  <pre><code>import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.Stack;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.tika.config.TikaConfig;
  import org.apache.tika.detect.DefaultDetector;
  import org.apache.tika.detect.Detector;
  import org.apache.tika.exception.TikaException;
  import org.apache.tika.extractor.ContainerExtractor;
  import org.apache.tika.extractor.EmbeddedResourceHandler;
  import org.apache.tika.io.TemporaryResources;
  import org.apache.tika.io.TikaInputStream;
  import org.apache.tika.metadata.Metadata;
  import org.apache.tika.mime.MediaType;
  import org.apache.tika.parser.AbstractParser;
  import org.apache.tika.parser.AutoDetectParser;
  import org.apache.tika.parser.ParseContext;
  import org.apache.tika.parser.Parser;
  import org.apache.tika.parser.pkg.PackageParser;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.SAXException;
  import org.xml.sax.helpers.DefaultHandler;
  
  public class UberContainerExtractor implements ContainerExtractor {
  
    /**
     * 
     */
    private static final long serialVersionUID = -6636138154366178135L;
  
    // statically populate SUPPORTED_TYPES
    static {
      Set&lt;MediaType&gt; supportedTypes = new HashSet&lt;MediaType&gt;();
      ParseContext context = new ParseContext();
      supportedTypes.addAll(new PackageParser().getSupportedTypes(context));
      SUPPORTED_TYPES = Collections.unmodifiableSet(supportedTypes);
    }
  
    /**
     * A stack that maintains the parent filenames for the recursion
     */
    Stack&lt;String&gt; parentFileNames = new Stack&lt;String&gt;();
    /**
     * The default tika parser
     */
    private final Parser parser;
    /**
     * Default tika detector
     */
    private final Detector detector;
    /**
     * The supported container types into which we can recurse
     */
    public final static Set&lt;MediaType&gt; SUPPORTED_TYPES;
    /**
     * The number of documents recursively extracted from the container and its
     * children containers if present
     */
    int extracted;
  
    public UberContainerExtractor() {
      this(TikaConfig.getDefaultConfig());
    }
  
    public UberContainerExtractor(TikaConfig config) {
      this(new DefaultDetector(config.getMimeRepository()));
    }
  
    public UberContainerExtractor(Detector detector) {
      this.parser = new AutoDetectParser(new PackageParser());
      this.detector = detector;
    }
  
    public boolean isSupported(TikaInputStream input) throws IOException {
      MediaType type = detector.detect(input, new Metadata());
      return SUPPORTED_TYPES.contains(type);
    }
  
    @Override
    public void extract(TikaInputStream stream, ContainerExtractor recurseExtractor, EmbeddedResourceHandler handler)
        throws IOException, TikaException {
  
      ParseContext context = new ParseContext();
      context.set(Parser.class, new RecursiveParser(recurseExtractor, handler));
      try {
        Metadata metadata = new Metadata();
        parser.parse(stream, new DefaultHandler(), metadata, context);
      } catch (SAXException e) {
        throw new TikaException("Unexpected SAX exception", e);
      }
    }
  
    private class RecursiveParser extends AbstractParser {
  
      /**
       * 
       */
      private static final long serialVersionUID = -7260171956667273262L;
  
      private final ContainerExtractor extractor;
  
      private final EmbeddedResourceHandler handler;
  
      private RecursiveParser(ContainerExtractor extractor, EmbeddedResourceHandler handler) {
        this.extractor = extractor;
        this.handler = handler;
      }
  
      public Set&lt;MediaType&gt; getSupportedTypes(ParseContext context) {
        return parser.getSupportedTypes(context);
      }
  
      public void parse(InputStream stream, ContentHandler ignored, Metadata metadata, ParseContext context)
          throws IOException, SAXException, TikaException {
        TemporaryResources tmp = new TemporaryResources();
        try {
          TikaInputStream tis = TikaInputStream.get(stream, tmp);
  
          // Figure out what we have to process
          String filename = metadata.get(Metadata.RESOURCE_NAME_KEY);
          MediaType type = detector.detect(tis, metadata);
  
          if (extractor == null) {
            // do nothing
          } else {
            // Use a temporary file to process the stream
            File file = tis.getFile();
            System.out.println("file is directory = " + file.isDirectory());
  
            // Recurse and extract if the filetype is supported
            if (SUPPORTED_TYPES.contains(type)) {
              System.out.println("encountered a supported file:" + filename);
              parentFileNames.push(filename);
              extractor.extract(tis, extractor, handler);
              parentFileNames.pop();
            } else { // produce the file
              List&lt;String&gt; parentFilenamesList = new ArrayList&lt;String&gt;(parentFileNames);
              parentFilenamesList.add(filename);
              String originalFilepath = StringUtils.join(parentFilenamesList, "/");
              System.out.println("producing " + filename + " with originalFilepath:" + originalFilepath
                  + " to kafka queue");
              ++extracted;
            }
          }
        } finally {
          tmp.dispose();
        }
      }
    }
  
    public int getExtracted() {
      return extracted;
    }
  
    public static void main(String[] args) throws IOException, TikaException {
      String filename = "/Users/rohit/Data/cd.zip";
      File file = new File(filename);
      TikaInputStream stream = TikaInputStream.get(file);
  
      ContainerExtractor recursiveExtractor = new UberContainerExtractor();
  
      EmbeddedResourceHandler resourceHandler = new EmbeddedResourceHandler() {
        @Override
        public void handle(String filename, MediaType mediaType, InputStream stream) {
          // do nothing
        }
      };
  
      recursiveExtractor.extract(stream, recursiveExtractor, resourceHandler);
  
      stream.close();
  
      System.out.println("extracted " + ((UberContainerExtractor) recursiveExtractor).getExtracted() + " files");
  
    }
  }
  </code></pre>
  
  <p>It works on multiple levels of zip as long as the files inside the zips are in a flat structure. for ex. 
  cd.zip 
    - c.txt
    - d.txt</p>
  
  <p>The code does not work if there the files in the zip are present inside a directory. for ex.
  ab.zip
    - ab/
      - a.txt
      - b.txt</p>
  
  <p>While debugging I came across the following code snippet in the PackageParser</p>
  
  <pre><code>try {
    ArchiveEntry entry = ais.getNextEntry();
    while (entry != null) {
      if (!entry.isDirectory()) {
          parseEntry(ais, entry, extractor, xhtml);
      }
      entry = ais.getNextEntry();
    }
  } finally {
    ais.close();
  }
  </code></pre>
  
  <p>I tried to comment out the if condition but it did not work. Is there a reason why this is commented? Is there any way of getting around this?</p>
  
  <p>I am using tika version 1.6</p>