package ch.cyberduck.core;
/*
* Copyright (c) 2005 David Kocher. All rights reserved.
* http://cyberduck.ch/
*
* 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.
*
* Bug fixes, suggestions and comments should be sent to:
* dkocher@cyberduck.ch
*/
//import ch.cyberduck.core.ftp.FTPPath;
import ch.cyberduck.core.io.service.BandwidthThrottleService;
import ch.cyberduck.core.serializer.Serializer;
//import ch.cyberduck.ui.growl.Growl;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.TimeZone;
import ch.cyberduck.core.service.SyncTransferService;
/**
* @version $Id: SyncTransfer.java 5830 2010-03-03 12:02:26Z dkocher $
*/
public class SyncTransfer extends Transfer implements SyncTransferService {
public SyncTransfer(Path root) {
super(root);
}
public <T> SyncTransfer(T dict, Session s) {
super(dict, s);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getAsDictionary()
*/
@Override
public <T> T getAsDictionary() {
final Serializer dict = super.getSerializer();
dict.setStringForKey(String.valueOf(KIND_SYNC), "Kind");
return dict.<T>getSerialized();
}
/**
* The delegate for files to upload
* @uml.property name="_delegateUpload"
* @uml.associationEnd
*/
private Transfer _delegateUpload;
/**
* The delegate for files to download
* @uml.property name="_delegateDownload"
* @uml.associationEnd
*/
private Transfer _delegateDownload;
@Override
protected void init() {
log.debug("init");
_delegateUpload = new UploadTransfer(this.getRoots());
_delegateDownload = new DownloadTransfer(this.getRoots());
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#setBandwidth(float)
*/
@Override
public void setBandwidth(float bytesPerSecond) {
;
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getBandwidth()
*/
@Override
public float getBandwidth() {
return BandwidthThrottleService.UNLIMITED;
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getName()
*/
@Override
public String getName() {
return this.getRoot().getName() + " \u2194 " /*left-right arrow*/ + this.getRoot().getLocal().getName();
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getSize()
*/
@Override
public double getSize() {
final double size = _delegateDownload.getSize() + _delegateUpload.getSize();
if(0 == size) {
return super.getSize();
}
return size;
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#isResumable()
*/
@Override
public boolean isResumable() {
return _delegateDownload.isResumable() && _delegateUpload.isResumable();
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getTransferred()
*/
@Override
public double getTransferred() {
final double transferred = _delegateDownload.getTransferred() + _delegateUpload.getTransferred();
if(0 == transferred) {
return super.getTransferred();
}
return transferred;
}
/**
* @uml.property name="action"
* @uml.associationEnd
*/
private TransferAction action = TransferAction.forName(
Preferences.instance().getProperty("queue.sync.action.default")
);
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#setTransferAction(ch.cyberduck.core.TransferAction)
*/
public void setTransferAction(TransferAction action) {
this.action = action;
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#getAction()
*/
/**
* @return
* @uml.property name="action"
*/
public TransferAction getAction() {
return this.action;
}
/**
* @uml.property name="aCTION_OVERWRITE"
* @uml.associationEnd
*/
private TransferFilter ACTION_OVERWRITE = new TransferFilter() {
/**
* Download delegate filter
*/
private TransferFilter _delegateFilterDownload
= _delegateDownload.filter(TransferAction.ACTION_OVERWRITE);
/**
* Upload delegate filter
*/
private TransferFilter _delegateFilterUpload
= _delegateUpload.filter(TransferAction.ACTION_OVERWRITE);
@Override
public void prepare(Path p) {
final Comparison compare = SyncTransfer.this.compare(p);
// if(compare.equals(COMPARISON_REMOTE_NEWER)) {
// _delegateFilterDownload.prepare(p);
// }
// else if(compare.equals(COMPARISON_LOCAL_NEWER)) {
// _delegateFilterUpload.prepare(p);
// }
}
public boolean accept(Path p) {
final Comparison compare = SyncTransfer.this.compare(p);
// if(!COMPARISON_EQUAL.equals(compare)) {
// if(compare.equals(COMPARISON_REMOTE_NEWER)) {
// // Ask the download delegate for inclusion
// return _delegateFilterDownload.accept(p);
// }
// else if(compare.equals(COMPARISON_LOCAL_NEWER)) {
// // Ask the upload delegate for inclusion
// return _delegateFilterUpload.accept(p);
// }
// }
return false;
}
};
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#filter(ch.cyberduck.core.TransferAction)
*/
@Override
public TransferFilter filter(final TransferAction action) {
log.debug("filter:" + action);
if(action.equals(TransferAction.ACTION_OVERWRITE)) {
// When synchronizing, either cancel or overwrite. Resume is not supported
return ACTION_OVERWRITE;
}
if(action.equals(TransferAction.ACTION_CALLBACK)) {
TransferAction result = prompt.prompt();
return this.filter(result); //break out of loop
}
return super.filter(action);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#childs(ch.cyberduck.core.Path)
*/
@Override
public AttributedList<Path> childs(final Path parent) {
final Set<Path> childs = new HashSet<Path>();
childs.addAll(_delegateDownload.childs(parent));
childs.addAll(_delegateUpload.childs(parent));
for(Path child: childs) {
boolean skipped = false;
final Comparison comparison = this.compare(child);
// Updating default skip settings for actual transfer
// if(COMPARISON_EQUAL.equals(comparison)) {
// skipped = child.attributes.isFile();
// }
// else if(child.attributes.isFile()) {
// if(comparison.equals(COMPARISON_REMOTE_NEWER)) {
// skipped = this.getAction().equals(ACTION_UPLOAD);
// }
// else if(comparison.equals(COMPARISON_LOCAL_NEWER)) {
// skipped = this.getAction().equals(ACTION_DOWNLOAD);
// }
// }
child.getStatus().setSkipped(skipped);
}
return new AttributedList<Path>(childs);
}
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#action(boolean, boolean)
*/
@Override
public TransferAction action(final boolean resumeRequested, final boolean reloadRequested) {
log.debug("action:" + resumeRequested + "," + reloadRequested);
// Always prompt for synchronization
return TransferAction.ACTION_CALLBACK;
}
/**
* @param p
* @see #compare(Path)
*/
@Override
protected void _transferImpl(final Path p) {
final Comparison compare = this.compare(p);
// if(compare.equals(COMPARISON_REMOTE_NEWER)) {
// _delegateDownload._transferImpl(p);
// }
// else if(compare.equals(COMPARISON_LOCAL_NEWER)) {
// _delegateUpload._transferImpl(p);
// }
}
@Override
protected void fireTransferDidEnd() {
if(this.isReset() && this.isComplete() && !this.isCanceled()) {
// Growl.instance().notify("Synchronization complete", getName());
}
super.fireTransferDidEnd();
}
@Override
protected void clear(final TransferOptions options) {
_delegateDownload.clear(options);
_delegateUpload.clear(options);
super.clear(options);
}
@Override
protected void reset() {
_delegateDownload.reset();
_delegateUpload.reset();
super.reset();
}
/**
*
*/
public static class Comparison {
@Override
public boolean equals(Object other) {
return super.equals(other);
}
}
/**
* Files differ in size
* @uml.property name="cOMPARISON_UNEQUAL"
* @uml.associationEnd
*/
private static final Comparison COMPARISON_UNEQUAL = new Comparison() {
@Override
public String toString() {
return "COMPARISON_UNEQUAL";
}
};
/* (non-Javadoc)
* @see ch.cyberduck.core.SyncTransferService#compare(ch.cyberduck.core.Path)
*/
public Comparison compare(Path p) {
log.debug("compare:" + p);
Comparison result = null; //COMPARISON_EQUAL;
if(p.getLocal().exists() && p.exists()) {
if(p.attributes.isFile()) {
// result = this.compareTimestamp(p);
}
}
else if(p.exists()) {
// only the remote file exists
// result = COMPARISON_REMOTE_NEWER;
}
else if(p.getLocal().exists()) {
// only the local file exists
// result = COMPARISON_LOCAL_NEWER;
}
return result;
}
/**
* @param p
* @return
*/
private Comparison compareSize(Path p) {
log.debug("compareSize:" + p);
if(p.attributes.getSize() == -1) {
p.readSize();
}
//fist make sure both files are larger than 0 bytes
if(p.attributes.getSize() == 0 && p.getLocal().attributes.getSize() == 0) {
// return COMPARISON_EQUAL;
}
if(p.attributes.getSize() == 0) {
// return COMPARISON_LOCAL_NEWER;
}
if(p.getLocal().attributes.getSize() == 0) {
// return COMPARISON_REMOTE_NEWER;
}
if(p.attributes.getSize() == p.getLocal().attributes.getSize()) {
// return COMPARISON_EQUAL;
}
//different file size - further comparison check
return COMPARISON_UNEQUAL;
}
/**
* @param p
* @return
*/
private Comparison compareTimestamp(Path p) {
log.debug("compareTimestamp:" + p);
if(p.attributes.getModificationDate() == -1) {//|| p instanceof FTPPath) {
// Make sure we have a UTC timestamp
p.readTimestamp();
}
final Calendar remote = this.asCalendar(p.attributes.getModificationDate(), Calendar.SECOND);
final Calendar local = this.asCalendar(p.getLocal().attributes.getModificationDate(), Calendar.SECOND);
if(local.before(remote)) {
// return COMPARISON_REMOTE_NEWER;
}
if(local.after(remote)) {
// return COMPARISON_LOCAL_NEWER;
}
//same timestamp
// return COMPARISON_EQUAL;
return null;
}
/**
* @param timestamp
* @param precision
* @return
*/
private Calendar asCalendar(final long timestamp, final int precision) {
log.debug("asCalendar:" + timestamp);
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
c.setTimeInMillis(timestamp);
if(precision == Calendar.MILLISECOND) {
return c;
}
c.clear(Calendar.MILLISECOND);
if(precision == Calendar.SECOND) {
return c;
}
c.clear(Calendar.SECOND);
if(precision == Calendar.MINUTE) {
return c;
}
c.clear(Calendar.MINUTE);
if(precision == Calendar.HOUR) {
return c;
}
c.clear(Calendar.HOUR);
return c;
}
}