package org.peerbox.watchservice.states;
import java.nio.file.Path;
import org.hive2hive.core.exceptions.NoPeerConnectionException;
import org.hive2hive.core.exceptions.NoSessionException;
import org.peerbox.app.manager.file.FileInfo;
import org.peerbox.app.manager.file.IFileManager;
import org.peerbox.app.manager.file.messages.FileExecutionSucceededMessage;
import org.peerbox.watchservice.IAction;
import org.peerbox.watchservice.filetree.IFileTree;
import org.peerbox.watchservice.filetree.composite.FileComponent;
import org.peerbox.watchservice.filetree.composite.FileLeaf;
import org.peerbox.watchservice.filetree.composite.FolderComposite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.SetMultimap;
/**
* The Initial state is used when a file is considered as new, or known but
* soft-deleted / de-synchronized.
* @author winzenried
*/
public class InitialState extends AbstractActionState {
private final static Logger logger = LoggerFactory.getLogger(InitialState.class);
public InitialState(IAction action) {
super(action, StateType.INITIAL);
}
public AbstractActionState getDefaultState() {
return this;
}
@Override
public AbstractActionState changeStateOnLocalCreate(){
if(action.getFile().isUploaded()){
logStateTransition(getStateType(), EventType.LOCAL_CREATE, StateType.ESTABLISHED);
logger.trace("Re-creation of a softdeleted file. Ignore LocalCreate for {}", action.getFile().getPath());
return new EstablishedState(action);
} else {
logStateTransition(getStateType(), EventType.LOCAL_CREATE, StateType.LOCAL_CREATE);
return new LocalCreateState(action);
}
}
public AbstractActionState changeStateOnRemoteMove(Path oldFilePath) {
logStateTransition(getStateType(), EventType.REMOTE_MOVE, StateType.INITIAL);
return new InitialState(action);
}
@Override
public AbstractActionState handleLocalCreate() {
final IFileTree fileTree = action.getFileEventManager().getFileTree();
final FileComponent file = action.getFile();
fileTree.putFile(file.getPath(), file);
addComponentToMoveTargetCandidates();
if (file.isFolder()) {
FolderComposite moveSource = fileTree.findDeletedByStructure((FolderComposite)file);
if (moveSource != null) {
return moveFolder(moveSource);
}
} else {
FileLeaf moveSource = fileTree.findDeletedByContent((FileLeaf)file);
if(moveSource != null){
return moveFile(moveSource);
}
}
if (file.isUploaded() && file.isSynchronized()) {
return updateSoftDeletedFileComponent();
} else {
return localCreateDefaultHandling();
}
}
@Override
public AbstractActionState handleLocalDelete() {
logger.debug("Local Delete is ignored in InitialState for {}", action.getFile().getPath());
return this;
}
@Override
public AbstractActionState handleRemoteCreate() {
FileComponent file = action.getFile();
action.getFileEventManager().getFileTree().putFile(file.getPath(), file);
logger.trace("put file {} to tree", file.getPath());
action.updateTimeAndQueue();
return changeStateOnRemoteCreate();
}
@Override
public ExecutionHandle execute(IFileManager fileManager) throws NoSessionException,
NoPeerConnectionException {
logger.warn("Execute is not defined in the initial state ({})", action.getFile().getPath());
return null;
}
private void addComponentToMoveTargetCandidates() {
if(action.getFile().getPath().toString().contains("CONFLICT")){
logger.trace("Workaround: Don't add conflict file {} to move target candidates",
action.getFile().getPath());
return;
}
if(action.getFile().isFile()){
SetMultimap<String, FileComponent> createdByContentHash = action.getFileEventManager().getFileTree().getCreatedByContentHash();
logger.trace("Put file {} with content hash {}", action.getFile().getPath(), action.getFile().getContentHash());
createdByContentHash.put(action.getFile().getContentHash(), action.getFile());
} else {
SetMultimap<String, FolderComposite> createdByStructureHash = action.getFileEventManager().getFileTree().getCreatedByStructureHash();
createdByStructureHash.put(((FolderComposite)action.getFile()).getStructureHash(), (FolderComposite)action.getFile());
}
}
private AbstractActionState localCreateDefaultHandling() {
logger.trace("Handle regular create of {}, no move source has been found.", action.getFile().getPath());
action.updateTimeAndQueue();
return changeStateOnLocalCreate();
}
private AbstractActionState updateSoftDeletedFileComponent() {
final FileComponent file = action.getFile();
logger.debug("File {} has been soft-deleted and recreated. This is regarded as a file update.", file.getPath());
if(file.isFolder()){
logger.debug("Soft-deleted file {} is a folder. Ignore recreation", file.getPath());
FileInfo fileHelper = new FileInfo(file);
action.getFileEventManager().getMessageBus().publish(new FileExecutionSucceededMessage(fileHelper, file.getAction().getCurrentState().getStateType()));
action.getFileEventManager().getFileComponentQueue().remove(action.getFile());
return this;
} else {
action.updateTimeAndQueue();
return changeStateOnLocalUpdate();
}
}
private AbstractActionState moveFile(FileLeaf source) {
final IFileTree fileTree = action.getFileEventManager().getFileTree();
final FileComponent file = action.getFile();
final Path filePath = file.getPath();
fileTree.deleteFile(source.getPath());
action.getFileEventManager().getFileComponentQueue().remove(file);
String contentHash = action.getFile().getContentHash();
boolean isRemoved = fileTree.getCreatedByContentHash().get(contentHash).remove(action.getFile());
logger.trace("InitialState.handleLocalDelete: IsRemoved for file {} with hash {}: {}", action.getFile().getPath(), contentHash, isRemoved);
if (source.isUploaded()) {
logger.trace("Handle move of {}, from {}.", filePath, source.getPath());
// eventManager.getFileTree().deleteFile(action.getFile().getPath());
source.getAction().handleLocalMoveEvent(filePath);
return this; //changeStateOnLocalMove(filePath);
} else {
logger.trace("No move of {}, as it was not uploaded.", source.getPath());
// action.getFileEventManager().getFileComponentQueue().remove(source);
fileTree.putFile(filePath, file);
action.updateTimeAndQueue();
return changeStateOnLocalCreate();
}
}
private AbstractActionState moveFolder(FolderComposite source) {
final IFileTree fileTree = action.getFileEventManager().getFileTree();
final FileComponent file = action.getFile();
final Path filePath = file.getPath();
fileTree.deleteFile(source.getPath());
logger.trace("Folder move detected from {} to {}", source.getPath(), filePath);
source.getAction().handleLocalMoveEvent(filePath);
action.getFileEventManager().getFileComponentQueue().remove(file);
SetMultimap<String, FolderComposite> createdByStructureHash = action.getFileEventManager().getFileTree().getCreatedByStructureHash();
createdByStructureHash.get(((FolderComposite)action.getFile()).getStructureHash()).remove((FolderComposite)action.getFile());
if (source.isUploaded()) {
logger.trace("Handle move of {}, from {}.", filePath, source.getPath());
// eventManager.getFileTree().deleteFile(action.getFile().getPath());
source.getAction().handleLocalMoveEvent(filePath);
return this; //changeStateOnLocalMove(filePath);
} else {
logger.trace("No move of {}, as it was not uploaded.", source.getPath());
fileTree.putFile(filePath, file);
action.updateTimeAndQueue();
return changeStateOnLocalCreate();
}
}
}