/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.taskdefs.optional.depend; import java.io.File; import java.io.IOException; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.io.InputStream; import java.nio.file.Files; /** * 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. * */ public class DirectoryIterator implements ClassFileIterator { /** * This is a stack of current iterators supporting the depth first * traversal of the directory tree. */ private Deque<Iterator<File>> 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 Iterator<File> currentIterator; /** * 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. * @exception IOException if there is a problem reading the directory * information. */ public DirectoryIterator(File rootDirectory, boolean changeInto) throws IOException { super(); enumStack = new ArrayDeque<>(); currentIterator = getDirectoryEntries(rootDirectory).iterator(); } /** * 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 List<File> getDirectoryEntries(File directory) { File[] filesInDir = directory.listFiles(); if (filesInDir == null) { return Collections.emptyList(); } return Arrays.asList(filesInDir); } /** * 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 stack is popped off to become the * current iterator. * * @return the next ClassFile in the iteration. */ @Override public ClassFile getNextClassFile() { ClassFile nextElement = null; try { while (nextElement == null) { if (currentIterator.hasNext()) { File element = currentIterator.next(); if (element.isDirectory()) { // push the current iterator onto the stack and then // iterate through this directory. enumStack.push(currentIterator); List<File> files = getDirectoryEntries(element); currentIterator = files.iterator(); } else { // we have a file. create a stream for it try (InputStream inFileStream = Files.newInputStream(element.toPath())) { 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.isEmpty()) { break; } else { currentIterator = enumStack.pop(); } } } catch (IOException e) { nextElement = null; } return nextElement; } }