/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:  
 *       "This product includes software developed by the 
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.tools.ant.taskdefs.optional.depend;

import java.util.*;
import java.io.*;

/**
 * An iterator which iterates through the contents of a java directory.
 * 
 * The iterator should be created with the directory at the root of the
 * Java namespace.
 * 
 * @author Conor MacNeill
 */
public class DirectoryIterator implements ClassFileIterator {

    /**
     * This is a stack of current iterators supporting the depth first
     * traversal of the directory tree.
     */
    private Stack enumStack;

    /**
     * The current directory iterator. As directories encounter lower level
     * directories, the current iterator is pushed onto the iterator
     * stack and a new iterator over the sub directory becomes the current
     * directory. This implements a depth first traversal of the directory namespace.
     */
    private Enumeration currentEnum;

    /**
     * The length of the root directory. This is used to remove the root directory
     * from full paths.
     */
    int rootLength;

    /**
     * Creates a directory iterator.
     * 
     * The directory iterator is created to scan the root directory. If the
     * changeInto flag is given, then the entries returned will be relative to this
     * directory and not the current directory.
     * 
     * @param rootDirectory the root if the directory namespace which is to be iterated over
     * @param changeInto if true then the returned entries will be relative to the rootDirectory
     * and not the current directory.
     * 
     * @throws IOException if there is a problem reading the directory information.
     */
    public DirectoryIterator(File rootDirectory, boolean changeInto) throws IOException {
        super();

        enumStack = new Stack();

        if (rootDirectory.isAbsolute() || changeInto) {
            rootLength = rootDirectory.getPath().length() + 1;
        } else {
            rootLength = 0;
        } 

        Vector filesInRoot = getDirectoryEntries(rootDirectory);

        currentEnum = filesInRoot.elements();
    }

    /**
     * Get a vector covering all the entries (files and subdirectories in a directory).
     * 
     * @param directory the directory to be scanned.
     * 
     * @return a vector containing File objects for each entry in the directory.
     */
    private Vector getDirectoryEntries(File directory) {
        Vector files = new Vector();

        // File[] filesInDir = directory.listFiles();
        String[] filesInDir = directory.list();

        if (filesInDir != null) {
            int length = filesInDir.length;

            for (int i = 0; i < length; ++i) {
                files.addElement(new File(directory, filesInDir[i]));
            } 
        } 

        return files;
    } 

    /**
     * Template method to allow subclasses to supply elements for the iteration.
     * 
     * The directory iterator maintains a stack of iterators covering each level
     * in the directory hierarchy. The current iterator covers the current directory
     * being scanned. If the next entry in that directory is a subdirectory, the current
     * iterator is pushed onto the stack and a new iterator is created for the
     * subdirectory. If the entry is a file, it is returned as the next element and the
     * iterator remains valid. If there are no more entries in the current directory,
     * the topmost iterator on the statck is popped off to become the current iterator.
     * 
     * @return the next ClassFile in the iteration.
     */
    public ClassFile getNextClassFile() {
        ClassFile nextElement = null;

        try {
            while (nextElement == null) {
                if (currentEnum.hasMoreElements()) {
                    File element = (File) currentEnum.nextElement();

                    if (element.isDirectory()) {

                        // push the current iterator onto the stack and then
                        // iterate through this directory.
                        enumStack.push(currentEnum);

                        Vector files = getDirectoryEntries(element);

                        currentEnum = files.elements();
                    } else {

                        // we have a file. create a stream for it
                        FileInputStream inFileStream = new FileInputStream(element);

                        if (element.getName().endsWith(".class")) {

                            // create a data input stream from the jar input stream
                            ClassFile javaClass = new ClassFile();

                            javaClass.read(inFileStream);

                            nextElement = javaClass;
                        } 
                    } 
                } else {
                    // this iterator is exhausted. Can we pop one off the stack
                    if (enumStack.empty()) {
                        break;
                    } else {
                        currentEnum = (Enumeration) enumStack.pop();
                    } 
                } 
            } 
        } catch (IOException e) {
            nextElement = null;
        } 

        return nextElement;
    } 

}

