//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
*
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.tools.dirsync;
import com.nexirius.util.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.net.URL;
import java.net.MalformedURLException;
public class DirectoryInfo {
private static final String DIR_IDENTIFIER_STRING = "D";
public static TextToken DIR_IDENTIFIER = new TextToken(DIR_IDENTIFIER_STRING, TextToken.IDENTIFIER);
DirectoryInfo parent;
String name;
ArrayList fileList = new ArrayList();
long size = -1L;
int files = 0;
long hash = 0L;
private ArrayList subdirectoryList;
boolean done = false;
Iterator iterator = null;
DirectoryInfo act = null;
private static final String DIR_INFO_FILENAME = "dirInfo.txt";
public DirectoryInfo(String name) {
this(name, null);
}
public DirectoryInfo(String name, DirectoryInfo parent) {
if (name.endsWith(File.separator)) {
this.name = name.substring(0, name.length() - 1);
} else {
this.name = name;
}
this.parent = parent;
}
public boolean isDifferent(DirectoryInfo other) {
if (other.getSize() != getSize() || other.getHash() != getHash()) {
return true;
}
return false;
}
private void clearLists() {
fileList = null;
subdirectoryList = null;
}
public void clearSubdirectories() {
if (subdirectoryList == null) {
return;
}
for (Iterator it = subdirectoryList.iterator(); it.hasNext();) {
((DirectoryInfo) it.next()).clearLists();
}
}
public TextToken getIdentifier() {
return DIR_IDENTIFIER;
}
public boolean isRoot() {
return parent == null;
}
public long getSize() {
return size;
}
public int getFiles() {
return files;
}
public long getHash() {
return hash;
}
public boolean isDone() {
return done;
}
public ArrayList getFileList() {
return fileList;
}
public ArrayList getSubdirectoryList() {
return subdirectoryList;
}
public DirectoryInfo nextDirectory(IStringFilter dirFilter, IStringFilter filter) {
if (done) {
return null;
}
if (size < 0L) {
// scan subdirectories at first call and set act to the first subdirectory and initialize hash and size to represent the file list
scanSubdirectoriesAndFiles(dirFilter, filter);
}
if (act == null) {
// no more subdirectories
iterator = null;
done = true;
return this;
}
DirectoryInfo ret = act.nextDirectory(dirFilter, filter);
if (ret != null) {
// some sub sub directory found
return ret;
}
// use the subdirectory information now when it has been fully scanned
size += act.getSize();
files += act.getFiles();
hash += act.getHash();
hash += getHashOf(act.name);
// step to next subdirectory
if (iterator.hasNext()) {
act = (DirectoryInfo) iterator.next();
} else {
act = null;
}
return nextDirectory(dirFilter, filter); // recurse
}
private void scanSubdirectoriesAndFiles(IStringFilter dirFilter, IStringFilter filter) {
size = 0L;
String dir = getFullName(null);
if (dir == null) {
dir = name;
} else {
dir = getRootDirectory() + File.separatorChar + dir;
}
StringVector dirList = new XFile(dir).getDirectories(false);
if (dirList.size() > 0) {
subdirectoryList = new ArrayList();
StringBuffer fullRelativeName = new StringBuffer();
getFullName(fullRelativeName);
for (String dirName = dirList.firstItem(); dirName != null; dirName = dirList.nextItem()) {
try {
if (dirFilter == null || dirFilter.match(fullRelativeName.toString() + dirName)) {
subdirectoryList.add(new DirectoryInfo(dirName, this));
}
} catch (Exception e) {
e.printStackTrace(); //To change body of catch
}
}
iterator = subdirectoryList.iterator();
if (iterator.hasNext()) {
act = (DirectoryInfo) iterator.next();
}
}
StringVector fileNames = new XFile(dir).getFiles(false);
if (filter != null) {
StringVector filteredNames = new StringVector();
for (String fileName = fileNames.firstItem(); fileName != null; fileName = fileNames.nextItem()) {
try {
if (filter == null || filter.match(fileName)) {
filteredNames.append(fileName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
fileNames = filteredNames;
}
files = fileNames.size();
if (files > 0) {
StringBuffer fileInfoStringBuffer = new StringBuffer();
for (String fileName = fileNames.firstItem(); fileName != null; fileName = fileNames.nextItem()) {
try {
XFile xf = new XFile(dir, fileName);
FileInfo fileInfo = generateFileInfo(xf);
fileInfo.name = fileName;
size += fileInfo.size;
fileInfo.getString(fileInfoStringBuffer);
fileList.add(fileInfo);
} catch (Exception e) {
e.printStackTrace(); //To change body of catch
}
}
hash = getHashOf(fileInfoStringBuffer.toString());
}
}
public DirectoryInfo nextDirectory() {
return nextDirectory(null, null);
}
public String getFullName() {
return getFullName(null);
}
public String getFullName(StringBuffer sb) {
if (parent == null) {
return null;
}
if (sb == null) {
sb = new StringBuffer();
}
parent.getFullName(sb);
sb.append(File.separatorChar);
sb.append(name);
return sb.toString();
}
public String getRootDirectory() {
if (parent != null) {
return parent.getRootDirectory();
}
return name;
}
public FileInfo generateFileInfo(XFile xf) throws Exception {
if (!xf.canRead()) {
return null;
}
FileInfo ret = new FileInfo();
InputStream in = xf.getBufferedInputStream();
getHashAndSize(in, ret);
return ret;
}
private long getHashOf(String s) {
try {
FileInfo fi = getHashAndSize(new BufferedInputStream(new ByteArrayInputStream(s.getBytes())), null);
return fi.hash;
} catch (IOException e) {
e.printStackTrace(); //should never happen
}
return 0L;
}
private FileInfo getHashAndSize(InputStream in, FileInfo fileInfo) throws
IOException {
if (fileInfo == null) {
fileInfo = new FileInfo();
}
long hash = 0L;
long i = 1L;
while (true) {
int c = in.read();
if (c < 0) {
break;
}
hash += c ^ i;
++i;
}
in.close();
fileInfo.size = i - 1L;
fileInfo.hash = hash;
return fileInfo;
}
public String toString() {
StringBuffer sb = new StringBuffer(name);
sb.append('\n');
sb.append(files);
sb.append('\n');
sb.append(size);
sb.append('\n');
sb.append(hash);
sb.append('\n');
Iterator iter = getFileList().iterator();
while (iter.hasNext()) {
FileInfo file = (FileInfo) iter.next();
file.getString(sb);
}
return sb.toString();
}
public String getName() {
return name;
}
public DirectoryInfo getParentDir() {
return parent;
}
/**
* @param rootDir
* @param result
* @return true if the path is pointing to an existing file (false for an URL)
*/
public boolean getPath(String rootDir, StringBuffer result) {
if (isRoot()) {
if (rootDir == null) {
result.append(getName());
} else {
result.append(rootDir);
}
return new File(result.toString()).exists();
}
boolean ret = getParentDir().getPath(rootDir, result);
result.append(ret ? File.separatorChar : '/');
result.append(getName());
return ret;
}
public String createDirectory(String targetDir) {
StringBuffer dirName = new StringBuffer(1024);
getPath(targetDir, dirName);
XFile file = new XFile(dirName.toString());
file.mkdirs();
return dirName.toString();
}
public void writeTo(OutputStream out) throws Exception {
new TextToken(name).writeTo(out);
FileInfo.SPACE.writeTo(out);
new TextToken(size).writeTo(out);
FileInfo.SPACE.writeTo(out);
new TextToken(hash).writeTo(out);
FileInfo.NEW_LINE.writeTo(out);
// write files
Iterator iterFiles = getFileList().iterator();
while (iterFiles.hasNext()) {
FileInfo file = (FileInfo) iterFiles.next();
file.writeTo(out);
}
if (subdirectoryList != null) {
// write subdirectories
Iterator dirIter = subdirectoryList.iterator();
while (dirIter.hasNext()) {
String subdirectory = ((DirectoryInfo) dirIter.next()).getName();
getIdentifier().writeTo(out);
FileInfo.SPACE.writeTo(out);
new TextToken(subdirectory).writeTo(out);
FileInfo.NEW_LINE.writeTo(out);
}
}
}
public void readFrom(PushbackInputStream in) throws IOException {
TextToken token = TextToken.nextToken(in);
if (token.isString()) {
if (name == null) {
name = token.getString();
}
} else {
throw new IOException("Unexpected Token (expect name):" + token);
}
token = TextToken.nextToken(in);
if (token.isLong()) {
size = token.getLong();
} else {
throw new IOException("Unexpected Token (expect size):" + token);
}
token = TextToken.nextToken(in);
if (token.isLong()) {
hash = token.getLong();
} else {
throw new IOException("Unexpected Token (expect hash):" + token);
}
subdirectoryList = new ArrayList();
fileList.clear();
while (true) {
token = TextToken.nextToken(in);
if (token == null) {
return;
}
if (token.isIdentifier(FileInfo.FILE_IDENTIFIER_STRING)) {
// read files
FileInfo fileInfo = new FileInfo();
fileInfo.readFrom(in);
fileList.add(fileInfo);
} else if (token.isIdentifier(DIR_IDENTIFIER_STRING)) {
// read subdirectories
token = TextToken.nextToken(in);
if (!token.isString()) {
throw new IOException("Unexpected Token (subdirectory name):" + token);
}
subdirectoryList.add(new DirectoryInfo(token.getString(), this));
} else {
throw new IOException("Unexpected Token (D or F):" + token);
}
}
}
public static void createDirInfoTree(String sourceDir, IStringFilter dirFilter, IStringFilter fileFilter, String targetDir) throws Exception {
new XFile(targetDir).delete();
DirectoryInfo root = new DirectoryInfo(sourceDir);
for (DirectoryInfo dir = root.nextDirectory(dirFilter, fileFilter); dir != null; dir = root.nextDirectory(dirFilter, fileFilter))
{
dir.createDirInfo(targetDir);
dir.clearSubdirectories();
}
root.createDirInfo(targetDir);
}
public void createDirInfo(String dirInfoDirectory) throws Exception {
String dirName = createDirectory(dirInfoDirectory);
XFile dirInfoFile = new XFile(dirName, DIR_INFO_FILENAME);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dirInfoFile));
writeTo(out);
out.close();
}
public void readDirInfo(String dirInfoDirectory) throws IOException {
StringBuffer dirName = new StringBuffer(1024);
boolean isFile = getPath(dirInfoDirectory, dirName);
PushbackInputStream in = getPushbackInputStream(dirName.toString(), DIR_INFO_FILENAME, isFile);
readFrom(in);
in.close();
}
public DirSyncManager sync(DirSyncManager dirSyncManager, DirectoryInfo targetDirectoryInfo, String srcDir, String targetDir) {
if (dirSyncManager == null) {
dirSyncManager = new DefaultDirSyncManager();
}
long volumeBefore = dirSyncManager.getActualVolume();
// sync subdirectories
try {
readDirInfo(null);
} catch (IOException e) {
e.printStackTrace();
dirSyncManager.setError("Cannot read dir info", e);
return dirSyncManager;
}
if (isRoot()) {
dirSyncManager.setTotalVolume(size);
}
if (targetDirectoryInfo != null) {
try {
targetDirectoryInfo.readDirInfo(null);
} catch (IOException e) {
dirSyncManager.setError("Cannot read dir info", e);
return dirSyncManager;
}
if (!isDifferent(targetDirectoryInfo)) {
dirSyncManager.setActualVolume(volumeBefore + size);
return dirSyncManager;
}
}
StringBuffer path1 = new StringBuffer();
boolean isFile = getPath(srcDir, path1);
String sourceDirName = path1.toString();
StringBuffer path2 = new StringBuffer();
getPath(targetDir, path2);
String targetDirName = path2.toString();
dirSyncManager.createDirectory(new XFile(targetDirName));
ArrayList subdirs = getSubdirectoryList();
ArrayList targetSubdirs = null;
Hashtable targetSubdirMap = new Hashtable();
if (targetDirectoryInfo != null) {
targetSubdirs = targetDirectoryInfo.getSubdirectoryList();
for (Iterator itTarget = targetSubdirs.iterator(); itTarget.hasNext();) {
DirectoryInfo directoryInfo = (DirectoryInfo) itTarget.next();
targetSubdirMap.put(directoryInfo.getName(), directoryInfo);
}
}
if (subdirs != null) {
for (Iterator it = subdirs.iterator(); it.hasNext();) {
DirectoryInfo subdirectoryInfo = (DirectoryInfo) it.next();
DirectoryInfo targetSubdirectoryInfo = (DirectoryInfo) targetSubdirMap.get(subdirectoryInfo.getName());
if (targetSubdirectoryInfo == null) {
// create target subdirectory
subdirectoryInfo.sync(dirSyncManager, null, srcDir, targetDir);
} else {
targetSubdirMap.remove(targetSubdirectoryInfo.getName());
// sync
subdirectoryInfo.sync(dirSyncManager, targetSubdirectoryInfo, srcDir, targetDir);
}
}
}
// delete remaining directories on target
for (Iterator itDelete = targetSubdirMap.keySet().iterator(); itDelete.hasNext();) {
DirectoryInfo directoryInfo = (DirectoryInfo) targetSubdirMap.get(itDelete.next());
dirSyncManager.removeDirectory(targetDirName, directoryInfo.getName());
}
// sync files
ArrayList fileList = getFileList();
Hashtable targetFileMap = new Hashtable();
if (targetDirectoryInfo != null) {
ArrayList targetFileList = targetDirectoryInfo.getFileList();
for (Iterator itTarget = targetFileList.iterator(); itTarget.hasNext();) {
FileInfo fileInfo = (FileInfo) itTarget.next();
targetFileMap.put(fileInfo.getName(), fileInfo);
}
}
for (Iterator it = fileList.iterator(); it.hasNext();) {
FileInfo fileInfo = (FileInfo) it.next();
FileInfo targetFileInfo = (FileInfo) targetFileMap.get(fileInfo.getName());
dirSyncManager.addActualVolume(fileInfo.size);
if (targetFileInfo == null) {
// create file
XFile targetFile = new XFile(targetDirName, fileInfo.getName());
try {
dirSyncManager.createFile(getPushbackInputStream(sourceDirName, fileInfo.getName(), isFile), targetFile);
} catch (Exception e) {
dirSyncManager.setError("Cannot create file", e);
return dirSyncManager;
}
} else {
targetFileMap.remove(targetFileInfo.getName());
// sync
if (fileInfo.isDifferent(targetFileInfo)) {
XFile targetFile = new XFile(targetDirName, fileInfo.getName());
try {
dirSyncManager.createFile(getPushbackInputStream(sourceDirName, fileInfo.getName(), isFile), targetFile);
} catch (Exception e) {
dirSyncManager.setError("Cannot create file", e);
return dirSyncManager;
}
}
}
}
if (targetFileMap.size() > 0) {
StringBuffer subdirectory = new StringBuffer();
getPath(null, subdirectory);
// delete remaining files on target
for (Iterator itDelete = targetFileMap.keySet().iterator(); itDelete.hasNext();) {
FileInfo fileInfo = (FileInfo) targetFileMap.get(itDelete.next());
dirSyncManager.removeFile(targetDir, subdirectory.toString(), fileInfo.getName());
}
}
return dirSyncManager;
}
private static PushbackInputStream getPushbackInputStream(String dirName, String fileName, boolean isFile) {
if (isFile) {
File file = new File(dirName, fileName);
if (file.canRead()) {
try {
return new PushbackInputStream(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
// should never happen
}
}
} else {
try {
URL url = new URL(dirName + '/' + fileName);
return new PushbackInputStream(url.openStream());
} catch (MalformedURLException e) {
e.printStackTrace(); //TODO
} catch (IOException e) {
e.printStackTrace(); //TODO
}
}
return new PushbackInputStream(new ByteArrayInputStream(new byte[0]));
}
public static void main(String argv[]) {
String srcDir = "C:\\temp\\foo3";
DirectoryInfo d = new DirectoryInfo(srcDir);
while (true) {
DirectoryInfo dir = d.nextDirectory(null, /*new FilterModel("\"*\"- \"*i*\"", "filterOr")*/null);
if (dir == null) {
break;
}
System.out.println(dir);
}
try {
String trgDir = "C:\\temp\\dirSync_backup_foo3";
String dirInfoSrc = "c:\\temp\\dirInfo_src";
String dirInfoTarget = "c:\\temp\\dirInfo_trg";
DirectoryInfo.createDirInfoTree(srcDir, new StringFilter("*"), new StringFilter("*"), dirInfoSrc);
DirectoryInfo.createDirInfoTree(trgDir, new StringFilter("*"), new StringFilter("*"), dirInfoTarget);
String srcDirURL = new File(srcDir).toURL().toString();
String dirInfoSrcURL = new File(dirInfoSrc).toURL().toString();
DirSyncManager mngr = new DirectoryInfo(dirInfoSrcURL).sync(null, new DirectoryInfo(dirInfoTarget), srcDirURL, trgDir);
System.out.println("mngr = " + mngr);
//DirectoryInfo.createDirInfoTree("c:\\temp", new StringFilter("*"), new StringFilter("*"), "c:\\tmp\\dirInfo");
} catch (Exception e) {
e.printStackTrace(); //TODO
}
}
}