/* * JFileSync * Copyright (C) 2002-2007, Jens Heidrich * * This program 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 2 of the License, or * (at your option) 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA */ package jfs.conf; import java.util.HashMap; import java.util.Map; import jfs.sync.JFSElement; import jfs.sync.JFSElement.ElementState; import jfs.sync.JFSFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class specifies a single synchronization mode. * * @author Jens Heidrich * @version $Id: JFSSyncMode.java,v 1.12 2007/03/29 13:20:54 heidrich Exp $ */ public class JFSSyncMode { private static final Logger LOG = LoggerFactory.getLogger(JFSSyncMode.class); /** * Actions for used for all modes. */ public enum SyncAction { NOP_ROOT("syncAction.nopRoot"), NOP("syncAction.nop"), COPY_SRC("syncAction.copySrc"), COPY_TGT( "syncAction.copyTgt"), DELETE_SRC("syncAction.deleteSrc"), DELETE_TGT("syncAction.deleteTgt"), DELETE_SRC_AND_TGT( "syncAction.deleteSrcAndTgt"), ASK_LENGTH_INCONSISTENT("syncAction.askLengthInconsistent"), ASK_FILES_GT_HISTORY( "syncAction.askFilesGtHistory"), ASK_FILES_NOT_IN_HISTORY("syncAction.askFilesNotInHistory"); private String name; SyncAction(String name) { this.name = name; } public String getName() { return name; } } /** * The identifier of the mode. */ private final int id; /** * The string alias of the mode. */ private final String alias; /** * Assigns actions to states of an element of the comparison table. */ private final Map<ElementState, SyncAction> stateActions = new HashMap<>(); /** * Determines whether the actions of the mode should be automatically set based on the synchronization history. */ private boolean automatic = false; /** * Creates a new mode. * * @param id * The identifier to use. * @param alias * The alias to use. */ public JFSSyncMode(int id, String alias) { this.id = id; this.alias = alias; } /** * Returns the action for a specific state of an element of the comparison table. If nothing is specified regarding * the given state, the default action (no operation) is returned. * * @param state * The state of the element of the comparisn table. * @return The corresponding action for the state. */ public SyncAction getAction(ElementState state) { if (stateActions.containsKey(state)) { return stateActions.get(state); } return SyncAction.NOP; } /** * Sets the action for a specific state of an element of the comparison table. * * @param state * The state of the element of the comparisn table. * @param action * The corresponding action for the state. */ public void setAction(ElementState state, SyncAction action) { stateActions.put(state, action); } /** * @return Returns whether the automatic option is set. */ public boolean isAutomatic() { return automatic; } /** * @param automatic * Sets the automatic option. If this option is set, all other actions are ignorned and determined * automatically based on the synchronization history. */ public void setAutomatic(boolean automatic) { this.automatic = automatic; } /** * @return Returns the identifier of the mode. */ public int getId() { return id; } /** * @return Returns the alias of the mode. */ public String getAlias() { return alias; } /** * @see Object#toString() */ @Override public String toString() { return alias+" ["+id+"]"; } /** * Computes the action that has to performed for an entry of the comparison table according to this synchronization * mode and a file history. * * @param element * The element to compute the action for. */ public final void computeAction(JFSElement element) { if (element.isManuallySetAction()&&JFSConfig.getInstance().isKeepUserActions()) { return; } if (!isAutomatic()) { // Add actions to comparison tables according to the chosen // mode: element.setAction(getAction(element.getState())); } else { // Add basic actions based on the stored history: computeBasicAction(element); LOG.info("computeAction() isAutomatic {}", element.getAction()); // Check whether the parent is also copied if the children are // copied: JFSElement parent = element.getParent(); if (element.getAction()==SyncAction.COPY_SRC&&parent.getAction()==SyncAction.DELETE_SRC) { parent.setAction(SyncAction.COPY_SRC); } else if (element.getAction()==SyncAction.COPY_TGT&&parent.getAction()==SyncAction.DELETE_TGT) { parent.setAction(SyncAction.COPY_TGT); } else if ((element.getAction()==SyncAction.COPY_SRC||element.getAction()==SyncAction.COPY_TGT) &&parent.getAction()==SyncAction.DELETE_SRC_AND_TGT) { parent.setAction(SyncAction.NOP); } } } /** * Computes the action that has to performed for an entry of the comparison table according to this synchronization * mode and a history of former synchronized files and directories. * * @param element * The element to compute the basic action for. */ private final void computeBasicAction(JFSElement element) { // Analyze comparison table: element.setAction(SyncAction.NOP); JFSElement.ElementState s = element.getState(); // Ask user, when length is inconsistent and continue: if (s==ElementState.LENGTH_INCONSISTENT) { element.setAction(SyncAction.ASK_LENGTH_INCONSISTENT); return; } // Use history, if a corresponding history item is available, and // else, merge the structures: JFSHistory history = element.getRoot().getHistory(); LOG.info("computeBasicAction() history={}", history); if (history==null) { merge(element); } else { JFSHistoryItem h = history.getHistory(element); if (LOG.isInfoEnabled()) { LOG.info("computeBasicAction("+element.getRelativePath()+") historyItem="+h); } // if if (h!=null) { useHistory(element, h); } else { merge(element); } } } /** * The current analyzed element is part of the history and matches a history item. In this case, the history can be * used in order to determine the correct actions. * * @param current * The element to analyze. * @param h * The corresponding history element. */ private void useHistory(JFSElement current, JFSHistoryItem h) { JFSFile src = current.getSrcFile(); JFSFile tgt = current.getTgtFile(); JFSElement.ElementState s = current.getState(); if (s==ElementState.SRC_GT_TGT) { if (JFSElement.compareToTime(tgt.getLastModified(), h.getLastModified())>0) { // Both files are newer than the history: current.setAction(SyncAction.ASK_FILES_GT_HISTORY); } else { // The source file is newer: current.setAction(SyncAction.COPY_SRC); } } else if (s==ElementState.TGT_GT_SRC) { if (JFSElement.compareToTime(src.getLastModified(), h.getLastModified())>0) { // Both files are newer than the history: current.setAction(SyncAction.ASK_FILES_GT_HISTORY); } else { // The source file is newer: current.setAction(SyncAction.COPY_TGT); } } else if (s==ElementState.SRC_IS_NULL) { if (LOG.isDebugEnabled()) { LOG.debug("useHistory() "+s+" "+h.getLastModified()+" / "+tgt.getLastModified()); } // if if (JFSElement.compareToTime(tgt.getLastModified(), h.getLastModified())>0) { // The target file is newer than the history: current.setAction(SyncAction.COPY_TGT); } else { // The file on source side was removed once: current.setAction(SyncAction.DELETE_TGT); } } else if (s==ElementState.TGT_IS_NULL) { if (JFSElement.compareToTime(src.getLastModified(), h.getLastModified())>0) { // The source file is newer than the history: current.setAction(SyncAction.COPY_SRC); } else { // The file on target side was removed once: current.setAction(SyncAction.DELETE_SRC); } } } /** * A new element is found, which was not in the history before. In this case, we have no history information at all * and therefore perform a simple merge. * * @param current * The element to analyze. */ private void merge(JFSElement current) { JFSElement.ElementState s = current.getState(); if (s==ElementState.SRC_GT_TGT) { // The source file is newer: current.setAction(SyncAction.ASK_FILES_NOT_IN_HISTORY); } else if (s==ElementState.TGT_GT_SRC) { // The target file is newer: current.setAction(SyncAction.ASK_FILES_NOT_IN_HISTORY); } else if (s==ElementState.SRC_IS_NULL) { // A new file was added to the target side: current.setAction(SyncAction.COPY_TGT); } else if (s==ElementState.TGT_IS_NULL) { // A new file was added to the source side: current.setAction(SyncAction.COPY_SRC); } } }