/*
* 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.sync;
import java.util.Arrays;
import java.util.List;
import jfs.conf.JFSConfig;
import jfs.conf.JFSDirectoryPair;
import jfs.sync.JFSProgress.ProgressActivity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Compares all JFS directory pairs and adds the results to the table.
*
* @author Jens Heidrich
* @version $Id: JFSComparison.java,v 1.31 2007/07/18 16:20:49 heidrich Exp $
*/
public final class JFSComparison {
private static final Logger LOG = LoggerFactory.getLogger(JFSComparison.class);
/**
* Stores the only instance of the class.
*
* SingletonHolder is loaded on the first execution of JFSComparison.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
public static final JFSComparison INSTANCE = new JFSComparison();
}
/**
* Creates a new comparison object.
*/
protected JFSComparison() {
// Avoid external instanciation.
}
/**
* Returns the reference of the only instance.
*
* @return The only instance.
*/
public static JFSComparison getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* Inserts an element to the comparison table and starts the comparison algorithm recursively, if and only if the
* matched files are directories. At least one (source or target) file has to be not equal to null.
*
* @param srcFile A source file which may be null if no corresponding source file exists, but a target file.
* @param tgtFile A target file which may be null if no corresponding target file exists, but a source file.
* @param parent The parent element.
* @param isDirectory Determines whether the files are directories.
*/
private void add(JFSFile srcFile, JFSFile tgtFile, JFSElement parent, boolean isDirectory) {
assert srcFile!=null||tgtFile!=null;
// Determine whether the comparison should be performed:
JFSConfig config = JFSConfig.getInstance();
if (!config.getIncludes().isEmpty()) {
if (srcFile!=null&&!config.matchesIncludes(srcFile)) {
return;
}
if (tgtFile!=null&&!config.matchesIncludes(tgtFile)) {
return;
}
}
if (!config.getExcludes().isEmpty()) {
if (srcFile!=null&&config.matchesExcludes(srcFile)) {
return;
}
if (tgtFile!=null&&config.matchesExcludes(tgtFile)) {
return;
}
}
// Add an element to the comparison table:
JFSElement element = new JFSElement(srcFile, tgtFile, parent, isDirectory);
if (isDirectory) {
if (LOG.isInfoEnabled()) {
LOG.info("add() comparison table "+srcFile+" "+tgtFile+" "+isDirectory+" : "+element.getAction());
} // if
} // if
JFSTable.getInstance().addElement(element);
// Start algorithm recursively, if the files are directories:
// (This is the case, if one of them is a directory!)
if (isDirectory&&!JFSProgress.getInstance().isCanceled()) {
compareDirectories(srcFile, tgtFile, element);
} // if
}
/**
* Compares two lists of files and writes the result to the comparison table. Both lists must be not equal to null.
*
* @param srcFiles
* The array of source files.
* @param tgtFiles
* The array of target files.
* @param parent
* The parent element.
* @param isDirectory
* Determines whether the files are directories.
*/
private void compareFiles(JFSFile[] srcFiles, JFSFile[] tgtFiles, JFSElement parent, boolean isDirectory) {
assert srcFiles!=null&&tgtFiles!=null;
// First, sort the input arrays:
// HeapSort<JFSFile> h = new HeapSort<JFSFile>();
// h.sort(srcFiles);
// h.sort(tgtFiles);
Arrays.sort(srcFiles);
Arrays.sort(tgtFiles);
int srcIndex = 0;
int tgtIndex = 0;
while (srcIndex<srcFiles.length&&tgtIndex<tgtFiles.length) {
int comp = srcFiles[srcIndex].compareTo(tgtFiles[tgtIndex]);
if (LOG.isDebugEnabled()) {
LOG.debug("compareFiles() I - srx "+srcIndex+" tgx "+tgtIndex+" comp "+comp+" "+srcFiles[srcIndex]+" "
+tgtFiles[tgtIndex]);
} // if
if (comp==0) {
// Case 1: We found two matching files:
add(srcFiles[srcIndex], tgtFiles[tgtIndex], parent, isDirectory);
srcIndex++;
tgtIndex++;
} else if (comp>0) {
// Case 2: No matching file was found and the source file is
// greater than the target file. In this case we have to write
// the target file to the comparison table and investigate the
// next target file in the list:
add(null, tgtFiles[tgtIndex], parent, isDirectory);
tgtIndex++;
} else if (comp<0) {
// Case 3: No matching file was found and the target file is
// greater than the source file. In this case we have to write
// the source file to the comparison table and investigate the
// next source file in the list:
add(srcFiles[srcIndex], null, parent, isDirectory);
srcIndex++;
}
}
// Case 4: All source files were already handled. In this case we have
// to write the rest of the target files into the table:
while (tgtIndex<tgtFiles.length) {
if (LOG.isDebugEnabled()) {
LOG.debug("compareFiles() II - srx "+srcIndex+" tgx "+tgtIndex+" "+tgtFiles[tgtIndex]);
} // if
add(null, tgtFiles[tgtIndex], parent, isDirectory);
tgtIndex++;
}
// Case 5: All target files were already handled. In this case we have
// to write the rest of the source files into the table:
while (srcIndex<srcFiles.length) {
if (LOG.isDebugEnabled()) {
LOG.debug("compareFiles() III - srx "+srcIndex+" tgx "+tgtIndex+" "+srcFiles[srcIndex]);
} // if
add(srcFiles[srcIndex], null, parent, isDirectory);
srcIndex++;
}
}
/**
* Compares two directories and writes the result to the comparison table. At least one (source or target) file has
* to be not equal to null.
*
* @param srcDir
* A source directory which may be null if no corresponding source directory exists, but a target
* directory.
* @param tgtDir
* A target directory which may be null if no corresponding target directory exists, but a source
* directory.
* @param parent
* The parent element.
*/
private void compareDirectories(JFSFile srcDir, JFSFile tgtDir, JFSElement parent) {
assert srcDir!=null||tgtDir!=null;
JFSProgress progress = JFSProgress.getInstance();
JFSComparisonMonitor monitor = JFSComparisonMonitor.getInstance();
JFSFile[] srcFileList = new JFSFile[0];
JFSFile[] tgtFileList = new JFSFile[0];
JFSFile[] srcDirectoryList = new JFSFile[0];
JFSFile[] tgtDirectoryList = new JFSFile[0];
int weight = 0;
if (srcDir!=null) {
srcFileList = srcDir.getFileList();
srcDirectoryList = srcDir.getDirectoryList();
monitor.setCurrentSrc(srcDir);
weight += 1;
}
if (tgtDir!=null) {
tgtFileList = tgtDir.getFileList();
tgtDirectoryList = tgtDir.getDirectoryList();
monitor.setCurrentTgt(tgtDir);
weight += 1;
}
monitor.increase(srcDirectoryList.length+tgtDirectoryList.length, weight);
progress.fireUpdate();
compareFiles(srcFileList, tgtFileList, parent, false);
compareFiles(srcDirectoryList, tgtDirectoryList, parent, true);
monitor.decrease();
}
/**
* Starts comparison for all directory pairs, computes the actions that have to be taken according to the chosen
* synchronization mode.
*/
public void compare() {
// Get all directory pairs:
List<JFSDirectoryPair> pairs = JFSConfig.getInstance().getDirectoryList();
// Prepare the progress computation:
JFSProgress progress = JFSProgress.getInstance();
progress.prepare(ProgressActivity.COMPARISON);
JFSComparisonMonitor monitor = JFSComparisonMonitor.getInstance();
monitor.clean();
// Start the progress computation:
progress.start();
monitor.increase(pairs.size(), 1);
// Start comparison:
JFSTable table = JFSTable.getInstance();
table.clean();
for (JFSDirectoryPair pair : pairs) {
monitor.increase(2, 1);
monitor.setRootUriSrc(pair.getSrc());
monitor.setRootUriTgt(pair.getTgt());
// Create root element and add it to the table:
if (!progress.isCanceled()) {
JFSRootElement root = new JFSRootElement(pair);
JFSTable.getInstance().addRoot(root);
// Start comparison if root is active:
if (root.isActive()) {
compareDirectories(root.getSrcFile(), root.getTgtFile(), root);
}
}
monitor.decrease();
progress.fireUpdate();
}
// End the progress computation:
progress.end();
monitor.decrease();
monitor.setRootUriSrc("");
monitor.setRootUriTgt("");
}
}