| /* |
| * 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 |
| * |
| * https://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.io.InputStream; |
| import java.nio.file.Files; |
| import java.util.ArrayDeque; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Deque; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * 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; |
| } |
| |
| } |