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

com.foreach.common.filemanager.services.AntPathMatchingFileVisitor Maven / Gradle / Ivy

/*
 * Copyright 2014 the original author or authors
 *
 * Licensed 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 com.foreach.common.filemanager.services;

import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.AntPathMatcher;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Arne Vandamme
 * @since 1.4.0
 */
class AntPathMatchingFileVisitor extends SimpleFileVisitor
{
	private static final Pattern INVALID_PATTERN = Pattern.compile( "/?\\.{1,2}(/|$)" );

	private final AntPathMatcher pathMatcher;
	private final String pattern;
	private final boolean matchOnlyDirectories;
	private final String absolutePathPrefix;
	private final Consumer matchedPathConsumer;
	private final boolean recursive;

	private int filesVisited;

	private AntPathMatchingFileVisitor( AntPathMatcher pathMatcher,
	                                    String pattern,
	                                    boolean matchOnlyDirectories,
	                                    Path basedir,
	                                    Consumer matchedPathConsumer ) {
		this.pathMatcher = pathMatcher;
		this.pattern = "/" + pattern;
		this.matchOnlyDirectories = matchOnlyDirectories;
		this.matchedPathConsumer = matchedPathConsumer;

		absolutePathPrefix = StringUtils.replace( basedir.toAbsolutePath().toString(), "\\", "/" );
		recursive = pattern.contains( "**" );
	}

	@Override
	public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) throws IOException {
		filesVisited++;
		String pathToMatch = pathToMatch( dir );

		if ( pathToMatch == null ) {
			return FileVisitResult.CONTINUE;
		}

		boolean exactMatch = false;

		if ( pathMatcher.match( pattern, pathToMatch ) ) {
			exactMatch = true;
			matchedPathConsumer.accept( dir );

			if ( !recursive ) {
				return FileVisitResult.SKIP_SUBTREE;
			}
		}

		if ( exactMatch || pathMatcher.matchStart( pattern, pathToMatch ) ) {
			return FileVisitResult.CONTINUE;
		}

		return FileVisitResult.SKIP_SUBTREE;
	}

	@Override
	public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) throws IOException {
		filesVisited++;

		if ( !matchOnlyDirectories ) {
			String pathToMatch = pathToMatch( file );
			if ( pathMatcher.match( pattern, pathToMatch ) ) {
				matchedPathConsumer.accept( file );
			}
		}

		/**if ( ( !shouldRecurse || ( usePathMatcher ? pathMatcher.match( antPattern, pathToMatch ) : StringUtils.equals( pathToMatch, antPattern ) ) )
		 && ( !matchOnlyDirectories || candidate.toFile().isDirectory() ) ) {
		 resources.add( toFileRepositoryResource( candidate, pathToMatch ) );
		 }**/

		return FileVisitResult.CONTINUE;
	}

	@Override
	public FileVisitResult visitFileFailed( Path file, IOException exc ) throws IOException {
		return FileVisitResult.CONTINUE;
	}

	@Override
	public FileVisitResult postVisitDirectory( Path dir, IOException exc ) throws IOException {
		return FileVisitResult.CONTINUE;
	}

	private String pathToMatch( Path file ) {
		String candidatePath = StringUtils.replace( file.toAbsolutePath().toString(), "\\", "/" );
		if ( candidatePath.length() > absolutePathPrefix.length() ) {
			return StringUtils.substring( candidatePath, absolutePathPrefix.length() );
		}
		return null;
	}

	@SneakyThrows
	static int walkFileTree( Path directory, String searchPattern, Consumer matchedPathConsumer ) {
		AntPathMatcher pathMatcher = new AntPathMatcher();

		validatePattern( searchPattern );

		String pattern = StringUtils.removeStart( searchPattern, "/" );

		boolean matchOnlyDirectories = pattern.endsWith( "/" );
		if ( matchOnlyDirectories ) {
			pattern = pattern.substring( 0, pattern.length() - 1 );
		}

		if ( !pathMatcher.isPattern( pattern ) ) {
			Path targetPath = directory.resolve( pattern );
			File file = targetPath.toFile();
			if ( file.exists() && ( !matchOnlyDirectories || file.isDirectory() ) ) {
				matchedPathConsumer.accept( targetPath );
			}
		}
		else {
			String prefix = getFixedPrefix( pattern );
			Path basedir = directory;

			if ( !prefix.isEmpty() ) {
				basedir = basedir.resolve( prefix );
				File file = basedir.toFile();
				if ( !file.exists() || !file.isDirectory() ) {
					return 0;
				}
				pattern = StringUtils.removeStart( pattern, prefix );
			}

			AntPathMatchingFileVisitor visitor = new AntPathMatchingFileVisitor( pathMatcher, pattern, matchOnlyDirectories, basedir, matchedPathConsumer );
			Files.walkFileTree( basedir, visitor );
			return visitor.filesVisited;
		}

		return 0;
	}

	@SuppressWarnings("Duplicates")
	private static String getFixedPrefix( String pattern ) {
		int starIndex = pattern.indexOf( '*' );
		int markIndex = pattern.indexOf( '?' );
		int index = Math.min(
				starIndex == -1 ? pattern.length() : starIndex,
				markIndex == -1 ? pattern.length() : markIndex
		);
		String beforeIndex = pattern.substring( 0, index );
		return beforeIndex.contains( "/" ) ? beforeIndex.substring( 0, beforeIndex.lastIndexOf( '/' ) + 1 ) : "";
	}

	private static void validatePattern( String pattern ) {
		Matcher matcher = INVALID_PATTERN.matcher( pattern );
		if ( matcher.find() ) {
			throw new IllegalArgumentException( "Search pattern may not contain dot-only path segments" );
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy