package
gsolano;
import
java.io.File;
import
java.io.IOException;
import
java.util.ArrayList;
import
java.util.HashSet;
import
java.util.Iterator;
import
java.util.List;
import
java.util.Map;
import
java.util.Set;
/**
*
* Class used to synchronize two directories. One directory (source)
* is used as base of another directory (target).
* The class determines the operations required to leave the target
* with the same structure as the source.
*
* @author gsolano
*
*/
public
class
Synchronizer {
public
static
void
main(String[] args) {
Synchronizer.run(
"c:\\gsolano_remote\\"
,
"c:\\gsolano_local\\"
);
}
public
static
void
run(String source, String target) {
System.out.println(
"Scanning source directory..."
);
Map<String, Long> sourceFiles = Dir.scan(source);
System.out.println(
"[DONE]"
);
System.out.println(
"Scanning target directory..."
);
Map<String, Long> targetFiles = Dir.scan(target);
System.out.println(
"[DONE]"
);
List<String> newFilesToCopy = getNewFilesToCopy(sourceFiles.keySet(), targetFiles.keySet());
System.out.println(
"Total new files to copy: "
+ newFilesToCopy.size());
List<String> filesToUpdate = getFilesToUpdate(sourceFiles, targetFiles);
System.out.println(
"Total files to update: "
+ filesToUpdate.size());
List<String> filesToRemove = getFilesToRemove(sourceFiles.keySet(), targetFiles.keySet());
System.out.println(
"Total files to remove: "
+ filesToRemove.size());
List<String> dirsToRemove = getDirectoriesToRemove(sourceFiles.keySet(), targetFiles.keySet());
System.out.println(
"Total dirs to remove: "
+ dirsToRemove.size());
System.out.println(
"Copying new files..."
);
for
(String fileToCopy : newFilesToCopy) {
try
{
FileCopy.copy(source + File.separator + fileToCopy,
target + File.separator + fileToCopy,
false
);
}
catch
(IOException e) {
System.out.println(
"Couldn't copy file: "
+ fileToCopy +
"("
+ e.getMessage() +
")"
);
}
}
System.out.println(
"Updating files..."
);
for
(String fileToUpdate : filesToUpdate) {
try
{
FileCopy.copy(source + File.separator + fileToUpdate,
target + File.separator +fileToUpdate,
true
);
}
catch
(IOException e) {
System.out.println(
"Couldn't copy file: "
+ fileToUpdate +
"("
+ e.getMessage() +
")"
);
}
}
System.out.println(
"Removing files from target..."
);
for
(String fileToRemove : filesToRemove) {
new
File(target + fileToRemove).delete();
}
System.out.println(
"Removing directories from target..."
);
for
(String dirToRemove : dirsToRemove) {
new
File(target + dirToRemove).delete();
}
}
/**
* Return the list of directories to be removed. A directory is removed
* if it is present in the target but not in the source.
* @param sourceFiles
* @param targetFiles
* @return
*/
private
static
List<String> getDirectoriesToRemove(Set<String> sourceFiles,
Set<String> targetFiles) {
List<String> directoriesToRemove =
new
ArrayList<String>();
Set<String> sourceDirs = buildDirectorySet(sourceFiles);
Set<String> targetDirs = buildDirectorySet(targetFiles);
for
(String dir : targetDirs) {
if
(!sourceDirs.contains(dir)) {
directoriesToRemove.add(dir);
}
}
return
directoriesToRemove;
}
/**
* Return the list of files to be removed.
* A file is removed if it is present in the target
* but not in the source.
* @param sourceFiles
* @param targetFiles
* @return
*/
private
static
List<String> getFilesToRemove(Set<String> sourceFiles,
Set<String> targetFiles) {
List<String> filesToRemove =
new
ArrayList<String>();
for
(String filePath : targetFiles) {
if
(!sourceFiles.contains(filePath) &&
!filePath.endsWith(File.separator +
"."
)) {
filesToRemove.add(filePath);
}
}
return
filesToRemove;
}
/**
* Gets the the list of files missing in the target directory.
* @param sourceFiles
* @param targetFiles
* @return
*/
private
static
List<String> getNewFilesToCopy(Set<String> sourceFiles,
Set<String> targetFiles) {
List<String> filesToCopy =
new
ArrayList<String>();
for
(String filePath : sourceFiles) {
if
(!targetFiles.contains(filePath)) {
if
(!filePath.endsWith(File.separator +
"."
)) {
filesToCopy.add(filePath);
}
}
}
return
filesToCopy;
}
/**
* Gets the list of files to be updated according to the last
* modified date.
* @param sourceFiles
* @param targetFiles
* @return
*/
private
static
List<String> getFilesToUpdate(Map<String, Long> sourceFiles,
Map<String, Long> targetFiles) {
List<String> filesToUpdate =
new
ArrayList<String>();
Iterator<Map.Entry<String, Long>> it = sourceFiles.entrySet().iterator();
while
(it.hasNext()) {
Map.Entry<String, Long> pairs = it.next();
String filePath = pairs.getKey();
if
(targetFiles.containsKey(filePath) &&
!filePath.endsWith(File.separator +
"."
)) {
long
sourceModifiedDate = sourceFiles.get(filePath);
long
targetModifiedDate = targetFiles.get(filePath);
if
(sourceModifiedDate != targetModifiedDate) {
filesToUpdate.add(filePath);
}
}
}
return
filesToUpdate;
}
/**
* Returns the set of directories contained in the set of file paths.
* @param files
* @return Set of directories representing the directory structure.
*/
private
static
Set<String> buildDirectorySet(Set<String> files) {
Set<String> directories =
new
HashSet<String>();
for
(String filePath : files) {
if
(filePath.contains(File.separator)) {
directories.add(filePath.substring(
0
,
filePath.lastIndexOf(File.separator)));
}
}
return
directories;
}
}