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

tv.hd3g.jobkit.watchfolder.WatchedFileScanner Maven / Gradle / Ivy

/*
 * This file is part of jobkit-watchfolder.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * Copyright (C) hdsdi3g for hd3g.tv 2023
 *
 */
package tv.hd3g.jobkit.watchfolder;

import static java.util.stream.Collectors.joining;
import static org.apache.commons.io.FilenameUtils.wildcardMatch;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import tv.hd3g.transfertfiles.AbstractFile;
import tv.hd3g.transfertfiles.AbstractFileSystemURL;
import tv.hd3g.transfertfiles.CachedFileAttributes;

@Slf4j
public class WatchedFileScanner {

	@Getter
	private final int maxDeep;
	private final ObservedFolder observedFolder;
	private final Duration minFixedStateTime;

	public WatchedFileScanner(final ObservedFolder observedFolder) {
		this(observedFolder, 10);
	}

	public WatchedFileScanner(final ObservedFolder observedFolder,
							  final int maxDeep) {
		log.debug("Start internal Watchfolder configuration checks {}", observedFolder);
		this.observedFolder = Objects.requireNonNull(observedFolder);
		observedFolder.postConfiguration();
		if (observedFolder.isRecursive()) {
			this.maxDeep = maxDeep;
		} else {
			this.maxDeep = 0;
		}
		minFixedStateTime = observedFolder.getMinFixedStateTime();

		try (var fs = observedFolder.createFileSystem()) {
			/** try only to load FileSystem/configured URL */
		} catch (final IOException e) {
			throw new UncheckedIOException(new IOException("Can't load FileSystem", e));
		}
		log.debug("Setup WFDB for {}, with minFixedStateTime={} and maxDeep={}",
				observedFolder, minFixedStateTime, maxDeep);
	}

	public List scan(final AbstractFileSystemURL fileSystem) {
		final var detected = new ArrayList();
		log.debug("Start scan {} to {}", observedFolder, fileSystem);
		actualScan(fileSystem.getRootPath(), maxDeep, detected);
		return detected;
	}

	/**
	 * Recursive
	 */
	private void actualScan(final AbstractFile aSource,
							final int deep,
							final Collection detected) {
		final var ignoreFiles = observedFolder.getIgnoreFiles();
		final var allowedHidden = observedFolder.isAllowedHidden();
		final var allowedLinks = observedFolder.isAllowedLinks();
		final var allowedExtentions = observedFolder.getAllowedExtentions();
		final var blockedExtentions = observedFolder.getBlockedExtentions();
		final var ignoreRelativePaths = observedFolder.getIgnoreRelativePaths();

		final var result = aSource.toCachedList()
				.peek(f -> log.trace("Detect file={}", f))// NOSONAR S3864
				.filter(f -> ignoreFiles.contains(f.getName().toLowerCase()) == false)
				.filter(f -> (allowedHidden == false && (f.isHidden() || f.getName().startsWith("."))) == false)
				.filter(f -> (allowedLinks == false && f.isLink()) == false)
				.filter(f -> {
					if (f.isDirectory()) {
						return true;
					} else if (allowedExtentions.isEmpty() == false) {
						return containExtension(f.getName(), allowedExtentions);
					}
					return true;
				})
				.filter(f -> {
					if (f.isDirectory()) {
						return true;
					}
					return containExtension(f.getName(), blockedExtentions) == false;
				})
				.filter(f -> {
					if (ignoreRelativePaths.isEmpty()) {
						return true;
					}
					return ignoreRelativePaths.contains(f.getPath()) == false;
				})
				.filter(this::checkAllowedNotBlocked)
				.filter(f -> f.isDirectory() || f.isSpecial() == false)
				.toList();

		detected.addAll(result);

		log.debug("Scanned files/dirs for \"" + aSource.getPath() + "\" (deep " + deep + "): "
				  + result.stream()
						  .map(CachedFileAttributes::getName)
						  .sorted()
						  .collect(joining(", "))
				  + " on \"" + aSource.getFileSystem().toString() + "\"");
		if (deep > 0) {
			result.stream()
					.filter(CachedFileAttributes::isDirectory)
					.forEach(f -> actualScan(f.getAbstractFile(), deep - 1, detected));
		}
	}

	private boolean checkAllowedNotBlocked(final CachedFileAttributes detectedFile) {
		final var name = detectedFile.getName();
		if (detectedFile.isDirectory()) {
			final var allowedDirNames = observedFolder.getAllowedDirNames();
			if (allowedDirNames.isEmpty() == false) {
				return allowedDirNames.stream().anyMatch(w -> wildcardMatch(name, w));
			}
			final var blockedDirNames = observedFolder.getBlockedDirNames();
			if (blockedDirNames.isEmpty() == false) {
				return blockedDirNames.stream().noneMatch(w -> wildcardMatch(name, w));
			}
		} else {
			final var allowedFileNames = observedFolder.getAllowedFileNames();
			if (allowedFileNames.isEmpty() == false) {
				return allowedFileNames.stream().anyMatch(w -> wildcardMatch(name, w));
			}
			final var blockedFileNames = observedFolder.getBlockedFileNames();
			if (blockedFileNames.isEmpty() == false) {
				return blockedFileNames.stream().noneMatch(w -> wildcardMatch(name, w));
			}
		}
		return true;
	}

	public static boolean containExtension(final String baseFileName, final Set candidates) {
		return candidates.stream()
				.anyMatch(c -> baseFileName.toLowerCase().endsWith("." + c));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy