/*
* Copyright 2012-2017 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 org.springframework.boot.devtools.filewatch;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.boot.devtools.filewatch.ChangedFile.Type;
import org.springframework.util.Assert;
/**
* A snapshot of a folder at a given point in time.
*
* @author Phillip Webb
*/
class FolderSnapshot {
private static final Set<String> DOT_FOLDERS = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(".", "..")));
private final File folder;
private final Date time;
private Set<FileSnapshot> files;
/**
* Create a new {@link FolderSnapshot} for the given folder.
* @param folder the source folder
*/
FolderSnapshot(File folder) {
Assert.notNull(folder, "Folder must not be null");
Assert.isTrue(folder.isDirectory(), "Folder must not be a file");
this.folder = folder;
this.time = new Date();
Set<FileSnapshot> files = new LinkedHashSet<>();
collectFiles(folder, files);
this.files = Collections.unmodifiableSet(files);
}
private void collectFiles(File source, Set<FileSnapshot> result) {
File[] children = source.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory() && !DOT_FOLDERS.contains(child.getName())) {
collectFiles(child, result);
}
else if (child.isFile()) {
result.add(new FileSnapshot(child));
}
}
}
}
public ChangedFiles getChangedFiles(FolderSnapshot snapshot,
FileFilter triggerFilter) {
Assert.notNull(snapshot, "Snapshot must not be null");
File folder = this.folder;
Assert.isTrue(snapshot.folder.equals(folder),
"Snapshot source folder must be '" + folder + "'");
Set<ChangedFile> changes = new LinkedHashSet<>();
Map<File, FileSnapshot> previousFiles = getFilesMap();
for (FileSnapshot currentFile : snapshot.files) {
if (acceptChangedFile(triggerFilter, currentFile)) {
FileSnapshot previousFile = previousFiles.remove(currentFile.getFile());
if (previousFile == null) {
changes.add(new ChangedFile(folder, currentFile.getFile(), Type.ADD));
}
else if (!previousFile.equals(currentFile)) {
changes.add(
new ChangedFile(folder, currentFile.getFile(), Type.MODIFY));
}
}
}
for (FileSnapshot previousFile : previousFiles.values()) {
if (acceptChangedFile(triggerFilter, previousFile)) {
changes.add(new ChangedFile(folder, previousFile.getFile(), Type.DELETE));
}
}
return new ChangedFiles(folder, changes);
}
private boolean acceptChangedFile(FileFilter triggerFilter, FileSnapshot file) {
return (triggerFilter == null || !triggerFilter.accept(file.getFile()));
}
private Map<File, FileSnapshot> getFilesMap() {
Map<File, FileSnapshot> files = new LinkedHashMap<>();
for (FileSnapshot file : this.files) {
files.put(file.getFile(), file);
}
return files;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof FolderSnapshot) {
return equals((FolderSnapshot) obj, null);
}
return super.equals(obj);
}
public boolean equals(FolderSnapshot other, FileFilter filter) {
if (this.folder.equals(other.folder)) {
Set<FileSnapshot> ourFiles = filter(this.files, filter);
Set<FileSnapshot> otherFiles = filter(other.files, filter);
return ourFiles.equals(otherFiles);
}
return false;
}
private Set<FileSnapshot> filter(Set<FileSnapshot> source, FileFilter filter) {
if (filter == null) {
return source;
}
Set<FileSnapshot> filtered = new LinkedHashSet<>();
for (FileSnapshot file : source) {
if (filter.accept(file.getFile())) {
filtered.add(file);
}
}
return filtered;
}
@Override
public int hashCode() {
int hashCode = this.folder.hashCode();
hashCode = 31 * hashCode + this.files.hashCode();
return hashCode;
}
/**
* Return the source folder of this snapshot.
* @return the source folder
*/
public File getFolder() {
return this.folder;
}
@Override
public String toString() {
return this.folder + " snapshot at " + this.time;
}
}