All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.googlecode.kevinarpe.papaya.filesystem.AbstractTraversePathIteratorImpl Maven / Gradle / Ivy

package com.googlecode.kevinarpe.papaya.filesystem;

/*
 * #%L
 * This file is part of Papaya.
 * %%
 * Copyright (C) 2013 - 2014 Kevin Connor ARPE ([email protected])
 * %%
 * Papaya is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GPL Classpath Exception:
 * This project is subject to the "Classpath" exception as provided in
 * the LICENSE file that accompanied this code.
 * 
 * Papaya is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Papaya.  If not, see .
 * #L%
 */

import com.google.common.collect.Lists;
import com.googlecode.kevinarpe.papaya.annotation.FullyTested;
import com.googlecode.kevinarpe.papaya.argument.ObjectArgs;
import com.googlecode.kevinarpe.papaya.exception.PathException;
import com.googlecode.kevinarpe.papaya.exception.PathRuntimeException;

import java.io.File;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

/**
 * Traverses a directory hierarchy similar to the UNIX command line tool {@code find}.  Use the
 * builder class {@link TraversePathIterableImpl} to construct path iterators.
 * 

* Terminology: *

    *
  • For "depth last" iterators, paths in each directory are iterated before descending * any directories
  • *
  • For "depth first" iterators, paths in each directory are iterated after descending * any directories
  • *
*

* Example directory structure: *

{@code
 * topDir
 *   |
 * file1 file2 dir1
 *              |
 *      file3 file4 dir2
 *                    |
 *                  file5
 * }
*

* Directories are traversed in this order: *

    *
  • For "depth last" iterators: topDir, dir1, dir2
  • *
  • For "depth first" iterators: dir2, dir1, topDir
  • *
*

* Possible path iterations: *

    *
  • For "depth last" iterators: topDir, file1, file2, dir1, file3, file4 dir2, file5
  • *
  • For "depth first" iterators: file5, file3, file4, dir2, file1, file2, dir1, topDir
  • *
* * @author Kevin Connor ARPE ([email protected]) * * @see TraversePathIterSettingsImpl */ @FullyTested abstract class AbstractTraversePathIteratorImpl extends TraversePathIterSettingsImpl implements TraversePathIterator { static interface Factory { TraversePathLevel newInstance(AbstractTraversePathIteratorImpl parent, File dirPath, int depth) throws PathException; } static final class FactoryImpl implements Factory { static final FactoryImpl INSTANCE = new FactoryImpl(); @Override public TraversePathLevel newInstance(AbstractTraversePathIteratorImpl parent, File dirPath, int depth) throws PathException { return new TraversePathLevel(parent, dirPath, depth); } } private final Factory _factory; private final LinkedList _levelList; AbstractTraversePathIteratorImpl( File dirPath, TraversePathDepthPolicy depthPolicy, TraversePathExceptionPolicy exceptionPolicy, PathFilter optDescendDirPathFilter, Comparator optDescendDirPathComparator, PathFilter optIteratePathFilter, Comparator optIteratePathComparator) { this( dirPath, depthPolicy, exceptionPolicy, optDescendDirPathFilter, optDescendDirPathComparator, optIteratePathFilter, optIteratePathComparator, FactoryImpl.INSTANCE); } AbstractTraversePathIteratorImpl( File dirPath, TraversePathDepthPolicy depthPolicy, TraversePathExceptionPolicy exceptionPolicy, PathFilter optDescendDirPathFilter, Comparator optDescendDirPathComparator, PathFilter optIteratePathFilter, Comparator optIteratePathComparator, Factory factory) { super( dirPath, depthPolicy, exceptionPolicy, optDescendDirPathFilter, optDescendDirPathComparator, optIteratePathFilter, optIteratePathComparator); _factory = ObjectArgs.checkNotNull(factory, "factory"); _levelList = Lists.newLinkedList(); } protected final TraversePathLevel tryDescendDirPath() { TraversePathLevel currentLevel = null; File dirPath = withRootDirPath(); PathFilter optDescendDirFilter = withOptionalDescendDirPathFilter(); if (null == optDescendDirFilter || optDescendDirFilter.accept(dirPath, 0)) { // If initial directory listing fails, but exceptions are ignored, '_currentLevel' // will remain null. currentLevel = tryAddLevel(dirPath); } return currentLevel; } protected final boolean canIterateDirPath() { File dirPath = withRootDirPath(); PathFilter optIteratePathFilter = withOptionalIteratePathFilter(); boolean result = (null == optIteratePathFilter || optIteratePathFilter.accept(dirPath, 0)); return result; } /** * Descend a directory by creating a directory listing. If result is not {@code null}, the * depth increases by one. * * @param dirPath * directory path to descend * * @return new deepest level or {@code null} if directory listing throws exception and exception * policy dictates to ignore it * * @throws PathRuntimeException * if directory listing throws exception and exception policy dictates to * rethrow as a runtime (unchecked) exception * * @see #tryRemoveAndGetNextLevel() * @see #getDepth() */ protected final TraversePathLevel tryAddLevel(File dirPath) throws PathRuntimeException { final int depth = 1 + _levelList.size(); TraversePathLevel level = null; try { level = _factory.newInstance(this, dirPath, depth); } catch (PathException e) { if (TraversePathExceptionPolicy.THROW == withExceptionPolicy()) { throw new PathRuntimeException(e); } } if (null != level) { _levelList.add(level); return level; } return null; } /** * Remove the deepest level and return the next deepest level. If result is not {@code null}, * the depth decreases by one. * * @return new deepest level or {@code null} if no remaining levels after removal * * @see #tryAddLevel(File) * @see #getDepth() */ protected final TraversePathLevel tryRemoveAndGetNextLevel() { if (_levelList.isEmpty()) { return null; } _levelList.removeLast(); TraversePathLevel level = (_levelList.isEmpty() ? null :_levelList.getLast()); return level; } /** * Tests if a next path exists for iteration. The first call to this method is comparatively * more expensive than future calls. *
* {@inheritDoc} * * @throws PathRuntimeException * if an exception is thrown during a directory listing, and if exception policy * dictates to throw the exception ({@link TraversePathExceptionPolicy#THROW}). * * @see #next() * @see #withExceptionPolicy() */ @Override public abstract boolean hasNext() throws PathRuntimeException; /** * Returns the next path in the iteration. *
* {@inheritDoc} * * @throws NoSuchElementException * if the iteration is complete. This is only thrown if {@link #hasNext()} returns * {@code false}. * @throws PathRuntimeException * if an exception is thrown during a directory listing, and if exception policy * dictates to throw the exception ({@link TraversePathExceptionPolicy#THROW}). * * @see #hasNext() * @see #withExceptionPolicy() */ @Override public abstract File next() throws PathRuntimeException; /** * Always throws {@link UnsupportedOperationException}. */ @Override public final void remove() { throw new UnsupportedOperationException(String.format( "Class is unmodifiable: %s", this.getClass().getName())); } /** * @return number of levels below {@link #withRootDirPath()}. Minimum value is zero. */ @Override public final int getDepth() { return _levelList.size(); } protected final void assertHasNext() { if (!hasNext()) { throw new NoSuchElementException( "Method hasNext() returns false: There is no next path to iterate"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy