/*
* This file is part of Fim - File Integrity Manager
*
* Copyright (C) 2017 Etienne Vrignaud
*
* Fim 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.
*
* Fim 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 Fim. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fim.tooling;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.fim.model.Constants;
import org.fim.model.Context;
import org.fim.model.FileHash;
import org.fim.model.FileState;
import org.fim.model.FileTime;
import org.fim.model.State;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import static java.lang.Math.min;
public class BuildableState extends State {
private static final Comparator<FileState> fileNameComparator = new FileState.FileNameComparator();
private transient final Context context;
public BuildableState(Context context) {
this.context = context;
}
public BuildableState addFiles(String... fileNames) {
return addFiles(10_000, fileNames);
}
public BuildableState addEmptyFiles(String... fileNames) {
return addFiles(0, fileNames);
}
public BuildableState addFiles(int maxFileLength, String... fileNames) {
BuildableState newState = clone();
for (String fileName : fileNames) {
if (findFileState(newState, fileName, false) != null) {
throw new IllegalArgumentException("New file: Duplicate fileName " + fileName);
}
// By default put the fileName as fileContent that will be the hash
int fileLength = min(maxFileLength, fileName.length());
String content = fileName.substring(0, fileLength);
FileState fileState = new FileState(fileName, fileLength, new FileTime(getNow()), createHash(content), null);
newState.getFileStates().add(fileState);
}
sortFileStates(newState);
return newState;
}
public BuildableState copy(String sourceFileName, String targetFileName) {
BuildableState newState = clone();
if (findFileState(newState, targetFileName, false) != null) {
throw new IllegalArgumentException("Copy: File already exist " + targetFileName);
}
FileState sourceFileState = findFileState(newState, sourceFileName, true);
FileState targetFileState = new FileState(targetFileName, sourceFileState.getFileLength(), new FileTime(sourceFileState.getFileTime()), new FileHash(sourceFileState.getFileHash()), null);
newState.getFileStates().add(targetFileState);
sortFileStates(newState);
return newState;
}
public BuildableState rename(String sourceFileName, String targetFileName) {
BuildableState newState = clone();
if (findFileState(newState, targetFileName, false) != null) {
throw new IllegalArgumentException("Rename: File already exist " + targetFileName);
}
FileState fileState = findFileState(newState, sourceFileName, true);
fileState.setFileName(targetFileName);
sortFileStates(newState);
return newState;
}
public BuildableState delete(String fileName) {
BuildableState newState = clone();
FileState fileState = findFileState(newState, fileName, true);
newState.getFileStates().remove(fileState);
return newState;
}
public BuildableState touch(String fileName) {
BuildableState newState = clone();
FileState fileState = findFileState(newState, fileName, true);
long now = getNow();
if (now < fileState.getFileTime().getLastModified() + 1_000) {
now = fileState.getFileTime().getLastModified() + 1_000;
}
fileState.getFileTime().setLastModified(now);
return newState;
}
public BuildableState setContent(String fileName, String fileContent) {
BuildableState newState = clone();
FileState fileState = findFileState(newState, fileName, true);
fileState.setFileLength(fileContent.length());
fileState.setFileHash(createHash(fileContent));
return newState;
}
public BuildableState appendContent(String fileName, String fileContent) {
BuildableState newState = clone();
FileState fileState = findFileState(newState, fileName, true);
fileState.setFileLength(fileState.getFileLength() + fileContent.length());
fileState.setFileHash(appendHash(fileState.getFileHash(), fileContent));
return newState;
}
private FileHash createHash(String content) {
String smallBlockHash = "small_block_" + content;
String mediumBlockHash = "medium_block_" + content;
String fullHash = "full_" + content;
return createFileHash(smallBlockHash, mediumBlockHash, fullHash);
}
private FileHash appendHash(FileHash fileHash, String content) {
String smallBlockHash = fileHash.getSmallBlockHash() + "_" + content;
String mediumBlockHash = fileHash.getMediumBlockHash() + "_" + content;
String fullHash = fileHash.getFullHash() + "_" + content;
return createFileHash(smallBlockHash, mediumBlockHash, fullHash);
}
private FileHash createFileHash(String smallBlockHash, String mediumBlockHash, String fullHash) {
switch (context.getHashMode()) {
case dontHash:
smallBlockHash = Constants.NO_HASH;
mediumBlockHash = Constants.NO_HASH;
fullHash = Constants.NO_HASH;
break;
case hashSmallBlock:
mediumBlockHash = Constants.NO_HASH;
fullHash = Constants.NO_HASH;
break;
case hashMediumBlock:
fullHash = Constants.NO_HASH;
break;
case hashAll:
// Nothing to do
break;
}
return new FileHash(smallBlockHash, mediumBlockHash, fullHash);
}
private void sortFileStates(BuildableState state) {
Collections.sort(state.getFileStates(), fileNameComparator);
state.updateFileCount();
state.updateFilesContentLength();
}
private FileState findFileState(BuildableState state, String fileName, boolean throwEx) {
for (FileState fileState : state.getFileStates()) {
if (fileState.getFileName().equals(fileName)) {
return fileState;
}
}
if (throwEx) {
throw new IllegalArgumentException("Unknown file " + fileName);
}
return null;
}
@JsonIgnore
private long getNow() {
return new Date().getTime();
}
@Override
public BuildableState clone() {
return (BuildableState) super.clone();
}
}